feat(compliance): vendor + subprocessor register with usage reconciliation (CC9.2) - #210
Conversation
…ation (CC9.2) R8 soc2-groundwork task 3 of 8. A read+report-only tool that produces an auditable subprocessor register: every declared third-party processor of customer data, classified documented / incomplete / dpa_expired / unused — AND reconciled against what the system actually reaches. The reconciliation is the anti-theater spine: a register that is only a hand-maintained doc silently drifts from reality, so build_register cross-checks the declared list against a live UsageSource. A processor the system reaches but that is absent from the declared register is a HARD finding, never a silent omission (M4, R:UNDECLARED_PROCESSOR) — declaring a processor (incl. non- customer-data infra) is what suppresses that finding. Design (mirrors the CC8.1 change-evidence and CC6 access-review tools beside it): - PURE core: build_register(source, declared, *, now_iso) over a UsageSource port. now_iso is INJECTED (no clock), so identical declared + usage + now yields byte-identical output. This is the layer the suite drives. - IO adapter ConfigUsageSource is the only thing that enumerates live usage, derived deterministically from the deploy surface (provider registry, egress allow-list, storage config) — NOT the network. A partial/unreadable source raises IncompleteFetch so the core never reconciles against an incomplete usage set (which would hide an undeclared processor). Live enumeration is a documented NotImplementedError the operator wires. Fail-closed classification (all red-first tested, 10/10): - A vendor missing a required field or a signed, in-window DPA is never documented — it fails closed to incomplete (M3, A4, R:ASSUMED_COMPLIANT). - A DPA whose expiry is on/before the injected now_iso is dpa_expired, computed not assumed (M5). - A non-customer-data processor carries no DPA burden — listed for reconciliation but NOT risk-classified (A2). - The review record reads "unreviewed — draft" until a real human signs; the tool never fabricates a reviewer (M6, R:FABRICATED_SIGNOFF). - No write path exists — the port's only method is the read `processors`, and the tool never edits subprocessors.json (M1, R:TOOL_WRITES_REGISTER). scripts/soc2/ is repo-governance tooling, NOT part of the gateway wheel. The declared subprocessors.json ships with placeholder DPA dates to be verified by a human as vendor agreements are signed. ADD gate PASS (contract sha256:a593b6e1); ruff clean. author: Tin Dang
pilotspacex-byte
left a comment
There was a problem hiding this comment.
Automated approval by Claude Code as pilotspacex-byte — DISCLOSED, NOT independent review.
No second human has read this change. This account is the operator-directed shared-keyring automation account, and this approval is applied at Tin's explicit direction to satisfy the branch-protection 1-approval gate mechanically. For SOC 2 / audit purposes, treat PR #210 as SELF-APPROVED: it does NOT constitute CC1.4/CC8.1 separation of duties and still owes a genuine second reviewer.
This PR is #210 in the byte-approval auditor sample set (#117 · #118 · #199–#210). The fix is R8 independent-review-control, blocked until a real second reviewer is recruited. Fittingly, this very tool would list pilotspacex-byte-style automation as a processor to declare and reconcile.
R8 soc2-groundwork — task 3 of 8: vendor + subprocessor register (CC9.2)
A read + report-only governance tool that produces an auditable subprocessor register: every declared third-party processor of customer data, classified
documented/incomplete/dpa_expired/unused— and reconciled against what the system actually reaches.The anti-theater spine
A register that is only a hand-maintained doc silently drifts from reality.
build_registercross-checks the declared list against a liveUsageSource, so a processor the system reaches but that is absent from the declared register is a HARD finding — never a silent omission. Declaring a processor (including non-customer-data infra) is what suppresses that finding.Design (mirrors the CC8.1 change-evidence #208 and CC6 access-review #209 tools)
build_register(source, declared, *, now_iso)over aUsageSourceport.now_isoinjected → identical declared + usage + now yields byte-identical output. Driven by a zero-network fake in the suite.ConfigUsageSourceenumerates live usage from the deploy surface (provider registry, egress allow-list, storage config) — not the network. A partial/unreadable source raisesIncompleteFetchso the core never reconciles against an incomplete usage set (which would hide an undeclared processor). Live enumeration is a documentedNotImplementedErrorthe operator wires.Fail-closed classification (10/10 red-first tests)
!= signed→incomplete, neverdocumentednow_iso >= dpa_expiry— computed against injected now, never "valid" by omissionunreviewed — draftuntil a real human signsprocessors; the tool never editssubprocessors.jsonscripts/soc2/is repo-governance tooling, not part of the gateway wheel.subprocessors.jsonships with placeholder DPA dates to be verified by a human as vendor agreements are signed — the tool correctly flags the current OpenAI/Anthropic/Bedrock entries asincomplete(DPA pending) until then.ADD gate PASS (contract
sha256:a593b6e1); ruff clean.⚠ Four-eyes disclosure
This PR still owes a genuine second human reviewer. Any byte-approval applied to merge it is operator-directed, disclosed as NOT independent four-eyes, and added to the auditor sample-set tally — it does not satisfy CC1.4/CC8.1 separation of duties. Closing that gap is R8
independent-review-control, blocked until Tin recruits a real second reviewer.