# Per-User API Keys — Architecture Analysis

## Current State

- Single shared `auth.json` at `~/.pi/agent/auth.json` (server-side)
- Pi CLI supports `--api-key` flag — we already use this in `pi-bridge.ts` via `resolveApiKeyForModel()`
- User settings have unused fields: `ai.enabledModels`, `ai.enabledProviders`, `ai.disabledProviders`
- 13 providers supported: anthropic, openai, google, groq, mistral, xai, openrouter, minimax, huggingface, azure-openai-responses, opencode, zai, cerebras

## Architecture

### Layer 1: Key Resolution

**Precedence:** User key > System key > fail

- Store user keys in `user_settings.ai.apiKeys` (JSONB column, no new DB table needed)
- Format: `{ "ai": { "apiKeys": { "openai": "sk-...", "anthropic": "sk-ant-..." } } }`
- `resolveApiKeyForModel()` in `pi-bridge.ts` checks user keys first, falls back to system `auth.json`
- Key passed to Pi via `--api-key` CLI flag (already implemented for system keys)
- Keys never touch the container filesystem — all resolution is server-side

### Layer 2: Model Visibility

- System access policy defines base available providers/models
- User's own provider keys unlock additional providers
- Existing `enabledProviders`/`enabledModels` settings fields drive filtering
- Model listing becomes a union: system models + user's provider models

## Key Design Decisions

1. **No new DB table** — use existing `user_settings` JSONB column
2. **No per-user auth.json files** — all key resolution server-side via `--api-key` flag
3. **Eventually remove PI_CONFIG_DIR mount** — auth.json in container no longer needed for keys (still needed for local providers/models.json)
4. **Model listing = union** of system + user models

## What This Enables

- Admin deploys with their keys → all users can chat
- User adds their own key → gets access to that provider
- User's key takes priority (user pays for their own usage)
- No credential leakage (keys in DB, passed as CLI args, never on container filesystem)

## Files to Change

| File | Change |
|------|--------|
| `src/servers/api/pi/pi-bridge.ts` | Update `resolveApiKeyForModel()` to check user settings first |
| `src/servers/api/settings/settings.ts` | API endpoint for saving/deleting user API keys (encrypted at rest ideally) |
| `src/workspaces/state/src/useSettings.ts` | Add `ai.apiKeys` to `UserSettings` type |
| `src/servers/api/pi/list-models.ts` | Union system + user provider models |
| `src/workspaces/officerdev/src/hooks/usePiModels.ts` | Filter models based on user's available providers |
| `src/apps/officer-web/Screens/Dashboard/Settings/ProfileSettings/AIModels.tsx` | UI for managing per-user API keys |
| `src/servers/api/server-settings/pi-mono.ts` | Distinguish admin vs user key management |

## Deep-Dive: Pi CLI & Auth

### CLI Flags
- `--provider` — force provider
- `--model` — force model (format: `provider/model-name`)
- `--api-key` — force API key (takes precedence over auth.json and env vars)
- `--system-prompt` — system prompt

### Supported Environment Variables
`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `MISTRAL_API_KEY`, `XAI_API_KEY`, `OPENROUTER_API_KEY`, etc.

### auth.json Format
```json
{
  "provider_id": {
    "type": "api_key",
    "key": "sk-..."
  }
}
```

### Key Resolution Precedence (inside Pi)
`--api-key` flag > auth.json entry > environment variable > built-in providers

### Pi Spawn Flow (pi-bridge.ts)
1. Determine model from user settings or default
2. `resolveApiKeyForModel(model)` extracts provider from `provider/model-name`
3. Looks up key in auth.json (currently system-level only)
4. Passes `--api-key <key>` when spawning Pi process
5. Container never sees raw credentials

## Implementation Steps

1. **Add key storage** — extend `UserSettings` type, add API endpoint for CRUD
2. **Update key resolution** — `resolveApiKeyForModel(model, userId?)` checks user DB first
3. **Update model listing** — merge system models with user-unlocked provider models
4. **Build settings UI** — API key input per provider in AI settings page
5. **Remove PI_CONFIG_DIR mount** (later) — once models.json is also handled server-side
