Documentation
Packages
Kynetra FX is published as 21 focused packages in a single monorepo. Every package has a single responsibility, ships its own TypeScript types, and can be installed independently. You only pay for what you use — a minimal API server needs only @kynetra/fx and a runtime adapter.
Monorepo structure#
All packages live under the packages/ directory. They share a common build pipeline, test runner, and TypeScript configuration but are versioned and published separately to npm under the @kynetra scope. Packages reference each other as peer dependencies rather than bundled dependencies so that your application's bundler can tree-shake across the whole graph.
The dependency graph is intentionally shallow. Most packages depend only on @kynetra/fx (the core) and optionally on @kynetra/fx-ports (the port interfaces). Runtime adapters, auth packages, and tooling packages do not depend on each other, which keeps installs fast and avoids accidental coupling.
- Core —
@kynetra/fx,@kynetra/fx-schemas - Runtime adapters —
@kynetra/fx-cloudflare,@kynetra/fx-node,@kynetra/fx-browser - Auth and access control —
@kynetra/fx-auth,@kynetra/fx-rbac - SaaS and multi-tenancy —
@kynetra/fx-saas,@kynetra/fx-tenancy - Ports and adapters —
@kynetra/fx-ports - Middleware —
@kynetra/fx-middleware - Polyglot —
@kynetra/fx-wasm - AI —
@kynetra/fx-ai - Plugins —
@kynetra/fx-plugin - Developer experience —
@kynetra/fx-cli,@kynetra/fx-client,@kynetra/fx-openapi,@kynetra/fx-standard-schema
Core packages#
@kynetra/fx
The main framework package. Everything starts here — routing, middleware, the context model, contracts, errors, plugins, and hooks. Install this in every project.
npm install @kynetra/fximport { createFX, fx } from '@kynetra/fx' const app = createFX() app.use(async (ctx, next) => { console.log(ctx.method, ctx.path) return next()}) app.route({ method: 'POST', path: '/items', input: fx.object({ name: fx.string().min(1) }), handler: (ctx) => ctx.json({ created: ctx.input.name }, { status: 201 }),}) return appKey exports: createFX, fx, FXContext, FXMiddleware, FXPlugin, FXError, cors, logger.
@kynetra/fx-schemas
The built-in schema builder. Provides the fx.* namespace used in contracts and validation. This package is re-exported from @kynetra/fx for convenience, but can be imported directly when you need only the schema primitives.
import { fx, type Infer } from '@kynetra/fx-schemas' const UserSchema = fx.object({ id: fx.string(), name: fx.string().min(1).max(100), email: fx.string().email(), age: fx.optional(fx.number().int().min(0).max(150)), role: fx.enum(['admin', 'member', 'viewer']),}) type User = Infer<typeof UserSchema>// { id: string; name: string; email: string; age?: number; role: 'admin' | 'member' | 'viewer' }Runtime adapters#
Runtime adapters convert a generic FX application into the entry-point shape expected by a specific execution environment. Only one adapter is needed per deployment target.
@kynetra/fx-cloudflare
Bridges FX applications to Cloudflare Workers. Provides cloudflare(app) (also exported as toCloudflareHandler), the d1Sql query helper, d1Store for key-value storage on D1, and the D1_STORE_MIGRATION SQL string to initialise the store table. See Cloudflare Workers and D1 for detailed usage.
import { createFX } from '@kynetra/fx'import { cloudflare, d1Sql } from '@kynetra/fx-cloudflare' const app = createFX<{ Env: { DB: D1Database } }>({ runtime: 'cloudflare' }) app.get('/users', async (ctx) => { const rows = await d1Sql(ctx.env.DB, 'SELECT * FROM users LIMIT 50') return ctx.json(rows)}) return cloudflare(app)@kynetra/fx-node
Runs FX applications on Node.js via node:http. The serve(app, options) function starts an HTTP server and returns a handle you can call .close() on. Compatible with Node 18+ and fully supports app.fetch for use in test environments without starting an actual server. See Node.js.
import { createFX } from '@kynetra/fx'import { serve } from '@kynetra/fx-node' const app = createFX()app.get('/', (ctx) => ctx.json({ hello: 'world' })) const server = serve(app, { port: 3000 })console.log('Listening on http://localhost:3000')@kynetra/fx-browser
A pre-bundled, tree-shaken ~32 KB build of the core framework targeting browser environments. Used internally by the in-browser playground and useful for client-side route matching or shared validation logic. Not intended for production API servers.
Auth and access control#
@kynetra/fx-auth
JWT signing and verification, a principal model, and three built-in auth strategies: jwtAuth, apiKeyAuth, and sessionAuth. Each strategy is a middleware factory that populates ctx.principal on success and short-circuits with a 401 on failure. See Auth and JWT for configuration details.
import { createFX } from '@kynetra/fx'import { jwtAuth, requireAuth } from '@kynetra/fx-auth' const app = createFX() const auth = jwtAuth({ secret: process.env.JWT_SECRET! }) app.get('/me', auth, requireAuth, (ctx) => { return ctx.json({ user: ctx.principal })}) app.post('/admin/action', auth, requireAuth, (ctx) => { if (ctx.principal.role !== 'admin') { return ctx.json({ error: 'forbidden' }, { status: 403 }) } return ctx.json({ ok: true })})@kynetra/fx-rbac
Role-based access control layered on top of @kynetra/fx-auth. Define roles and their permissions with defineRoles, create a checked RBAC instance with createRbac, then protect routes with requirePermission or requireRole middleware. Use the can(principal, action, resource) helper for inline permission checks. See RBAC.
import { defineRoles, createRbac, requirePermission } from '@kynetra/fx-rbac' const roles = defineRoles({ admin: { permissions: ['users:read', 'users:write', 'billing:read', 'billing:write'] }, member: { permissions: ['users:read'] }, viewer: { permissions: [] },}) export const rbac = createRbac(roles) // In a routeapp.delete('/users/:id', auth, requireAuth, requirePermission(rbac, 'users:write'), (ctx) => { // Only admins reach here return ctx.json({ deleted: ctx.params.id })})SaaS and multi-tenancy#
@kynetra/fx-saas
A complete multi-tenant SaaS kernel. Provides domain models and service logic for users, organisations, workspaces, memberships, audit logs, feature flags, and billing seats. Designed to plug in via the ports model so the same kernel code runs on D1, Postgres, or an in-memory fake. See SaaS kernel.
@kynetra/fx-tenancy
Lightweight multi-tenancy middleware. The tenant() middleware resolves the current tenant from a header (X-Tenant-Id by default), a subdomain, or a path segment, and exposes it as ctx.tenantId. Use getTenantId(ctx) anywhere downstream to read the resolved value. See Tenancy.
import { createFX } from '@kynetra/fx'import { tenant, getTenantId } from '@kynetra/fx-tenancy' const app = createFX() // Resolve tenant from X-Tenant-Id headerapp.use(tenant({ strategy: 'header' })) app.get('/data', async (ctx) => { const tenantId = getTenantId(ctx) // tenantId is the value of the X-Tenant-Id request header return ctx.json({ tenantId, rows: [] })})Ports and adapters#
@kynetra/fx-ports
Port interfaces and in-memory fakes for every external dependency your application might have. Using ports keeps your domain logic decoupled from infrastructure so you can swap a Cloudflare KV binding for a Redis adapter or an in-memory fake without changing any application code. See Ports.
import { createFX } from '@kynetra/fx'import { type KvPort, type StorePort, type CachePort, type QueuePort, memoryKv, memoryStore,} from '@kynetra/fx-ports' // Wire up in-memory fakes for local developmentconst kv: KvPort = memoryKv()const store: StorePort = memoryStore() // In production, swap for Cloudflare KV / D1 adapters// const kv = cloudflareKv(ctx.env.KV)// const store = d1Store(ctx.env.DB)Available port interfaces:
| Name | Type | Description |
|---|---|---|
| KvPort | interface | Key-value store: get, set, delete, list. |
| StorePort | interface | Document store: find, findOne, insert, update, remove. |
| CachePort | interface | Time-bounded cache: get, set with TTL, invalidate. |
| QueuePort | interface | Message queue: enqueue, dequeue, ack. |
| BlobPort | interface | Binary object storage: put, get, delete, list. |
| VectorPort | interface | Vector store: upsert, query by embedding, delete. |
| SqlPort | interface | Relational query runner: execute, transaction. |
| ClockPort | interface | Mockable clock: now(), sleep(). |
| IdPort | interface | ID generator: generate() returning a unique string. |
Middleware#
@kynetra/fx-middleware
Production-ready middleware for common API concerns. All middleware is composable and individually tree-shakeable. See Middleware.
| Name | Type | Description |
|---|---|---|
| requestId() | middleware | Generates a UUID request ID and sets it on X-Request-Id. |
| logger() | middleware | Logs method, path, status code, and duration for every request. |
| cors(options) | middleware | Sets CORS headers; supports origin allowlist, credentials, and preflight caching. |
| secureHeaders() | middleware | Sets X-Content-Type-Options, X-Frame-Options, and other security headers. |
import { createFX } from '@kynetra/fx'import { requestId, logger, cors, secureHeaders } from '@kynetra/fx-middleware' const app = createFX() app.use(requestId())app.use(logger())app.use(cors({ origin: ['https://app.example.com', 'https://admin.example.com'], credentials: true, maxAge: 86400,}))app.use(secureHeaders()) app.get('/', (ctx) => ctx.json({ ok: true }))Polyglot and AI#
@kynetra/fx-wasm
Runs compiled WASM guests alongside your FX application. The wasmHandler factory creates a route handler that invokes a WASM module over a typed ABI; wasmMiddleware allows a WASM module to intercept and transform requests mid-pipeline. The WasmRequest / WasmResponse types define the stable binary interface. See WASM and WASM languages.
@kynetra/fx-ai
An AI port and provider adapters so your application can call LLMs through a stable interface without being coupled to a specific SDK. Ships adapters for OpenAI, Anthropic, and Cloudflare Workers AI, plus a mockAi fake for tests. The createRag helper wires a vector store (via VectorPort) and an AI provider into a basic retrieval-augmented generation pipeline. See AI providers.
import { createFX, fx } from '@kynetra/fx'import { anthropicAdapter } from '@kynetra/fx-ai' const ai = anthropicAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! }) const app = createFX() app.route({ method: 'POST', path: '/chat', input: fx.object({ message: fx.string().min(1) }), handler: async (ctx) => { const reply = await ai.complete({ model: 'claude-sonnet-4-5', messages: [{ role: 'user', content: ctx.input.message }], }) return ctx.json({ reply }) },})Developer experience#
@kynetra/fx-plugin
The plugin definition helper. Use definePlugin to declare a named plugin with its dependencies, setup function, and exports. The framework resolves the dependency graph and calls setup functions in topological order. See Plugins.
@kynetra/fx-cli
The kynetra CLI for scaffolding new projects. kynetra new my-api generates a project with a runtime adapter of your choice, a sensible directory structure, and working example routes. Also exports scaffoldProject, parseArgs, and run for programmatic use. See CLI.
# Scaffold a new Cloudflare Workers projectnpx @kynetra/fx-cli new my-api --runtime cloudflare # Scaffold a Node.js projectnpx @kynetra/fx-cli new my-api --runtime node@kynetra/fx-client
A typed HTTP client generated from your FX route definitions. Use createFXClient with a base URL and the inferred type of your app to get fully-typed get, post, put, patch, and delete methods. See Typed client.
import { createFXClient } from '@kynetra/fx-client'import type { App } from './server' const client = createFXClient<App>({ baseUrl: 'https://api.example.com' }) // Fully typed — input and response are inferred from the route definitionconst user = await client.post('/users', { name: 'Alice', email: 'alice@example.com' })console.log(user.id)@kynetra/fx-openapi
Generates OpenAPI 3.1 documents from your FX application's route definitions. Call generateOpenAPI(app, info) and serve the result as JSON or YAML. The schemaToJSON helper converts fx.* schemas into JSON Schema objects for embedding in external documents. See OpenAPI.
@kynetra/fx-standard-schema
Connects third-party schema libraries to FX contracts via the Standard Schema specification. Pass a Zod, Valibot, or ArkType schema as the input, query, or output field of an app.route call and the adapter handles validation automatically. See Standard Schema.
import { createFX } from '@kynetra/fx'import { z } from 'zod' const app = createFX() app.route({ method: 'POST', path: '/signup', // Zod schema works directly — @kynetra/fx-standard-schema adapts it automatically input: z.object({ email: z.string().email(), password: z.string().min(8), }), handler: (ctx) => { // ctx.input is typed as { email: string; password: string } return ctx.json({ ok: true, email: ctx.input.email }) },})Note
@kynetra/fx-standard-schema is auto-detected when you pass a Standard Schema-compatible object as an input or output field. You do not need to call any adapter function explicitly — the package just needs to be installed.Full package reference#
| Name | Type | Description |
|---|---|---|
| @kynetra/fx | core | Main framework: createFX, routing, middleware, context, contracts, errors, plugins, hooks. |
| @kynetra/fx-cloudflare | runtime | Cloudflare Workers adapter: cloudflare(), d1Sql(), d1Store(), D1_STORE_MIGRATION. |
| @kynetra/fx-node | runtime | Node.js adapter: serve() over node:http, app.fetch compatibility. |
| @kynetra/fx-browser | tooling | Browser bundle (~32 KB) powering the in-browser playground. |
| @kynetra/fx-auth | auth | JWT signing/verification, principal model, jwtAuth, apiKeyAuth, sessionAuth middleware. |
| @kynetra/fx-rbac | auth | Role-based access control: defineRoles, createRbac, requirePermission, requireRole, can(). |
| @kynetra/fx-saas | saas | Multi-tenant SaaS kernel: users, orgs, workspaces, memberships, audit logs, feature flags. |
| @kynetra/fx-tenancy | saas | Multi-tenancy middleware: tenant(), getTenantId(), header/subdomain/path resolution. |
| @kynetra/fx-ports | ports | Port interfaces and in-memory fakes: Kv, Store, Cache, Queue, Blob, Vector, Sql, Clock, Id. |
| @kynetra/fx-middleware | middleware | Built-in middleware: requestId, logger, cors, secureHeaders. |
| @kynetra/fx-wasm | polyglot | Polyglot WASM: wasmHandler, wasmMiddleware, WasmRequest/WasmResponse ABI. |
| @kynetra/fx-ai | ai | AI port and adapters: AiPort, openAiAdapter, anthropicAdapter, workersAiAdapter, mockAi, createRag. |
| @kynetra/fx-plugin | plugins | Plugin definition helper: definePlugin, dependency resolution. |
| @kynetra/fx-cli | tooling | CLI scaffolding: kynetra new, scaffoldProject, parseArgs, run. |
| @kynetra/fx-client | client | Typed HTTP client: createFXClient, typed GET/POST/PUT/PATCH/DELETE. |
| @kynetra/fx-openapi | openapi | OpenAPI 3.1 generation: generateOpenAPI, schemaToJSON. |
| @kynetra/fx-schemas | validation | Built-in schema builder (fx.*): string, number, object, array, Infer<T>. |
| @kynetra/fx-standard-schema | validation | Standard Schema adapter: use Zod, Valibot, or ArkType as input/query/output validators. |
Tip
@kynetra/fx plus the runtime adapter for your target environment (@kynetra/fx-cloudflare or @kynetra/fx-node). Add auth, ports, and SaaS packages as your application grows. The CLI scaffolds a sensible starting set automatically.