### Create and Setup a New Keryx Project Source: https://www.keryxjs.com/guide Creates a new Keryx project named 'my-app', navigates into the project directory, copies the example environment file, and installs project dependencies using Bun. The `--no-interactive` flag can be used to skip prompts during project creation. ```bash bunx keryx new my-app cd my-app cp .env.example .env bun install ``` ```bash bunx keryx new my-app --no-interactive ``` -------------------------------- ### Install Bun and Setup Databases (macOS) Source: https://www.keryxjs.com/guide Installs the Bun runtime, PostgreSQL, and Redis on macOS using Homebrew and creates a new PostgreSQL database named 'bun'. Ensure you have Homebrew installed before running these commands. ```bash # install bun curl -fsSL https://bun.sh/install | bash # install postgres and redis brew install postgresql redis brew services start postgresql brew services start redis # create a database createdb bun ``` -------------------------------- ### Start Keryx Server with CLI Source: https://www.keryxjs.com/guide/cli Use the `keryx start` command to launch the Keryx server. This is the primary command for running your Keryx application. ```bash bunx keryx start ``` -------------------------------- ### Run Keryx Development Server Source: https://www.keryxjs.com/guide Starts the Keryx development server. This command enables hot reloading, allowing changes to be reflected immediately upon saving files. ```bash bun dev ``` -------------------------------- ### Scaffold New Keryx Project with CLI Source: https://www.keryxjs.com/guide/cli Use the `keryx new` command to scaffold a new Keryx project. This command can be run interactively or with default options using `--no-interactive` or `-y`. It also supports skipping database setup files with `--no-db` and example files with `--no-example`. Additionally, it scaffolds customizable OAuth template files. ```bash bunx keryx new my-app bunx keryx new --no-interactive bunx keryx new -y --no-db --no-example ``` -------------------------------- ### CLI Server Usage Source: https://www.keryxjs.com/reference/servers Instructions and examples for using the KeryxJS CLI to manage actions and start the server. ```APIDOC ## CLI "Server" The CLI isn't technically a server — it's a separate entry point (`keryx.ts`) that uses [Commander](https://github.com/tj/commander.js) to register every action as a CLI command. But it goes through the same `Connection → act()` pipeline as HTTP and WebSocket. The server boots in `RUN_MODE.CLI`, which tells initializers to skip transport-specific setup (like binding to a port). After the action executes, the process exits with the appropriate exit code. ### Examples ```bash # List all available actions ./keryx.ts actions # Run an action ./keryx.ts "user:create" --name Evan --email evan@example.com --password secret -q | jq # Start the full server ./keryx.ts start ``` ``` -------------------------------- ### Run Keryx with Docker Compose Source: https://www.keryxjs.com/guide/deployment Starts the Keryx backend along with PostgreSQL and Redis using a docker-compose.yml file. This setup is useful for development and understanding the architecture. ```bash docker compose up ``` -------------------------------- ### Example Generated Action File Source: https://www.keryxjs.com/guide/cli This TypeScript code snippet shows an example of an action file generated by the `keryx generate` command. It includes necessary imports, the action class definition with its name, description, input schema using Zod, and web route/method configuration. The `run` method is a placeholder for the action's logic. ```typescript import { z } from "zod"; import { Action, type ActionParams } from "keryx"; import { HTTP_METHOD } from "keryx/classes/Action.ts"; export class UserDelete implements Action { name = "user:delete"; description = "TODO: describe this action"; inputs = z.object({}); web = { route: "/api/user/delete", method: HTTP_METHOD.GET }; async run(params: ActionParams) { // TODO: implement return {}; } } ``` -------------------------------- ### WebSocket Connection and Action Example (JavaScript) Source: https://www.keryxjs.com/guide/channels Demonstrates how to establish a WebSocket connection, send an action request, and handle the response. It uses the standard WebSocket API and JSON stringification for message formatting. Ensure the server is running at the specified address. ```javascript const ws = new WebSocket("ws://localhost:8080"); ws.onopen = () => { // Run an action over WebSocket ws.send( JSON.stringify({ messageType: "action", action: "status", messageId: "req-1", // echoed back in response for correlation }), ); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log(data); // → { messageId: "req-1", name: "server", uptime: 12345, ... } }; ``` -------------------------------- ### WebSocket Channel Subscription Example (JavaScript) Source: https://www.keryxjs.com/guide/channels Shows how to subscribe to and unsubscribe from channels using WebSockets, and how to listen for broadcast messages. Messages are sent as JSON strings, and incoming messages are parsed to identify channel broadcasts. ```javascript // Subscribe ws.send( JSON.stringify({ messageType: "subscribe", channel: "messages", }), ); // Listen for broadcasts ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.channel) { console.log(`Broadcast on ${data.channel}:`, data.message); } }; // Unsubscribe ws.send( JSON.stringify({ messageType: "unsubscribe", channel: "messages", }), ); ``` -------------------------------- ### Compile and Start Keryx Backend Source: https://www.keryxjs.com/guide/deployment Compiles the Keryx backend using bun and starts the production server. Ensure NODE_ENV is set to 'production' in your .env file before starting. ```bash cd backend && bun compile # set NODE_ENV=production in .env, then start bun start ``` -------------------------------- ### Bun Test: Database Clearing Setup Source: https://www.keryxjs.com/guide/testing Demonstrates how to set up the test environment by clearing the database before running tests. It uses `api.db.clearDatabase()` which truncates all tables and includes a safeguard against running in a production environment. ```typescript beforeAll(async () => { await api.start(); await api.db.clearDatabase(); }); ``` -------------------------------- ### Status Action Example Source: https://www.keryxjs.com/guide/actions An example of a simple Keryx Action that exposes an HTTP GET endpoint to return server status. ```APIDOC ## GET /api/status ### Description This endpoint returns the status of the Keryx server, including its name and uptime. ### Method GET ### Endpoint /api/status ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **name** (string) - The name of the server process. - **uptime** (number) - The uptime of the server in milliseconds. #### Response Example ```json { "name": "keryx-server", "uptime": 1678886400000 } ``` ``` -------------------------------- ### HTTP Action Example (TypeScript) Source: https://www.keryxjs.com/reference/actions Illustrates how to implement an Action for HTTP transport. The `UserCreate` class defines its name, HTTP route and method, Zod schema for inputs, and the `run` method to handle the user creation logic. This example shows a typical setup for exposing an action as a RESTful API endpoint. ```typescript export class UserCreate implements Action { name = "user:create"; web = { route: "/user", method: HTTP_METHOD.PUT }; inputs = z.object({ name: z.string().min(3), email: z.string().email(), password: secret(z.string().min(8)), }); async run(params: ActionParams) { // ... return { user: serializeUser(user) }; } } ``` -------------------------------- ### Action to MCP Tool Conversion Example Source: https://www.keryxjs.com/guide/mcp Demonstrates how a KeryxJS action, defined with name, description, and inputs, is automatically converted into an MCP tool. The action's properties are mapped to the tool's name, description, and input schema. ```typescript // This action... export class UserView extends Action { name = "user:view"; description = "View a user's profile"; inputs = z.object({ userId: z.string() }); // ... } // ...becomes MCP tool "user-view" with: // - description: "View a user's profile" // - inputSchema: { type: "object", properties: { userId: { type: "string" } } } ``` -------------------------------- ### Bun Test: Authenticating and Making Requests Source: https://www.keryxjs.com/guide/testing Provides an example of testing an authenticated API endpoint in Bun. It outlines the process of creating a user, logging in to obtain a session ID, and then using that session ID in the `Cookie` header for subsequent authenticated requests. ```typescript import { config } from "../../config"; test("authenticated request", async () => { // Create a user await fetch(url + "/api/user", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Test User", email: "test@example.com", password: "password123", }), }); // Log in const sessionRes = await fetch(url + "/api/session", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "test@example.com", password: "password123", }), }); const sessionBody = (await sessionRes.json()) as ActionResponse; const sessionId = sessionBody.session.id; // Make an authenticated request const res = await fetch(url + "/api/user", { method: "POST", headers: { "Content-Type": "application/json", Cookie: `${config.session.cookieName}=${sessionId}`, }, body: JSON.stringify({ name: "New Name" }), }); expect(res.status).toBe(200); }); ``` -------------------------------- ### Implement Channel Middleware in TypeScript Source: https://www.keryxjs.com/guide/channels Provides an example of channel middleware for authorization and cleanup. The `runBefore` function checks for a valid user session before subscription, throwing an error if none exists. The `runAfter` function is a placeholder for cleanup logic upon unsubscription. ```typescript import type { ChannelMiddleware } from "../classes/Channel"; const AuthMiddleware: ChannelMiddleware = { runBefore: async (channel, connection) => { if (!connection.session) { throw new TypedError({ message: "Must be logged in to subscribe", type: ErrorType.CONNECTION_SESSION_NOT_FOUND, }); } }, runAfter: async (channel, connection) => { // cleanup on unsubscribe — presence tracking, etc. }, }; ``` -------------------------------- ### API Lifecycle Management Source: https://www.keryxjs.com/guide/initializers Demonstrates the core methods for managing the API lifecycle: starting, stopping, and restarting all initializers. The restart method includes flap prevention to avoid cascading restart loops. ```typescript await api.start(); // initialize + start all initializers await api.stop(); // stop all initializers in reverse priority await api.restart(); // stop + start (with flap prevention) ``` -------------------------------- ### TypeScript Initializer Example with Module Augmentation Source: https://www.keryxjs.com/guide/initializers Demonstrates how to create a TypeScript initializer for a database service. It uses module augmentation to extend the global 'API' interface, making the 'db' namespace and its types available throughout the application. This includes defining initialization, connection, and cleanup logic. ```typescript import { Initializer } from "../classes/Initializer"; import { api, logger } from "../api"; const namespace = "db"; // This is the magic — tells TypeScript that api.db exists and what type it is declare module "../classes/API" { export interface API { [namespace]: Awaited>; } } export class DB extends Initializer { constructor() { super(namespace); this.loadPriority = 100; this.startPriority = 100; this.stopPriority = 910; } async initialize() { const dbContainer = {} as { db: ReturnType; pool: Pool; }; return Object.assign( { generateMigrations: this.generateMigrations, clearDatabase: this.clearDatabase, }, dbContainer, ); } async start() { api.db.pool = new Pool({ connectionString: config.database.connectionString, }); api.db.db = drizzle(api.db.pool); // migrations run here if configured... } async stop() { await api.db.pool.end(); } } ``` -------------------------------- ### GET /metrics Source: https://www.keryxjs.com/guide/observability Exposes OpenTelemetry metrics in Prometheus exposition format. This endpoint is enabled by default and can be configured via environment variables. ```APIDOC ## GET /metrics ### Description This endpoint serves OpenTelemetry-based metrics for Keryx, including HTTP requests, WebSocket connections, action executions, and background tasks. The metrics are exposed in the Prometheus exposition format. ### Method GET ### Endpoint /metrics ### Parameters #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **Content-Type**: text/plain; version=0.0.4; charset=utf-8 - **Body**: Metrics in Prometheus exposition format. #### Response Example ``` # HELP keryx_http_requests Total HTTP requests received # TYPE keryx_http_requests counter keryx_http_requests{method="GET",route="/",status="200"} 1 # HELP keryx_http_request_duration HTTP request duration # TYPE keryx_http_request_duration histogram keryx_http_request_duration_bucket{method="GET",route="/",status="200",le="0.1"} 1 keryx_http_request_duration_bucket{method="GET",route="/",status="200",le="0.5"} 1 keryx_http_request_duration_bucket{method="GET",route="/",status="200",le="1"} 1 keryx_http_request_duration_bucket{method="GET",route="/",status="200",le="5"} 1 keryx_http_request_duration_bucket{method="GET",route="/",status="200",le="10"} 1 keryx_http_request_duration_bucket{method="GET",route="/",status="200",le="+Inf"} 1 keryx_http_request_duration_sum{method="GET",route="/",status="200"} 0.05 keryx_http_request_duration_count{method="GET",route="/",status="200"} 1 # HELP keryx_http_active_connections Currently active HTTP connections # TYPE keryx_http_active_connections gauge keryx_http_active_connections 0 ``` ``` -------------------------------- ### Process Management for Keryx Backend Source: https://www.keryxjs.com/guide/deployment Examples of using process managers to keep the Keryx backend running in production. Includes configurations for Docker, systemd, and PM2. ```bash # Docker — use `restart: unless-stopped` in docker-compose # systemd — create a service unit for the backend process # PM2 — `pm2 start "bun start" --name keryx-backend` ``` -------------------------------- ### HTTP Transport Example Source: https://www.keryxjs.com/reference/actions Demonstrates how to expose an Action as an HTTP endpoint by defining the `web` property. It includes input validation using Zod and an example `curl` command for testing. ```APIDOC ## HTTP Transport ### Description Expose an action as an HTTP endpoint by adding a `web` property. The server matches requests by route and method, extracts parameters, validates them, and executes the `run` method. ### Example Action (`UserCreate`) ```typescript export class UserCreate implements Action { name = "user:create"; web = { route: "/user", method: HTTP_METHOD.PUT }; inputs = z.object({ name: z.string().min(3), email: z.string().email(), password: secret(z.string().min(8)), }); async run(params: ActionParams) { // ... logic to create user ... return { user: serializeUser(user) }; } } ``` ### Request Example (curl) ```bash curl -X PUT http://localhost:8080/api/user \ -H "Content-Type: application/json" \ -d '{"name":"Evan","email":"evan@example.com","password":"secret123"}' ``` ### Response Example ```json { "user": { "id": 1, "name": "Evan", ... } } ``` ### Parameter Loading Order Parameters are loaded in the following order, with later sources overriding earlier ones: path parameters → URL query parameters → request body. Routes support `:param` path parameters and RegExp patterns. ``` -------------------------------- ### API Singleton Management (TypeScript) Source: https://www.keryxjs.com/reference/classes The global API singleton manages the server lifecycle, including initialization, starting, and stopping. It's accessible globally and initializers attach their namespaces to it during boot. The lifecycle is initialize() → start() → [running] → stop(). ```typescript class API { rootDir: string; initialized: boolean; started: boolean; stopped: boolean; bootTime: number; logger: Logger; runMode: RUN_MODE; initializers: Initializer[]; /** Run all initializers in loadPriority order */ async initialize(): Promise; /** Start all initializers in startPriority order */ async start(runMode?: RUN_MODE): Promise; /** Stop all initializers in stopPriority order */ async stop(): Promise; /** Stop then start */ async restart(): Promise; // Initializer namespaces are added dynamically: // api.db, api.redis, api.actions, api.session, etc. [key: string]: any; } ``` -------------------------------- ### Enabling JSON Logging with Environment Variable Source: https://www.keryxjs.com/guide/config Shows how to enable JSON formatted logging by setting the `LOG_FORMAT` environment variable. This is useful for production environments where structured logging is preferred. ```bash # Enable JSON logging in production LOG_FORMAT=json bun run start ``` -------------------------------- ### Enabling the MCP Server Source: https://www.keryxjs.com/guide/mcp The MCP server can be enabled using an environment variable or by direct configuration in the server settings. ```APIDOC ## Enabling the MCP Server The MCP server is disabled by default. To enable it, set the `MCP_SERVER_ENABLED` environment variable to `true`. ```bash MCP_SERVER_ENABLED=true ``` Alternatively, you can enable it directly in your server configuration file (`backend/config/server/mcp.ts`): ```ts export const configServerMcp = { enabled: true, route: "/mcp", // ... }; ``` Once enabled, the server will be accessible at `http://localhost:8080/mcp` or your configured `applicationUrl` combined with the `route`. ``` -------------------------------- ### Generate Keryx Project Components Source: https://www.keryxjs.com/guide Uses the Keryx CLI to generate new project components such as actions and initializers. The 'g' alias can be used as a shorthand for 'generate'. This process includes creating the component file and a corresponding test file. ```bash bunx keryx generate action user:create ``` ```bash bunx keryx g initializer cache # "g" is a shorthand alias ``` -------------------------------- ### Loading Configuration from Environment Variables in TypeScript Source: https://www.keryxjs.com/guide/config Illustrates the use of the `loadFromEnvIfSet` helper function to load configuration values from environment variables, with fallback to default values. It supports type coercion for booleans and numbers. ```typescript import { loadFromEnvIfSet } from "../util/config"; export const configDatabase = { connectionString: await loadFromEnvIfSet("DATABASE_URL", "x"), autoMigrate: await loadFromEnvIfSet("DATABASE_AUTO_MIGRATE", true), }; ``` -------------------------------- ### Applying Middleware to an Action (TypeScript) Source: https://www.keryxjs.com/guide/middleware Demonstrates how to apply middleware to a KeryxJS action by including an array of middleware instances in the `middleware` property of the action class. Middleware executes in the order they appear in the array. ```typescript export class UserEdit implements Action { name = "user:edit"; middleware = [SessionMiddleware]; // ... } ``` -------------------------------- ### CLI Transport Example Source: https://www.keryxjs.com/reference/actions Explains how Actions are automatically registered as CLI commands using Commander.js, with Zod schema fields mapping to command flags and descriptions providing help text. ```APIDOC ## CLI Transport ### Description Actions are automatically available as CLI commands through Commander.js integration. Input fields from the Zod schema become command-line flags, and descriptions serve as help text. ### Command Execution Example ```bash ./keryx.ts "user:create" \ --name Evan \ --email evan@example.com \ --password secret123 \ -q | jq ``` ### Command Output Example ```json { "response": { "user": { "id": 1, "name": "Evan", ... } } } ``` ### Help Command Example ```bash ./keryx.ts "user:create" --help ``` ### Server Boot Mode When running in `CLI` mode, initializers not relevant to the CLI (e.g., web server) are skipped based on their `runModes` setting. ``` -------------------------------- ### Accessing Configuration Values in TypeScript Source: https://www.keryxjs.com/guide/config Demonstrates how to import and access configuration values within a TypeScript application. It assumes a 'config' object is available after aggregation. ```typescript import { config } from "../config"; config.database.connectionString; // Postgres URL config.server.web.port; // 8080 config.logger.level; // "info" ``` -------------------------------- ### Enable MCP Server - Configuration File Source: https://www.keryxjs.com/guide/mcp Configure the MCP server directly within the KeryxJS configuration file. This allows for more granular control over server settings like the enabled status and route. ```typescript export const configServerMcp = { enabled: true, route: "/mcp", // ... }; ``` -------------------------------- ### Login Action Implementation (TypeScript) Source: https://www.keryxjs.com/guide/authentication An example of a KeryxJS login action that validates user credentials (email and password) and creates a session upon successful authentication. It includes input validation using Zod and password checking. ```typescript import { SessionMiddleware } from "../middleware/session"; import type { SessionImpl } from "./session"; export class SessionCreate implements Action { name = "session:create"; description = "Sign in with email and password"; web = { route: "/session", method: HTTP_METHOD.PUT }; mcp = { enabled: false, isLoginAction: true }; middleware = [RateLimitMiddleware]; inputs = z.object({ email: z .string() .email() .transform((val) => val.toLowerCase()), password: secret(z.string().min(8)), }); run = async ( params: ActionParams, connection: Connection, ) => { const [user] = await api.db.db .select() .from(users) .where(eq(users.email, params.email)); if (!user || !(await checkPassword(user, params.password))) { throw new TypedError({ message: "Invalid email or password", type: ErrorType.CONNECTION_ACTION_RUN, }); } await connection.updateSession({ userId: user.id }); return { user: serializeUser(user), session: connection.session! }; }; } ``` -------------------------------- ### OAuth 2.1 Authentication Flow Source: https://www.keryxjs.com/guide/mcp Details the OAuth 2.1 authentication process for MCP clients, including the sequence of requests and endpoints involved. ```APIDOC ## OAuth 2.1 Authentication MCP clients authenticate using the OAuth 2.1 protocol with PKCE (Proof Key for Code Exchange) for enhanced security. The authentication flow proceeds as follows: 1. **Initial Connection**: An MCP client initiates a connection to the `/mcp` endpoint. If authentication is required and not provided, the server responds with a `401 Unauthorized` status. 2. **Discover Protected Resources**: The client fetches the `/.well-known/oauth-protected-resource` endpoint to discover metadata about the protected resources. 3. **Discover Authorization Server**: The client then fetches `/.well-known/oauth-authorization-server` to obtain metadata about the authorization server, including its endpoints. 4. **Dynamic Client Registration**: The client registers itself dynamically by making a `POST` request to the `/oauth/register` endpoint. 5. **Authorization Request**: The client redirects the user's browser to the `/oauth/authorize` endpoint, including a PKCE challenge. 6. **User Authentication**: The user interacts with the authorization page, either logging in or signing up. 7. **Authorization Code Issuance**: Upon successful authentication, the server issues an authorization code and redirects the user's browser back to the client's registered redirect URI. 8. **Token Exchange**: The client exchanges the received authorization code for an access token by making a `POST` request to the `/oauth/token` endpoint. 9. **Authenticated Requests**: Subsequent MCP requests from the client must include the obtained access token in the `Authorization` header as a Bearer token (e.g., `Authorization: Bearer `). ### OAuth Endpoints The following endpoints are part of the OAuth 2.1 authentication process: | Endpoint | Method | Description | | ----------------------------------------- | ------ | -------------------------------------- | | `/.well-known/oauth-protected-resource` | GET | Resource metadata (RFC 9728) | | `/.well-known/oauth-authorization-server` | GET | Authorization server metadata | | `/oauth/register` | POST | Dynamic client registration | | `/oauth/authorize` | GET | Authorization page (login/signup form) | | `/oauth/authorize` | POST | Process login/signup form submission | | `/oauth/token` | POST | Exchange authorization code for token | ``` -------------------------------- ### Enable OpenTelemetry Metrics (Bash) Source: https://www.keryxjs.com/guide/deployment This command enables OpenTelemetry metrics for KeryxJS by setting the OTEL_METRICS_ENABLED environment variable to 'true' before running the application with 'bun run start'. This allows Keryx to expose metrics for monitoring. ```bash OTEL_METRICS_ENABLED=true bun run start ``` -------------------------------- ### Production Environment Variables Checklist Source: https://www.keryxjs.com/guide/security Essential environment variables for deploying the application to a production environment. These settings enhance security, manage traffic, and control application behavior. ```bash SESSION_COOKIE_SECURE=true WEB_SERVER_ALLOWED_ORIGINS=https://yourapp.com RATE_LIMIT_ENABLED=true RATE_LIMIT_UNAUTH_LIMIT=20 RATE_LIMIT_AUTH_LIMIT=200 NODE_ENV=production WEB_SECURITY_CSP="default-src 'self'; script-src 'self'" ``` -------------------------------- ### Channel Name Matching Patterns Source: https://www.keryxjs.com/guide/channels Demonstrates how channel names can be defined as exact strings or regular expressions. This allows for flexible routing of messages to specific or dynamically named channels, such as per-resource channels like 'room:123'. ```typescript // Exact match — only "messages" name: "messages"; // Pattern match — "room:123", "room:abc", etc. name: /^room:.*$/; ``` -------------------------------- ### List Discovered Actions with CLI Source: https://www.keryxjs.com/guide/cli The `keryx actions` command lists all discovered actions within your Keryx project, including their associated routes and descriptions. This is useful for understanding the available API endpoints. ```bash bunx keryx actions ``` -------------------------------- ### Bun Test Structure with Server Lifecycle Source: https://www.keryxjs.com/guide/testing Demonstrates the basic structure for a Bun test file. It includes setting up and tearing down the server using `beforeAll` and `afterAll` hooks, and utilizes dynamic port binding for isolated test runs. This approach ensures the entire server stack is tested. ```typescript import { api } from "../../api"; import { serverUrl, HOOK_TIMEOUT } from "../setup"; let url: string; beforeAll(async () => { await api.start(); url = serverUrl(); }, HOOK_TIMEOUT); afterAll(async () => { await api.stop(); }, HOOK_TIMEOUT); test("status endpoint returns server info", async () => { const res = await fetch(url + "/api/status"); const body = (await res.json()) as ActionResponse; expect(res.status).toBe(200); expect(body.name).toBe("server"); expect(body.uptime).toBeGreaterThan(0); }); ``` -------------------------------- ### Parameter Normalization Middleware (TypeScript) Source: https://www.keryxjs.com/guide/middleware An example of middleware that normalizes input parameters before an action is executed. This specific middleware converts the `email` parameter to lowercase. It returns an `ActionMiddlewareResponse` with the modified parameters. ```typescript export const NormalizeMiddleware: ActionMiddleware = { runBefore: async (params) => { return { updatedParams: { ...params, email: params.email?.toLowerCase(), }, }; }, }; ``` -------------------------------- ### CLI Action Execution (Bash) Source: https://www.keryxjs.com/reference/actions Shows how to execute an Action as a command-line interface command using Keryx.js. The example demonstrates calling the `user:create` action with parameters as flags. The `-q` flag is used to suppress server logs for clean JSON output, and `--help` can be used to view action-specific help. ```bash ./keryx.ts "user:create" \ --name Evan \ --email evan@example.com \ --password secret123 \ -q | jq # → { "response": { "user": { "id": 1, "name": "Evan", ... } } } ./keryx.ts "user:create" --help ``` -------------------------------- ### Response Enrichment Middleware (TypeScript) Source: https://www.keryxjs.com/guide/middleware An example of `runAfter` middleware that enriches the response. The `TimingMiddleware` calculates the duration of the request execution and adds it to the response payload. This runs after the main action logic has completed. ```typescript export const TimingMiddleware: ActionMiddleware = { runAfter: async (_params, connection) => { return { updatedResponse: { requestDuration: Date.now() - connection.startTime, }, }; }, }; ``` -------------------------------- ### Test MCP Actions with KeryxJS SDK Client (TypeScript) Source: https://www.keryxjs.com/guide/mcp This snippet demonstrates how to test MCP actions using the `@modelcontextprotocol/sdk` client. It covers initializing the client, establishing a connection via an HTTP transport, and calling a 'status' tool. Ensure you have a valid `accessToken` and the MCP service is running at `http://localhost:8080/mcp`. ```typescript import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const transport = new StreamableHTTPClientTransport( new URL("http://localhost:8080/mcp"), { requestInit: { headers: { Authorization: `Bearer ${accessToken}`, }, }, }, ); const client = new Client({ name: "test-client", version: "1.0.0" }); await client.connect(transport); const tools = await client.listTools(); const result = await client.callTool({ name: "status", arguments: {}, }); ``` -------------------------------- ### API Singleton Source: https://www.keryxjs.com/reference/classes The global singleton that manages the full server lifecycle. Initializers attach their namespaces to it during boot. ```APIDOC ## API Singleton ### Description The global singleton that manages the full server lifecycle. Stored on `globalThis` so it's accessible everywhere. Initializers attach their namespaces to it during boot. The lifecycle is `initialize() → start() → [running] → stop()`. Calling `start()` automatically calls `initialize()` first if it hasn't been called yet. ### Class Definition ```typescript class API { rootDir: string; initialized: boolean; started: boolean; stopped: boolean; bootTime: number; logger: Logger; runMode: RUN_MODE; initializers: Initializer[]; /** Run all initializers in loadPriority order */ async initialize(): Promise; /** Start all initializers in startPriority order */ async start(runMode?: RUN_MODE): Promise; /** Stop all initializers in stopPriority order */ async stop(): Promise; /** Stop then start */ async restart(): Promise; // Initializer namespaces are added dynamically: // api.db, api.redis, api.actions, api.session, etc. [key: string]: any; } ``` ``` -------------------------------- ### Upgrade Keryx Framework Files with CLI Source: https://www.keryxjs.com/guide/cli The `keryx upgrade` command updates framework-owned files, such as configuration files, built-in actions, and OAuth templates, to match the installed Keryx version. It offers `--dry-run` to preview changes and `--force` or `-y` to overwrite all framework files without confirmation. ```bash bunx keryx upgrade bunx keryx upgrade --dry-run bunx keryx upgrade --force ``` -------------------------------- ### Test Background Task Execution with waitFor (TypeScript) Source: https://www.keryxjs.com/guide/testing Demonstrates testing background tasks by enqueuing a task and then using the `waitFor` utility to poll for its side effects. This example enqueues a 'messages:cleanup' task and waits until a database query returns an empty result set, indicating the task has completed successfully. It requires the `api` object and a `messages` table definition. ```typescript test("cleanup task removes old messages", async () => { // Insert test data... // Enqueue the task await api.actions.enqueue("messages:cleanup", { age: 1000 }); // Wait for the side effect await waitFor( async () => { const remaining = await api.db.db.select().from(messages); return remaining.length === 0; }, { interval: 100, timeout: 5000 }, ); }); ``` -------------------------------- ### Configure OTLP Metric Exporter with MeterProvider Source: https://www.keryxjs.com/guide/observability Sets up a custom MeterProvider with an OTLPMetricExporter for push-based metric reporting. This involves creating an exporter instance, configuring a PeriodicExportingMetricReader, and registering the provider as the global MeterProvider. This allows Keryx's instruments to automatically report metrics to the configured endpoint. ```typescript import { metrics } from "@opentelemetry/api"; import { MeterProvider } from "@opentelemetry/sdk-metrics"; import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; const exporter = new OTLPMetricExporter({ url: "https://your-collector:4318/v1/metrics", }); const provider = new MeterProvider({ readers: [ new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 30_000, }), ], }); metrics.setGlobalMeterProvider(provider); ``` -------------------------------- ### Server Class Source: https://www.keryxjs.com/reference/classes Base class for transport servers, providing a common interface for initialization, starting, and stopping server instances. ```APIDOC ## Server Class ### Description Base class for transport servers. The framework ships with a web server (`Bun.serve` for HTTP + WebSocket), but you could add others. ### Class Definition ```typescript abstract class Server { name: string; /** The underlying server object (e.g., Bun.Server) */ server?: T; abstract initialize(): Promise; abstract start(): Promise; abstract stop(): Promise; } ``` ``` -------------------------------- ### Error Stack Traces Configuration Source: https://www.keryxjs.com/guide/security Explains how error stack traces are included in responses based on the environment (development vs. production). ```APIDOC ## Error Stack Traces Configuration ### Description Controls the inclusion of stack traces in error responses based on the environment. ### Web Server - **WEB_SERVER_INCLUDE_STACK_IN_ERRORS** (boolean) - Optional - Determines if stack traces are included in HTTP error responses. - Defaults to `true` in development environments (`NODE_ENV` is not `production`). - Defaults to `false` in production environments (`NODE_ENV=production`). ### CLI - **CLI_INCLUDE_STACK_IN_ERRORS** (boolean) - Optional - Determines if stack traces are included in CLI error outputs. Defaults to `true`. ### Production Behavior - In production, stack traces are automatically hidden from HTTP responses to prevent leaking internal implementation details. ``` -------------------------------- ### WebSocket Protections Source: https://www.keryxjs.com/guide/security Details the security measures implemented for WebSocket connections, including origin validation, message limits, and channel validation. ```APIDOC ## WebSocket Protections ### Description Provides layers of protection for WebSocket connections to prevent abuse and ensure security. ### Origin Validation - Before upgrading an HTTP connection to WebSocket, the server validates the `Origin` header against `config.server.web.allowedOrigins`. - Rejects connections if the origin does not match to prevent Cross-Site WebSocket Hijacking (CSWSH). ### Message Limits #### Configuration Options - **WS_MAX_PAYLOAD_SIZE** (integer) - Optional - Maximum message size in bytes. Defaults to `65536` (64 KB). - **WS_MAX_MESSAGES_PER_SECOND** (integer) - Optional - Per-connection rate limit for messages. Defaults to `20`. - **WS_MAX_SUBSCRIPTIONS** (integer) - Optional - Maximum number of channel subscriptions per connection. Defaults to `100`. ### Channel Validation - **Channel Name Pattern**: `/^[a-zA-Z0-9:._-]{1,200}$/` (alphanumeric characters plus `:`, `.`, `_`, `-`, max 200 characters). - **Undefined Channels**: Subscriptions to non-registered channels are rejected with a `CHANNEL_NOT_FOUND` error. ``` -------------------------------- ### OAuth Security Source: https://www.keryxjs.com/guide/security Details the security measures for the OAuth 2.1 implementation, including redirect URI validation and client registration rate limiting. ```APIDOC ## OAuth Security ### Description Hardening measures for the OAuth 2.1 implementation to ensure secure client interactions. ### Redirect URI Validation - **Registration**: During client registration via `/oauth/register`, redirect URIs are validated. - Must be a valid URL. - Must not contain a fragment (`#`). - Must not contain userinfo (username/password). - Non-localhost URIs must use HTTPS. - **Authorization Code Exchange**: The redirect URI must exactly match the registered URI (origin + pathname). ### Registration Rate Limiting - **Endpoint**: `POST /oauth/register` #### Configuration Options - **RATE_LIMIT_OAUTH_REGISTER_LIMIT** (integer) - Optional - Maximum number of registrations per window. Defaults to `5`. - **RATE_LIMIT_OAUTH_REGISTER_WINDOW_MS** (integer) - Optional - Registration rate limit window size in milliseconds. Defaults to `3600000` (1 hour). ``` -------------------------------- ### CORS Configuration Source: https://www.keryxjs.com/guide/security Details the configuration options for Cross-Origin Resource Sharing (CORS) on the web server, including allowed origins, methods, and headers. ```APIDOC ## CORS Configuration ### Description Configures Cross-Origin Resource Sharing (CORS) settings for the web server. ### Method Configuration ### Parameters #### Environment Variables - **WEB_SERVER_ALLOWED_ORIGINS** (string) - Optional - Comma-separated list of allowed origins. Defaults to `"*"`. - **WEB_SERVER_ALLOWED_METHODS** (string) - Optional - Comma-separated list of allowed HTTP methods. Defaults to `"HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS"`. - **WEB_SERVER_ALLOWED_HEADERS** (string) - Optional - Comma-separated list of allowed HTTP headers. Defaults to `"Content-Type"`. ### Important Notes - When `WEB_SERVER_ALLOWED_ORIGINS` is `"*"`, `Access-Control-Allow-Credentials: true` is not sent, adhering to browser specifications. - For production environments, specify exact domains in `WEB_SERVER_ALLOWED_ORIGINS` to enable credentialed requests (cookies, auth headers). ``` -------------------------------- ### Web Server Configuration Source: https://www.keryxjs.com/reference/servers Settings for the web server, including port, host, API routes, CORS settings, and static file serving. ```APIDOC ## Configuration All web server settings are in `config.server.web`: | Key | Default | What it does | | ----------------------- | ------------- | ----------------------------- | | `enabled` | `true` | Enable/disable the web server | | `port` | `8080` | Listen port | | `host` | `"localhost"` | Bind address | | `apiRoute` | `"/api"` | URL prefix for action routes | | `allowedOrigins` | `"*"` | CORS allowed origins | | `staticFiles.enabled` | `true` | Serve static files | | `staticFiles.directory` | `"assets"` | Directory for static files | ```