11 KiB
Implementation Plan: API Access, Member Dashboard & Admin Dashboard
Status: Planning (not yet implemented) Last updated: September 13, 2026
Goal
Give members direct API access to the cooperative's models (alongside chat), a dashboard to view their usage and manage their own API keys, and an admin dashboard for co-op-wide monitoring.
Guiding principles
- One unified budget. Chat and API draw from the same $15/month credit pool. No separate accounting.
- The member surface holds no master key. A member-facing app must never touch the LiteLLM master key, the Cloudron token, or the Open Collective token. Key creation happens through a tightly-scoped broker.
- Leverage LiteLLM's native primitives. Teams for budgets/spend, spend-log endpoints for usage. Don't reinvent what the gateway already does.
- No commercial dependencies for now. Stay on the open-source LiteLLM build.
Verified facts (from research)
- LiteLLM Teams (open-source) provide: per-team budget, per-team spend tracking, and key attribution to both user and team. This is the abstraction layer for "unified account + multiple keys."
- Clarification on "teams": we use LiteLLM's open-source team budget feature (a budget container, referenced as
team_id). We do not use the Premiumteam_adminrole / team-member permissions — those are the paid features that would let members self-manage keys. We act as the "team admin" ourselves via the broker/master key instead. - Member-facing vocabulary: members see "your account" / "your budget" / "your API keys" — never "team" (that's an internal implementation term).
- Spend endpoints are live on our gateway:
/global/spend,/spend/logs,/user/info,/team/list(all return 200). - LiteLLM is v1.74.0, with Postgres — so budgets and teams are fully supported.
- Key self-serve requires Premium. The
team_adminrole and "team member can create keys" permission are both ✨ Premium Features. On the OSS build, only the master key can mint keys. Hence the broker. - Team members can still view key info on OSS — read-only usage/key visibility is viable without Premium.
Target architecture
Member (browser) ──SSO──▶ Member Dashboard (new Cloudron app, "members" group)
│ read-only: my spend, my keys, my usage
│ writes: "create/revoke key" → Broker
▼
Broker (portal or admin app; holds master key)
│ mints/revokes a key scoped to the member's team
▼
LiteLLM Gateway (Teams + budgets + spend logs)
- Chat key: portal-held, per member, under their team, hidden from the member.
- API keys: member-managed, under their team, visible in the member dashboard.
- Both draw from the same team's $15/30d budget.
Phases
Phase 1 — Teams migration (foundation)
- Create a Team per member (
team_alias: member:<email>) with$15/30dbudget and the three models. - Two keys per team:
- Chat key — created/handled by the portal injector (existing
member:<email>key becomes this). - API key(s) — created via the broker on member request.
- Chat key — created/handled by the portal injector (existing
- Update
provision_member()to create the team + chat key (instead of a bare key). - Migrate existing members (Nathan, Liz, Dan, Ed, Lee).
- Verify: chat still works end-to-end; spend now attributed per-team.
Phase 2 — Broker (key management)
- A token-protected endpoint that, given a member's email + a requested key name:
- Verifies the caller (SSO identity or portal JWT).
- Mints a key scoped to that member's team (via master key).
- Revokes a key on request.
- Lives in the portal (which already holds the master key) or the admin app.
- This is the only place the master key is used for key creation.
Phase 3 — Member dashboard (new Cloudron app)
- Installed in the Cloudron "members" group → SSO-gated (same pattern as OpenWebUI/Loomio). No custom auth to build; trust the forwarded user identity.
- Read-only views (via team-scoped endpoints, no master key):
- My current spend vs. $15.
- My API keys (list, with the chat key hidden).
- Recent usage (from
/spend/logsscoped to my key/team).
- Write actions (call the broker):
- Create API key.
- Revoke API key.
- Holds no master key, Cloudron token, or OC token.
Phase 4 — Admin dashboard
- Co-op-wide view (admin-gated, holds master key):
- All members + status (active/inactive/founder).
- Aggregate spend + per-model breakdown.
- Budget adjustment, key/team management.
- Either a section of the portal or a distinct app. Decision deferred until Phase 3 is built (the admin view is lower urgency — LiteLLM's built-in UI already covers some of it).
Phase 5 — Documentation
- Document the API endpoint (
https://gateway.inference.coop/v1), OpenAI-compatible format, and "shares your $15 budget with chat." - Document the member dashboard (how to get/rotate keys).
- Add an open question to
open-questions.md: how to split/communicate the chat-vs-API budget if members want separate limits (future governance decision).
Security model (the load-bearing part)
| Component | Holds master key? | Notes |
|---|---|---|
| Portal (control plane) | Yes | Existing: provisioning, key injection, webhook, admin |
| Broker | Yes | Scoped: only mints/revokes team keys |
| Member dashboard | No | SSO-gated, team-scoped, read-only + broker calls |
| Admin dashboard | Yes | Admin-only |
The invariant: a member's session can only ever (a) read their own team's data, or (b) ask the broker to act on their own team. No member-path code can enumerate or touch another member's keys.
Open decisions (to resolve before/at build time)
- Broker location — portal (simplest, already has master key) vs. admin app. Leaning portal.
- Member dashboard framework — a small custom app (FastAPI, reusing the portal's patterns) vs. an app-store tool. Leaning custom: the app is a thin read/write wrapper, not a BI dashboard, and existing tools (Metabase/Superset/Baserow) don't fit "create my key."
- Key naming/metadata — what a member sees when they create a key (name, purpose tag) for their own cost tracking.
- Premium later? If the co-op grows an enterprise offering, LiteLLM Premium unlocks true key self-serve (drop the broker). Deferred.
Risks
- Broker is a new attack surface — must be narrowly scoped and rate-limited.
- Migration touches existing members — needs careful idempotency (reuse existing keys by alias).
- Spend-log scoping — must verify
/spend/logscan be filtered per-key/per-team so the member dashboard can't see others' usage.
Stepwise development process
Build order with dependencies and verification gates. Each step must be verified in isolation before the next begins — no batching that obscures which step broke.
Step 1 — Confirm spend-log scoping (the riskiest unknown)
COMPLETED (2026-09-13). Outcome: the member dashboard cannot read LiteLLM directly — all reads AND writes must go through the broker/portal.
Findings:
- Member keys cannot read spend endpoints. A member's own key (both
sk-virtual keys and legacy raw-hash tokens) gets401 "Only proxy admin"on/spend/logs,/spend/keys, and/user/info. Only the master key can query spend. - Consequence: the member dashboard must route reads through the portal (which holds the master key) too, not just writes. This actually simplifies the security model — the member dashboard holds no LiteLLM credential at all; it just calls the portal, which already authenticates members and already holds the master key.
/spend/keysis the clean per-member read primitive — it returns one row per key withkey_alias(member:<email>),spend, andteam_id. Filtering by alias gives a member's spend directly./spend/logsfiltering is inconsistent:api_key=<hash>filters correctly, butteam_id=<x>is silently ignored (returned all 92 rows for a bogus team id). Do not rely onteam_idfiltering on/spend/logs; prefer/spend/keys+key_alias.- Data inconsistency found (must fix in Step 2): three members (Dan, Ed, Nathan) have raw-hash key tokens (not valid LiteLLM virtual keys), while four (Lee, Joseph, Liz, Roz) have
sk-keys. The raw-hash tokens are a legacy artifact; the Teams migration will recreate all keys consistently assk-virtual keys. /user/info?user_id=works with the master key but showsspend: 0and emptykeys/teamsbecause current keys haveuser_id: null(key-only, not user-bound). After the Teams migration binds keys to teams, per-team spend becomes queryable.
Revised architecture (all LiteLLM access via the portal):
Member (browser) ──SSO──▶ Member Dashboard (new Cloudron app, "members" group)
│ holds NO LiteLLM/Cloudron/OC credentials
│ calls the portal for everything
▼
Portal (holds master key; authenticates member)
│ read: /spend/keys?key_alias=member:<email>
│ write: mint/revoke key via master key
▼
LiteLLM Gateway (Teams + budgets + spend logs)
Step 2 — Teams migration (Phase 1)
- Extend the portal: create a team per member, move/create the chat key under it, keep
provision_member()idempotent. - Write a one-off migration script; dry-run it against the DB, then run against live members (Nathan, Liz, Dan, Ed, Lee).
- Gate: chat works end-to-end for a test member;
/team/listshows one team per member; spend attributes per-team.
Step 3 — Broker (Phase 2)
- Add token-protected
create-key/revoke-keyendpoints in the portal, scoped to a member's team, rate-limited. - Gate: mint a key for a test member, confirm it draws from their team budget and appears under their team; revoke works.
Step 4 — Member dashboard (Phase 3)
- New Cloudron app in the "members" group (SSO-gated). Read-only (spend/keys/usage) + broker calls for create/revoke.
- Gate: a member logs in, sees only their own data, creates and revokes a key end-to-end, and the app holds no master key (verify via env inspection).
Step 5 — Admin dashboard (Phase 4)
- Co-op-wide view, admin-gated. Build after the member dashboard stabilizes (lower urgency — LiteLLM's UI covers some of it).
- Gate: admins see all members + aggregate spend; members cannot reach it.
Step 6 — Documentation (Phase 5)
- API endpoint + format + budget-sharing note; member dashboard how-to; add the budget-split open question.
- Gate: a brand-new member could follow the docs to retrieve a key and make an API call without help.
Rollback
- Every phase is additive; a failed phase can be reverted by re-pointing the injector at the prior key scheme (Step 2) or removing the new app (Steps 4–5). No data loss: keys are re-creatable, teams are idempotent by alias.
Definition of done
- A member can: chat, retrieve an API key, make a direct
curlcall togateway.inference.coop/v1, see their own usage, and create/revoke keys — all within one $15 budget, with no member path touching the master key. - An admin can see co-op-wide membership, spend, and per-model breakdown.