Changelog
1.0.0 — 2026-08-05
Capix is stable. The API stability policy's top tier is now in force: the public API and wire contracts (the { data } envelope, the error shape, route inference, GraphQL/MCP naming) change only in a major version, with deprecations living for at least one minor release before removal. latest now points here — install without a dist-tag:
npm install @capixjs/core @capixjs/transport-rest zod@^4Added
capability.guard(...)— guard-first builder. Declare guards before the resolver and itsctxis inferred fully narrowed, with no annotation and nocapability.withContext<T>()factory:capability.guard(mustBeUser)(schema, (_, ctx) => ctx.user.id, 'query'). Fixes the ordering limitation wherecapability(resolver).guard(g)type-checks the resolver before the guard is visible, and closes the two-factory pattern's footgun — narrowing here is earned by actually calling.guard(), not granted by a pre-typed factory alone that still compiles if you forget the guard. Purely additive:capability(), postfix.guard(), andcapability.withContext()are unchanged. See TypeScript workarounds.capix newscaffolds and the AI-context docs now demonstrate it directly, including a guarded example capability- Two new migration guides: From Fastify and From tRPC, alongside the existing From Express guide
- Expanded test coverage: end-to-end integration tests for the GraphQL and Queue transports (previously unit-tested only), and unit tests for the remaining untested CLI commands (
generate,show,list,docs,check,diff,call,ai-context)
Fixed
@capixjs/plugin-helmet:mergeHooks()now carries thecorsfield through. The documented pattern for combiningcors()+helmet()—restTransport({ ...mergeHooks(corsOpts, helmetOpts) })— silently dropped the CORS origin restriction:mergeHooks()only merged each argument'shooks, nevercors, so the transport fell back to its defaultorigin: '*'while Helmet's headers still applied. Anyone following the documented pattern had no real origin restriction in production regardless of what they configuredcapix check's route-conflict detection was a no-op.generateRoutes()only infers routes — it never throws on duplicates; conflict detection lives incompileRouter(), whichchecknever called. Two capabilities inferring to the same method and path silently reported "no conflicts"capix check's scaffold-placeholder detection could never match anything. It readcap.resolve.toString()— the framework's guard-running wrapper, whose source is constant regardless of what you wrote — instead of the actual resolver source- Docs and four package READMEs (
@capixjs/core,@capixjs/plugin-cors,@capixjs/plugin-helmet,@capixjs/plugin-logging) showed acorsPlugin/helmetPlugin/loggingPluginAPI viacreateServer({ plugins: [...] })that was never shipped. The real API:cors()/helmet()spread intorestTransport(),loggingEnhancer()via.enhance() - CI test flakiness: every test file that boots a real server picked its port by probing port 0 and closing the probe before the real server bound to it — under CI-level parallelism, another file's probe could claim that exact ephemeral port in the gap. All 11 affected files now retry with a fresh port on
EADDRINUSE
Security
fast-uri(pulled in via@capixjs/transport-rest→fast-json-stringify→ajv) andip-address/@hono/node-server(via@capixjs/transport-mcp→@modelcontextprotocol/sdk) updated past known advisories — high-severity host-confusion and SSRF/trust-boundary issues, and a moderate Windows-only path-traversal issue respectively. All transitive; no Capix code changed- CI now runs
pnpm auditon every push — blocking on high+ severity findings in production dependencies (what actually reaches consumers of@capixjs/*packages), informational for everything else vitestand@vitest/coverage-v8updated from 1.6.x to 3.2.7 across every package, fixing a critical advisory (arbitrary file read/execute whenvitest --uiis running). Dev-only; no published package depends on vitest at runtime
This closes the gate 0.1.0-beta.3 named: soak time and field feedback, plus everything above found along the way.
0.1.0-beta.3 — 2026-07-03
Added
- Amazon SQS queue adapter:
SqsQueueAdapterin@capixjs/transport-queue. Pass an@aws-sdk/client-sqsaggregated client and a queue-name → URL map; nothing is bundled. Long-polling receive loop with concurrent batch processing; success deletes the message; failed capability results stay queued so the visibility timeout and your redrive policy / DLQ drive retries; unparseable bodies are deleted (poison-message protection); FIFO queues getMessageGroupId/MessageDeduplicationIdautomatically;stop()drains in-flight handlers. 13 tests against a fake SQS with real visibility semantics, including an end-to-end run throughqueueTransportand the execution engine - The queue docs now show the shipped
BullMQAdapterinstead of a hand-rolled example that predated it
This completes the named pre-1.0 feature candidates — what remains before 1.0 is soak time and field feedback.
0.1.0-beta.2 — 2026-07-03
Added
- Cross-instance event bus:
createRedisEventBusin@capixjs/store-redis. The in-memory bus delivers only within one process — behind a load balancer, an event published on instance A never reached WebSocket clients on instance B. The Redis bus is a drop-inEventBus(samewsTransport({ eventBus })wiring, samepublish/subscribe/filters) that routes events through Redis pub/sub; every instance, including the publisher's own, receives through the same broker path. Verified end to end: two full servers, a REST mutation on one delivering to a WebSocket subscriber on the other
0.1.0-beta.1 — 2026-07-03
Capix is in beta. From this release on, the API stability policy is in force: the documented public API and the wire contracts (the { data } envelope, the error shape, route inference, GraphQL/MCP naming) only change with an explicit Breaking changelog section and a migration note. Install with the beta tag (also tracked by latest):
npm install @capixjs/core@beta @capixjs/transport-rest@beta zod@^4What the alpha hardening delivered, in one place: an audited and frozen API surface, graceful shutdown on every transport, WebSocket payload/heartbeat/subscription hardening, tests for every package with a Node 20/22/24 + Windows CI matrix and a coverage gate, pluggable cache/rate-limit stores with a Redis adapter, JWKS/RS256 auth with pinned algorithms, lifecycle hooks for tracing and error reporting, npm provenance attestations, and a clean multi-million-request soak.
Changed
capix newscaffolds and the docs now install from thebetadist-tag
0.1.0-alpha.24 — 2026-07-03
Added
- npm provenance. Packages are now published with provenance attestations linking each tarball to the exact commit and workflow run that built it — verify with
npm audit signatures SECURITY.md— private vulnerability reporting channel, supported versions, and scope notes- Streaming: documented as an explicit 1.0 non-goal in the API stability page, with the reasoning (one capability contract across five transports) and the supported alternatives: event bus for incremental client updates, the REST
onRequesthook or a CDN for file downloads, and queue + status polling for long-running work
Verified
- Soak test: ~2 million mixed requests (success, validation failures, guard rejections, 404s, create/delete cycles) at ~7 000 req/s over sustained load — zero errors, memory flat at steady state with no growth between consecutive load bursts
This completes the 1.0-readiness gate: pluggable distributed stores (alpha.21), JWKS/RS256 auth (alpha.22), lifecycle hooks and the observability guide (alpha.23), and this release's engineering items.
0.1.0-alpha.23 — 2026-07-03
Added
- Lifecycle hooks.
createServer({ hooks })observes every capability invocation on every transport:onRequest,onResponse(withdurationMsand data), andonError(fires for unknown capabilities, guard rejections, validation failures, and resolver throws alike). The same request object flows through a call's hooks, so aWeakMapkeys tracing spans — hook errors are isolated and never affect the request. Also available oncreateExecutionEnginedirectly - New guide: Observability — lifecycle hooks, an OpenTelemetry span recipe, Sentry error reporting, and per-capability metrics
Fixed
- Publishing was blocked since alpha.19: the packages that gained tests in that release shipped compiled test files in their tarballs, failing the audit's pack-integrity gate. alpha.19–alpha.21 never reached npm; their changes ship in alpha.22+
0.1.0-alpha.22 — 2026-07-03
Added
- RS256 and JWKS verification in
@capixjs/plugin-auth. All auth entry points now accept exactly one of:secret(HS family, as before),publicKey/privateKeyPEM pair (RS/ES/PS families), orjwks: { url }— verify against an issuer's JWKS endpoint (Auth0, Clerk, Cognito, Keycloak) with kid-based key resolution, a cached key set, rate-limited refetch on rotation, and stale-serving when the endpoint is down. No new dependencies — JWK conversion uses Node's native crypto
Security
- Verification algorithms are now always pinned.
jwt.verifywas called without analgorithmslist; each mode now pins its family (HS forsecret, RS/ES/PS for keys and JWKS), rejecting algorithm-confusion tokens such as an HS256 token signed with the RS256 public key as its secret
0.1.0-alpha.21 — 2026-07-03
Added
- Pluggable stores for
withCacheandwithRateLimit. Both enhancers accept astoreoption; the interfaces (CacheStore,RateLimitStore) live in core and the in-memory defaults are unchanged (and now exported ascreateMemoryCacheStore/createMemoryRateLimitStore). This is the multi-instance fix: the defaults are per-process, so behind a load balancer each instance cached independently and N instances enforced N× the intended rate limit - New package:
@capixjs/store-redis.redisCacheStore(client)(JSON values, Redis-native expiry) andredisRateLimitStore(client)(atomic fixed-window Lua counter — one round-trip per request, no race past the limit across instances). Works with any ioredis-compatible client; nothing is bundled
0.1.0-alpha.20 — 2026-07-03
Fixed
capix --versionreported a hardcoded0.1.0regardless of the installed version. It now reads the CLI's own package.json at runtime
Added
- Coverage gate in CI. Core's existing coverage thresholds (85% statements / 80% branches / 90% functions / 85% lines in
vitest.config.ts) were never enforced — the audit workflow now runs the coverage suite and fails the publish if they regress
This release completes the beta gate: API surface frozen (alpha.16), graceful shutdown (alpha.17), WebSocket hardening (alpha.18), full test coverage of every package plus a Node 20/22/24 CI matrix (alpha.19), and these final correctness items.
0.1.0-alpha.19 — 2026-07-03
Added
- Every package now has tests.
plugin-cors,plugin-helmet,plugin-logging, and@capixjs/testingshipped with zero tests (two of them hid behind--passWithNoTests); they now have 27 tests covering origin matching and Vary handling, security header defaults/overrides andmergeHooks, the logging enhancer's success/error paths and input/output redaction defaults, and the fulltestServersurface - CI now tests the Node versions we claim. The audit workflow gained a matrix: Node 20 and 24 on Ubuntu (blocking —
enginespromises>=20but only 22 was ever tested) and Node 22 on Windows (non-blocking until it has a track record)
Fixed
- The pre-publish audit's typecheck, version-consistency, peer-dependency, LICENSE/README, and pack-integrity checks never included
@capixjs/transport-mcp— it is now covered by all of them
0.1.0-alpha.18 — 2026-07-03
Added
- WebSocket hardening. Three new
wsTransportoptions:maxPayloadBytes(default 1 MiB, was thewslibrary's 100 MiB) — oversized frames close the connection with1009heartbeatIntervalMs(default 30 s) — the server pings each client every interval and terminates clients that missed the previous ping, so dead connections stop holding subscriptions foreverauthorizeSubscribe(event, headers)— reject event subscriptions with aForbiddenreply; headers come from the HTTP upgrade request
Fixed
- Docs: WebSocket auth section described per-message headers, which the transport never supported. It now documents the actual behavior: context is built from the upgrade-request headers on every message
0.1.0-alpha.17 — 2026-07-03
Added
- Graceful shutdown on every transport.
server.stop()now drains instead of dropping: the HTTP transports (REST, GraphQL, MCP) stop accepting connections, drop idle keep-alive sockets immediately, give in-flight requests a drain window, then force-close stragglers — before this, a single keep-alive connection madestop()hang forever. The WebSocket transport sends clients a clean1001close frame and terminates sockets that never finish the handshake. New per-transport option:shutdownTimeoutMs(default10_000) - New core export:
closeHttpServerGracefully(server, drainMs)— the shared drain sequence, for custom HTTP transports
0.1.0-alpha.16 — 2026-07-03
Changed
- API surface audit — first beta-gate release. New API stability policy defines three tiers (public, extension-author, internal) and the semver commitment per release stage. Internal capability fields (
_capix, phantom type fields,_intentExplicit,_skipValidation) are now marked@internalin the type definitions - New core export:
resolveIntent(cap, key). The shared effective-intent rule (explicit intent wins, otherwise inferred from the key name). REST routing, GraphQL placement, MCP annotations, and the CLI all use it now — transport authors should too, instead of readingintentdirectly - GraphQL: key-name intent inference now applies. Capabilities without an explicit intent whose name infers
query(getUser,listPosts) are now Query fields instead of Mutation fields, matching how REST routes them as GET. Explicit intents behave as before - CLI:
check,show,docs,diff, andai-contextreport effective intent.capix checkno longer warns "mutation capability has no input schema" for capabilities that route as queries via name inference
0.1.0-alpha.15 — 2026-07-02
Added
- New package:
@capixjs/transport-mcp. Exposes every capability as a Model Context Protocol tool so AI clients (Claude Code, editors, agents) can call your server directly. Dot-path names become tool names (users.getUser→users_getUser), Zod input schemas become toolinputSchema, object output schemas becomeoutputSchema+structuredContent, and intent maps to tool annotations (query→readOnlyHint,delete→destructiveHint, matching REST route inference). Guards, validation, and typed errors run through the same execution engine as every other transport. Two modes: stdio (local MCP clients spawn the process) and stateless Streamable HTTP (portoption, request headers reach the context builder for auth guards) - New CLI command:
capix mcp. Serves your capabilities file as an MCP stdio server (claude mcp add my-api -- npx capix mcp), or over Streamable HTTP with--port
0.1.0-alpha.14 — 2026-07-02
Breaking
- Zod 4. All packages now require
zod@^4(previously^3.23). Your capability schemas keep working unchanged — the public Zod API used in Capix apps (z.object,z.string,.optional(),.default(), guards, enhancers) is the same. What changed under the hood:- Schema introspection (REST coercion, OpenAPI generation, GraphQL schema building,
capix show/docs/client) now reads Zod 4's internals @capixjs/transport-restuses Zod 4's nativez.toJSONSchemafor response serializers and OpenAPI output — thezod-to-json-schemadependency is gone- If you use
z.record, Zod 4 requires an explicit key schema:z.record(z.unknown())→z.record(z.string(), z.unknown()) - Validation error messages follow Zod 4's format (e.g.
Invalid input: expected string, received number) capix newscaffolds new projects withzod@^4
- Schema introspection (REST coercion, OpenAPI generation, GraphQL schema building,
Fixed
- npm
latesttag now tracks the newest release.npm install @capixjs/*previously resolved to0.1.0-alpha.1— the first-ever publish claimedlatestand prerelease publishes never moved it. The publish workflow now retagslateston every release
0.1.0-alpha.13 — 2026-07-02
Added
- OpenAPI 3.1 generation. New
generateOpenAPI(registry, options)export in@capixjs/transport-restbuilds an OpenAPI 3.1 document from a compiled registry using the same route inference as the running server: path parameters from:idsegments, query parameters for GET/DELETE, JSON request bodies for POST/PATCH/PUT (with required lists derived from the Zod schema), the{ data }response envelope, per-operation400responses for schema-validated capabilities, and a sharedErrorResponsecomponent. Supportstitle,version,description,servers,urlCase, and routeoverrides - New CLI command:
capix openapi. Generates the spec from your capabilities file and prints it to stdout or writes it with--output. Flags:--config,--title,--api-version,--description,--server,--url-case
0.1.0-alpha.12 — 2026-06-12
Changed
- REST transport: query/multipart coercion is now schema-aware. Previously every query-string and multipart value was blindly coerced —
?name=123became the number123and failedz.string()validation, and?code=01234was silently corrupted to1234. Values are now coerced to number/boolean only when the capability's Zod input schema types that field as number/boolean (throughoptional/default/nullable/refinement wrappers); everything else stays a raw string. Capabilities without an object schema (z.record, schemaless) receive raw strings - REST transport: path params are now coerced too.
GET /things/42withz.object({ id: z.number() })now validates (path params were never coerced before, so numeric ids always failed) - JSON body values are never coerced — JSON expresses numbers and booleans itself, so a string where a number belongs remains a type error
0.1.0-alpha.11 — 2026-06-12
Fixed
- Event bus: a throwing subscriber no longer breaks the publisher. A sync throw in one subscriber (or its filter) used to propagate into the resolver that called
publish()— turning a successful mutation into a 500 after the write committed — and skipped delivery to the remaining subscribers. Subscriber errors are now caught, logged, and isolated - Queue transport: in-memory adapter no longer drops failures silently. Handler throws are now logged by default
Added
MemoryQueueAdapternow accepts{ onResult, onError }hooks.onResultfires for every processed message — includingok: falseresults (validation failures, guard rejections, resolver errors), which were previously invisible.MemoryQueueAdapterOptionsis exported
0.1.0-alpha.10 — 2026-06-12
Fixed
- GraphQL transport: typed errors are no longer flattened into message strings. Capability errors used to surface as
Error('NotFound: Item not found'), losing the status code and meta. Errors are now thrown asGraphQLErrorwithextensions: { code, status, meta }so clients can branch onextensions.codeinstead of parsing messages. The error message is now the human-readable message alone (noCode:prefix)
0.1.0-alpha.9 — 2026-06-12
Fixed
- Queue transport: BullMQ adapter no longer opens a Redis connection per job.
enqueueused to create a newQueueand close it for every single message. Queue instances are now cached per queue name and reused for the adapter's lifetime;stop()closes them. Concurrent first enqueues share a single instance
0.1.0-alpha.8 — 2026-06-12
Fixed
withCacheno longer grows without bound. The cache is now a true LRU with amaxSizecap (default 1,000 entries); expired entries are removed on access instead of occupying capacity foreverwithRateLimitno longer leaks tracked keys. With a per-user or per-IPkeyFn, every key ever seen stayed in memory permanently. Stale keys are now swept and amaxKeyshard cap (default 10,000) bounds the store
Added
withCache(ttl, { keyFn })— derive the cache key from input and context. The default key ignores context, which serves one user's cached response to every other user when the output depends onctx. UsekeyFnfor any context-dependent capabilitywithCache(ttl, { maxSize })andwithRateLimit({ maxKeys })optionsCacheOptionsexported from@capixjs/core
0.1.0-alpha.7 — 2026-06-12
Fixed
- REST transport: per-request timeout no longer retains memory after the response. Every completed request used to leave its
AbortSignal.timeouttimer and an abort-listener closure alive for the full timeout window (default 30s) — at high request rates that meant hundreds of thousands of dead closures held at steady state. The timer is now cleared the moment the invocation settles. Behavior is unchanged: hung capabilities still get a504and the request signal still aborts at the deadline
0.1.0-alpha.6 — 2026-06-12
Fixed
- REST transport: malformed percent-encoding crashed the process. A request like
GET /users/%zzthrew an uncaughtURIErrorfrom the synchronous request path and killed the server. Undecodable path params now return400 Bad Request; undecodable query-string text falls back to its raw form (WHATWGURLSearchParamsbehavior) - REST transport: synchronous errors in the request handler are now caught and answered with
500instead of escaping as anuncaughtException
Security
- REST transport:
__proto__keys are stripped from query strings, JSON bodies, and multipart fields before merging into capability input - REST transport: JSON bodies that are not objects (arrays, primitives) are rejected with
400instead of being merged as index-keyed garbage
0.1.0-alpha.5 — 2026-06-01
Added
ROADMAP.md— documents pre-1.0 gaps and post-1.0 plansCONTRIBUTING.md— contributing guidelines for bug reports and local setup- Scaffold now generates
.cursor/ruleswith idiomatic Capix patterns for AI-assisted development
Changed
- Scaffold
capabilities.tsnow definesAppUser,AuthContext, andauthCapout of the box — the two-factory guard pattern is visible from the start instead of requiring discovery through a confusing TypeScript error
Removed
- README no longer promises a uWebSockets.js transport — moved to
ROADMAP.mdunder "After 1.0"
0.1.0-alpha.4 — 2026-06-01
Fixed
capix showwas displayingZodStringinstead ofstringfor field types — now uses the same schema prettifier ascapix docs- Scaffold template now includes
pnpm.onlyBuiltDependenciesto prevent esbuild postinstall errors under pnpm 9 - Scaffold template now generates
.npmrcwithminimum-release-age=0to prevent pnpm 9 blocking installs within 24 hours of a new release - Scaffold template now shows the
authCaptwo-factory pattern so developers see the correct guard narrowing approach from the start
0.1.0-alpha.3 — 2026-06-01
Fixed
- Scaffold template generated
"@capixjs/core": "^0.1.0"which excluded prerelease versions — changed to"@capixjs/core": "alpha"(dist-tag) so installs always resolve correctly during the alpha period
0.1.0-alpha.2 — 2026-06-01
Fixed
- Publishing CI was leaving
workspace:*protocol in published packages instead of substituting real version numbers — fixed by switching fromnpm publishtopnpm publish --no-git-checks
0.1.0-alpha.1 — 2026-05-30
Initial public alpha.
Core (capix)
capability()— typed pure function primitive with input/output schemascapability.withContext<TContext>()— scoped factory for typed contextdefineContext(fn)— request context builderdefineGuard(fn)/defineGuardFor<T>()— guards with optional type narrowingdefineInputGuard(fn)— guards that run after input validationdefineError(status, message, code?)— typed error factoriesdefineEnhancer(fn)— enhancer definition helperdefinePlugin(plugin)— plugin bundlingcreateServer(config)— server factory with per-transport capability registriescreateEventBus<TEvents>()— typed pub/sub for server push- Built-in enhancers:
withCache,withRateLimit,withCircuitBreaker,withTimeout,withRetry,withRollback,withMetrics,withLogging defaultErrors— pre-built error factories for common HTTP status codes
Transports
@capixjs/transport-rest— Node.jshttpserver with automatic URL inference from capability names, path parameter extraction, query string coercion, multipart/file upload, route overrides@capixjs/transport-ws— WebSocket server with request/response capability invocation and EventBus-powered server push@capixjs/transport-graphql— GraphQL schema auto-generated from Zod schemas, GraphiQL playground,ZodDefault/ZodEffectsunwrapping@capixjs/transport-queue— background job worker via pluggable adapters (MemoryQueueAdapterincluded; BullMQ, SQS, etc. via custom adapters)
Plugins
@capixjs/plugin-auth—jwtContextBuilder,createJWTHelpers,authPlugin,mustBeAuthenticatedguard, JWT cache@capixjs/plugin-cors— CORS headers for REST@capixjs/plugin-helmet— security headers for REST@capixjs/plugin-logging— structured request logging via pino
CLI (@capixjs/cli)
12 commands: new, generate capability, generate group, dev, list, show, call, check, docs, client, diff, ai-context
Testing (@capixjs/testing)
mockContext, testServer — run the full execution engine without an HTTP server
Performance (v4, Node.js v25, Linux)
| Scenario | req/s |
|---|---|
| Hello World | 28,488 |
| Zod Validation | 26,097 |
| Auth + Guard | 27,102 |
Beats Express by 65–71% and Hono by 19–27%. Within 3% of Fastify.