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 |
|
--remote-only |
(off) | Disable local (loopback) execution — all workflow steps must declare placement (target, labels, or platform) and be dispatched to remote workers. Steps without placement error immediately. See Remote-only mode |
--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) |
|
--oauth-client-name <name> |
Override the OAuth client_name used during dynamic client registration. Default: swamp-serve-{repoName}-{hostname} (truncated to 128 chars). Useful in container/k8s environments where hostname is ephemeral |
|
--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 |
--hydration-timeout <duration> |
60s |
Maximum time to wait for initial datastore cache hydration at startup. Accepts seconds (60), explicit units (60s, 5m). Increase for large repos where the initial pull takes longer |
--max-concurrent-runs <n> |
100 |
Maximum number of concurrent detached runs across all principals |
--max-runs-per-principal <n> |
Maximum number of concurrent detached runs per authenticated principal. Unset by default (no per-principal limit) | |
--max-run-duration <duration> |
Maximum wall-clock time a detached run may execute before being aborted. Accepts seconds (3600), explicit units (1h, 30m). Unset by default (no limit) |
|
--grants-dir <path> |
Path to a directory of grants YAML files loaded at startup | |
--dashboard |
(off) | Enable the web dashboard at /dashboard. The dashboard is opt-in and adds no overhead when disabled. See Dashboard |
--enable-internal-api |
(off) | Enable the /internal/runs endpoint for full run history access |
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.
Resource limits
At startup, swamp serve automatically raises the process's open-file soft
limit to 8192 using setrlimit. Each connected client, worker WebSocket, and
datastore handle consumes a file descriptor, so the stock macOS soft limit of
256 is too low for most server workloads.
| Condition | Behavior |
|---|---|
| Hard limit ≥ 8192 (stock macOS: unlimited) | Soft limit raised to 8192 automatically; an info-level log confirms the raise |
| Hard limit < 8192 | Raise skipped; a warning is logged with instructions to raise the limit before starting |
No user action is needed on stock macOS, where the soft limit is 256 and the hard limit is unlimited — the server fixes it automatically. When the hard limit is also restricted (some container runtimes and hardened Linux hosts), raise it before starting the server:
# Shell session (applies to the current shell and its children)
ulimit -Sn 8192
# systemd unit (permanent)
# Add to the [Service] section of the unit file:
LimitNOFILE=8192Health 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:
- CLI flag (explicit)
- Environment variable
- Config file
- 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. Most flags are
available in the config file, including authentication and TLS settings via the
auth: and tls: nested blocks.
Top-level keys
| YAML key | Type | CLI equivalent |
|---|---|---|
port |
integer | --port |
host |
string | --host |
auth |
object | See auth: block |
tls |
object | See tls: block |
webhooks |
object[] | --webhook (repeated) |
triggers |
object | See triggers: block |
remote-only |
boolean | --remote-only |
schedule |
boolean | --no-schedule (inverted: schedule: false = --no-schedule) |
grant-reload |
string | --grant-reload |
grants-file |
string | --grants-file |
grants-dir |
string | --grants-dir |
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 |
trusted-hosts |
string[] | --trusted-hosts |
heartbeat-interval |
string | --heartbeat-interval |
stale-ttl |
string | --stale-ttl |
reconciliation-interval |
string | --reconciliation-interval |
max-concurrent-runs |
integer | --max-concurrent-runs |
max-runs-per-principal |
integer | --max-runs-per-principal |
max-run-duration |
string | --max-run-duration |
hydration-timeout |
string | --hydration-timeout |
dashboard |
boolean | --dashboard |
enable-internal-api |
boolean | --enable-internal-api |
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.internalauth: block
Authentication settings are nested under auth:. In the config file,
list-valued keys use YAML sequences; on the CLI the same flags accept
comma-separated strings.
| Sub-key | Type | CLI equivalent |
|---|---|---|
mode |
string | --auth-mode |
admins |
string[] | --admins |
allowed-collectives |
string[] | --allowed-collectives |
allowed-users |
string[] | --allowed-users |
oauth-provider |
string | --oauth-provider |
oauth-client-id |
string | --oauth-client-id |
oauth-client-name |
string | --oauth-client-name |
groups-field |
string | --groups-field |
restricted-model-types |
string[] | --restricted-model-types |
restricted-commands |
string[] | --restricted-commands |
group-refresh-interval |
string | --group-refresh-interval |
No environment variable exists for
auth.mode,auth.admins,auth.allowed-collectives,auth.allowed-users,auth.oauth-provider, orauth.oauth-client-id. These must be set via the config file or CLI flags.
auth:
mode: token
admins:
- "user:abc-123"
- "user:def-456"
restricted-model-types:
- command/shelltls: block
TLS certificate paths are nested under tls:.
| Sub-key | Type | CLI equivalent |
|---|---|---|
cert-file |
string | --cert-file |
key-file |
string | --key-file |
tls:
cert-file: /etc/certs/server.crt
key-file: /etc/certs/server.keytriggers: block
Trigger overrides define or override scheduled execution for workflows. Each key
is a workflow name; the value is an object with schedule (a cron expression)
and/or inputs (a mapping of input values).
Trigger overrides can also be managed on a running server via
swamp workflow trigger set and swamp workflow trigger remove with --server
(see Hot Reload).
triggers:
nightly-backup:
schedule: "0 2 * * *"
hourly-sync:
schedule: "0 * * * *"
inputs:
target: production
dry-run: false| Field | Required | Type | Description |
|---|---|---|---|
schedule |
No | string | Cron expression (must specify at least schedule or inputs) |
inputs |
No | object | Input values passed to the workflow on each triggered run |
Flags not available in the config file
The following flags are CLI-only and are not recognized in the YAML config file:
--config— identifies the config file itself--repo-dir— sets the repository directory that the config file resolves relative to--no-schedule— useschedule: falsein the config file instead
The global flags --no-telemetry and --log-level are also CLI-only.
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=, @vault=) |
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"
auth:
mode: token
admins:
- "user:abc-123"
tls:
cert-file: /etc/certs/server.crt
key-file: /etc/certs/server.key
grant-reload: auto
trust-proxy: true
hot-reload: true
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
triggers:
nightly-report:
schedule: "0 6 * * *"# All settings are in the config file — start with no extra flags
swamp serveEnvironment variables
| Variable | Description |
|---|---|
SWAMP_API_KEY |
Collective API token for server authentication. With serve:* scope, authenticates the server for instance registration and heartbeats. With oauth:manage scope (in addition to serve:*), enables headless OAuth client registration and admin username resolution — no browser interaction needed on first boot. Read fresh from the environment on each start; key rotation requires only a restart |
SWAMP_REMOTE_ONLY |
Default for --remote-only (true or 1 to enable) |
SWAMP_GRANTS_FILE |
Default for --grants-file |
SWAMP_GRANTS_DIR |
Default for --grants-dir |
SWAMP_SERVE_URL |
Default server URL for --server on client commands. Takes precedence over SWAMP_SERVER_URL |
SWAMP_SERVER_URL |
Routing alias for SWAMP_SERVE_URL. Used as fallback when SWAMP_SERVE_URL is not set. Precedence: --server flag > SWAMP_SERVE_URL > SWAMP_SERVER_URL |
SWAMP_SERVE_CERT_FILE |
Default for --cert-file / tls.cert-file |
SWAMP_SERVE_KEY_FILE |
Default for --key-file / tls.key-file |
SWAMP_SERVER_TOKEN |
Server token (alternative to stored credential) |
SWAMP_OAUTH_CLIENT_NAME |
Default for --oauth-client-name / auth.oauth-client-name. Default: swamp-serve-{repoName}-{hostname} |
SWAMP_WS_IDLE_TIMEOUT |
Default for --ws-idle-timeout (default: 30s) |
SWAMP_QUEUE_TIMEOUT |
Default for --queue-timeout (default: 10m) |
SWAMP_VERIFY_ON_ENROLL |
Default for --verify-on-enroll (true or 1 to enable) |
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) |
SWAMP_HYDRATION_TIMEOUT |
Default for --hydration-timeout (default: 60s) |
SWAMP_MAX_CONCURRENT_RUNS |
Default for --max-concurrent-runs (default: 100) |
SWAMP_MAX_RUNS_PER_PRINCIPAL |
Default for --max-runs-per-principal (no default — no per-principal limit) |
SWAMP_MAX_RUN_DURATION |
Default for --max-run-duration (no default — no limit) |
SWAMP_DASHBOARD |
Default for --dashboard (true or 1 to enable) |
SWAMP_ENABLE_INTERNAL_API |
Default for --enable-internal-api (true or 1 to enable) |
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, jira,
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 |
@vault=<name>:<key> |
Named vault key | @vault=prod-secrets:webhook-key |
| (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'
swamp serve --webhook '/hooks/github:deploy:@vault=prod-secrets:webhook-key'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.
Related
- Use a Config File — replace long CLI flag chains with a YAML config file
- Configuration Layers — why the four resolution sources have the precedence they do
- Hot Reload —
swamp serve reloadsubcommand, extension bundle reload, and trust list refresh - Webhooks — provider schemes, secret sources, signature verification, and CEL context
- Set Up TLS for swamp serve — certificate setup and reverse proxy configuration
- TLS and Proxies — client-side trust stores and server-side TLS details
- Access Commands — CLI reference for tokens, grants, and groups
- Load External Grants at Startup — load grants from a file outside the repository