# X2 — AI BYOK Proxy — Session Card

> Per-module session list for **X2 AI BYOK Proxy**. Read this alongside `docs/plan/13-execution-modules-and-sessions.md` (the execution framework this card implements), `docs/plan/10-security-integrations-b2b.md` (the authoritative BYOK/AI design), and `docs/plan/02-platform-divergences.md` §2.4 (divergence #4). This is a **planning artifact** — no production code, no invented constants.

## Purpose

X2 delivers the **Bring-Your-Own-Key (BYOK) AI layer**: users supply their own API key for one or more of four supported providers (OpenAI, Anthropic, Google, Apple Intelligence); the ToGO backend stores those keys **encrypted at rest**, and **all** AI calls are proxied **server-side** so no key ever reaches any client. The AI does **behavioral pattern analysis only** — never diagnosis, prescription, or drug-category guessing — and the mandatory **"Consult your doctor."** deferral is enforced deterministically **in the proxy layer, not left to the prompt**. Every client "AI settings" screen is a **pure passthrough form** to the backend: write-only key submission, no read-back, nothing stored locally. This module closes **divergence #4** (AI working on legacy iOS via Apple Intelligence, missing on Android) once the Android session lands, by turning "integrate AI" into "render a passthrough screen against a contract that already exists."

## Feature parameters relevant to this module (exact — do not invent)

These are the fixed facts this module builds against. No numeric engine constants apply to the AI proxy; the binding constraints are categorical/security rules.

- **Supported providers (enum):** `openai`, `anthropic`, `google`, `apple_intelligence`. This is the only provider set.
- **No key ever reaches a client.** Keys travel once (client → backend, authenticated HTTPS) and are never returned, cached, or transited by any client afterward.
- **No key read-back endpoint.** Clients may learn only *that* a key exists for a provider (status: exists / not set), never *what* it is. A masked hint is **PENDING DESIGN** (open question #8).
- **Encrypted at rest.** Keys are encrypted before persistence in PostgreSQL; decrypted only in server memory at the moment of an outbound provider call. **Encryption mechanism (application-level envelope encryption vs. external KMS) and the encryption-key rotation policy are an OPEN QUESTION** (doc 10 §6 #4) — do not pick one silently.
- **All AI calls proxied server-side.** No client constructs prompts, selects providers, or post-processes AI output. Client role is strictly: submit key (write-only) / send analysis request / render response.
- **Deterministic guardrail (Core Opinion 2).** Every AI output ends with **"Consult your doctor."**, appended/verified **in the proxy layer** (post-processing), independent of model obedience. The AI is strictly prohibited from **diagnosing, prescribing, or guessing drug categories**.
- **Categorical, not quantitative (Core Opinion 1).** The AI performs **no quantitative food deduction**; behavioral analysis runs over categorical protocol events only.
- **`ai_provider_keys` schema:** every column `NOT NULL` with a defined default (per the strict schema rule). Planning sketch (doc 10 §2.5): `provider text NOT NULL DEFAULT 'openai'` (enum-checked `openai|anthropic|google|apple_intelligence`), `encrypted_key bytea NOT NULL DEFAULT ''` (ciphertext only; plaintext never stored), `key_status text NOT NULL DEFAULT 'active'` (`active|revoked`), plus `id`/`user_id`/`created_at`/`updated_at`. One row per (user, provider); rotation-history retention is undecided.
- **API surface sketch (names are placeholders, finalized against the Phase 1 API-conventions doc):** `PUT /v1/ai/keys/{provider}` (submit/replace — returns no key material), `GET /v1/ai/keys` (list provider status — **never** returns material), `DELETE /v1/ai/keys/{provider}` (revoke), `POST /v1/ai/analyze` (behavioral analysis — returns analysis text only).

## Session table

One row per session. Surfaces follow the module order (`backend → web → chrome → apple → android → desktop`); **there is no `mcp` session** — the MCP surface holds no AI keys and no protocol/AI logic (doc 13 §5). Backend must land before any client session. Replace `<date>` (YYYY-MM-DD) and `NN` (zero-padded index) at session start.

| Session ID | Surface | Scope (what this one session builds) | Entry deps | Done criteria (doc 13 §5) | Plan docs to read |
|---|---|---|---|---|---|
| `S-<date>-X2-backend-NN` | backend | The `aiproxy` module/plugin: `ai_provider_keys` schema + migration; encrypt-at-rest write path; `PUT`/`GET`/`DELETE` key endpoints (write-only, status-only list, **no read-back**); `POST /v1/ai/analyze` proxy that builds the guardrail system prompt server-side, calls the user's configured provider, and **deterministically appends/verifies "Consult your doctor." in the proxy layer**; provider adapters (`openai`/`anthropic`/`google`/`apple_intelligence`) stubbed or wired per Phase-1 scope decision. | M0.1–M0.4 (kernel, TOGO auth + per-user `users` row, plugin framework, migration harness); **M1 backend exists** (X-modules land after M1 — doc 13 §4). | **Backend-engine template, adapted for a non-engine module:** module registered on the kernel; migration is expand-contract and **every column is NOT NULL with a default**; endpoints return computed state; **no endpoint returns key material** (verified); key is encrypted before persistence and decrypted only in-memory server-side; the `"Consult your doctor."` deferral is enforced in the proxy and covered by a unit test that passes **regardless of model output**; no diagnosis/prescription/drug-category output path exists; unit tests encode the provider enum + deferral literally; integration test vs. real Postgres (key write → rotate → revoke; no-readback; deferral append; cross-user isolation 404-not-403) passes; **no key material leaves the backend**. Record the encryption-mechanism choice as an open question if still unresolved. | 10 §2–§3 & §6; 04 §2 (`aiproxy` module) & §4–§5; 13 §5–§6; 01 (Core Opinions). |
| `S-<date>-X2-web-NN` | web | `/settings/ai` — the BYOK passthrough form: provider picker + key field; write-only submit to `PUT /v1/ai/keys/{provider}`; renders provider **status** from `GET /v1/ai/keys`; renders proxied analysis (incl. the server's "Consult your doctor." deferral) verbatim where surfaced. Never displays stored key material. | `S-<date>-X2-backend-NN` done; M0.5–M0.7 (web shell + typed API client, theme engine, i18n engine). | **Client-engine template, adapted for a settings passthrough:** screen renders server key-status and submits key intents only (write-only); **thin-client/SSOT audit passes** — no key persisted in browser storage, no AI-provider SDK/endpoint in client code, no prompt construction, no client-side deferral logic (the deferral is rendered from server output, not synthesized); both themes render per the strict color spec; Arabic RTL mirrors correctly; strings are ICU, none concatenated. Masked-hint treatment is **PENDING DESIGN**. | 05 §1 (constraints), §4 (API map), §9.3; 10 §2.3; 13 §5. |
| `S-<date>-X2-chrome-NN` | chrome | Options-page AI BYOK section: provider + key entry submitted write-only to the backend (`POST /ai/keys`); renders provider status; no AI calls originate in the extension. | `S-<date>-X2-backend-NN` done; Chrome extension shell + TOGO auth linkage (from M1 chrome). | Client template (as above). Additionally: **keys are never written to `chrome.storage`** (which is not encrypted at rest), never attached to any client-side call, never echoed; a **static scan proves zero AI keys/SDKs/provider endpoints** in the extension bundle; both themes + Arabic RTL + ICU strings. | 06 §1 (row 4), §3.2, §9.3, §11–§12; 10 §2.3; 13 §5. |
| `S-<date>-X2-apple-NN` | apple | Settings AI BYOK passthrough screen (iOS, shared with macOS): provider + key entry, write-only submit, status render, proxied-analysis render with the deferral verbatim. | `S-<date>-X2-backend-NN` done; Apple workspace + shared packages + TOGO auth session (from M1 apple / Apple scaffold). | Client template (as above). Additionally: **no provider key in Keychain, memory, or entitlements**; copy audit confirms no diagnostic/prescriptive phrasing and that AI-surface copy carries the "Consult your doctor." deferral; both themes + native RTL mirroring + ICU. **Apple Intelligence BYOK shape is an OPEN QUESTION** (doc 10 §6 #5, doc 07 OQ #8) — do **not** ship a client-side Apple Intelligence path that violates "no key on client / all calls proxied"; if the platform forces an exception, document it narrowly rather than implement silently. | 07 §1 (rows 3–4), §4 (AI row), §9 (audits), Open Questions #8; 10 §2.3 & §6 #5; 13 §5. |
| `S-<date>-X2-android-NN` | android | Settings AI BYOK passthrough screen (Kotlin/Compose): provider + key entry, write-only submit, status render, proxied-analysis render with the deferral. **Closes divergence #4** (AI missing on Android). | `S-<date>-X2-backend-NN` done; Android app shell + TOGO auth + `core/network` (from M1 android). | Client template (as above). Additionally, the **divergence-#4 closure criteria**: static scan shows **zero AI-provider SDKs/keys/endpoints in the client**; runtime shows AI features function **through the ToGO proxy only**; no key on device; both themes render **on physical device**; Arabic RTL mirrors; ICU strings. Whether Apple Intelligence is selectable from Android is an open question (doc 02 OQ #1) — render only what the backend offers. | 08 §1 (row 4), §4.3 (AI row), §10 (#4 closure), §11 acceptance; 02 §2.4; 10 §2.3; 13 §5. |
| `S-<date>-X2-desktop-NN` | desktop | Settings AI BYOK section reusing the shared web component: submit the key **once** over HTTPS, render status + proxied results. No key caching, no key display after entry. | `S-<date>-X2-backend-NN` done; Electron shell + shared React app + `packages/api-client` (from M1 desktop). | Client template (as above). Additionally: **no AI provider key in config, memory, the renderer, or the main process**; key submitted once and never displayed again; reuses the web `/settings/ai` component so both themes + RTL + ICU inherit from Phase 2; SSOT audit confirms `packages/api-client` holds no AI logic. | 09 (AI & BYOK section), §3 constraints; 05 §9.3 (shared component); 10 §2.3; 13 §5. |

## Notes & gotchas

- **Legacy precedent worth reusing (concept only, not code):** legacy iOS already had a working API-key **proxy** for AI via Apple Intelligence (divergence #4, ahead side). The greenfield design generalizes that proxy into the server-side `aiproxy` module so every client is a thin passthrough — reuse the *idea* of server-side proxying, port none of the legacy client-side AI code.
- **Divergence #4 is only closed by the Android session.** Backend + iOS parity is not the closure; the ledger entry for #4 flips only when `S-<date>-X2-android-NN` passes its acceptance (doc 02 §2.4, doc 08 §10).
- **Liabilities to avoid (hard rules):**
  - Never store, cache, echo, or log a provider key on any client — including `chrome.storage` (not encrypted at rest), Keychain, entitlements, `localStorage`, config files, the Electron renderer/main process, or memory beyond the single submit request.
  - **Never add a key read-back endpoint.** `GET /v1/ai/keys` returns status only.
  - Enforce **"Consult your doctor."** in the **proxy layer deterministically** — never rely on the model/prompt alone to produce it.
  - No client builds prompts, selects providers, or post-processes AI text; no client-side diagnosis/prescription/drug-category logic; no quantitative food modeling (Core Opinion 1).
  - **No `mcp` session for X2** — the MCP surface holds no AI keys and no AI logic (doc 13 §5).
- **PENDING DESIGN items** (do not invent; blocked on the Figma/design track):
  - Whether a **masked key hint** is shown at all in the AI settings screen (open question #8).
  - The **AI settings screen visuals per platform** — dark-glow / light-flat palette application and full RTL/Arabic mirroring (open question #9).
- **OPEN QUESTIONS to carry (undecided — surface in each session's handoff, do not resolve unilaterally):** key-encryption mechanism (app-level envelope vs. KMS) + encryption-key rotation (doc 10 §6 #4); **`aiproxy` Phase-1 scope — ship vs. reserve table + plugin slot only** (doc 04 §2); Apple Intelligence BYOK shape / possible narrow proxy exception (doc 10 §6 #5, doc 02 OQ #1); AI proxy audit logging & retention (doc 10 §6 #6); server-side output filtering beyond the mandatory deferral (doc 10 §6 #7).

## Handoff reminder

At the end of **every** X2 session, write **one `session-handoff` memory** to the `healthdebug` brain (retain the content in the doc 13 §6 structure, then `memory/edit` to attach metadata — `retain` cannot set metadata) with a unique `source_ref: handoff/X2-ai-byok/<surface>/<n>` (never reuse) and `metadata` `{ project: "healthdebug", category: "session-handoff", era: "greenfield", status: "current", surface: "<surface>", module: "X2", title: "Handoff <session-id>", tags: [...], next: "<module+surface>" }`. Then **update the single `build-ledger` memory** (`source_ref: build-ledger`, edited in place) so one recall shows the whole frontier and the recommended next session. Any new architectural decision or open question also gets its own classified memory and, if it changes the plan, an edit to the relevant plan doc. See doc 13 §6 and `docs/brain/BRAIN-INDEX.md`.
