Files
docs/infrastructure.md

207 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Infrastructure
How the Inference Cooperative is built, for members who want to understand it —
and contributors who want to work on it.
## The big picture
Members reach the co-op through two front doors: the **chat** (Open WebUI) and
the **API** (the LiteLLM gateway). Both route through a single gateway to cloud
LLM providers running inside **TEE-protected enclaves** (Tinfoil), so inference
is private end-to-end. A thin layer of custom middleware — the member portal —
ties Open Collective membership to Cloudron accounts and LiteLLM keys.
```
Members ──> Open WebUI (chat) ─┐
├──> LiteLLM gateway ──> Tinfoil (TEE) ──> models
Members ──> your own tools ────┘
(API)
```
The whole stack runs on [Cloudron](https://cloudron.io), which handles hosting,
single sign-on, and per-app isolation.
## Components
| Component | Purpose | URL | Code |
|-----------|---------|-----|------|
| **Open WebUI** | Member-facing chat (web search, file uploads) | chat.inference.coop | — |
| **LiteLLM** | AI gateway — keys, metering, model routing | gateway.inference.coop | [code/litellm](https://git.inference.coop/code/litellm) |
| **Member Portal** | Membership middleware — onboarding, key provisioning, Loomio sync | portal.inference.coop | [code/member-portal](https://git.inference.coop/code/member-portal) |
| **Member Dashboard** | Member-facing usage view + API key management | dashboard.inference.coop | [code/member-dashboard](https://git.inference.coop/code/member-dashboard) |
| **Admin Panel** | Admin-only member overview, spend, balance management | panel.inference.coop | [code/admin-panel](https://git.inference.coop/code/admin-panel) |
| **SearXNG** | Self-hosted web search (feeds the chat's search) | search.inference.coop | — |
| **Loomio** | Member governance | forum.inference.coop | — |
| **Gitea** | Git hosting (code + docs) | git.inference.coop | — |
| **Surfer** | Static hosting (landing page, thanks page) | inference.coop | [co-op/website](https://git.inference.coop/co-op/website) |
| **Listmonk** | Member newsletter (auto-synced from membership) | newsletter.inference.coop | — |
| **Vaultwarden** | Password manager (admin credentials) | vault.inference.coop | — |
| **SnappyMail** | Webmail | mail.inference.coop | — |
Open Collective (billing and fiscal sponsorship) is the one external service in
the flow — see [opencollective.com/inference-cooperative](https://opencollective.com/inference-cooperative).
## How membership is wired
Membership flows through the member portal, which is the single source of truth
for "who is a member":
1. A member contributes on Open Collective.
2. Open Collective fires a webhook to the portal, which provisions a Cloudron
account, a LiteLLM key, and a welcome email.
3. A nightly sweep reconciles against the live Open Collective list, so a missed
webhook self-heals within 24 hours.
The portal database is authoritative for member emails and balances; LiteLLM's
team budget is the source of truth for a member's allowance, shared between chat
and API.
### The bot account on Open Collective
You may notice **"Inference Co-op Bot"** listed as an admin of the collective on
Open Collective. This is infrastructure, not a member or a person:
- **What it is:** a dedicated Open Collective account owned by the co-op. It
holds a **Personal Token** that the member portal uses to read membership
data — the roster, tiers (needed to route credit-pack purchases correctly),
and contributor emails (including guests, which only admins can see).
- **What it can do:** read member and tier data via Open Collective's API.
It cannot move money, issue refunds, or change the collective — Open
Collective restricts those to dashboard actions by human admins. Its API
access was verified read-mostly during the October 2026 security review.
- **Why it has admin:** reading guest contributor emails requires admin on
Open Collective; without it, membership reconciliation and credit-pack
routing can't function.
- **Who supervises it:** all of its code is open source in
[code/member-portal](https://git.inference.coop/code/member-portal) — every
API call it makes is documented there. Nathan Schneider (collective admin)
oversees its use and can revoke or rotate its token at any time from the
Open Collective dashboard.
Nothing on Open Collective is charged, transferred, or refunded by automation:
payments always come from a human's own contribution action, and any automated
system only *reads* the resulting records.
## The code
The cooperative's software is open source, in the [`code`](https://git.inference.coop/code)
organization on Gitea:
- [**code/member-portal**](https://git.inference.coop/code/member-portal) — membership middleware (Python).
- [**code/member-dashboard**](https://git.inference.coop/code/member-dashboard) — member-facing usage + API keys (Python).
- [**code/admin-panel**](https://git.inference.coop/code/admin-panel) — admin overview (Python).
- [**code/litellm**](https://git.inference.coop/code/litellm) — the gateway packaged as a Cloudron app.
Plus the `co-op` organization:
- [**co-op/website**](https://git.inference.coop/co-op/website) — landing page and static site.
- [**co-op/docs**](https://git.inference.coop/co-op/docs) — this documentation.
- [**co-op/design-assets**](https://git.inference.coop/co-op/design-assets) — logos, fonts, brand materials.
## Contributing (the Git is yours, too)
Everything the co-op runs is open source and lives at
[git.inference.coop](https://git.inference.coop) — the website, this
documentation, the member portal, the dashboard, the admin panel, and the
gateway. As a member, you can browse all of it and propose changes.
**Who can sign in.** Git access is tied to your membership: you log into the Git
with the same single sign-on you use for the chat. Members are in automatically
(through the `members` group); when a membership lapses, Git access lapses with it.
**How to contribute.** Anyone can read the repos without an account. To make a
change, fork the repo, edit in your fork, and open a **pull request**:
1. Log into [git.inference.coop](https://git.inference.coop) with your co-op account.
2. **Fork** the repo you want to change (e.g. `co-op/docs`).
3. Make your edits in your fork and commit them.
4. Open a **pull request** back to the original repo, describing your change.
**All changes are reviewed.** Nothing merges directly into `main` — every change
comes in as a pull request that an admin reviews and approves first. You don't
need to be able to create repos or organizations to contribute; you just fork,
edit, and ask for review.
Questions about the Git itself? Write to info@inference.coop.
## Models and providers
The co-op serves models from multiple providers (Tinfoil for privacy, GreenPT
for renewable energy, PublicAI coming soon). See the [model list](models.md) for
the current catalog — that's the single source of truth for which models are
available and how they're named.
One infra detail worth knowing: a local Tinfoil proxy sidecar handles the
enclave encryption on our side of the gateway — only Tinfoil traffic goes
through it; the other providers are called directly.
## Security posture (plain language)
- **Token-gated control plane.** The portal is publicly reachable, but every
sensitive surface requires a secret token (admin, broker, webhook). All fail
closed — unauthenticated requests get `401`.
- **Separated secrets.** Chat, broker, admin, and model-layer secrets are
independent and independently rotatable, so a leak has bounded scope.
- **Member isolation.** Each API key is scoped to its owner's LiteLLM team and
budget; a member can only ever act on their own keys.
- **Rate limiting** on control-plane endpoints; the inference path is capped by
each member's budget instead.
- **No secrets in version control.** Credentials live in Cloudron env vars, never
in the repos.
- **Accepted trade-offs** (documented, not hidden): member keys are stored in
plaintext in the portal database (functionally necessary), and the gateway
must stay publicly reachable for API access (its admin UI and docs are
disabled; only the `/v1/*` API is exposed).
## For operators
Maintaining the co-op's infrastructure — rotating Cloudron tokens, editing the
portal's environment, and troubleshooting the provisioning pipeline — is
documented in the [operator runbook](operator-token-runbook.md).
## Gateway version and security update policy
The LiteLLM gateway is pinned to an **exact upstream tag** in
[`code/litellm`](https://git.inference.coop/code/litellm) (currently
`v1.84.0`). Two reasons:
1. **CVE fix line.** A member security review (Oct 2026) found the prior pin
(`main-v1.74.0-stable`) sat below the fix line for two published
vulnerabilities: CVE-2026-35029 (authorization bypass on
`/config/update`, fixed in 1.83.0, with documented in-the-wild probing)
and CVE-2026-59822 (MCP session auth bypass, fixed in 1.84.0, listed in
the CISA Known Exploited Vulnerabilities catalog). `v1.84.0` is at or
above both fix lines.
2. **Floating-tag drift.** `main-v1.74.0-stable` is a *floating* tag — the
registry silently moved it to a newer build. A rebuild intended as a
no-op therefore pulled different bits and triggered a full schema
migration, which is how the 2026-10-02 outage happened (see below).
Rule: **never pin a floating tag** (`main-*` / `latest`); always the exact
version tag. When upgrading, expect the first boot after the upgrade to take
10–15 minutes (prisma migration with internal retries) — plan the restart
window accordingly.
CORS: the gateway answers browser preflights only for
`https://chat.inference.coop` (via LiteLLM's native
`LITELLM_CORS_ORIGINS`). The old `LITELLM_CORS_ALLOWED_ORIGINS` name
(our v1.74.0-era sed-patch variable) is dead — v1.84.0 reads the native
name only.
## Incident log
**2026-10-02 — gateway outage after security-upgrade rebuild.** A Cloudron
update triggered by the CVE report rebuilt the app image; the floating
`main-v1.74.0-stable` tag had moved to newer bits (v1.84.0-era), so the
rebuild changed the running version and kicked off a full prisma DB
migration that takes 10–15 minutes per boot with internal timeouts/retries.
The gateway was down ~50 minutes (04:50–05:34 UTC); no data loss. Repairs:
version pinned to the exact tag in the repo (PR
[code/litellm#1](https://git.inference.coop/code/litellm/pulls/1) — pending
merge), `LITELLM_CORS_ORIGINS` set as a Cloudron env var (the upgrade had
silently reverted CORS to `*`), packaging synced to mirror the live
deployment. Also during the incident: the domain's authoritative
nameservers (yoursrs.com) were intermittently unreachable, briefly failing
resolution of some hostnames (git, portal) — transient registrar-side
issue, self-recovered, no records were wrong.