Files
docs/implementation-plan-api-dashboards.md
T

161 lines
9.5 KiB
Markdown
Raw 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.
# 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
1. **One unified budget.** Chat and API draw from the same $15/month credit pool. No separate accounting.
2. **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.
3. **Leverage LiteLLM's native primitives.** Teams for budgets/spend, spend-log endpoints for usage. Don't reinvent what the gateway already does.
4. **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 Premium **`team_admin` role / 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_admin` role 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/30d` budget 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.
- 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/logs` scoped 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)
1. **Broker location** — portal (simplest, already has master key) vs. admin app. Leaning portal.
2. **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."
3. **Key naming/metadata** — what a member sees when they create a key (name, purpose tag) for their own cost tracking.
4. **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/logs` can 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)
*Resolves the "Risks" item that could invalidate the whole member dashboard.*
- Query `/spend/logs` and `/user/info` filtered by a single key/team; confirm a team-scoped request returns **only that member's** rows, not everyone's.
- Also confirm the broker can read *only* the target member's team (no cross-team enumeration).
- **Gate:** if scoping isn't clean, the member dashboard must go through the broker for *reads too*, which changes the architecture. Resolve before any build.
### 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/list` shows one team per member; spend attributes per-team.
### Step 3 — Broker (Phase 2)
- Add token-protected `create-key` / `revoke-key` endpoints 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 `curl` call to `gateway.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.