Privacy / access matrix (T1.11)
Status: ready for test (implement attestation 2026-08-12)
Purpose: Lock role × screen × data-class access for internal CX Orbit users. This is the policy matrix. T5.5 assigns roles; T5.6 enforces the matrix in backend + UI. Neither is done.
Product contract: SCREENS-SPEC.md jobs. As-built P0 layouts: P0-WIREFRAMES.md (T1.8). Privacy classes: fixtures/events/README.md (T1.12).
This is a documentation/spec lock. It does not change T2.10 / T3.2 / T3.3 / T3.4 / T3.8 pages or APIs.
1. How to read this doc
1.1 What this task is / is not
| This task (T1.11) | Later — do not treat as done |
|---|---|
| Role × screen × data-class policy | T5.5 — users/teams/roles admin (SET-01/02). Status: ready. |
| PII classes labeled; support 360 minimum necessary; export rules | T5.6 — field-level matrix UI + backend enforce (SET-03). Blocked by T1.11 + T5.5. |
| Payment-secrets policy for support timeline (this file §5) | T1.14 — browser consent, anonymous ID storage, data-minimization. Policy: BROWSER-CONSENT-POLICY.md. SDK implementation is T2.13 (P1). This matrix does not own cookie rules. |
| Aligns with T1.14 / T4.10 path | T4.10 — security review checklist SECURITY-REVIEW-T4.10.md (implement attestation 2026-08-12). Named human sign-off residual. Enforcement remains T5.6. |
T5.6 DoD: access dimensions view / export / admin / sensitive case; PII, finance, ORS/SRS gated by role; backend enforce (UI reflect-only). This file is the matrix T5.6 must implement. Until then, as-built auth is a shared staff token (P0-WIREFRAMES.md §3; ADR 0001).
1.2 Evidence basis
| Artifact | What it contributes |
|---|---|
SCREENS-SPEC.md |
Primary users per screen; Capability 15 role shortcuts |
P0-WIREFRAMES.md |
Four shipped P0 screens 1:1 with T2.10 / T3.2 / T3.3 / T3.4+T3.8 |
T1.12 fixtures/events/README.md + contracts.yaml |
Property-level privacy classes |
DATA-MODEL.md §1 / §9.10 |
Restricted ORS/SRS / free-text; identifier minimization |
SOURCE-EVENT-MAPPING.md |
Event-level restricted on FDB-* (not in the T1.12 12-event pack) |
| PRD C360-03 / C360-05 / C360-06 / SET-03 | Timeline vs raw event; operational finance; ORS/SRS gating; export mask |
| ADR 0001 | Role tags pm / ops / support / matching / leadership / admin |
Residual: named security/product sign-off of this matrix (same class as T1.8 named PM/ops interview). T4.10 is that review. Do not invent a sign-off.
1.3 Capacity guardrail
P0 is the decision loop. This matrix fully specifies the four shipped P0 screens. P1 screens (executive, match, feedback, insights, standalone actions) are mapped at class level so T3.9 / T5.6 are not blocked — they are not a nine-screen permission redesign.
2. Roles
Closed set for MVP. Tags match ADR 0001. One person may hold one primary role.
| Role tag | Who | Job in Orbit | P0 home |
|---|---|---|---|
pm |
Product | Funnel friction, retention peak, product themes | /journey, /retention, /feedback |
ops |
CX / ops | At-risk queue, assign/close, payment-friction follow-up | /at-risk (+ 360 for the client under action) |
support |
Support | Orient on one client in < 1 minute; no analytics suite | /clients/[clientId] |
matching |
Matching team | Therapist fit (P1 T3.5 /match-quality); P0: 360 therapist + journey therapist filter |
/match-quality |
leadership |
Leadership | “Are we healthier this week?” (P1 T3.1); P0: retention + at-risk counts | P1 executive; P0 /retention |
admin |
Orbit admin (as needed) | Invite/disable (T5.5); matrix edits (T5.6); not a decision-loop analyst | Settings (Phase 5) |
admin is not a substitute for ops. It does not grant raw gateway payloads or clinical notes.
No consumer, therapist-as-user, or finance-only role in MVP. If a finance specialist needs row-level amounts, assign ops (or a future finance tag in T5.5 — out of this lock).
3. PII / privacy classes (labeled)
Property-level classes are the T1.12 closed set, plus restricted from DATA-MODEL / T1.3 for feedback that is not in the 12-event schema pack. Do not invent a seventh class.
| Class | Meaning | T1.12 / mapping examples | PII? |
|---|---|---|---|
operational |
Business metadata safe for general internal analytics | session_number, booking_context, canonical_type, risk_level, funnel counts |
No |
identifier |
Direct client (or device) identifiers | user_id, anonymous_id, device_id, Client 360 clientId / display label |
Yes (direct) |
financial |
Payment amounts and gateway metadata | amount, currency, gateway on payment_* |
Sensitive commercial; not a card secret |
internal |
Staff/ops identifiers and case workflow | owner_id, resolution_code, cancel_actor, action owner |
Staff PII, not client clinical |
scheduling |
Slot/time display (UTC store; Asia/Tehran UI) | scheduled_start_utc, user_timezone, expected_return_date |
Indirect (can fingerprint with identifier) |
restricted |
Clinical-adjacent scores and free text | FDB scores / ORS/SRS, free_text (DATA-MODEL.md feedback_response) |
Yes (sensitive) |
Not a privacy class — never stored or shown in Orbit UI:
| Forbidden | Why |
|---|---|
| Therapy notes, recordings, session content | Architecture §12 / SDK ingestion §12 |
| PAN, CVV, IBAN, cardholder name, gateway tokens, write keys, raw gateway callbacks | Payment secrets (§5) |
Collector write_key secret (write_key_id only) |
T0.4 / T2.2 |
Event-level T1.12 labels (payment_succeeded = financial) describe the payload, not the timeline row. A timeline row that shows only canonical_type + time + entity id is operational + identifier, even when the underlying event is financial.
4. Access dimensions (T5.6)
SET-03 / T5.6 dimensions. This matrix fills them. Enforcement is T5.6.
| Dimension | Meaning |
|---|---|
| view | API may return the field; UI may render it |
| export | Field may appear in CSV / download / scheduled report |
| admin | May change workspace, users, or this matrix (T5.5 / T5.6) |
| sensitive case | May open restricted case notes / ORS-SRS raw / free-text (MVP: no P0 role except a future explicit grant; default deny) |
UI hiding without API deny is not access. T5.6 must enforce on the domain API.
Legend used in tables below:
| Code | Meaning |
|---|---|
| V | view allow |
| E | export allow (implies view of the exported grain) |
| M | view masked — type/status/count only; values stripped |
| A | aggregate only (no row-level identifier) |
| — | deny view and export |
| S | sensitive-case grant (not assigned to P0 roles) |
5. Payment secrets policy (locked)
Test bar: Support can see the Client 360 timeline without unnecessary payment secrets.
5.1 Policy (normative)
- Support may view the event timeline for a client they are allowed to open. Timeline is the P0 “what happened?” job (
SCREENS-SPEC.md#4; T2.10). - Timeline must not include payment secrets or financial values. A
payment_succeeded/payment_failedrow is allowed as:occurred_at,canonical_type,entity_type,entity_id(payment/order id). Not allowed on the timeline:amount,currency,gateway, refund breakdown, or any secret in §5.2. - Operational payment status is minimum-necessary for support (PRD C360-05): succeeded / failed / duplicate-or-missing, plus
order_id/payment_id. That is enough to interpret a stuck booking or return without amounts. - Row-level
financial(amount,currency,gateway) is ops-only on Client 360. PM, matching, leadership, and support: deny (or mask to status-only). - Payment secrets are deny for every role, including
admin. They must not be in canonicalproperties, quarantine messages, fixtures, or exports. Collector already forbids write-key storage.
5.2 What counts as a payment secret vs `financial`
| Kind | Examples | Support timeline | Support 360 payments panel | Ops 360 | Any export |
|---|---|---|---|---|---|
| Secret (never) | PAN, CVV, IBAN, cardholder name, gateway token / authority secret, webhook secret, write key, raw callback body, refund destination account | — | — | — | — |
financial (gated) |
amount, currency, gateway (zarinpal / idpay / manual) |
— (strip from timeline) | — (status only) | V | M except ops+admin audited export |
| Operational payment | canonical_type payment_*, status succeeded/failed, order_id / payment_id, failure_code |
V | V | V | M (ids hashed or omitted in general export) |
5.3 As-built vs policy (honest)
| Surface | As-built (T2.10) | Policy |
|---|---|---|
| Timeline | occurred_at + canonical_type + entity id — no amount/gateway (apps/web/src/app/clients/[clientId]/page.tsx timeline <ol>) |
Matches. Support can see timeline without payment secrets. |
| Payments panel | Columns Payment / Status / When / Amount | Gap. Amount is financial; support must not see it. T5.6 strips amount/currency/gateway from the 360 API for support (and pm / matching / leadership). |
| Auth | Any holder of INTERNAL_STAFF_TOKEN sees the same 360 payload |
Gap. Role tags are T5.5; field deny is T5.6. |
Do not “fix” the payments-panel amount in this task. Document the gap; T5.6 closes it.
6. Support 360 — minimum necessary
Support’s job: open one client and understand context in < 1 minute. Not: analytics, bulk export, raw events, clinical scores, payment amounts.
6.1 Allowlist (view)
| 360 block (T1.8 / T2.10) | Classes | Support |
|---|---|---|
At a glance: clientId + display label, current_stage, therapist id/label, session count, last session, gap, pause/completion/next-booked, risk_v1 level + reasons |
identifier + operational + scheduling |
V — required to orient |
| Sessions table: session number, status, completed time, therapist id, gap | operational + scheduling + therapist identifier |
V |
Event timeline: time, canonical_type, entity id |
operational + identifier (entity id) |
V — no payload dump, no financial properties, no secrets |
| Payments panel: payment id, status, when | operational + identifier |
V |
| Payments panel: amount, currency, gateway | financial |
— |
| Feedback: form type + submitted time + “present/missing” | operational |
V (empty is OK; T2.7 not in slice) |
| Feedback: ORS/SRS scores, free_text | restricted |
— (C360-06: general role gets summary/flag only; MVP flag = missing vs present) |
| Actions: title, source, owner, status, result | internal + operational |
V (read). Assign/close primary path is /at-risk (ops). Support may view; may not reassign unless T5.6 later grants it. |
Raw event / payload / quarantine body |
mixed | — (C360-03: raw event only for authorized role; not support) |
| Clinical notes / session content | forbidden | — |
6.2 Deny list (support)
financialvalues (amount,currency,gateway)- Payment secrets (§5.2)
restrictedscores and free textanonymous_id/device_id(keepclientIdonly)- Bulk client list export, funnel CSV, or “download 360”
/dqevidence snippets beyond already-masked operational IDs (T5.2 already masks PII at write)- Admin / matrix / invite
6.3 Other roles on 360
| Role | 360 view | Why |
|---|---|---|
ops |
Full support allowlist plus financial on the payments panel (not secrets) |
Payment is the top friction stage (T1.1); C360-05 authorized finance for follow-up |
pm / matching / leadership |
Same as support (no financial, no restricted) when they drill from at-risk / journey |
Need path context, not amounts or ORS |
admin |
Same as ops for 360 data; plus audit of access (T5.5). Still no secrets, no clinical notes |
7. Screen access (role × screen)
P0 routes are shipped. P1 routes are mapped (T1.8 §8) — grant is for when built.
| Screen | Route | Task | pm |
ops |
support |
matching |
leadership |
admin |
|---|---|---|---|---|---|---|---|---|
| Client 360 | /clients/[clientId] |
T2.10 | V | V | V (primary) | V | V | V |
| Client journey | /journey |
T3.2 | V (primary) | V | — | V | V | V |
| Retention & churn | /retention |
T3.3 | V (primary) | V | — | V | V | V |
| At-risk + actions | /at-risk |
T3.4+T3.8 | V | V (primary) | V (read; assign is ops) | V | V | V |
| Executive summary | /summary |
T3.1 | V | V | — | — | V (primary) | V |
| Match quality | /match-quality |
T3.5 | V | V | — | V (primary) | V | V |
| Feedback (aggregate) | /feedback |
T3.6 | V | V | — | V | V | V |
| Insights | /insights |
T3.7 | V | V | — | V | V | V |
| Actions (standalone) | P1 | — | V | V | V | — | V | V |
| Role saved views | /reports |
T3.9 | per role shortcut | per role | per role | per role | per role | V |
| DQ dashboard / issues | /dq |
T2.11, T5.1–T5.2 | — | V | — | — | — | V |
| Integrations | /integrations |
T5.3 | — | — | — | — | — | V |
| Settings / matrix | Phase 5 | T5.5–T5.6 | — | — | — | — | — | V |
Support does not need journey/retention to do 360. Deny keeps the role on minimum necessary. Ops does need 360 for the client on the at-risk row.
As-built gap: /journey is ungated; /clients, /retention, /at-risk share one staff token (T1.8 §3). T5.5/T5.6 apply this table.
8. Role × screen × data-class (P0)
Grain: what the screen may render for that role. Aggregate screens must not leak row-level identifier / financial / restricted in the default view.
8.1 Client 360 (`/clients/[id]`, T2.10)
| Class | pm |
ops |
support |
matching |
leadership |
|---|---|---|---|---|---|
operational |
V | V | V | V | V |
identifier (client/therapist ids, labels) |
V | V | V | V | V |
scheduling |
V | V | V | V | V |
internal (actions) |
V | V | V | V | V |
financial |
— | V | — | — | — |
restricted |
— | — | — | — | — |
| Payment secrets | — | — | — | — | — |
| Export | — | — | — | — | — |
Support timeline row = operational + entity identifier only (§5). That is the Test Agent check.
8.2 Client journey (`/journey`, T3.2)
| Class | pm |
ops |
support |
matching |
leadership |
|---|---|---|---|---|---|
operational (stage counts, conversion, drop-off, median time) |
A+E | A | — (no screen) | A | A |
identifier |
— | — | — | — | — |
financial |
A (paid-stage count only, not amounts) | A | — | A | A |
restricted |
— | — | — | — | — |
scheduling (median time between stages) |
A | A | — | A | A |
| Export | E aggregate | E aggregate | — | — | E aggregate |
Drill-down from a stage to a client list is not P0 (PRD RPT-06). When built: requires view on 360 identifier and is not a general export.
8.3 Retention & churn (`/retention`, T3.3)
| Class | pm |
ops |
support |
matching |
leadership |
|---|---|---|---|---|---|
operational (continue N→N+1, churn-by-session, gap indicators) |
A+E | A | — | A | A+E |
identifier |
— | — | — | — | — |
financial |
— | — | — | — | — |
restricted |
— | — | — | — | — |
| Export | E aggregate | E aggregate | — | — | E aggregate |
8.4 At-risk + actions (`/at-risk`, T3.4 + T3.8)
| Class | pm |
ops |
support |
matching |
leadership |
|---|---|---|---|---|---|
operational (risk level, reasons, suggested step, gap days) |
V | V | V | V | V |
identifier (client id/label on the row) |
V | V | V | V | V |
scheduling (last session time) |
V | V | V | V | V |
internal (owner, action status, outcome) |
V | V (assign/close) | V (read) | V (read) | V (read) |
financial |
— | — | — | — | — |
restricted |
— | — | — | — | — |
| Export | — | E list without financial/restricted; ids allowed for ops |
— | — | — |
risk_v1 reasons are operational copy (no clinical claims). They may mention payment failure as a reason code, not an amount.
9. Export rules (locked)
| Rule | Policy |
|---|---|
| Default | Export is opt-in per role × grain. If a cell is not E, export is deny. |
| Aggregate vs row | Journey and retention exports are counts and rates only — no user_id, email, phone, amount. |
| Client-level | No 360 “download client” in P0. Ops at-risk list export may include client_id + operational risk fields; mask financial; omit restricted. |
| Financial in export | Public / general / PM / leadership / support: mask (C360-05, RPT-06). Ops/admin row export of amounts: only via T5.6 grant + audit (T5.5). Until T5.6, treat as deny. |
| Restricted | Never in general export. Never for support. Sensitive-case dimension only (unassigned in P0). |
| Identifiers in export | Hash or omit in any file that leaves the staff UI (email to self, CSV). Ops queue CSV may keep client_id as an operational key. |
| Attribution / insight labels | If an export includes insights later (P1), keep causal-strength badges (PRD OUT-04) — not a P0 path. |
| Scheduled exports | P1 reports (PRD). Same class rules as interactive export. |
| Fixtures / reports in git | Synthetic IDs only (usr_*, thr_*). No real PII. Unchanged. |
Capability 15 shortcuts (SCREENS-SPEC) are saved views, not extra data grants. Shipped (T3.9) at /reports. Ops / PM / support first. Shortcuts do not expand class access.
| Role | Saved view (T3.9) | Filtered href | Priority | Export |
|---|---|---|---|---|
| Ops | at-risk + actions | /at-risk?level=high_risk&sort=gap |
P0-critical destination | Row operational + client_id; no finance/restricted |
| PM | journey + feedback product themes (theme=tech) |
/reports?view=pm-journey-feedback |
P1 | Aggregate only |
| Support | client 360 + sensitive flags (not raw ORS) | /reports?view=support-360-flags |
P0-critical destination | No export |
| Matching | fit quality — /match-quality?therapist=thr_a |
/match-quality?therapist=thr_a |
P1 | Aggregate |
| Leadership | T4.7 KPI pack + action completion | /reports?view=leadership-kpi |
P1 | Aggregate |
| Leadership | Executive summary (T3.1) | /summary |
P1 | Aggregate |
10. P1 / DQ / admin (mapped, not redesigned)
| Surface | Data classes on screen | Who | Note |
|---|---|---|---|
| Executive (T3.1) | operational aggregates |
leadership, pm | No identifier |
| Match quality (T3.5) | operational per therapist id |
matching primary | Therapist id is provider identifier, not client PII |
| Feedback aggregate (T3.6) | operational themes; restricted denied in default |
pm, matching, ops | Raw ORS/SRS = sensitive-case only |
| Insights (T3.7) | operational |
all except support (optional later) | |
/dq |
operational counters + masked snippets (T5.2) | ops, admin | Page copy: no PII |
| Settings (T5.5–T5.6) | internal staff records |
admin | Audit invite/revoke/matrix edits |
11. T5.6 field catalog (implementation input)
Backend must deny by field, not by hiding a card. Minimum fields to gate:
| Field / path | Class | support |
ops |
pm / matching / leadership |
|---|---|---|---|---|
client.client_id, display label |
identifier | V | V | V |
anonymous_id, device_id, email, phone |
identifier | — | — | — |
current_stage, session_count, gaps, pause/completion, risk_* |
operational | V | V | V |
Timeline canonical_type, occurred_at, entity_id |
operational + identifier | V | V | V |
Timeline / API properties.amount (and full payload) |
financial / mixed | — | V on payments panel only; — on timeline | — |
payment.amount, currency, gateway |
financial | — | V | — |
payment.status, payment_id, order_id, failure_code |
operational | V | V | V |
feedback.scores, free_text |
restricted | — | — | — |
feedback.form_type, submitted_at |
operational | V | V | V |
action.* (no extra client PII) |
internal | V | V + write assign/close | V |
| Gateway secrets / PAN / write keys | secret | — | — | — |
Negative access (T5.6 test-as-role): support GET /api/clients/:id must omit payments[].amount (and currency/gateway) even if the UI is patched.
12. Out of scope / residuals
| Item | Owner |
|---|---|
| Browser consent, anonymous ID cookies, what the browser must not send | T1.14 — BROWSER-CONSENT-POLICY.md. Implementation T2.13. Named security/product sign-off residual (T4.10). |
| Auth.js users, invite/disable, role assignment | T5.5 (not done) |
| Backend enforce + matrix admin UI + test-as-role | T5.6 (blocked by this task + T5.5) |
| Signed security checklist, audit logging bar, residency | T4.10 checklist SECURITY-REVIEW-T4.10.md. Named human sign-off residual. Per-user access logs owned to T5.5. Residency still parked (ADR 0001 / T4.1). |
| Strip amount from as-built 360 payments panel | T5.6 (documented gap §5.3) |
| Named security/product approval of this matrix | T4.10 / Approve Agent — do not invent |
| SSO / IdP | Parked in ADR 0001; optional T5.5 |
| Finance-only role | Not in MVP role set |
| Clinical notes in Orbit | Forever out of scope |
13. References
SCREENS-SPEC.md— roles and screen jobsP0-WIREFRAMES.md— as-built P0 screens (T1.8)fixtures/events/README.md— T1.12 privacy classesDATA-MODEL.md— restricted feedback; identifier principleSDK-AND-DATA-INGESTION.md§12 — collector privacy defaultsBROWSER-CONSENT-POLICY.md— T1.14 client-browser consent / ID storage / denylist (staff matrix is this file)adr/0001-stack-hosting-auth.md— role tags; field rules deferred to T1.11 / T5.6workflows/dod/month-1.md— T1.11 / T1.14 DoDworkflows/dod/phase-5.md— T5.5 / T5.6 DoDworkflows/dod/month-4.md— T4.10 DoD