Audit of the operator docs against live reality (2026-11-04): - app-inventory.md: all 19 Cloudron apps with id, fqdn and registry image pin. Corrects the portal path (code/member-portal, not co-op/member-portal) and the panel path (local/coop.inference.admin-panel, not code/admin-panel). - maintenance/backups.md: the two backup systems; the deliberate per-user restic cache split and the --group-by "" retention fix, with why. - maintenance/woodpecker-ci.md: CI server + agent, image build/pin, manual-only trigger, git-lfs requirement, and its current not-yet-green state (agent's job-collection long-poll gets HTTP 500 from the server). - provider-pricing/: index + GreenPT, Tinfoil (local EHBP proxy architecture) and the Metis /v1/systemone decision-model route; regolo.md updated to record the gateway prices were applied. - agent-fleet.md: steward, infrastructure-bot and socialbot (Unix users, gateway units, teams); member-bot tier 2 planned but not yet provisioned. - README: file list; operator-token-runbook: correct the membership_sweep cadence (3x daily, not nightly); migration doc: post-migration addendum (16->19 apps, DNS split resolved, old box still on with apps stopped).
4.2 KiB
Tinfoil pricing and architecture
Tinfoil serves models inside TEE-protected enclaves. The co-op does not self-host it — Tinfoil is a hosted cloud service; models run in Tinfoil's TEEs. Tinfoil is the co-op's architecturally-private tier (contrast: GreenPT and Regolo are the renewable tier, with policy-based privacy, not TEE).
The architecture — read this before touching the Tinfoil route
LiteLLM does not talk to Tinfoil's enclave directly. Every Tinfoil model in the gateway config points at:
api_base: http://127.0.0.1:3301/v1
That is a local proxy running inside the gateway container
(/app/data/tinfoil-proxy -b 127.0.0.1 -p 3301). LiteLLM speaks plaintext
OpenAI to the proxy; the proxy verifies the enclave attestation and wraps the
request/response bodies in EHBP (HPKE) before forwarding to the Tinfoil
enclave. The provider key is set via the environment variable TINFOIL_API_KEY.
Consequences:
- The proxy is load-bearing. If it is not running, every Tinfoil model fails; LiteLLM's own config still looks correct. Check the process, not just the config.
http://127.0.0.1:3301is intentional, not a misconfiguration. Do not "fix" it to a public Tinfoil URL — the encryption happens in the proxy.- The proxy binary lives on the persistent volume (
/app/data/tinfoil-proxy), so it survives restarts; it is started by the app'sstart.sh.
What we serve, and what it costs
Rates as recorded in the gateway config, USD per 1M tokens unless noted:
| our model | input | output | cached input |
|---|---|---|---|
tinfoil/deepseek-v4-1-flash |
$0.65 | $1.45 | $0.13 |
tinfoil/gpt-oss-120b |
$0.15 | $0.60 | — |
tinfoil/glm-5.3 |
$1.80 | $5.75 | $0.45 |
tinfoil/whisper-large-v3-turbo (STT) |
$0 / 1M | — | $0.01 per request |
tinfoil/voxtral-tts (TTS) |
$0 / 1M | — | $0.002 per request |
tinfoil/nomic-embed-text (embeddings) |
$0.05 | $0 | — |
tinfoil/glm-5.3replacedtinfoil/glm-5-3-flash(Tinfoil deprecated the flash variant on 2026-10-09). Text-only (no vision), reasoning + tool calling.tinfoil/deepseek-v4-1-flashand the glm-flash variant carry an "Experimental: may be deprecated with minimal notice" flag from Tinfoil — which is part of why the GreenPT counterpart is the default.
Audio is billed per REQUEST, and needs a build-time patch
Tinfoil bills the audio models per request (its model list carries a
requestPrice), not per token. Reading only the two token prices made TTS look
free and left STT billed off an older catalog. LiteLLM cannot price per request
on the audio paths, so patch_audio_request_cost.py (built into the image via
the Dockerfile) makes the cost calculator honour input_cost_per_request.
Re-check that build-log line after every LiteLLM upgrade.
One non-obvious load-bearing detail: for TTS, input_cost_per_character: 0 is
required. Without it the TTS cost branch raises ValueError("cost for tts call is None") and aborts the whole calculation, so the spend write is skipped and
speech bills nothing — whatever input_cost_per_request says.
Audio keep-alive (the Cloudron scheduler task audio_keepalive, every 10 min)
pings the Tinfoil audio models so voice mode does not pay the ~24s TEE cold-start
after idle. It is TTS-only by design: the STT warm ping was disabled
2026-10-11 because it costs $0.01/request — roughly $52/month at 6 pings/hour.
docs.inference.coop-style "keep-alive economics" detail is in the script.
The embedding model is operator-only
tinfoil/nomic-embed-text (768-dim) is not a member-facing model — it is
scoped to the co-op's own admin team (model_info.team_id) for the docs-RAG, so
member keys cannot call it. The portal also filters it out of the chat-facing
model list by its mode: embedding.
Fallbacks
The router treats tinfoil/deepseek-v4-1-flash as the fallback for
greenpt/deepseek-v4.1-flash, and tinfoil/gpt-oss-120b as the fallback for
tinfoil/deepseek-v4-1-flash. The timeout is deliberately generous (180s) with
TimeoutErrorRetries: 0 — DeepSeek generations routinely exceed 30s, and a
timed-out generation is already billed by Tinfoil, so re-hitting it re-bills.