Skip to content

CLI ​

Keryx includes a CLI (keryx) for common project tasks. When installed locally, run it with bunx keryx. When scaffolding a brand-new project, use bunx keryx@latest.

The CLI has two kinds of commands: framework commands that ship with Keryx, and action commands that are automatically generated from the actions you write.

Framework Commands ​

These are built-in commands for managing your project.

keryx new ​

Scaffold a new Keryx project. See Getting Started for the full walkthrough.

bash
bunx keryx new my-app

Options:

  • -y / --yes — skip prompts, use defaults
  • --no-interactive — skip prompts, use defaults (same effect as -y)
  • --no-db — run without PostgreSQL: no Drizzle files or dependencies, and an empty DATABASE_URL so the app boots without a database
  • --no-redis — run without a Redis server: REDIS_URL="memory://" uses an in-process Redis (single process, not persisted)
  • --no-example — skip the example action

bunx keryx new my-app --no-db --no-redis gives you a project that runs with nothing but Bun installed — background tasks and scheduled jobs included.

  • --force — scaffold into an existing directory; files that already exist are left untouched (merge-skip, never overwrite)

keryx new also scaffolds OAuth template files into templates/ (login/signup page, shared CSS, and the lion SVG). These are customizable — see the MCP guide for details.

keryx generate ​

Generate a new component file with boilerplate. Aliased as keryx g.

bash
bunx keryx generate <type> <name>

Supported Types ​

TypeDirectoryExample
actionactions/keryx g action user:delete
initializerinitializers/keryx g initializer cache
middlewaremiddleware/keryx g middleware auth
channelchannels/keryx g channel notifications
opsops/keryx g ops UserOps
pluginplugins/keryx g plugin analytics

Naming Conventions ​

Colon-separated names create nested directories and map to routes for actions:

bash
keryx g action user:delete
# Creates: actions/user/delete.ts
# Route:   /api/user/delete
# Class:   UserDelete

Simple names stay flat:

bash
keryx g initializer cache
# Creates: initializers/cache.ts
# Class:   Cache

Options ​

  • --dry-run — preview what would be generated without writing files
  • --force — overwrite existing files
  • --no-test — skip generating the companion test file

By default, each generated component also creates a matching test file in __tests__/.

Example Output ​

bash
$ bunx keryx g action user:delete

Generated:
  actions/user/delete.ts
  __tests__/actions/user/delete.test.ts

The generated action file:

ts
import { z } from "zod";
import { Action, type ActionParams, HTTP_METHOD } from "keryx";

export class UserDelete implements Action {
  name = "user:delete";
  description = "TODO: describe this action";
  inputs = z.object({});
  web = { route: "/user/delete", method: HTTP_METHOD.GET };

  async run(params: ActionParams<UserDelete>) {
    // TODO: implement
    return {};
  }
}

keryx upgrade ​

Update framework-owned files (config, built-in actions, OAuth templates) to match the installed version of Keryx.

bash
bunx keryx upgrade

Options:

  • --dry-run — show what would change without writing
  • --force — overwrite all framework files without confirmation
  • -y / --yes — overwrite all framework files without confirmation (same effect as --force)

keryx build ​

Pre-generate swagger response schemas so the server can skip the expensive and slow ts-morph analysis at startup. This is recommended for production deploys.

bash
bunx keryx build

The command writes .cache/swagger-schemas.json. When the server starts and finds a valid cache, it skips ts-morph entirely.

Recommended Dockerfile pattern:

dockerfile
COPY . .
RUN bun keryx.ts build
CMD ["bun", "keryx.ts", "start"]

If you skip this step, the server generates the schemas on the fly at startup instead. That analysis can peak over 1 GB of RSS on a large app, which is enough to get the process OOM-killed on a small container before it finishes booting. The server logs a warning naming this remedy before it starts generating, so the message survives a kill. If ts-morph fails without crashing the process, the server starts with empty swagger response schemas.

See Deployment for the full picture.

keryx start ​

Start the server.

bash
bunx keryx start

keryx actions ​

List all discovered actions with their routes and descriptions.

bash
bunx keryx actions

Action Commands ​

Every action you write is automatically registered as a CLI command — no extra configuration needed. Keryx discovers your actions and adds them to the CLI, so you can invoke any action directly from the terminal:

bash
bunx keryx <action-name> [--input value]

When you run an action this way, Keryx starts the server in CLI mode (initializers run, but the web server doesn't bind a port), executes the action, prints the JSON result, and exits.

Inputs as Flags ​

Action inputs map directly to CLI flags. Required inputs in your Zod schema become required CLI options; optional inputs become optional flags:

bash
bunx keryx user:create --name evan --email "evan@example.com" --password secret

Run --help on any action to see its available parameters:

bash
bunx keryx user:create --help

Quiet Mode ​

By default, Keryx logs server startup output alongside the action response. Use -q / --quiet to suppress everything except the action's JSON result — handy for piping into jq or other tools:

bash
bunx keryx status -q | jq

You can also set CLI_QUIET=true as an environment variable to make quiet mode the default.

Error Output ​

When an action throws a TypedError, the CLI includes the error's message, type, key, and value in the JSON output. Stack traces are included by default; set CLI_INCLUDE_STACK_IN_ERRORS=false to omit them.

Released under the MIT License.