Skip to content

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:

bash
npm install @capixjs/core @capixjs/transport-rest zod@^4

Added

  • capability.guard(...) — guard-first builder. Declare guards before the resolver and its ctx is inferred fully narrowed, with no annotation and no capability.withContext<T>() factory: capability.guard(mustBeUser)(schema, (_, ctx) => ctx.user.id, 'query'). Fixes the ordering limitation where capability(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(), and capability.withContext() are unchanged. See TypeScript workarounds. capix new scaffolds 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 the cors field through. The documented pattern for combining cors() + helmet()restTransport({ ...mergeHooks(corsOpts, helmetOpts) }) — silently dropped the CORS origin restriction: mergeHooks() only merged each argument's hooks, never cors, so the transport fell back to its default origin: '*' while Helmet's headers still applied. Anyone following the documented pattern had no real origin restriction in production regardless of what they configured
  • capix check's route-conflict detection was a no-op.generateRoutes() only infers routes — it never throws on duplicates; conflict detection lives in compileRouter(), which check never 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 read cap.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 a corsPlugin/helmetPlugin/loggingPlugin API via createServer({ plugins: [...] }) that was never shipped. The real API: cors()/helmet() spread into restTransport(), 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-restfast-json-stringifyajv) and ip-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 audit on every push — blocking on high+ severity findings in production dependencies (what actually reaches consumers of @capixjs/* packages), informational for everything else
  • vitest and @vitest/coverage-v8 updated from 1.6.x to 3.2.7 across every package, fixing a critical advisory (arbitrary file read/execute when vitest --ui is 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: SqsQueueAdapter in @capixjs/transport-queue. Pass an @aws-sdk/client-sqs aggregated 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 get MessageGroupId/MessageDeduplicationId automatically; stop() drains in-flight handlers. 13 tests against a fake SQS with real visibility semantics, including an end-to-end run through queueTransport and the execution engine
  • The queue docs now show the shipped BullMQAdapter instead 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: createRedisEventBus in @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-in EventBus (same wsTransport({ eventBus }) wiring, same publish/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):

bash
npm install @capixjs/core@beta @capixjs/transport-rest@beta zod@^4

What 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 new scaffolds and the docs now install from the beta dist-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 onRequest hook 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 (with durationMs and data), and onError (fires for unknown capabilities, guard rejections, validation failures, and resolver throws alike). The same request object flows through a call's hooks, so a WeakMap keys tracing spans — hook errors are isolated and never affect the request. Also available on createExecutionEngine directly
  • 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/privateKey PEM pair (RS/ES/PS families), or jwks: { 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.verify was called without an algorithms list; each mode now pins its family (HS for secret, 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 withCache and withRateLimit. Both enhancers accept a store option; the interfaces (CacheStore, RateLimitStore) live in core and the in-memory defaults are unchanged (and now exported as createMemoryCacheStore / 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) and redisRateLimitStore(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 --version reported a hardcoded 0.1.0 regardless 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/testing shipped with zero tests (two of them hid behind --passWithNoTests); they now have 27 tests covering origin matching and Vary handling, security header defaults/overrides and mergeHooks, the logging enhancer's success/error paths and input/output redaction defaults, and the full testServer surface
  • CI now tests the Node versions we claim. The audit workflow gained a matrix: Node 20 and 24 on Ubuntu (blocking — engines promises >=20 but 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 wsTransport options:
    • maxPayloadBytes (default 1 MiB, was the ws library's 100 MiB) — oversized frames close the connection with 1009
    • heartbeatIntervalMs (default 30 s) — the server pings each client every interval and terminates clients that missed the previous ping, so dead connections stop holding subscriptions forever
    • authorizeSubscribe(event, headers) — reject event subscriptions with a Forbidden reply; 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 made stop() hang forever. The WebSocket transport sends clients a clean 1001 close frame and terminates sockets that never finish the handshake. New per-transport option: shutdownTimeoutMs (default 10_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 @internal in 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 reading intent directly
  • 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, and ai-context report effective intent. capix check no 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.getUserusers_getUser), Zod input schemas become tool inputSchema, object output schemas become outputSchema + structuredContent, and intent maps to tool annotations (queryreadOnlyHint, deletedestructiveHint, 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 (port option, 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-rest uses Zod 4's native z.toJSONSchema for response serializers and OpenAPI output — the zod-to-json-schema dependency 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 new scaffolds new projects with zod@^4

Fixed

  • npm latest tag now tracks the newest release. npm install @capixjs/* previously resolved to 0.1.0-alpha.1 — the first-ever publish claimed latest and prerelease publishes never moved it. The publish workflow now retags latest on every release

0.1.0-alpha.13 — 2026-07-02

Added

  • OpenAPI 3.1 generation. New generateOpenAPI(registry, options) export in @capixjs/transport-rest builds an OpenAPI 3.1 document from a compiled registry using the same route inference as the running server: path parameters from :id segments, 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-operation 400 responses for schema-validated capabilities, and a shared ErrorResponse component. Supports title, version, description, servers, urlCase, and route overrides
  • 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=123 became the number 123 and failed z.string() validation, and ?code=01234 was silently corrupted to 1234. Values are now coerced to number/boolean only when the capability's Zod input schema types that field as number/boolean (through optional/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/42 with z.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

  • MemoryQueueAdapter now accepts { onResult, onError } hooks. onResult fires for every processed message — including ok: false results (validation failures, guard rejections, resolver errors), which were previously invisible. MemoryQueueAdapterOptions is 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 as GraphQLError with extensions: { code, status, meta } so clients can branch on extensions.code instead of parsing messages. The error message is now the human-readable message alone (no Code: prefix)

0.1.0-alpha.9 — 2026-06-12

Fixed

  • Queue transport: BullMQ adapter no longer opens a Redis connection per job. enqueue used to create a new Queue and 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

  • withCache no longer grows without bound. The cache is now a true LRU with a maxSize cap (default 1,000 entries); expired entries are removed on access instead of occupying capacity forever
  • withRateLimit no longer leaks tracked keys. With a per-user or per-IP keyFn, every key ever seen stayed in memory permanently. Stale keys are now swept and a maxKeys hard 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 on ctx. Use keyFn for any context-dependent capability
  • withCache(ttl, { maxSize }) and withRateLimit({ maxKeys }) options
  • CacheOptions exported 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.timeout timer 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 a 504 and 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/%zz threw an uncaught URIError from the synchronous request path and killed the server. Undecodable path params now return 400 Bad Request; undecodable query-string text falls back to its raw form (WHATWG URLSearchParams behavior)
  • REST transport: synchronous errors in the request handler are now caught and answered with 500 instead of escaping as an uncaughtException

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 400 instead 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 plans
  • CONTRIBUTING.md — contributing guidelines for bug reports and local setup
  • Scaffold now generates .cursor/rules with idiomatic Capix patterns for AI-assisted development

Changed

  • Scaffold capabilities.ts now defines AppUser, AuthContext, and authCap out 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.md under "After 1.0"

0.1.0-alpha.4 — 2026-06-01

Fixed

  • capix show was displaying ZodString instead of string for field types — now uses the same schema prettifier as capix docs
  • Scaffold template now includes pnpm.onlyBuiltDependencies to prevent esbuild postinstall errors under pnpm 9
  • Scaffold template now generates .npmrc with minimum-release-age=0 to prevent pnpm 9 blocking installs within 24 hours of a new release
  • Scaffold template now shows the authCap two-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 from npm publish to pnpm 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 schemas
  • capability.withContext<TContext>() — scoped factory for typed context
  • defineContext(fn) — request context builder
  • defineGuard(fn) / defineGuardFor<T>() — guards with optional type narrowing
  • defineInputGuard(fn) — guards that run after input validation
  • defineError(status, message, code?) — typed error factories
  • defineEnhancer(fn) — enhancer definition helper
  • definePlugin(plugin) — plugin bundling
  • createServer(config) — server factory with per-transport capability registries
  • createEventBus<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.js http server 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/ZodEffects unwrapping
  • @capixjs/transport-queue — background job worker via pluggable adapters (MemoryQueueAdapter included; BullMQ, SQS, etc. via custom adapters)

Plugins

  • @capixjs/plugin-authjwtContextBuilder, createJWTHelpers, authPlugin, mustBeAuthenticated guard, 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)

Scenarioreq/s
Hello World28,488
Zod Validation26,097
Auth + Guard27,102

Beats Express by 65–71% and Hono by 19–27%. Within 3% of Fastify.

Released under the MIT License.