Auditor Handbook

On this page

Purpose

This page is the canonical entry point for auditors. It does not duplicate the operational documents — it links to them and tells the auditor which document satisfies which requirement. Issue #264.

For the day-to-day operator perspective, start at Security Operations & Runbooks. For framework control mappings, start at ATO Readiness or NIST 800-53 Mapping.

One-page artefact map

Auditor request Document Source

NIST SP 800-53 Rev. 5 control mapping

NIST Architecture Mapping

237 lines, full Rev. 5 control catalogue traced to Canopy components

IRS Publication 1075 compliance matrix (Federal Tax Information)

ATO Readiness §IRS Pub 1075

Per-control mapping for FTI handling in canopy-tanf, canopy-medicaid

HIPAA compliance summary (Protected Health Information)

ATO Readiness §HIPAA

Per-control mapping for PHI handling in canopy-medicaid, canopy-chip

IEVS / SSA SOLQ-BENDEX authorisation

ATO Readiness §IEVS

Authorisation matrix at compliance/data-tenancy-authorisation.toml; CI enforcement in cargo xtask compliance audit-data-tenancy

Data-flow diagrams (PII / FTI / IEVS / signing)

Security Operations §Data Flow Diagrams

4 Mermaid diagrams: PII (SSN), FTI, IEVS, determination signing

Encryption inventory (rest + transit)

Security Operations §Encryption Inventory

Per-data-class table: AES-256-GCM (SSN field-level), TLS 1.3 (transit), ECDSA P-256 (signing — integrity, not confidentiality), SHA-256 (audit hash chain — integrity)

Access control / RBAC model

RBAC Matrix

Real role names from canopy-auth::Claims + which roles can call which endpoints

Incident response procedure

Security Operations §Incident Response + Incident Response Runbook

6-phase procedure (DETECT, CLASSIFY, CONTAIN, ERADICATE, RECOVER, NOTIFY)

Key rotation runbook (signing + encryption + JWKS)

Security Operations §Key Rotation + Signing Key Rotation Runbook

ECDSA P-256, Keycloak JWKS, AES-256-GCM

Breach notification chain (federal + state)

Security Operations §Breach Notification Chain

Per-data-class deadlines: IRS (FTI ≤ 24 h), CMS (HIPAA per HHS rule), state breach notification law

Data retention policy

ATO Readiness §Data Retention Policy

Per-data-class retention: FTI audit logs 7 yr floor (Pub 1075 AU-11); HIPAA audit logs 6 yr (45 CFR §164.530(j)); audit retention is a per-jurisdiction ruleset value bounded below by the federal floor — becoming a general config-driven retention + legal-hold lifecycle under ADR-041 (#1303, superseding the ADR-014 chain-v2 §C7 mechanism)

ADR catalogue (architectural decisions)

ADR-001 through ADR-015 in nav.adoc

Each ADR carries a Status / Context / Decision / Consequences block

Federal program requirements

Federal Requirements Mapping

SNAP, TANF, Medicaid, CHIP, CAPS, WIC mandatory fields traced to source regulations

Gap analysis (what’s not yet ATO-ready)

ATO Readiness §Gap Analysis

Per-control gap with target deadline

What’s in scope for an audit today

  • SNAP only (UAT target: September 2026). TANF, Medicaid, CHIP, CAPS, WIC services are implemented but not part of the September UAT scope. Audit-readiness for those programs follows the same model — ATO mapping under ato-readiness.adoc, Pub 1075 / HIPAA matrices already cover their data classes.

  • Federal data classes: FTI (TANF + Medicaid only, ADR-004), HIPAA PHI (Medicaid only), IEVS (canopy-verification, scoped to SNAP under 7 USC §2025(e)), SSA SOLQ-BENDEX (canopy-verification, SSA CMA pending).

  • Production deployment posture: not yet — Canopy ships as open-source reference; jurisdictions deploy and obtain their own ATO. Georgia DHS pursues ATO via the path documented in ato-readiness.adoc.

Hash-chain audit (FTI + general events)

NOTE

Superseded by ADR-041 (epic &74). The FTI-special hash chain and the chain-v2 machinery documented in this section are being retired for a general configurable structured-logging facility + jurisdiction-owned per-field redaction, with tamper-evidence + retention of the exported audit copy delegated to the deployment logging facility. This section describes the currently-live mechanism; retirement (including the citation-for-hearing redesign and the chain-verification endpoints below) is gated behind a proven replacement and tracked as #1304. Read it as the outgoing design until that cutover lands.

Two append-only hash chains underpin tamper-evidence:

  • audit_events in canopy-security — every event published on the canopy.events exchange persists with previous_hash / event_hash SHA-256 columns.

  • fti_audit_log in canopy-tanf and canopy-medicaid — Pub 1075 §9 reportable. Same hash-chain mechanism, serialised per database on the constant canopy.fti_chain advisory lock (#1245 — the pre-#1245 caller-derived lock could fork the chain). See ADR-014.

Reading chain-verification status (#1205, ADR-014 Amendment 9)

Chain verification is served by the unified /v1/security/chain/* namespace (the old GET /v1/security/verify-chain and POST /v1/security/fti/chain-verify are deleted; full endpoint reference: canopy-security API). What an auditor needs:

  • StatusGET /v1/security/chain/status?family={audit|fti}[&service=] returns one of SIX states: healthy, verifying (HTTP 200) or unknown, stale, error, breached (HTTP 503 — the SAME typed body, so a consumer reading only the status code fails closed). Only healthy means "verified, at head, anchored, fresh"; verifying means coverage is advancing but incomplete/unanchored; everything on the 503 side means treat the chain as unverified (unknown), no longer fresh (stale), environmentally broken (error), or integrity-breached (breached — Pub 1075 §9 reportable). The body’s reasons[] enumerates every firing input; on breached it carries the incident id + exact position.

  • Manual verificationPOST /v1/security/chain/verify enqueues a durable job (202 + job_id + poll URL; 409 if one is already active for the target); poll GET /v1/security/chain/verify-jobs/{id} through queued → running → done|error. Manual runs never alter the status machine — a manual pass is evidence, not a reset.

  • AttestationGET /v1/security/chain/attest?event_id=…&family=… attests ONE event: attested: true only when the row is found, belongs to the ACTIVE topology, and sits within BOTH the verified-through cursor AND the trusted (verifier-checked, externally anchored) manifest tip, with the family in an attestable state. This is the input behind "Cite for hearing" — no attestation, no citation (502).

  • A breach LATCHES. Once breached, the state persists — a clean scheduled pass never clears it. Resolution is the documented manual runbook only (evidence inspection under the incident-admin credential → ticket → manual revalidation job → guarded SQL resolve with the actor recorded from session_user): Security Operations §chain-v2 Incident-Resolution Runbook.

IMPORTANT

Dormancy (until the #1279 cutover): the background verifiers ship disabled (CANOPY_SECURITY__CHAIN_V2_VERIFY_ENABLED=false) and their database roles are NOLOGIN, so GET /v1/security/chain/status reports unknown503 by design (the #1245 fail-closed posture, preserved by status code) and verify/attest return 503 verifier_unavailable. An auditor MUST NOT read the dormant unknown as intact. The FTI chains are audited through the SAME flow (#1206 MR-3; the legacy GET /v1/security/fti/chain-status is deleted): GET /v1/security/chain/status?family=fti&service=canopy-tanf (or canopy-medicaid) + GET /v1/security/chain/attest with the same target. One FTI-only reason exists: breached with legacy_breach_latched is the #1245 latched-breach posture riding the unified endpoint — a v1 chain-break finding that stays visible (sticky, Pub 1075 §9 reportable) even while the family is dormant, retiring with the v1 evidence table at #1279. The worker portal degrades accordingly: chain badges show "Unable to verify chain", and "Cite for hearing" refuses to issue (502).

CI / build-time enforcement

Auditors should know that several compliance properties are gated at build time, not only at runtime:

Gate Enforces

cargo xtask compliance audit-data-tenancy (CI job compliance-data-tenancy)

FTI / IEVS / SSA SOLQ-BENDEX field names cannot appear in unauthorised services. Authorisation matrix at compliance/data-tenancy-authorisation.toml. Build fails on violation.

cargo xtask policy audit-literals (CI job adr-011-literal-audit)

Hardcoded Decimal::from(<n>) / dec!(<n>) literals outside params.rs and tests must be allowlisted with a written reason at compliance/adr-011-literal-allowlist.toml. Prevents un-cited policy thresholds.

cargo xtask policy audit-jurisdiction-literals (pre-push jurisdiction-literals gate)

Jurisdiction values cannot leak outside the ruleset mechanism (#1226): the Georgia benefits helpline in any form anywhere in services//crates/ code — test fixtures must use 555 numbers — and quoted "GA" state-code literals in non-test code. Allowlist with written reasons at compliance/jurisdiction-literal-allowlist.toml.

cargo xtask policy audit-completeness-reads (pre-push completeness-reads gate)

A federal/statutory universe in canopy-reporting must arrive as one of the two blessed completeness types — CompletenessRead (drain-to-Vec) or its page-at-a-time sibling UniversePager (#1202 MR4, the report worker’s Draining phase) — each constructed only behind the fail-closed total_in_scope tripwire (#1249, ADR-001 Amendment 1 §B3): a totalless first page is refused and exhaustion must reconcile pulled == total. The gate pins both constructors' visibility, the universe fetches' return types, the federal extract modules' marker-taking signatures, and bans hand-rolled page-cursor/total handling — so a silently truncated federal extract (scale-audit C1/C2) cannot be reintroduced.

cargo xtask policy audit (CI job adr-011-policy-audit)

Every jurisdiction.toml value has a citation in citations.toml tracing to PAMMS or the Federal Register. Build fails on missing citations or excessive staleness (default 365 days).

cargo xtask docs plan-lint (CI job adr-013-plan-lint)

Every plan Status row uses the canonical vocabulary (Not started / In progress / Done / Deferred / Blocked / N/A). Surfaces drift between Status tables and code state.

Contacts

  • Security operations: see security-operations.adoc §Incident Response — escalation chain.

  • Compliance questions: file a GitLab issue with label type::compliance and the appropriate compliance:: scoped label (pub-1075, hipaa, cma, ievs, wcag-21-aa).

  • Federal partner notifications: see security-operations.adoc §Breach Notification Chain.

Edit this page · default