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

Operator config reference

caparra-gate reads one TOML file, given with --config (or the CAPARRA_CONFIG environment variable). Unknown keys are an error. Relative paths are resolved from the directory the gate runs in. Secrets never sit in the file itself: keys name files that hold them, or, for NWC, an environment variable.

caparra-gate check --config gate.toml runs every start-up check and exits 0 when the gate would start. Two complete examples are in the repository: examples/quickstart/gate.toml (fake rail, local) and examples/llm-proxy/gate.toml (phoenixd, process signer, a metered OpenAI-compatible route).

[gate]

KeyDefaultMeaning
originrequiredThe origin the manifest is issued for, as an RFC 6454 serialization (https://api.example)
listen127.0.0.1:8080The address to serve on
frontproxyproxy (reverse proxy to upstream), forward_auth (Caddy), auth_request (nginx); envoy_ext_authz is refused at start
upstreamnoneThe application, http://host:port with no path; plain HTTP only; proxy front only
replay_window_s86400Must equal the manifest’s value (manifest sign writes it)
pending_timeout_s300Must exceed the longest action; must equal the manifest’s value
offer_lifetime_s300Lifetime of exact-scope offers, at least 60
route_offer_lifetime_s3600Lifetime of route-scope offers, at least 60
mac_key_filerequiredFile holding K_mac, 64 hex (caparra-gate key new)
retired_mac_key_files[]Retired K_mac files, kept until every offer made under them has expired
max_request_bytes1048576Largest request content the gate buffers
max_response_bytes16777216Largest application answer the gate buffers
keep_response_bytes65536Answers up to this size are kept for replays; 0 keeps the receipt only
trust_forwarded_forfalseTake the client address from the rightmost X-Forwarded-For hop; set it only behind a trusted front; always on behind an auth front

[manifest]

KeyDefaultMeaning
filerequiredThe manifest made offline by caparra-gate manifest sign, served byte for byte at /.well-known/bnap
rootnoneThe root public key (64 hex) the manifest must be signed by; recommended
requiredfalseThe required flag manifest sign writes: clients then pay this origin only through BNAP offers
max_age_s604800The manifest’s max_age, at most 30 days

The gate refuses to start when the manifest’s signature, root, origin, routes, limits or keys do not match the configuration, when it has expired, or when the signer cannot sign with its offer and receipt keys.

[store]

KeyDefaultMeaning
kindsqlitesqlite, or memory (a temporary SQLite file, for tests, logged loudly)
pathnoneThe SQLite file of the use store
issuer_pathpath + .issuerThe SQLite file of the bnap:notes issuer tables

The PostgreSQL store exists as a crate but is not wired into the gate yet.

[signer]

KeyMeaning
kindfile or process
keysfile: the signer key file (mode 0600; see crates/bnap-signer-file)
socketprocess: the Unix socket of bnap-signer
signer_uidprocess: the user bnap-signer runs as; the gate checks the peer

bnap:notes needs the file signer for now.

[[rail]]

KeyMeaning
idThe name schemes refer to
kindlnd, phoenixd, nwc or fake
invoice_expiry_sInvoice lifetime; must not outlive an exact-scope offer
address, cert, macaroonlnd: gRPC address, TLS certificate file, macaroon file
url, password_filephoenixd: HTTP API URL and the file holding its password
uri_envnwc: the environment variable that holds the NWC URI (default NWC_URI)
registry, seedfake: the registry rail entry it claims to be (required, for example ln:lnd) and an optional 64-hex seed

The fake rail moves no money and logs a warning at start.

[[scheme]]

KeyDefaultMeaning
idrequiredbnap:notes, httpauth:L402 or payment:lightning/charge
rail / railsThe rail or rails it settles over
enabledtrue
secret_fileL402 and MPP: file holding the scheme’s secret, 64 hex
realmorigin’s hostMPP: the challenge realm
networkmainnetMPP: mainnet, signet or regtest
max_fund_satbnap:notes: funding inside the 402 up to this many sat
issuer_url<origin>/cashubnap:notes: the issuer’s Cashu API base. On the gate’s origin, the gate answers GET and HEAD of {issuer}/v1/keysets, {issuer}/v1/keys and {issuer}/v1/keys/{id} itself; it refuses to start when this is the origin’s root or a route lies under those paths

[[attest]]

KeyMeaning
idhttpauth:DPoP, httpauth:Bearer or httpsig:web-bot-auth
enabledDefault true
keysDPoP and Bearer: base64url JWK thumbprints of registered keys
tokensDPoP and Bearer: access tokens, { token = "...", jkt = "..." }; with jkt the token is bound to that key
agentsWeb Bot Auth: { url = "...", key = "<64 hex Ed25519 public key>" } entries

Keys and agents are static lists; there is no token introspection or key directory fetching yet.

[[route]]

KeyDefaultMeaning
idrequiredThe route ID, part of the route scope
matchrequiredA method, a space and a path, such as POST /v1/chat/completions; /* matches every path below
scoperouteroute (one shared offer) or exact (an offer per request, bound to its content)
price{ sat = N }: the price per call, or the ceiling on a metered route
meteredfalseThe charge is set at settlement by the meter
meterThe meter’s name, such as tokens
meter_path/usage/total_tokens for tokensJSON pointer to the count in the answer
ratePrice per 1,000 meter units, sent in the offer
require{}Required scheme properties, such as { use = "single" }
accept[]Value schemes, in preference order
attest[]{ scheme, effect, value?, epoch? } with effect one of required, waive, discount (value in basis points) or quota
headers[]Request fields hashed into the scope, lower case
allow_experimentalfalseNeeded for every Lightning scheme and bnap:notes, which the registry marks experimental
allow_non_bitcoinfalseNeeded for schemes on non-Bitcoin rails or units
upstreamhttphttp, or openai for OpenAI-compatible APIs (metering by tokens; "stream": true refused on metered routes)

A route that its enabled schemes cannot satisfy refuses to start.

[pricing]

KeyMeaning
kindtable
table{ <route id> = { sat = N } }: overrides a route’s price

[limits]

Per client address, per minute (draft-00 section 12, DECISIONS row 24).

KeyDefaultMeaning
mints_per_minute20Offers that mint an invoice, and fund quotes
verifies_per_minute600Credentials that reach a scheme plug
verifies_per_offer_per_minute6000The same, per offer (route offers are shared)
max_change_outputs64Most change outputs one outcome may have signed

Defaults from the specification

SettingDefaultSource
pending_timeout300 sProvisional decision for ERRATA row 21
replay_window86,400 sProvisional decision for ERRATA row 21
Manifest max_ageat most 2,592,000 s (30 days)draft-00 9.1
Credential size on HTTPat most 9,216 bytes; larger is TOO_LARGEProvisional decision for ERRATA row 30