# Yumi — Data-leak closure (2026-06-22)

Ben: "close any data leaks that we have." This records what was leaking, what was
fixed in code, the central architecture that prevents it going forward, and the
remaining steps to close it **live**.

## The leak
The live `mmd-cowork-mobile` and `mmd-cowork-office` runtimes read a **shared**
`HINDSIGHT_API_MCP_AUTH_TOKEN` and pointed Hindsight at `/mcp/mmd/` (the shared
bank). Result: **any signed-in user could read/write the shared bank** — a
cross-user data leak. The desktop app used a static `OPENCOWORK_HINDSIGHT_MMD_TOKEN`
(not per-user). This contradicts Yumi's core trust promise.

## Closed in code this pass
- **`apps/mmd-cowork-mobile/server.js`** — `mcpCall` is now fail-closed: `tools/call`
  (data) requires the signed-in user's own bank + PAT via `resolveUserHindsight()`,
  derived from `session.user.username` (`cowork-core/lib/auth.js` stores the Entra
  `preferred_username`). The shared token is kept **only** for the capability probe
  (initialize/tools/list metadata). `YUMI_PERUSER_HINDSIGHT=0` is a single-user
  **dev escape** only. `node --check` passes.
- **`services/mmd-cowork-office/server.js`** — identical fix (same chokepoint).
  `node --check` passes.
- **`services/yumi-hub/`** — the **central broker** (`src/hindsight-broker.js`):
  the single, reusable resolver from signed-in user → bank + PAT. Surfaces should
  route Hindsight through the hub so isolation lives in one place, not per flavor.
  Unit-tested (`test/broker.test.js`): fail-closed verified.

## Remaining to close it LIVE (not code-blockable from Ben's Mac)
1. **Desktop (`apps/open-cowork-mmd`)** — replace the static
   `OPENCOWORK_HINDSIGHT_MMD_TOKEN` (`mcp-config-store.ts:399`) with a per-user PAT
   exchange via the hub. (Next code change — same fail-closed pattern.)
2. **Deploy** the patched mobile + office to the cluster (kube — run from a
   VPN/build01 host; Ben's Mac can't reach `148.251.187.183:6443`). The leak is
   only fully closed once these ship.
3. **Provision `HINDSIGHT_BANK_PATS`** to each deployment from `auth-router-config`
   (the vault-portal PAT table) so every signed-in user resolves to their own bank.
4. **Remove the shared `HINDSIGHT_API_MCP_AUTH_TOKEN`** from the mobile/office
   deployment envs (it's now the probe-only token; ideally a distinct
   `HINDSIGHT_PROBE_TOKEN`).
5. **Office `sealed-secret.yaml`** commits an *encrypted* SealedSecret (cluster-only,
   not plaintext) — acceptable; flagged for awareness.

## Verification
- Per-user slug: `bankSlugFromEmail("Kelvin.Amboso@baobabmcl.com") → "kelvin-amboso"`
  (matches the existing bank convention).
- Fail-closed: a user with no PAT gets `null` → hub returns **403**, surfaces throw
  on `tools/call` — never a fallback to the shared bank.
- Live proof (after deploy): sign in as two different users; confirm each only sees
  their own bank via `/api/me` + that cross-bank recall returns nothing.

## Trust posture
- Data in transit: TLS to the bank (Hindsight public URL).
- At rest: Hindsight Postgres (cluster). At-rest encryption + TEE = separate module
  (`docs/YUMI_MASTER.md §14`).
- This change closes the **in-app authorization** leak (the actual live hole).
