Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

boatramp.cfg schema

boatramp.cfg is the server config, read by boatramp serve. It is RON. Every value can also be set as a flag or an environment variable, which take precedence. The whole file is optional — serve runs with defaults without it.

boatramp serve --config boatramp.cfg

Precedence for any value: flag / environment variable > boatramp.cfg > built-in default.

Top-level sections, all optional:

SectionPurpose
serveBind address, data dir, auth keys, upload limits.
securityOperator security posture (profile + per-knob overrides).
secretsEnvelope encryption for cert private keys at rest.
handlersWasm handler runtime (needs the handlers feature).
clusterSelf-hosted Raft cluster (needs the cluster feature).
computeContainer / microVM execution backends.

serve

FieldTypeDefaultDescription
addrsocket address127.0.0.1:8080Bind address. Env BOATRAMP_ADDR.
data_dirpath./dataRoot for the filesystem blob + KV backends. Env BOATRAMP_DATA_DIR.
auth_root_private_key"<alg>:<hex>"Root signing key: this node verifies and mints tokens. Env BOATRAMP_AUTH_ROOT_PRIVATE_KEY.
auth_root_public_key"<alg>:<hex>"Root verify key: this node verifies only, cannot mint. Env BOATRAMP_AUTH_ROOT_PUBLIC_KEY.
bootstrap_secretstringSingle-use secret enabling token bootstrap. Prefer the env var / flag so it is not written to disk. Env BOATRAMP_BOOTSTRAP_SECRET.
signersigner enumExternal signer (KMS/HSM/Vault) in place of an in-process key. See below.
max_upload_bytesintegerunlimitedReject blob uploads larger than this.
default_sitestringSite served for a Host matching no domain, instead of 404.
protect_previewsboolfalseRequire a control-plane token to view /_deploy previews.
pop_originstringThe fleet’s canonical public origin (e.g. https://cp.example.com) a per-request proof-of-possession must bind (aud). Required for holder-bound (cnf/PoP) tokens; compared against the proof, never a Host/X-Forwarded-* header. Env BOATRAMP_POP_ORIGIN. See PoP-bind a token.
blob_notify_tierdry-run | provision | verify-only | refuseCloud blob-change notification provisioning tier for blob triggers on a cloud object store (S3→SQS / GCS→Pub/Sub / Azure→Event Grid). Absent ⇒ no provisioning (blob triggers work only on a self-watching backend like fs). See Cloud blob triggers.
blob_notify_account_idstringScopes the provisioned notification pipeline: the AWS account id (S3 queue policy) or GCP project id (GCS topic + notificationConfig). Unused by Azure (the queue shares the account’s shared-key auth).

Warning: with no auth_root_* key configured, control-plane auth is disabled. Under the default multi-tenant posture, serve refuses to start that way on a non-loopback addr. Configure a key, bind 127.0.0.1, or select a looser security posture.

serve.signer

Selects an external signer so the root key never sits in process memory. Written as a RON enum. Credentials (tokens, PINs) come from the named environment variables, never this file.

VariantFields
Localprivate_key: "<alg>:<hex>"
Vaultaddress, key, token_env, alg (Es256 | Ed25519)
AwsKmskey_id, region (optional)
GcpKmskey_version, access_token_env
AzureKvvault_url, key, key_version, access_token_env
Pkcs11module, token_label, key_label, pin_env, alg
serve: ( signer: Vault(
    address: "https://vault:8200",
    key: "boatramp-root",
    token_env: "VAULT_TOKEN",
    alg: Es256,
) )

See Hold the signing key in a KMS/HSM/Vault.

security

The operator security posture: a profile preset plus per-knob overrides. Absent means the strict multi-tenant default. This section is operator-only — it is never part of site config, so a site writer cannot relax it. Inspect the resolved posture with boatramp security explain.

FieldTypeDefaultDescription
profilestringmulti-tenantmulti-tenant (strict), single-tenant (one trusted operator), dev (loopback-loose), or a name from profiles.
overridesknob tableIndividual knobs; a knob is the source of truth, a profile is sugar.
profilesmapCustom named profiles, each a set of overrides over the strict baseline.

Override knobs (byte caps: 0 = unlimited):

KnobDescription
allow_unauthenticated_public_bindPermit a non-loopback bind with auth off.
max_upload_bytesBlob upload cap.
allow_site_unix_upstreamsLet a site’s gateway target unix: sockets.
allow_site_private_upstreamsLet a site’s gateway target private IPs.
max_handler_blob_bytesPer-handler blobstore write cap.
max_component_bytesWasm component size cap.
oidc_require_audienceRequire an aud claim on OIDC exchange.
domain_verify_allow_privateAllow domain-verification probes to private hosts.
domain_verify_self_serveServe pending HTTP ownership challenges from the edge (before host routing) so an unattached host can verify itself. On by default; disable to require out-of-band token placement.
allow_shared_kernel_computePermit container (shared-kernel) compute; off ⇒ microVM only.
ratelimit_fail_openServe rather than reject if the rate-limit store is unavailable.
allow_implicit_routingResolve an unmatched host to a site without a registered domain (first-label <site>.host / sole site). Off under multi-tenant; a loopback bind enables it regardless. See addressing.
require_popRequire every control-plane token to be holder-bound (cnf) and present a valid per-request proof-of-possession. Off by default (a cnf token always requires a proof regardless; this knob additionally bans plain bearer tokens fleet-wide). Needs pop_origin set. See PoP-bind a token.

See Choose & inspect a security posture and The security posture model.

secrets

Envelope-encrypt cluster-managed certificate private keys so they are never cleartext in the replicated control plane. Absent means keys are stored cleartext.

FieldTypeDescription
envelopestringlocal (machine-local AES-256-GCM KEK) or vault (Vault Transit).
kek_filepathLocal KEK file (auto-generated 0600). In a cluster the same file must be on every node.
vaulttableFor envelope: "vault": addr, key (a Transit key), token_env.

See Encrypt secrets at rest.

handlers

Wasm handler runtime. Parsed always, consumed only with the handlers feature.

FieldTypeDefaultDescription
poolingboolfalseUse the wasmtime pooling allocator (faster instantiation, large virtual-memory reservation).
sync_max_timeout_msint10000Safety-max wall-clock for a connection-bearing invocation (a site handler or a synchronous function/webhook invoke). A route/function may declare a lower timeout, never a higher one. Kept tight: a client + proxy + the shared request pool block while it runs.
async_max_timeout_msint900000Safety-max for a durable async invocation — the drain running ?mode=async calls, workflow steps, cron/queue/blob triggers, and messaging consumers. No client is connected and the work is retried + dead-lettered, so this can be far larger (default 15 min). Runs on its own concurrency budget, so a long job never starves live traffic.
async_max_concurrencyint8Max concurrent in-flight async-lane invocations — a pool separate from (and smaller than) the request pool, so a burst of long background jobs can’t exhaust the slots live site traffic needs.
async_max_fuelintOptional CPU fuel ceiling for an async-lane invocation. A large async timeout bounds only wall-clock; pair it with a fuel bound to keep a CPU-bound guest from spinning the whole window. Omit ⇒ unmetered.
outbound_timeout_msintOptional ceiling on a guest’s outbound wasi:http call (connect + first-byte), independent of the invocation timeout, so a hung upstream is bounded on its own terms. The streaming (between-bytes) timeout is left at the default so a slow token stream isn’t cut. Omit ⇒ wasmtime default.
bindings.sqltableThe sql host binding. Omit for single-node (a per-site embedded libsql file); set url for a shared sqld.

bindings.sql fields: dir, url, admin_url, replica_url, token_env, admin_token_env, preview_mode (empty | branch | shared), preview_init, databases. See Use handler bindings.

External SQL databases

bindings.sql.databases is a map of name → external database, each a Postgres/MySQL a guest opens by that name (sql.open("<name>")) instead of a per-site libsql one. Needs the sql-postgres / sql-mysql build feature. Isolation is the operator’s — such a database is shared across every guest granted the sql binding — so it bypasses the per-site libsql boundary; libsql stays the managed default. A name here shadows the same name on the libsql default.

Each database has one of two sources, mutually exclusive:

  • Bring-your-own (url_env) — you run the database anywhere; boatramp reads its connection URL from an env var.
  • Compute-backed (compute) — the database is a compute workload boatramp runs (see compute). boatramp resolves the workload’s live endpoint on demand and builds the connection, so there is no URL to hand-map and it follows the workload across restarts. With password_env set you bring the credential; omit it and boatramp fully manages the credential — it generates a strong password once, seals it with the secrets envelope, injects it into the DB workload’s server-init env at launch, and connects the handler with it, so you set no DB secret at all. A managed database therefore requires a [secrets] envelope (it refuses to store a credential it cannot seal) and a persistent volume on the DB workload (so the password the server was initialized with survives a restart).
FieldTypeDefaultDescription
kindstringEngine: postgres (aliases postgresql/pg) or mysql (alias mariadb). Required.
url_envstringBring-your-own source. Env var holding the connection URL, e.g. postgres://user:pw@host/db. A secret — never the URL in-file. Required unless compute is set.
read_url_envstringEnv var holding a read-replica URL. When set, open-read-only routes there; writes stay on url_env.
computestringCompute-backed source. Name of a compute workload (a Postgres/MySQL boatramp runs) to source this database from. Mutually exclusive with url_env.
databasestringCompute-backed: the database name inside the server (non-secret). Required with compute.
userstringCompute-backed: the connecting user (non-secret). Required with compute.
password_envstringCompute-backed: env var holding the password for user. Omit to let boatramp generate + manage the credential (needs [secrets]); set it to bring your own.
pool_maxint8Maximum pooled connections.
read_onlyboolfalseOpen every transaction READ ONLY (the engine rejects writes).
allow_previewboolfalsePermit preview deployments to reach it. Default refuses them, so a preview can’t touch live external data.
connect_timeout_secsint10Connection/acquire timeout, in seconds.

cluster

Self-hosted Raft cluster. Parsed always, consumed only with the cluster feature. The peer mesh runs over RFC 7250 raw-public-key mutual TLS. A cluster is defined by its root of trust — there is no peer map; nodes self-identify and join by redeeming a ticket.

FieldTypeDefaultDescription
listensocket addressBind for the Raft peer mesh (distinct from serve.addr).
root_pubkeyslist of stringsserve.auth_root_public_keyThe cluster root anchor set (es256:/ed25519: hex). Every join/trust decision verifies against it. A set enables make-before-break root rotation.
seedslist of stringsControl-plane addresses of existing members. Present ⇒ this node joins; absent + --cluster-init ⇒ it founds.
join_tokenstringThe single-use bearer join token used when seeds are set. Keep the secret out of the file: env:VAR, path:/file, or an inline literal.
store_dirpath<data-dir>/raftThis node’s durable Raft store. Never shared between nodes.
meshtableMesh identity + TLS: key_file, key_rotation, join_token_ttl, gate_client_writes.

The node id is derived from the node’s mesh key — there is no node_id field. Founding and joining are driven from the command line: serve --cluster-init founds a new cluster, serve --cluster-join <ticket> joins one (from cluster add). The old static-genesis fields (node_id, peers, voters, bootstrap) have been removed.

Warning: a non-loopback listen refuses to start with an empty trust set (found with --cluster-init or join with --cluster-join <ticket>). Never point two nodes at one store_dir.

See Deploy a self-hosted cluster and Mesh identity & the single root anchor.

compute

Container / microVM execution backends. Present ⇒ this node advertises compute capacity to the scheduler; backends are capability-detected: the native container backend on Linux; the KVM microVM (vmm-embedded) where /dev/kvm exists; the macOS-native microVM (vmm-vz) on Apple silicon + macOS 15+, which boots each replica as a Linux VM via Virtualization.framework (strong per-VM isolation, the same user surface as the KVM backend — no config change); and remote docker wherever a Docker daemon is reachable. macOS 26 is recommended for the vmm-vz backend: macOS 15’s vmnet cannot do container-to-container networking, so multi-replica cross-VM comms needs 26 (single-node serve works on 15). Nothing in the spec, CLI, or the fields below differs by backend — the environment difference lives behind the backend.

FieldTypeDefaultDescription
bridgestringbr-boatrampBridge the guest veths / VM taps attach to.
subnetstring10.0.0.0/24Guest IP subnet.
vcpusintegerdetectvCPUs this node advertises as schedulable (0 = detect).
mem_mibinteger1024Memory (MiB) advertised as schedulable (0 = 1 GiB).
sql_shim_urlurlGuest-reachable base URL of the compute sql-shim — set ⇒ a workload’s --bind sql reaches the managed database through a listener bound on 0.0.0.0:<port>. Use the address the guest reaches the host at: the compute bridge gateway for the native container backend (http://10.0.0.1:8081), the docker bridge gateway for rootful docker (http://172.17.0.1:8081), or http://host.containers.internal:8081 for rootless podman. None ⇒ compute sql bindings off.
docker_endpointpublished | bridgepublishedHow the remote-Docker backend reports a workload’s reachable endpoint. published publishes the container port on 127.0.0.1:<ephemeral> and routes there, so a host-native serve reaches it on any daemon — including Docker Desktop / macOS, where the container bridge IP is not host-routable. bridge routes to the container bridge IP directly; only reachable when serve shares the daemon’s network (e.g. serve itself runs in a container on the same Docker bridge).
docker_volume_modenamed | bindnamedHow the remote-Docker backend backs a workload’s persistent volumes. named attaches a daemon-managed docker volume by name (portable — works with a remote daemon and Docker Desktop / macOS). bind bind-mounts a host directory under <data_dir>/compute/volumes/<name> (matches the native-container layout, local daemon only). Docker volumes are node-local and outside the blob-snapshot durability story (consistent with the docker backend’s no scale-to-zero); named volumes survive restarts but not cross-node migration.
regionstringThis node’s region tag (FA-8). Advertised on the node so a gateway routing to a compute:-backed workload with --lb nearest sends each request to the nearest replica by its node’s region — no manual --region map. See Route to the nearest region.
kernel_signing_pubkeyslistboatramp’s built-in keyStatic trust anchors ("<alg>:<hex>") for the strict-posture kernel bar; a signed default kernel must verify against one.
kernel_allowed_hasheslistthe released boatramp-vmlinux hashStatic allow-list of kernel content hashes a dynamic default may select under multi-tenant. Ships pre-seeded with the first-party signed release so it verifies out of the box; replace it to allow only your own kernels.

The kernel-signing keys and hash allow-list are static (host-access-gated) trust anchors — the fleet default kernel itself is a dynamic setting (compute.default_kernel), changeable without a restart but verified against these anchors at boot. See Run a container or microVM.

Note: vcpus, mem_mib, and the default kernel are also settable at runtime via boatramp config — the boatramp.cfg values are the baseline a dynamic override layers over.