Skip to main content

SERVE FLAGS

Flags

Flag Default Description
--config <path> Path to a YAML config file. Default: .swamp/serve.yaml (loaded silently if present). See Config file
--port <port> 9090 Port to listen on
--host <host> 127.0.0.1 Host to bind to
--cert-file <path> Path to PEM-encoded TLS certificate
--key-file <path> Path to PEM-encoded TLS private key
--auth-mode <mode> none Authentication mode: none (deprecated), token, or oauth. token and oauth require swamp auth login or SWAMP_API_KEY with serve:* scope; none has no Swamp auth requirement
--admins <principals> Comma-separated principal IDs for admin access
--grant-reload <mode> manual Policy snapshot reload: manual or auto
--grants-file <path> Path to an external grants YAML file loaded at startup. Grants from this file are merged with any grants in the grants/ directory
--no-schedule Disable scheduled workflow execution
--webhook <spec> Register a webhook endpoint (repeatable)
--allowed-collectives Comma-separated collective slugs for OAuth admission
--allowed-users Comma-separated user identifiers for OAuth admission
--oauth-provider <url> OAuth authorization server URL
--oauth-client-id <id> OAuth client ID (required for oauth mode)
--groups-field <field> Userinfo field name for group memberships (default: collectives)
--trust-proxy Trust X-Forwarded-For for client IP in token auth rate limiting
--ws-idle-timeout <duration> WebSocket idle timeout — how long the server waits for a pong before closing the connection. Accepts seconds (30), explicit units (2m, 5m), or 0 to disable
--queue-timeout <duration> 10m How long a placed step queues for a matching worker before timing out. Accepts seconds (60), explicit units (2m, 10m), or 0 to disable
--verify-on-enroll (off) Run a fleet probe on each enrolling worker before it becomes schedulable. Workers that fail the probe are marked unverified and excluded from dispatch until they re-enroll successfully
--hot-reload (off) Enable SIGHUP-based hot-reload for pulled extension bundles and the trusted collectives list. Writes a PID file to .swamp/serve.pid; use swamp serve reload to trigger a reload
--group-refresh-interval <d> 4h How often the server refreshes IdP group memberships from the OAuth provider's userinfo endpoint. Accepts duration units (30m, 4h, 1d), or 0 to disable
--restricted-model-types <types> Comma-separated model types that require admin on access:* to create or run. Non-admin users are denied with Access denied: <principal> does not have 'admin' on access:*. Types are normalized on parse — Command::Shell, COMMAND.SHELL, and command/shell all match
--restricted-commands <cmds> Comma-separated server commands that require admin authority (e.g. datastore.namespace.list,extension.install). Non-admin users are denied with Access denied: <principal> does not have 'admin' on access:<command>. Requires --auth-mode token or oauth
--trusted-hosts <hosts> Comma-separated hostnames to trust for Host header validation when binding off-loopback (e.g. host.docker.internal,host.minikube.internal)
--detach-runs (off) Deprecated. Accepted for backwards compatibility but has no effect. HA mode is now auto-detected based on the datastore configuration — when the datastore supports a control plane (e.g. S3), runs are automatically detached and reconciliation is enabled
--heartbeat-interval <duration> 30s How often to write an instance heartbeat to the control-plane store. Accepts duration units (15s, 30s, 1m). Only effective with a control-plane-capable datastore
--stale-ttl <duration> 90s How long a heartbeat can go without update before the instance is considered dead. Must be at least 2× --heartbeat-interval. Accepts duration units (60s, 90s, 3m). Only effective with a control-plane-capable datastore
--reconciliation-interval <duration> 60s How often to scan for dead peer instances and reconcile their orphaned runs. Accepts duration units (30s, 60s, 2m). Only effective with a control-plane-capable datastore

Off-loopback binding (--host set to anything other than 127.0.0.1 or ::1) requires both TLS (--cert-file + --key-file) and authentication (--auth-mode other than none, which is itself deprecated). The server refuses to start without them. By default, Host header validation rejects requests whose Host does not match the bound address — a DNS rebinding defense. Use --trusted-hosts to whitelist additional hostnames (such as host.docker.internal) so Docker and Kubernetes workers can connect via platform-specific hostnames without disabling the protection.

Health endpoint

GET /ready returns a JSON object indicating whether the server is ready to accept requests. The response includes an instanceId field — a unique identifier for this server instance, stable for the lifetime of the process:

{ "ready": true, "instanceId": "a1b2c3d4" }

The instanceId is used internally for heartbeat registration and run ownership. In a multi-instance deployment, each instance reports a distinct instanceId, which can help identify which instance is handling a request or owns a particular run.

Config file

swamp serve can read configuration from a YAML file instead of (or in addition to) CLI flags. By default, the server looks for .swamp/serve.yaml in the repository directory. Use --config <path> to specify a different location.

Resolution priority

When the same setting is provided through multiple sources, the highest-priority source wins:

  1. CLI flag (explicit)
  2. Environment variable
  3. Config file
  4. Built-in default

See Configuration Layers for background.

Loading behavior

Scenario Behavior
Default path (.swamp/serve.yaml) exists Loaded automatically
Default path does not exist Silently skipped — the server starts with defaults
Explicit --config <path> exists Loaded
Explicit --config <path> does not exist Hard error: Serve config file not found: <path>
Invalid YAML Hard error at startup
Unknown keys in the file Warning logged per key (Unknown key "<key>") — server continues

Supported keys

The config file uses kebab-case keys matching the CLI flag names. Not all flags are available in the config file — security-sensitive flags (authentication, TLS, admin identity) are CLI-only and environment-variable-only.

YAML key Type CLI equivalent
port integer --port
host string --host
grant-reload string --grant-reload
trust-proxy boolean --trust-proxy
hot-reload boolean --hot-reload
detach-runs boolean --detach-runs (deprecated)
verify-on-enroll boolean --verify-on-enroll
ws-idle-timeout string --ws-idle-timeout
queue-timeout string --queue-timeout
heartbeat-interval string --heartbeat-interval
stale-ttl string --stale-ttl
reconciliation-interval string --reconciliation-interval
trusted-hosts string[] --trusted-hosts
webhooks object[] --webhook (repeated)

Duration strings accept the same formats as the corresponding CLI flag: plain seconds (30), explicit units (2m, 5m), or 0 to disable.

List-typed keys (trusted-hosts) use YAML sequences instead of comma-separated values:

trusted-hosts:
  - host.docker.internal
  - host.minikube.internal

Flags not available in the config file

The following flags must be provided via CLI flags or environment variables. They are not recognized in the YAML config file:

--auth-mode, --admins, --cert-file, --key-file, --grants-file, --no-schedule, --allowed-collectives, --allowed-users, --oauth-provider, --oauth-client-id, --groups-field, --group-refresh-interval, --restricted-model-types, --restricted-commands

Webhook format in config files

On the CLI, webhooks use a colon-delimited string:

--webhook '<route>:<workflow>:<secret>[:<scheme>[:<header>[:<prefix>]]]'

In a config file, each webhook is a YAML object with named fields:

webhooks:
  - route: /hooks/github
    workflow: deploy-on-push
    secret: "@env=WEBHOOK_SECRET"
    scheme: github
  - route: /hooks/custom
    workflow: process-event
    secret: "@file=/run/secrets/webhook"
    scheme: generic
    header: X-Custom-Signature
    prefix: "sha256="
Field Required Description
route Yes URL path for the endpoint
workflow Yes Workflow to trigger on valid requests
secret Yes Signing secret or indirection (@env=, @file=)
scheme No Provider scheme (default: github)
header No Custom header name (generic scheme only)
prefix No Expected value prefix (generic scheme only)

See Webhooks for provider schemes, signature verification, and secret indirection.

Example config file

# .swamp/serve.yaml
port: 8080
host: "0.0.0.0"
grant-reload: auto
trust-proxy: true
hot-reload: true
# detach-runs is deprecated — HA mode is auto-detected from the datastore
heartbeat-interval: "30s"
stale-ttl: "90s"
reconciliation-interval: "60s"
trusted-hosts:
  - host.docker.internal
webhooks:
  - route: /hooks/github
    workflow: deploy-on-push
    secret: "@env=WEBHOOK_SECRET"
    scheme: github
# TLS and auth remain as CLI flags or env vars
swamp serve --auth-mode token --admins 'user:abc-123' \
  --cert-file server.crt --key-file server.key

Environment variables

Variable Description
SWAMP_GRANTS_FILE Default for --grants-file
SWAMP_SERVE_URL Default server URL for --server on client commands
SWAMP_SERVE_CERT_FILE Default for --cert-file
SWAMP_SERVE_KEY_FILE Default for --key-file
SWAMP_SERVER_TOKEN Server token (alternative to stored credential)
SWAMP_WS_IDLE_TIMEOUT Default for --ws-idle-timeout (default: 30s)
SWAMP_QUEUE_TIMEOUT Default for --queue-timeout (default: 10m)
SWAMP_SERVE_EXTRA_HEADERS Extra HTTP headers sent by clients through proxies/tunnels (newline-separated Name: value)
SWAMP_GROUP_REFRESH_INTERVAL Default for --group-refresh-interval (default: 4h)
SWAMP_TRUSTED_HOSTS Default for --trusted-hosts
SWAMP_HEARTBEAT_INTERVAL Default for --heartbeat-interval (default: 30s)
SWAMP_STALE_TTL Default for --stale-ttl (default: 90s)
SWAMP_RECONCILIATION_INTERVAL Default for --reconciliation-interval (default: 60s)

Authentication modes

none (default — deprecated)

Warning

Deprecated. --auth-mode none will be removed in a future release. See Set Up Token Authentication.

No authentication. Connections on loopback are accepted without credentials. Off-loopback binding is refused.

token

Token-based authentication. Users authenticate with a server token minted via swamp access token mint. Unauthenticated connections receive HTTP 401.

WebSocket token transports

The server accepts tokens on the WebSocket upgrade request via three transports:

Transport Header / parameter Format
Authorization header Authorization: Bearer <name>.<secret> Standard HTTP Bearer scheme
WebSocket subprotocol Sec-WebSocket-Protocol: bearer.<name>.<secret> Single subprotocol value, dot-delimited
Query parameter ?token=<name>.<secret> Appended to the WebSocket URL

When multiple transports are present on the same request, the server uses the first match in this priority order: Authorization header, Sec-WebSocket-Protocol, query parameter.

The Authorization header does not expose the token in URLs. Query-string tokens appear in proxy access logs, browser history, and Referer headers. See Token transports on WebSocket for background on when each transport applies.

See Connect with a token for connection examples.

oauth

OAuth-based authentication. Users authenticate via an OAuth device flow. The server validates tokens against the OAuth provider specified by --oauth-provider. See Set Up OAuth Authentication for the setup guide.

Credential storage

swamp auth server-login stores tokens in ~/.config/swamp/servers.json. Each entry maps a server URL to a token. When a command uses --server, the stored credential is used automatically. The --server flag can be omitted if SWAMP_SERVE_URL is set — the command resolves the URL the same way other remote commands do.

swamp auth server-login --server wss://swamp.example.com \
  --token 'paul-token.<secret>'

# Or, if SWAMP_SERVE_URL is already exported:
swamp auth server-login --token 'paul-token.<secret>'

Webhook endpoints

See Webhooks for the full reference — provider schemes, secret sources, signature verification, and CEL context.

The --webhook flag registers an HTTP endpoint that triggers a workflow on incoming requests:

--webhook '<route>:<workflow>:<secret>[:<scheme>[:<header>[:<prefix>]]]'

Supported schemes: github (default), linear, stripe, slack, generic. The generic scheme requires a custom header name and accepts an optional value prefix.

Secret indirection

The <secret> field supports indirection prefixes so signing secrets don't appear as literal values in process argv:

Prefix Source Example
@env=VAR_NAME Environment variable VAR_NAME @env=WEBHOOK_SECRET
@file=/path File contents (trailing newline trimmed) @file=/run/secrets/webhook
(plain string) Literal value (backward compatible) mysecret
swamp serve --webhook '/hooks/github:my-workflow:@env=WEBHOOK_SECRET'
swamp serve --webhook '/hooks/linear:wf:@file=/run/secrets/webhook:linear'

The server reads the resolved secret at startup. If the environment variable is unset or the file cannot be read, the server refuses to start.