> ## Documentation Index
> Fetch the complete documentation index at: https://smithers-feat-claude-workflow-mirror.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Server & Gateway API

> Host Smithers workflows over HTTP and WebSocket via a single-run server, a single-workflow serve app, and the multi-run Gateway control plane.

Three host surfaces ship from `packages/server`, each a tighter fit for a
different deployment.

* **`startServer`**: a self-contained HTTP server with REST routes for run
  lifecycle, SSE event streams, and approvals. One server, many workflows by
  path. Reach for it when you want a plain HTTP API and no client SDK.
* **`createServeApp`**: a Hono app bound to one already-loaded workflow and
  run. This is what `bunx smithers-orchestrator up --serve` mounts. Compose it
  into a larger Hono app or call `app.fetch` in tests.
* **`Gateway`**: the headless control plane. Authenticate once, stream events
  over WebSocket with resume-on-reconnect, decide approvals, inject signals,
  run cron schedules, and serve many registered workflows. This is the surface
  the [Gateway client](/reference/gateway-client) and custom UIs talk to.

```ts theme={null}
import { startServer, createServeApp, Gateway } from "smithers-orchestrator";
```

<Note>
  `createServeApp` needs a `SmithersDb` adapter and a loaded `SmithersWorkflow`;
  both come from your workflow module (`new SmithersDb(workflow.db)`). The
  `Gateway` is constructed with `new Gateway(opts)`, then you `register()`
  workflows and `listen()`.
</Note>

## startServer

Boot a listening `http.Server` exposing REST run lifecycle, SSE streams, and
approval routes. Synchronous: it returns the bound server immediately.

```ts theme={null}
function startServer(opts?: ServerOptions): import("node:http").Server;
```

<ParamField path="opts" type="ServerOptions">
  <Expandable title="ServerOptions">
    <ParamField path="port" type="number" default="7331">
      Port to bind.
    </ParamField>

    <ParamField path="db" type="unknown">
      Database handle. When set, enables `GET /v1/runs` and approvals listing.
    </ParamField>

    <ParamField path="authToken" type="string">
      Bearer token required on every route except `/health`. Falls back to
      `process.env.SMITHERS_API_KEY`; auth is disabled when neither is set.
    </ParamField>

    <ParamField path="maxBodyBytes" type="number" default="1048576">
      Maximum accepted request body size. Larger bodies get `PAYLOAD_TOO_LARGE`.
    </ParamField>

    <ParamField path="rootDir" type="string">
      Sandbox root that workflow paths and tools resolve against. Paths outside
      it are rejected with `WORKFLOW_PATH_OUTSIDE_ROOT`.
    </ParamField>

    <ParamField path="allowNetwork" type="boolean" default="false">
      Allow network access from the built-in `bash` tool.
    </ParamField>

    <ParamField path="headersTimeout" type="number" default="30000">
      Maximum time (ms) the HTTP parser may take to receive complete request
      headers. Mitigates slowloris.
    </ParamField>

    <ParamField path="requestTimeout" type="number" default="60000">
      Maximum time (ms) to receive and parse a single request, body included.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="http.Server" type="object">
  A listening Node `http.Server`. `headersTimeout` and `requestTimeout` are
  applied to it to bound slow header/body uploads. The full route table (run
  start/resume/cancel, SSE events, frames, approvals, signals, `/metrics`) lives
  in [Server Mode](/integrations/server).
</ResponseField>

```ts theme={null}
const server = startServer({
  port: 7331,
  authToken: process.env.SMITHERS_API_KEY,
  rootDir: process.cwd(),
});
```

**Source** [`index.js`](https://github.com/smithersai/smithers/blob/main/packages/server/src/index.js) · [`ServerOptions.ts`](https://github.com/smithersai/smithers/blob/main/packages/server/src/ServerOptions.ts) · **Tests** [`server.test.js`](https://github.com/smithersai/smithers/blob/main/packages/server/tests/server.test.js) · **See also** [Server Mode](/integrations/server), [Control-plane deployment](/deployment/control-plane)

## createServeApp

Build a Hono app for a single, already-loaded workflow and run. Returns a
standard Hono app: mount it with `Bun.serve`, route it into another Hono app, or
drive `app.fetch` directly in tests. This backs `bunx smithers-orchestrator up --serve`.

```ts theme={null}
function createServeApp(opts: ServeOptions): import("hono").Hono;
```

<ParamField path="opts" type="ServeOptions" required>
  <Expandable title="ServeOptions">
    <ParamField path="workflow" type="SmithersWorkflow<unknown>" required>
      The loaded workflow instance.
    </ParamField>

    <ParamField path="adapter" type="SmithersDb" required>
      Smithers DB adapter, e.g. `new SmithersDb(workflow.db)`.
    </ParamField>

    <ParamField path="runId" type="string" required>
      The active run id this app serves.
    </ParamField>

    <ParamField path="abort" type="AbortController" required>
      Shared cancellation controller. Aborting it cancels the served run.
    </ParamField>

    <ParamField path="authToken" type="string">
      Bearer token for every route except `/health`. Falls back to
      `SMITHERS_API_KEY`; disabled when unset. Accepts `Authorization: Bearer`
      or `x-smithers-key`.
    </ParamField>

    <ParamField path="metrics" type="boolean" default="true">
      Expose `/metrics` (Prometheus exposition).
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="Hono" type="object">
  A Hono app. Routes (`/health`, run status, SSE events, approvals, signals,
  `/metrics`) are documented in [Serve Mode](/integrations/serve).
</ResponseField>

```ts theme={null}
import { SmithersDb, createServeApp } from "smithers-orchestrator";

const abort = new AbortController();
const app = createServeApp({
  workflow,
  adapter: new SmithersDb(workflow.db),
  runId: "RUN_ID",
  abort,
  authToken: process.env.SMITHERS_API_KEY,
});
Bun.serve({ port: 7331, fetch: app.fetch });
```

**Source** [`serve.js`](https://github.com/smithersai/smithers/blob/main/packages/server/src/serve.js) · [`ServeOptions.ts`](https://github.com/smithersai/smithers/blob/main/packages/server/src/ServeOptions.ts) · **Tests** [`serve.test.js`](https://github.com/smithersai/smithers/blob/main/packages/server/tests/serve.test.js) · **See also** [Serve Mode](/integrations/serve), [Server Mode](/integrations/server)

## Gateway

The multi-run control plane. Construct it, `register()` one or more workflows,
then `listen()`. It serves RPC over `POST /v1/rpc/<method>` (and `POST /rpc`)
and over WebSocket on the same origin, with a `/health`, `/metrics`,
`/workflows`, and per-workflow webhook endpoints. The RPC methods (`launchRun`,
`getRun`, `listRuns`, `submitApproval`, `submitSignal`, cron, time-travel, and
more) are documented per method; see [`launchRun`](/rpc/launch-run) for the
request/response and scope shape they share.

```ts theme={null}
class Gateway {
  constructor(options?: GatewayOptions);
  register(key: string, workflow: SmithersWorkflow, options?: GatewayRegisterOptions): this;
  extend(namespace: string, definition: unknown): this;
  listen(options?: { port?: number; host?: string; path?: string }): Promise<import("node:http").Server>;
  close(): Promise<void>;
}
```

`register`, `extend`, and the constructor are chainable. `listen` defaults to
port `7331`; pass `path` for a Unix socket. `close` aborts active runs, drains
inflight work, and tears down connections and the scheduler.

```ts theme={null}
import { Gateway } from "smithers-orchestrator";

const gateway = new Gateway({
  heartbeatMs: 15_000,
  auth: {
    mode: "token",
    tokens: { "operator-token": { role: "operator", scopes: ["*"] } },
  },
});

gateway.register("deploy", deploy, { schedule: "0 8 * * 1-5" });
await gateway.listen({ port: 7331 });
```

### GatewayOptions

<ParamField path="options" type="GatewayOptions">
  <Expandable title="GatewayOptions">
    <ParamField path="protocol" type="number" default="1">
      Wire protocol version advertised to clients.
    </ParamField>

    <ParamField path="features" type="string[]" default="[&#x22;streaming&#x22;, &#x22;runs&#x22;]">
      Feature flags advertised on `/health` and in the connect handshake.
    </ParamField>

    <ParamField path="heartbeatMs" type="number" default="15000">
      WebSocket heartbeat interval. Also clamps the cron poll interval (1 s to
      15 s).
    </ParamField>

    <ParamField path="auth" type="GatewayAuthConfig">
      Authentication mode. Omit to leave the gateway unauthenticated. See
      [auth](#gatewayauthconfig) below.
    </ParamField>

    <ParamField path="ui" type="GatewayUiConfig">
      Custom gateway-level UI. `true` mounts the built-in operator console at
      `/`. See [UI config](#ui-config) below.
    </ParamField>

    <ParamField path="workspaceRoot" type="string">
      Absolute path to the workspace root that holds `.smithers/` and
      `smithers.db`. Disk-backed registry reads (e.g. the `listPrompts` RPC)
      resolve against it. Defaults to `process.cwd()`.
    </ParamField>

    <ParamField path="operatorUi" type="GatewayOperatorUiConfig | false" default="{ path: &#x22;/console&#x22; }">
      Built-in operator browser console. Set `false` to disable.
    </ParamField>

    <ParamField path="defaults" type="GatewayDefaults">
      Per-gateway defaults. See [defaults](#defaults) below.
    </ParamField>

    <ParamField path="maxBodyBytes" type="number" default="1048576">
      Maximum accepted body for `POST /rpc`.
    </ParamField>

    <ParamField path="maxPayload" type="number" default="1048576">
      Maximum size of a single WebSocket frame.
    </ParamField>

    <ParamField path="maxConnections" type="number" default="1000">
      Maximum concurrent connections. Over-limit upgrades get `503`.
    </ParamField>

    <ParamField path="eventWindowSize" type="number" default="10000">
      Per-run replay window for run event streams, in frames.
    </ParamField>

    <ParamField path="outOfProcessEventBridge" type="boolean" default="true">
      Bridge persisted run events from the workspace DB into live streams for
      runs executed by another process.
    </ParamField>

    <ParamField path="outOfProcessEventBridgePollMs" type="number" default="1000">
      Poll interval (ms) for the out-of-process event bridge.
    </ParamField>

    <ParamField path="headersTimeout" type="number" default="30000">
      Maximum time (ms) to receive complete request headers. Applied to the
      Node HTTP server on `listen()`.
    </ParamField>

    <ParamField path="requestTimeout" type="number" default="60000">
      Maximum time (ms) to receive and parse a single request.
    </ParamField>
  </Expandable>
</ParamField>

#### GatewayAuthConfig

A discriminated union on `mode`. Runs started through an authenticated gateway
expose `ctx.auth = { triggeredBy, role, scopes, createdAt }`.

<Expandable title="mode: &#x22;token&#x22;">
  <ParamField path="tokens" type="Record<string, GatewayTokenGrant>" required>
    Map of bearer token to its grant.

    <Expandable title="GatewayTokenGrant">
      <ParamField path="role" type="string" required>
        Role assigned to requests bearing this token.
      </ParamField>

      <ParamField path="scopes" type="string[]" required>
        Granted scopes; `["*"]` grants all.
      </ParamField>

      <ParamField path="userId" type="string">
        Stable user id recorded as the actor.
      </ParamField>

      <ParamField path="tokenId" type="string">
        Identifier for the token, for revocation and audit.
      </ParamField>

      <ParamField path="issuedAtMs" type="number">
        Issue time (epoch ms).
      </ParamField>

      <ParamField path="expiresAtMs" type="number">
        Expiry time (epoch ms); past tokens are rejected.
      </ParamField>

      <ParamField path="revokedAtMs" type="number">
        Revocation time (epoch ms); revoked tokens are rejected.
      </ParamField>
    </Expandable>
  </ParamField>

  <ParamField path="allowedOrigins" type="string[]" default="[]">
    Origin allowlist (defense-in-depth). `[]` enforces no check. When non-empty,
    a request or WebSocket upgrade whose browser `Origin` header is not on the
    list is rejected; requests with no `Origin` (server-to-server / CLI) are
    allowed.
  </ParamField>
</Expandable>

<Expandable title="mode: &#x22;jwt&#x22;">
  Validates `alg=HS256`, HMAC, `iss`, `aud`, `exp`, `nbf`.

  <ParamField path="issuer" type="string" required>
    Expected `iss` claim.
  </ParamField>

  <ParamField path="audience" type="string | string[]" required>
    Accepted `aud` claim(s).
  </ParamField>

  <ParamField path="secret" type="string" required>
    HS256 shared secret.
  </ParamField>

  <ParamField path="scopesClaim" type="string" default="&#x22;scope&#x22;">
    Claim holding scopes (array or space/comma-separated string).
  </ParamField>

  <ParamField path="roleClaim" type="string" default="&#x22;role&#x22;">
    Claim holding the role.
  </ParamField>

  <ParamField path="userClaim" type="string" default="&#x22;sub&#x22;">
    Claim holding the user id.
  </ParamField>

  <ParamField path="defaultRole" type="string" default="&#x22;operator&#x22;">
    Role used when the role claim is absent.
  </ParamField>

  <ParamField path="defaultScopes" type="string[]" default="[]">
    Scopes used when the scope claim is absent.
  </ParamField>

  <ParamField path="clockSkewSeconds" type="number" default="60">
    Allowed `exp`/`nbf` skew. Negative values clamp to 0.
  </ParamField>

  <ParamField path="allowedOrigins" type="string[]" default="[]">
    Origin allowlist (defense-in-depth). `[]` enforces no check. When non-empty,
    a request or WebSocket upgrade whose browser `Origin` header is not on the
    list is rejected; requests with no `Origin` (server-to-server / CLI) are
    allowed.
  </ParamField>
</Expandable>

<Expandable title="mode: &#x22;trusted-proxy&#x22;">
  Identity is read from request headers. Only safe behind a proxy you control
  (Cloudflare Access, an internal API gateway) that strips and rewrites these
  headers.

  <ParamField path="trustedHeaders" type="string[]" default="[&#x22;x-user-id&#x22;,&#x22;x-user-scopes&#x22;,&#x22;x-user-role&#x22;]">
    Headers read as `[user, scopes, role]`.
  </ParamField>

  <ParamField path="allowedOrigins" type="string[]" default="[]">
    Origin allowlist (defense-in-depth), enforced the same way across all auth
    modes. `[]` enforces no check. When non-empty, a request or WebSocket upgrade
    whose browser `Origin` header is not on the list is rejected; requests with
    no `Origin` (server-to-server / CLI) are allowed.
  </ParamField>

  <ParamField path="defaultRole" type="string" default="&#x22;operator&#x22;">
    Role used when the role header is absent.
  </ParamField>

  <ParamField path="defaultScopes" type="string[]" default="[&#x22;*&#x22;]">
    Scopes used when the scopes header is absent.
  </ParamField>
</Expandable>

#### UI config

<Expandable title="GatewayUiConfig">
  `true` mounts the built-in operator console. Otherwise an object:

  <ParamField path="entry" type="string" required>
    Browser entry module for the React app. Smithers bundles it with Bun and
    serves it from the gateway origin.
  </ParamField>

  <ParamField path="path" type="string">
    Mount path. Gateway-level UI defaults to `/`; workflow-level UI (via
    `register`) defaults to `/workflows/<workflowKey>`.
  </ParamField>

  <ParamField path="title" type="string">
    Document title for the generated HTML shell.
  </ParamField>

  <ParamField path="props" type="Record<string, unknown>">
    JSON-serializable boot data exposed to the browser.
  </ParamField>
</Expandable>

<Expandable title="GatewayOperatorUiConfig">
  <ParamField path="path" type="string" default="&#x22;/console&#x22;">
    URL path for the built-in operator console.
  </ParamField>

  <ParamField path="title" type="string">
    Document title for the generated HTML shell.
  </ParamField>

  <ParamField path="props" type="Record<string, unknown>">
    JSON-serializable boot data exposed to the browser.
  </ParamField>
</Expandable>

#### defaults

<Expandable title="GatewayDefaults">
  <ParamField path="cliAgentTools" type="&#x22;all&#x22; | &#x22;explicit-only&#x22;">
    Default tool exposure for CLI-launched agents.
  </ParamField>

  <ParamField path="outOfProcessEventBridge" type="boolean">
    Default for the out-of-process event bridge when the top-level option is
    unset.
  </ParamField>

  <ParamField path="outOfProcessEventBridgePollMs" type="number">
    Default bridge poll interval (ms) when the top-level option is unset.
  </ParamField>
</Expandable>

### register

Register a workflow under `key`. Wires up its DB tables, optional cron schedule,
webhook config, and embedded UI bundle. Returns `this`, so calls chain. A
schedule writes a cron row keyed `gateway:<key>`; cron-fired runs get
`ctx.auth.role = "system"`, `triggeredBy = "cron:gateway"`, `scopes = ["*"]`.

<ParamField path="key" type="string" required>
  Workflow key. Becomes the path segment for workflow-level UI and webhooks.
</ParamField>

<ParamField path="workflow" type="SmithersWorkflow" required>
  The loaded workflow instance.
</ParamField>

<ParamField path="options" type="GatewayRegisterOptions">
  <Expandable title="GatewayRegisterOptions">
    <ParamField path="schedule" type="string">
      Cron expression. Fires the workflow on schedule.
    </ParamField>

    <ParamField path="webhook" type="GatewayWebhookConfig">
      Inbound webhook config for `POST /webhooks/<key>`. See below.
    </ParamField>

    <ParamField path="ui" type="GatewayUiConfig">
      Workflow-level UI, mounted at `/workflows/<key>` by default.
    </ParamField>
  </Expandable>
</ParamField>

<Expandable title="GatewayWebhookConfig">
  <ParamField path="secret" type="string" required>
    Shared secret for HMAC signature verification.
  </ParamField>

  <ParamField path="signatureHeader" type="string">
    Header carrying the request signature.
  </ParamField>

  <ParamField path="signaturePrefix" type="string">
    Prefix stripped from the signature value before comparison (e.g.
    `"sha256="`).
  </ParamField>

  <ParamField path="signal" type="GatewayWebhookSignalConfig">
    Map a verified webhook into a run signal.

    <Expandable title="GatewayWebhookSignalConfig">
      <ParamField path="name" type="string" required>
        Signal name delivered to the run.
      </ParamField>

      <ParamField path="correlationIdPath" type="string">
        JSON path to a correlation id used to match the target run.
      </ParamField>

      <ParamField path="runIdPath" type="string">
        JSON path to an explicit run id in the payload.
      </ParamField>

      <ParamField path="payloadPath" type="string">
        JSON path to the sub-payload delivered with the signal.
      </ParamField>
    </Expandable>
  </ParamField>

  <ParamField path="run" type="GatewayWebhookRunConfig">
    Start a new run from a verified webhook.

    <Expandable title="GatewayWebhookRunConfig">
      <ParamField path="enabled" type="boolean">
        Whether a webhook may launch a run.
      </ParamField>

      <ParamField path="inputPath" type="string">
        JSON path to the sub-payload used as the run input.
      </ParamField>
    </Expandable>
  </ParamField>
</Expandable>

### listen / close

<ParamField path="options" type="object">
  <Expandable title="listen options">
    <ParamField path="port" type="number" default="7331">
      TCP port to bind.
    </ParamField>

    <ParamField path="host" type="string">
      Host/interface to bind.
    </ParamField>

    <ParamField path="path" type="string">
      Unix socket path. When set, `port`/`host` are ignored.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="listen" type="Promise<http.Server>">
  Resolves once the server is bound and the scheduler and event bridge have
  started.
</ResponseField>

<ResponseField name="close" type="Promise<void>">
  Aborts active runs, awaits inflight work, closes connections, and stops the
  scheduler and watchers.
</ResponseField>

**Source** [`gateway.js`](https://github.com/smithersai/smithers/blob/main/packages/server/src/gateway.js) · [`GatewayOptions.ts`](https://github.com/smithersai/smithers/blob/main/packages/server/src/GatewayOptions.ts) · [`GatewayAuthConfig.ts`](https://github.com/smithersai/smithers/blob/main/packages/server/src/GatewayAuthConfig.ts) · **Tests** [`gateway.test.jsx`](https://github.com/smithersai/smithers/blob/main/packages/server/tests/gateway.test.jsx) · **See also** [Gateway](/integrations/gateway), [Control-plane deployment](/deployment/control-plane), [`launchRun`](/rpc/launch-run), [Gateway client](/reference/gateway-client)

***

For the full route tables and wire shapes, see [Server Mode](/integrations/server),
[Serve Mode](/integrations/serve), and [Gateway](/integrations/gateway). The
exact type definitions are mirrored in the [Types reference](/reference/types).
