# Routstr provider — development candidate This is the local-server foundation of [`docs/routstr-fips-llama-integration.md`](../../docs/routstr-fips-llama-integration.md). It is **not yet lifecycle-qualified or catalog-published**. The node's FIPS gateway (`/routstr/v1/models`, `/routstr/v1/chat/completions`) serves it to mesh peers only after the operator sets `routstr_selling` on; it has not yet been exercised between two real nodes. Do not install it over an existing Routstr wallet/database to test it. ## Package boundary - Builds Routstr Core `main` at immutable source commit `8ce9ef69fc1b5ecd6f92f1baf54d8c2620ea0e89` (2026-10-24; v0.5.0 plus 87 commits, upstream CI green), using its frozen uv dependency lock. It includes upstream #829 and #872: an X-Cashu payment is refunded when forwarding or settlement fails (connect or read timeout, upstream error), in the error response's `x-cashu` header and as saved change. No release is tagged after v0.5.0 yet; move to the next tag once there is one. Routstr reports its version as `0.5.0+g8ce9ef6`. - Upstream's image workflow publishes moving `latest` images; we therefore build from reviewed source instead of inventing an upstream versioned image tag. The base image is pinned by multi-arch index digest; a production image build remains an acceptance gate. - Host API binds only `127.0.0.1:8200`. `auth: local` prevents the app gate from opening external listeners. There is deliberately no browser launch interface. - Root filesystem is read-only; `/data` contains the database, encryption key, relative `.wallet` directory and logs. `/tmp` is disposable. - A dedicated generated admin secret enters through Podman secrets. No appliance Nostr private key is copied. Admin credential UI is not wired yet. - Upstream is the node's private `llama-server` (`http://llama-server:8080/v1`), authenticated with llama-server's generated key (`llama-server-api-key`). - **The node ecash wallet owns payment settings.** `CASHU_MINTS` renders from `{{ECASH_MINTS}}` (the wallet's accepted mints; Minibits by default) and `RECEIVE_LN_ADDRESS` from `{{ECASH_LN_ADDRESS}}` (the wallet's Minibits address), so earnings above Routstr's 210-sat payout floor are paid to the node wallet, where the existing claim flow redeems them. Never leave `CASHU_MINTS` empty: Routstr then silently trusts its built-in `mint.minibits.cash` default. - Routstr's database copy of settings wins over the environment after the first start, so `archy-start.py` (the image's PID 1) re-applies the mints and payout address through the admin API on every start. Mint changes made in Routstr's own admin UI are therefore overwritten by the node wallet's list. - The `archy-llama` model is priced once at install defaults ($1 / $2 per million prompt / completion tokens, plus Routstr's 1% fee). Routstr refuses to serve an unpriced model; an operator's later price is never replaced. - No Nostr identity or publication address; analytics and upstream 5xx retries are disabled. - Upstream starts exchange-rate/background tasks; disabled pricing refresh does **not** establish zero external traffic. Runtime egress qualification is pending. - TCP health means the API listener is up, not that inference/payments are ready. ## llama-server connection contract The reviewed upstream `GenericUpstreamProvider` accepts an OpenAI-compatible base URL and optional API key. The initial target is llama.cpp's `llama-server`: - provider base URL points at the private runtime's `/v1` API; - fetch `/v1/models`, then explicitly import/enable the chosen model and pricing; - enforce a stable model alias and bounded context/output/concurrency; - verify `usage` token accounting and the actual model's chat template; - do not advertise tool/streaming support until tested with that model; - keep the raw runtime off external listeners and give it a dedicated upstream key. A candidate managed runtime now lives in `apps/llama-server`; its source build and lifecycle still need qualification. Private network isolation, configuration RPC/key handoff and model acquisition UI are still to be implemented. There is intentionally no automatic model download or default cloud upstream. Merely sharing `archy-net` does not isolate the runtime from other installed apps; that is a remaining security gate. ## Payment modes (observed at the pinned revision) `X-Cashu: ` pays for one request and returns change in the `X-Cashu` response header (the whole token when the request is refused). `Authorization: Bearer ` instead funds a server-side `sk-` balance and returns no change until `POST /v1/balance/refund`, which replays the same token if repeated. Buyers must use `X-Cashu`; the existing Archipelago clients are fixed by the ngit proposal "fix(routstr): pay per request with X-Cashu so change is refunded". A buyer whose reply never carried its change claims it over the mesh at `POST /routstr/v1/balance/refund` with the original token: the seller gateway forwards only that token to Routstr's `POST /v1/balance/refund`, which returns the change it saved for the request (425 while the request is still running). Upstream `routstr/balance.py` has persisted refund retrieval and pending outcomes; `routstr/upstream/base.py` emits millisatoshi cost headers. Neither is evidence that an ambiguous completion can safely be resubmitted. Recovery must not issue another payment or inference request by default. ## Source-only checks From the repository root: ```sh bash scripts/validate-app-manifest.sh apps/routstr/manifest.yml python3 -m unittest discover -s tests/routstr-provider -p 'test_*.py' -v ``` These tests do not build/start containers, contact a provider, or touch wallets. For future disposable builds, use `docker/routstr` as the build context. For node packaging the manifest references `/opt/archipelago/docker/routstr`, following the normal runtime payload contract. Do not stage this on a live node for acceptance without an explicit test plan and isolated data.