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

Quickstart

This puts caparra-gate in front of a local file server with one priced path, using the fake rail, which moves no money. It is the same run as the quickstart in the repository’s README, and every command below was run as written.

You need Rust (the toolchain is pinned in rust-toolchain.toml), protoc (the LND rail compiles .proto files at build time), curl and Python 3.

1. Build the binaries

Binary releases are not published yet. Build from a checkout:

git clone https://github.com/bnap-protocol/caparra.git
cd caparra
cargo build --locked -p caparra-gate -p bnap-cli -p bnap-mcp
export PATH="$PWD/target/debug:$PATH"

This builds caparra-gate, bnap and bnap-mcp. The first build takes a few minutes.

2. Configure the gate and sign its manifest

examples/quickstart/gate.toml protects GET /hello.txt at 21 sat, payable with httpauth:L402 or payment:lightning/charge over the fake rail, with a file signer and a SQLite use store. Paths in it are relative to the directory the gate runs in.

mkdir -p target/quickstart/www && cd target/quickstart
cp ../../examples/quickstart/gate.toml .
sh ../../examples/quickstart/setup.sh
caparra-gate check --config gate.toml

setup.sh makes the root and backup keys, K_mac, the L402 and MPP secrets (each with caparra-gate key new), a signer key file keys.toml with an offer key and a receipt key valid for 7 days, and then signs the manifest:

caparra-gate manifest sign --config gate.toml --root-key root.key \
  --backup "$(cat backup.pub)" --seq 1 --keys-from keys.toml --out manifest.json

caparra-gate check runs every start-up check and prints ok: caparra-gate would start with gate.toml. It warns that the fake rail is in use. In production the root key never touches the gate’s host; see examples/llm-proxy/README.md.

3. Start a service and the gate

echo 'hello, paid world' > www/hello.txt
python3 -m http.server 9000 --bind 127.0.0.1 --directory www &
caparra-gate run --config gate.toml &

4. See what a client gets

curl -s http://127.0.0.1:8080/.well-known/bnap   # the manifest, byte for byte as signed
curl -si http://127.0.0.1:8080/hello.txt          # 402 Payment Required
curl -s http://127.0.0.1:8080/                    # no route: forwarded unpaid

The 402 carries three WWW-Authenticate challenges (a BNAP offer=..., the L402 challenge and the Payment challenge, which share one invoice), the field BNAP-Error with the code REQUIRED, and Cache-Control: private, no-store.

5. Make a wallet and try to pay

export BNAP_WALLET="$PWD/wallet.db" BNAP_PASSPHRASE='use a long passphrase'
bnap init
bnap budget set --allow-experimental true --per-request 100
bnap budget show
bnap get http://127.0.0.1:8080/hello.txt

bnap init shows the 12-word mnemonic once. A new wallet pays nothing until --allow-experimental true, because every scheme in the registry is still experimental. The last command fails with error[BAD_INPUT]: Paid requests go over https only.: the wallet pays only over HTTPS, and the gate does not terminate TLS. The fake rail’s invoices can also be paid only from inside the process that holds it. The paid round trip on the fake rail (402, payment, admission, replay, SPENT, receipt, notes with change) therefore runs in the tests:

cargo test --locked -p caparra-gate --test gate --test notes
cargo test --locked -p caparra-wallet --test flows

To pay for real, serve the gate on a public HTTPS origin behind a TLS proxy, use an lnd, phoenixd or nwc rail, and give the wallet a pay source with bnap paysource nwc (or BNAP_NWC_URI) or bnap paysource command .... Agents use the same wallet through bnap-mcp; see agent usage.

6. Run the Lightning rails on regtest

devenv/regtest/ is a small Lightning network in Docker: one bitcoind and two LND nodes, payer and payee. The payee is the operator’s node; the bnap-rail-lnd plug talks to it over gRPC on 127.0.0.1:10009.

devenv/regtest/up.sh          # start, fund the payer, open a payer -> payee channel
. devenv/regtest/.state/env   # BNAP_REGTEST_LND_ADDRESS, _CERT, _MACAROON
cargo nextest run -p e2e --features regtest
devenv/regtest/down.sh        # stop and delete everything

up.sh is idempotent: it waits until every container is healthy, mines to the payer until it can pay, opens the channel once, and copies the payee’s tls.cert and invoice.macaroon to devenv/regtest/.state/. The e2e tests run it themselves, so CI only starts the stack (docker compose up -d) and runs them. They create an invoice through the LND rail, pay it from the payer with lncli, check that the rail reports it final with its amount, and present the one payment through L402 and payment:lightning/charge: one action is admitted, the other credential is SPENT.

To point a gate at that node, change its [[rail]] to kind = "lnd" with address, cert and macaroon (see the operator config reference). The wallet still needs an HTTPS origin, so a full client run also needs a TLS proxy in front of the gate.