Reconcile resolves secret_env for every catalog app before it checks the app is installed. Routstr's UPSTREAM_API_KEY reads llama-server's key, so a node without llama-server (archy-x250-beta) logged "required secret 'llama-server-api-key' is missing" on every pass. Mark the entry optional, as btcpay does for its LND secret. Installing Routstr still installs llama-server first (a declared dependency), so the key is present whenever Routstr runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Routstr provider — development candidate
This is the local-server foundation of
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
mainat immutable source commit8ce9ef69fc1b5ecd6f92f1baf54d8c2620ea0e89(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'sx-cashuheader 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 as0.5.0+g8ce9ef6. - Upstream's image workflow publishes moving
latestimages; 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: localprevents the app gate from opening external listeners. There is deliberately no browser launch interface. - Root filesystem is read-only;
/datacontains the database, encryption key, relative.walletdirectory and logs./tmpis 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_MINTSrenders from{{ECASH_MINTS}}(the wallet's accepted mints; Minibits by default) andRECEIVE_LN_ADDRESSfrom{{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 leaveCASHU_MINTSempty: Routstr then silently trusts its built-inmint.minibits.cashdefault. - 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-llamamodel 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
/v1API; - fetch
/v1/models, then explicitly import/enable the chosen model and pricing; - enforce a stable model alias and bounded context/output/concurrency;
- verify
usagetoken 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: <token> pays for one request and returns change in the X-Cashu
response header (the whole token when the request is refused). Authorization: Bearer <token> 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:
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.