Control Register¶
The spine of the data room. One row per control: what it is, its status, owner, evidence link, when it was last verified, and how it maps to SOC 2 / ISO 27001:2022 / UK GDPR.
Statuses follow the shared model (not started → designed → implemented →
verified → operational). Honesty rule applies — most controls below are
not started/designed because we are pre-beta with no real users yet; that is
the correct, defensible state to record. The policy layer added under
policies/ provides the designed basis for many controls; a drafted policy
does not by itself advance a control past designed — that needs evidence the
process operates. Statuses below are unchanged by the policy layer; only the
Evidence column gains policy/procedure pointers.
Owners are roles, not people: DIR (director/operator), INFRA (backend executor), OPS (infra/ops executor), MOBILE (mobile executor), COUNSEL (legal/privacy). Framework mappings are provisional; GDPR mappings are [counsel required] before external reliance.
Evidence column links to narrative docs (readiness/…), code paths in idea
worktrees (partile-infra:…), registers (registers/…), policies/procedures
(policies/…, procedures/…), or Tier-2 restricted artifacts (named, not
contained).
Last-verified date format: YYYY-MM-DD or — if never verified.
Security (SEC)¶
| ID | Control | Status | Owner | Evidence | Last verified | SOC 2 | ISO 27001 | UK GDPR |
|---|---|---|---|---|---|---|---|---|
| SEC-01 | Session tokens are opaque, high-entropy, and stored only as SHA-256 hashes | implemented | INFRA | partile-infra: app/auth/tokens.py:16-28; readiness/threat-model-and-auth-review.md; policies/cryptography-and-secrets-policy.md, policies/access-control-policy.md |
— | CC6.1 | A.8.5, A.8.24 | Art 32 |
| SEC-02 | OAuth CSRF state is single-use (atomic GETDEL) with short TTL | implemented | INFRA | app/auth/state_store.py:37-70; policies/access-control-policy.md |
— | CC6.1 | A.8.5 | Art 32 |
| SEC-03 | Mobile auth handoff uses one-time codes; no token in URL; fixed redirect (no open redirect) | implemented | INFRA | app/auth/handoff_store.py; routes/auth.py:128-167; policies/access-control-policy.md |
— | CC6.1 | A.8.5 | Art 32 |
| SEC-04 | Object-level authorization enforced in SQL; non-visible → 404, no existence leak | implemented | INFRA | repositories/conversations.py:48-72; repositories/matches.py:196-201; infra PR-15 (305b4b4) security_regression_smoke.py; policies/secure-development-policy.md, procedures/release-review-procedure.md |
2026-06-07 | CC6.1, CC6.3 | A.8.3 | Art 32 |
| SEC-05 | Secrets never logged/persisted; OAuth tokens not persisted; access logs off | implemented | INFRA/OPS | app/auth/service.py:5; Dockerfile --no-access-log; infra PR-16 (9f64915) verifies LinkedIn id_token in memory only; production secrets live in GCP Secret Manager via partile-ops: infra/envs/prod-gcp/ with generated DB/session secrets and operator-supplied LinkedIn/Anthropic secrets; policies/cryptography-and-secrets-policy.md, policies/data-classification-and-handling-policy.md |
2026-06-13 | CC6.1 | A.8.10, A.8.12 | Art 32 |
| SEC-06 | Boarding-pass intake rejects raw artifacts and manual product presence | implemented | INFRA/MOBILE | infra PR-21 (52cd2c8) app/models.py (PresenceIntakeRequest: required boarding_pass_barcode + document_verified, banned raw fields, extra="forbid"); app/api.py sanitized 422 handler; app/presence_smoke.py; mobile PR-15 (a078460) removes manual Set Presence UI and locks product requests behind fresh verified-presence checks; mobile PR-16 (305b387) adds camera scan/on-device BCBP parse/sanitized confirmation and submits only derived document-verified presence fields; mobile PR-17 (c478b26) adds Photos/Files image import through local scanFromURLAsync, shared derived-only confirmation/submission, cache-copy cleanup, and fail-closed unsupported/PDF/non-image states; mobile PR-18 (075b035) physically verifies iPhone scan/import/gate/unlock behavior and fixes the camera preview hard gate while preserving sanitized camera-unavailable handling; mobile PR-19 (56ee67f) adds synthetic BCBP parser tests proving derived-only output, fail-closed invalids, default TTL, and sanitized test output with no raw payloads; mobile PR-20 (1527911) adds generic, non-sensitive QA selectors for the boarding-pass gate/scan/import/product/safety states and validates uniqueness/shape with npm run test:selectors; mobile PR-21 (245d5d6) adds hard-gated local/dev automation seams with static non-secret fixtures and npm run test:automation, absent from default/EAS/product builds; mobile PR-22 (c49fc1a) adds Maestro flows plus npm run test:maestro static validation for the same synthetic/gated paths, with no real pass artifacts or app/EAS flag enablement; policies/secure-development-policy.md, policies/data-classification-and-handling-policy.md |
2026-06-10 | CC6.1 | A.8.12 | Art 5(1)(c), Art 32 |
| SEC-07 | Rate limiting on auth/presence/match/directory/request/message paths | implemented | INFRA/OPS | partile-infra: app/rate_limit.py (shared Redis fixed-window limiter); applied to routes/auth.py (start/callback/mobile-exchange by IP), routes/presence.py + routes/match_candidates.py (by user), infra PR-22 (2680a01) message-send bucket verified by app/message_send_smoke.py, infra PR-23 (75d8337) directory bucket verified by app/directory_smoke.py, infra PR-24 (a6cf2af) connection-request bucket verified by app/connection_request_smoke.py, and infra PR-28 (d2db03f) AI interview bucket verified by app/interview_smoke.py; GCP production injects the RATE_LIMIT_* plain env contract through Cloud Run env in partile-ops: infra/envs/prod-gcp/locals.tf; readiness/threat-model-and-auth-review.md (S1) |
2026-06-13 | CC6.6, CC7.2 | A.8.6, A.8.20 | Art 32 |
| SEC-08 | Production data-plane isolation and transport protection | implemented | OPS | partile-ops: infra/envs/prod-gcp/ and infra/modules/gcp-network, gcp-data, gcp-runservice: Cloudflare edge HTTPS, Cloud Run managed TLS origin, Serverless VPC Access, Cloud SQL private IP/socket with no public IPv4, Memorystore on private VPC, and Private Service Access; technical/infrastructure-and-deployment.md; policies/cryptography-and-secrets-policy.md, policies/acceptable-use-and-device-policy.md |
2026-06-13 | CC6.7 | A.8.20, A.8.24 | Art 32 |
| SEC-09 | Production preflight asserts APP_ENV=production; dev routes unmounted |
implemented | OPS/INFRA | app/config.py:54-62; guardrail_smoke; GCP Cloud Run plain env includes APP_ENV=production in partile-ops: infra/envs/prod-gcp/locals.tf; policies/secure-development-policy.md, procedures/release-review-procedure.md |
2026-06-13 | CC6.1, CC8.1 | A.8.9 | Art 32 |
| SEC-10 | LinkedIn id_token verified via JWKS (not userinfo-only) | implemented | INFRA | infra PR-16 (9f64915) app/auth/linkedin.py (verify_id_token: RS256/kid/signature/issuer/audience/exp/sub), app/auth/config.py (LINKEDIN_ISSUER, LINKEDIN_JWKS_URL), app/linkedin_oidc_smoke.py; readiness/threat-model-and-auth-review.md; policies/vulnerability-management-policy.md, procedures/vulnerability-triage-procedure.md |
2026-06-08 | CC6.1 | A.8.5 | Art 32 |
| SEC-11 | Audit logging for security/admin actions | not started | INFRA | threat doc (S5); policies/incident-response-policy.md (detection input) |
— | CC7.2, CC7.3 | A.8.15, A.8.16 | Art 32, Art 33 |
| SEC-12 | Session expiry/idle/rotation policy + sweep of expired rows | implemented | INFRA | infra PR-12 (c99a9a0) retention_jobs.sweep_sessions; idle/rotation still open; policies/access-control-policy.md, registers/retention-schedule.md |
2026-06-07 | CC6.1 | A.8.5 | Art 5(1)(e), Art 32 |
| SEC-13 | Incident response plan incl. breach-notification clock and comms | not started | DIR/COUNSEL | registers/incident-log.md; policies/incident-response-policy.md, procedures/incident-response-playbook.md (drafted; not yet exercised) |
— | CC7.3, CC7.4, CC7.5 | A.5.24-A.5.28 | Art 33, Art 34 |
| SEC-14 | Vulnerability/dependency scanning + pre-launch pentest | not started | OPS | threat doc; policies/vulnerability-management-policy.md, procedures/vulnerability-triage-procedure.md |
— | CC7.1 | A.8.8 | Art 32 |
| SEC-15 | Cross-user IDOR regression smoke | implemented | INFRA | infra PR-15 (305b4b4) app/security_regression_smoke.py; conversation block-predicate fix in repositories/conversations.py; readiness/threat-model-and-auth-review.md; policies/secure-development-policy.md, procedures/release-review-procedure.md |
2026-06-07 | CC6.3 | A.8.29 | Art 32 |
| SEC-16 | GCP runtime IAM is least-privilege by service role | implemented | OPS | partile-ops: infra/modules/gcp-security/main.tf creates dedicated API, worker, and scheduler service accounts; API/worker receive runtime roles for Artifact Registry read, Cloud SQL client, logging, and metrics; infra/modules/gcp-secrets/main.tf grants per-secret accessor to runtime identities; infra/modules/gcp-runservice/main.tf uses the dedicated accounts for Cloud Run service/jobs and Scheduler invocation; selfheal PR-1 adds responder SA …-remediation@partile-prod with roles/logging.viewer + roles/monitoring.viewer at project scope and roles/run.developer scoped only to the api Cloud Run service, with no secret/IAM-admin/data-plane/delete roles; Pub/Sub invoker SA …-remediate-push@partile-prod has roles/run.invoker on remediation-responder only. Director-identity de-privilege (root-and-branch review 2026-07-02 R1/R4/R10, closed as specified): director-agent@partile-prod no longer holds roles/owner — verified 2026-07-04 via gcloud projects get-iam-policy (SA holds roles/editor + compute.osAdminLogin + iap.tunnelResourceAccessor; roles/owner is human-only admin@partile.app); secrets carry per-secret secretAccessor bindings scoped to their consuming identities (verified on runtime secrets); billing-card-* / recovery secrets carry no standing secret-level bindings — access requires an admin@ break-glass grant, exercised for real 2026-07-03 for runpod-api-key onboarding (DIGEST 2026-07-03/04). Residuals: durable vendor-* conditioned grant queued; OS-level executor/director isolation on partile-control still open (ACT-009) |
2026-07-04 | CC6.1, CC6.3 | A.5.15, A.5.18, A.8.2 | Art 32 |
| SEC-17 | Director wake-ups from external comms are sender-allowlisted and content-free (prompt-injection defence for the agentic control plane) | implemented | DIR | Root-and-branch review 2026-07-02 R2; tools/otto-watch-comms.sh (master): (1) hard sender allowlist — WhatsApp wake only from the vaulted twilio-ceo-whatsapp number, email wake only from the CEO's verified address; (2) the auto-submitted wake pointer carries integer counts only, never message bodies/subjects, so no attacker-controllable bytes enter the director's input; (3) contents are read via separate tools under an explicit treat-as-untrusted-data rule. Residual (recorded, not closed): SPF/DKIM/ARC verification of the email sender is not yet implemented — allowlisting is address-based; tracked with R2's full prescription |
2026-07-04 | CC6.1, CC6.6 | A.8.26 | Art 32 |
Privacy & Data Protection (PRIV)¶
| ID | Control | Status | Owner | Evidence | Last verified | SOC 2 | ISO 27001 | UK GDPR |
|---|---|---|---|---|---|---|---|---|
| PRIV-01 | Record of Processing Activities (RoPA) maintained | designed | DIR/COUNSEL | registers/ropa.md; readiness/data-inventory-and-retention.md; policies/privacy-and-retention-policy.md |
— | P1.1, CC2.1 | A.5.34 | Art 30 |
| PRIV-02 | Lawful basis registered per processing purpose | designed | COUNSEL | readiness/data-inventory-and-retention.md; policies/privacy-and-retention-policy.md; counsel-queue.md C1 |
— | P2.1 | A.5.34 | Art 6 |
| PRIV-03 | Retention schedule by data class with hard-delete jobs | implemented | INFRA/COUNSEL | infra PR-12 (c99a9a0) retention_jobs.py (sessions/presences/candidates/conversations); infra PR-14 (18047a3) adds deleted-user purge; durations env-configurable pending counsel C4; registers/retention-schedule.md, policies/privacy-and-retention-policy.md |
2026-06-07 | P4.2 | A.5.33, A.8.10 | Art 5(1)(e) |
| PRIV-04 | Account deletion / erasure path (cascades + scheduled purge) | implemented | INFRA | infra PR-14 (18047a3) routes/account.py, account_service.py, migrations/0004_account_erasure.sql, retention_jobs.sweep_deleted_users, account_erasure_smoke.py; readiness/data-inventory-and-retention.md; policies/privacy-and-retention-policy.md; counsel-queue.md C6/C7 |
2026-06-07 | P4.2 | A.8.10 | Art 17 |
| PRIV-05 | Data subject rights process (access/correction/portability/objection) | not started | COUNSEL/INFRA | registers/dsar-log.md; policies/privacy-and-retention-policy.md |
— | P5.1, P6.1 | A.5.34 | Art 15-21 |
| PRIV-06 | Data minimization — no raw boarding-pass artifacts retained | implemented | INFRA/MOBILE | infra PR-21 (52cd2c8) app/models.py + app/presence_smoke.py enforce boarding-pass-derived product presence and reject raw pass fields; mobile PR-15 (a078460) implements the gate without adding capture/storage and removes manual product presence; mobile PR-16 (305b387) handles raw barcode strings transiently during camera scan/on-device BCBP parse and stores/submits only derived trip fields; mobile PR-17 (c478b26) handles picked image URIs as transient function args, deletes cache copies best-effort after local decode, rejects PDFs/non-images without upload/OCR/PDF render, and stores/submits no raw image/barcode/filename/MIME; mobile PR-18 (075b035) records sanitized physical iPhone QA only (no real pass payload, image, passenger, device, token, or build details) and verifies scan/import/gate/unlock behavior using safe synthetic evidence; mobile PR-19 (56ee67f) adds synthetic BCBP parser tests that assert only derived parser keys and never print raw payloads; mobile PR-20 (1527911) makes gated MVP UI states addressable with public testID metadata only, with no dynamic IDs or boarding-pass/user/account values in selector values/docs; mobile PR-21 (245d5d6) adds hard-gated local/dev automation fixtures/seams with no real pass artifacts, no raw payloads, no persistent automation session, and no production/default enablement; mobile PR-22 (c49fc1a) adds Maestro flow scaffolding/static checks using only those synthetic seams and scans for sensitive patterns in the committed suite/docs; product/product-decisions.md PD-012; technical/data-architecture.md; policies/data-classification-and-handling-policy.md, policies/privacy-and-retention-policy.md; counsel C25 remains open for legal ratification |
2026-06-10 | P3.1 | A.8.10 | Art 5(1)(c) |
| PRIV-07 | Privacy notice v1 published and version-tracked | not started | COUNSEL | readiness/data-inventory-and-retention.md; counsel-queue.md C22 |
— | P1.1 | A.5.34 | Art 13, Art 14 |
| PRIV-08 | Data protection by design & default | designed | DIR/INFRA | readiness/ (all); policies/privacy-and-retention-policy.md, policies/data-classification-and-handling-policy.md |
— | CC2.1 | A.5.34 | Art 25 |
| PRIV-09 | DPIA for matching/profiling/proximity risks | not started | COUNSEL | readiness/threat-model-and-auth-review.md; counsel-queue.md C21 |
— | P1.1 | A.5.34 | Art 35 |
| PRIV-10 | Consent management (analytics/ML/personalization/marketing), granular + revocable | implemented | INFRA/MOBILE/COUNSEL | infra PR-17 (7562d25) migrations/0005_consent_events.sql (user_consents), routes/consents.py self-scoped GET/PUT /consents, event_consent_smoke.py; infra PR-18 (b0590be) product hookpoints use try_emit_event so T1/T2 capture requires matching consent; mobile PR-13 (b63e586) adds Settings privacy choices and Home first-run prompt for analytics/ml_training/marketing, sending internal notice version mobile-internal-2026-06-08; readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md; counsel C2/C22 (final notice/legal text still pending) |
2026-06-08 | P2.1 | A.5.34 | Art 7 |
| PRIV-11 | International transfer mechanism + residency decision | not started | COUNSEL/OPS | READINESS.md Q4; policies/vendor-and-subprocessor-policy.md; counsel-queue.md C15 |
— | P6.7 | A.5.34 | Art 44-49 |
| PRIV-12 | Age/minors policy and enforcement | not started | COUNSEL | READINESS.md Q3; policies/privacy-and-retention-policy.md; counsel-queue.md C16 |
— | P2.1 | A.5.34 | Art 8 |
| PRIV-13 | Enrichment data minimization: internal matching signal only; not displayed or redistributed | designed | DIR/COUNSEL | product/user-journeys.md; product/product-spec.md; product/product-decisions.md PD-017/PD-018/PD-024; registers/ropa.md; registers/subprocessors.md; counsel-queue.md C26. Enrichment is deferred/not active; PDL/ZoomInfo are planned, not contracted, and no broker processing occurs yet. |
— | P3.1, P4.1 | A.5.34, A.8.10 | Art 5(1)(c), Art 25 |
| PRIV-14 | Persistent connection retention & erasure | not started | DIR/COUNSEL | product/user-journeys.md; product/product-spec.md; product/product-decisions.md PD-020/PD-025; infra PR-29 (1fbcf6e) implements GET /connections over accepted connection_requests and app/connections_smoke.py verifies accepted contacts from both directions, no presence gate, block hiding, self-scoping, and minimized counterpart fields. The retention policy itself remains unresolved: registers/retention-schedule.md; registers/ropa.md; counsel-queue.md C28. |
— | P4.2, P5.1 | A.5.33, A.8.10 | Art 5(1)(e), Art 17 |
| PRIV-15 | Pre-launch seed-data lifecycle and purge-before-GA gate | designed | INFRA/OPS/DIR | infra PR-30 adds users.is_seed, app.seed, app.purge_seed, and app.seed_smoke for explicit mock traveler graphs; registers/retention-schedule.md; registers/action-register.md ACT-004. Seed data is mock only, not real PII; purge verification is required before GA/real users. |
2026-06-13 | P4.2, P3.1 | A.8.10 | Art 5(1)(c), Art 5(1)(e) |
| PRIV-16 | SSSS/selectee screening indicator: parse-and-DROP — never persisted, never logged, never emitted | designed | DIR/INFRA/COUNSEL | Data Sourcing Map canon + director delta 3 (docs/data/DATA-SOURCING-MAP.md, master 933de4d, adopted 2026-07-04): the boarding-pass BCBP payload can carry a selectee/SSSS indicator; the stateful-boarding-pass design constraint is that the parser recognizes and immediately discards it — stricter than the map's own text. Not yet code-enforced (the stateful pass is being built — ios-native PR-31 in flight); becomes implemented only with parser tests proving the field is dropped. The indicator can reveal security-sensitive categorization about a person — treat as high-sensitivity by design |
— | P3.1 | A.8.10 | Art 5(1)(c), Art 25 |
| PRIV-17 | Leave-by computation stays on-device: declared origin/home location is never transmitted; server-side storage only by future explicit opt-in | designed | DIR/MOBILE/COUNSEL | Data Sourcing Map director delta 4 (docs/data/DATA-SOURCING-MAP.md, master 933de4d): leave-by v1 computes on-device from a DECLARED origin that never leaves the device, resolving the map's §2 tension; any later server-push storage of origin is a separate explicit opt-in with its own RoPA row. Not yet built; privacy-by-design commitment recorded before implementation |
— | P3.1 | A.8.10 | Art 5(1)(c), Art 25 |
Trust & Safety (SAF)¶
| ID | Control | Status | Owner | Evidence | Last verified | SOC 2 | ISO 27001 | UK GDPR |
|---|---|---|---|---|---|---|---|---|
| SAF-01 | Block enforced at all match read/derive paths (symmetric) | implemented | INFRA | infra PR-12 (c99a9a0) repositories/blocks.py predicate + 4 enforcement points + teardown; verified safety_smoke.py; policies/secure-development-policy.md |
2026-06-07 | CC6.1 | A.8.3 | Art 32 |
| SAF-02 | Report capture (closed-enum reasons) + durable storage | implemented | INFRA | infra PR-12 (c99a9a0) routes/reports.py + report_service.py, closed enum, sanitized 422; registers/incident-log.md (safety linkage) |
2026-06-07 | CC2.1 | A.5.24 | Art 5(1)(f) |
| SAF-03 | Block/report UI surfaces in mobile | implemented | MOBILE | mobile PR-11 (c08a004) src/safety/*, MatchCandidatesScreen.tsx, ConversationScreen.tsx, SettingsScreen.tsx; mobile PR-25 (12615d7) restores/extends SafetyActionBar to directory cards and incoming connection-request cards (src/screens/DirectoryScreen.tsx, src/screens/RequestsScreen.tsx), so block/report/report-and-block coverage exists before a conversation is accepted; docs/mobile-mvp-contract.md §10A; readiness/report-block-and-safety-baseline.md |
2026-06-11 | — | — | — |
| SAF-04 | No exact gate/location reveal without mutual match + deliberate share | implemented | INFRA | candidate/conversation models omit presence fields; policies/data-classification-and-handling-policy.md (minimization) |
— | CC6.1 | A.8.3 | Art 25 |
| SAF-05 | First-contact + anti-spam limits on messaging | implemented | INFRA | infra PR-22 (2680a01) app/message_send_smoke.py verifies participant-only message send, no-leak authz, symmetric-block rejection, closed-conversation rejection, sanitized validation, and 429 + Retry-After; infra PR-24 (a6cf2af) app/connection_request_smoke.py verifies directional request-with-note create/accept/decline, no-leak target checks, sanitized note validation, duplicate/terminal conflict handling, conversation bridge, and connection-request 429 + Retry-After; readiness/report-block-and-safety-baseline.md |
2026-06-11 | CC6.6 | A.8.6 | — |
| SAF-06 | Moderation queue + enforcement ladder | not started | DIR | readiness/report-block-and-safety-baseline.md |
— | CC2.1 | A.5.24 | — |
| SAF-07 | Physical-safety guidance for in-person context | not started | DIR/COUNSEL | readiness/report-block-and-safety-baseline.md; counsel-queue.md C17 |
— | — | — | — |
ML Governance (ML)¶
| ID | Control | Status | Owner | Evidence | Last verified | SOC 2 | ISO 27001 | UK GDPR |
|---|---|---|---|---|---|---|---|---|
| ML-01 | Event taxonomy with PII class + consent tier per event | designed | INFRA | readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md; infra PR-22 (2680a01) implements message.sent as T1 metadata-only; infra PR-24 (a6cf2af) implements connection.requested as T1 and connection.accepted/connection.declined as T2 |
— | P3.1 | A.5.34 | Art 5(1)(b) |
| ML-02 | Special-category (Art 9) inference constrained out of matching | designed | DIR/COUNSEL | readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md (hard constraint); counsel-queue.md C13 |
— | P3.2 | A.5.34 | Art 9 |
| ML-03 | Safety/abuse events excluded from ML training (tier T0) | designed | INFRA | readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md (hard constraint) |
— | P3.2 | A.5.34 | Art 5(1)(a) |
| ML-04 | No content/secrets in events (length not body, UUID not identity) | implemented | INFRA | infra PR-17 (7562d25) app/events.py recursively rejects banned content/secret/identity keys and email/JWT-like values before insert; infra PR-18 (b0590be) event_hookpoint_smoke.py verifies product fields omit terminal/gate/details/email/subject/token/code/state; infra PR-19 (94d4018) worker_event_hookpoint_smoke.py verifies worker fields carry only side/airport_code and omit user ids, terminal/gate/departure time, email, token, code, state; infra PR-22 (2680a01) app/message_send_smoke.py verifies message.sent fields are body_length only and never carry message body; infra PR-24 (a6cf2af) adds note to BANNED_EVENT_FIELDS and app/connection_request_smoke.py verifies connection events are note-free; app/event_consent_smoke.py; readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md, policies/data-classification-and-handling-policy.md |
2026-06-11 | P3.1 | A.8.10 | Art 5(1)(c) |
| ML-05 | Tiered-event emission gated by matching consent (enforced in code) | implemented | INFRA | infra PR-17 (7562d25) app/events.py (emit_event: T0 free, T1 requires analytics, T2 requires ml_training, inactive actors fail closed), app/repositories/consents.py, event_consent_smoke.py; infra PR-18 (b0590be) try_emit_event wires auth/presence/match/safety hookpoints and event_hookpoint_smoke.py verifies T0 emits without consent, T1 requires analytics, T2 requires ml_training, marketing alone authorizes neither; infra PR-19 (94d4018) wires worker match.candidate_proposed/match.expired as actor-scoped T1 rows and worker_event_hookpoint_smoke.py verifies per-participant analytics consent, no marketing authorization, no pair-wide/system bypass, and skip-not-fail worker behavior; infra PR-22 (2680a01) wires message.sent as T1 analytics-gated metadata only and app/message_send_smoke.py verifies sends still succeed without analytics consent but emit no event; infra PR-24 (a6cf2af) wires connection.requested as T1 plus connection.accepted/connection.declined as T2 and app/connection_request_smoke.py verifies consent-gated, note-free emission |
2026-06-11 | P2.1 | A.5.34 | Art 7 |
| ML-06 | Dataset/model lineage — which data fed which model | not started | INFRA | readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md |
— | P3.1 | A.5.34 | Art 5(2) |
| ML-07 | Erasure propagates to the pseudonymized event store ONLY; models train on anonymized snapshots and are NOT deleted/retrained on erasure (anonymization boundary protects them) | implemented | INFRA/COUNSEL | infra PR-20 (9e828db) migrations/0006_ml_snapshots.sql, app/ml_snapshots.py, and app/ml_snapshot_smoke.py implement a T2-only, whitelisted, k-anonymous aggregate snapshot boundary; smoke verifies low-k suppression, stripping of non-whitelisted fields, no user/object ids or per-event timestamps in snapshot rows, source app_events cascade on user delete, and snapshot rows survive erasure; readiness/ml-event-taxonomy.md; decision 2026-06-07; policies/ai-ml-data-governance-policy.md; counsel C8/C9 (final legal ratification still pending before real model training) |
2026-06-08 | P4.2 | A.5.34 | Art 17 |
| ML-08 | Bias/fairness review of matching outcomes | not started | DIR/COUNSEL | policies/ai-ml-data-governance-policy.md; counsel-queue.md C12 |
— | P3.2 | A.5.34 | Art 5(1)(a) |
| ML-09 | Model cards for ranking/matching models | not started | INFRA | policies/ai-ml-data-governance-policy.md |
— | — | A.5.34 | Art 5(2) |
| ML-10 | No fully-automated decisions with significant effect (human/consent element) | designed | DIR/COUNSEL | readiness/ml-event-taxonomy.md; policies/ai-ml-data-governance-policy.md (hard constraint); counsel-queue.md C11 |
— | — | A.5.34 | Art 22 |
Governance & Operations (GOV / OPS)¶
| ID | Control | Status | Owner | Evidence | Last verified | SOC 2 | ISO 27001 | UK GDPR |
|---|---|---|---|---|---|---|---|---|
| GOV-01 | Decision log maintained with dates and rationale | operational | DIR | READINESS.md Decision Log; policies/information-security-policy.md, policies/risk-management-policy.md |
2026-06-07 | CC1.1, CC2.1 | A.5.1 | Art 5(2) |
| GOV-02 | Subprocessor register + DPAs | designed | COUNSEL/DIR | registers/subprocessors.md reconciled 2026-07-04 (dataroom PR-12): GCP-as-host corrected on the stale master fork, AirLabs/AeroDataBox/AeroAPI data providers, Sentry, Z.ai (PRC cross-border flag), and RunPod recorded with purpose/data/region/evidence; CFO commercial view in registers/vendor-spend.md + registers/renewal-calendar.md. The register operates; every DPA remains OPEN (C20) — control cannot pass designed until DPAs are executed; policies/vendor-and-subprocessor-policy.md |
2026-07-04 | CC9.2 | A.5.19-A.5.22 | Art 28 |
| GOV-03 | Data room kept current via per-PR upkeep rule | operational | DIR | README.md; policies/change-management-policy.md, procedures/release-review-procedure.md; dataroom PR-9 reconciles the pack from superseded AWS staging to live GCP production and adds accepted-risk/open-action tracking; dataroom PR-11 reconciles selfheal PR-1 remediation responder, alert wiring, least-privilege service accounts, and control-plane prod-access caveat |
2026-06-14 | CC2.1 | A.5.1 | Art 5(2) |
| GOV-04 | Policy & procedure layer (ISO 27001 / SOC 2 shaped) maintained | designed | DIR | policies/policy-index.md + 16 policy docs; procedures/ (5); evidence/evidence-register.md; dataroom PR-1 |
2026-06-07 | CC1.1, CC2.2, CC2.3 | A.5.1, A.5.2, A.5.4 | Art 5(2), Art 24 |
| GOV-05 | Physical/office controls scoped out with recorded rationale | designed | DIR | policies/not-applicable-controls.md; registers/not-applicable-register.md |
2026-06-07 | CC3.1 | A.7.* (N/A) | — |
| GOV-06 | AI/automated-processing transparency & no-Art-22 posture for match rationale and AI interview | implemented | DIR/COUNSEL | product/user-journeys.md; product/product-spec.md; product/product-decisions.md PD-017/PD-018/PD-023; infra PR-27 (e43b60c) app/ai/rationale.py, app/rationale_service.py, migrations/0009_match_rationales.sql, and app/rationale_smoke.py implement key-gated Claude Haiku 4.5 rationale with prompt minimization (display_name, headline, intent_tags only), pair/hash caching, symmetric reuse, null rationale when no key, and no rationale text in events; infra PR-28 (d2db03f) app/ai/interview.py, routes/profile.py, and app/interview_smoke.py implement key-gated Claude Sonnet 4.6 profile interview with structured output, bounded turn history, clean no-key 503, rate limiting, advisory suggested summary, no automatic profile DB write, and no interview content in events/logs; registers/subprocessors.md (Anthropic Claude API integrated but not live until key set); counsel-queue.md C27. Counsel still confirms transparency and no-Art-22 posture before real-user launch. |
2026-06-11 | CC2.3, P3.2 | A.5.34 | Art 13, Art 14, Art 22 |
| GOV-07 | Accepted-risk and open-action register maintained | designed | DIR/OPS | registers/action-register.md records the GCP Domain Restricted Sharing/public-invoker accepted risk, seed-data accepted risk, control-plane prod-access caveat, Cloudflare token rotation, staging-before-GA, HA-before-SLA, restore-drill, SLO/on-call, remediation shadow-to-enforce gate, prod-access hardening, and DPA actions |
2026-06-14 | CC3.2, CC4.1 | A.5.1, A.5.36 | Art 24, Art 32 |
| OPS-01 | Backups + restore drills | designed | OPS | partile-ops: infra/modules/gcp-data/main.tf configures Cloud SQL automated backups and PITR; policies/backup-and-recovery-policy.md; registers/action-register.md ACT-005 tracks the missing restore drill |
2026-06-13 | A1.2 | A.8.13 | Art 32 |
| OPS-02 | SLOs + alerting + on-call runbooks | designed | OPS | Initial Cloud Monitoring observe alert policies exist: Uptime check failing (…/alertPolicies/4068533737113267562) and API elevated 5xx rate (…/alertPolicies/4053361739307914661) now also notify remediation channel projects/partile-prod/notificationChannels/7003417658785730995; formal SLOs, on-call ownership, and runbooks remain open in registers/action-register.md ACT-006 |
2026-06-14 | A1.1, CC7.2 | A.8.16 | Art 32 |
| OPS-03 | DR/failover with RTO/RPO | not started | OPS | partile-ops: infra/envs/prod-gcp/prod-gcp.auto.tfvars.example records low-cost ZONAL / non-HA defaults and HA upgrade path; policies/backup-and-recovery-policy.md; registers/action-register.md ACT-003 |
— | A1.2, A1.3 | A.5.29, A.8.14 | Art 32 |
| OPS-04 | Change management via reviewed PRs + version control | implemented | DIR | git history; Atelier review checkpoint; mobile PR-22 pass/reflection/decision; policies/change-management-policy.md (adopted), procedures/release-review-procedure.md |
2026-06-10 | CC8.1 | A.8.32 | Art 32 |
| OPS-05 | Production Cloud SQL automated backups/PITR configured | implemented | OPS | partile-ops: infra/modules/gcp-data/main.tf enables Cloud SQL backups and point-in-time recovery with retained backups/log retention variables; technical/infrastructure-and-deployment.md; restore drills remain tracked separately under OPS-01/ACT-005 |
2026-06-13 | A1.2 | A.8.13 | Art 32 |
| OPS-06 | Automated remediation shadow control: detect -> decide -> audit with bounded blast radius | implemented | OPS/INFRA | selfheal PR-1 deploys private Cloud Run service remediation-responder at https://remediation-responder-u5ylen7vtq-nw.a.run.app with ENFORCE=false; Cloud Monitoring notification channel projects/partile-prod/notificationChannels/7003417658785730995 receives Uptime check failing and API elevated 5xx rate alert policies and publishes via partile-prod-remediation-alerts Pub/Sub push to POST /pubsub; responder is default-deny, allowlists only api_down restart-last-known-good and api_5xx_spike_post_deploy rollback-previous-revision decisions, enforces <=3 action decisions/hr/resource plus repeat-fingerprint thrash guard, and writes structured JSON audit logs for every decision. Shadow-to-enforce rollout is gated by registers/action-register.md ACT-008. |
2026-06-14 | A1.1, CC7.2, CC8.1 | A.8.15, A.8.16, A.8.32 | Art 32 |