canopy-reporting Data Model
On this page
#1456 (ADR-004 A8b): every table and function in this schema is owned by
canopy_reporting_owner (NOLOGIN); the runtime connects as the restricted
canopy_reporting_app login holding only the enumerated per-object grants
(migrations 20261111000000/20261111000001). The sealed extract is never
UPDATEd and the six generation-scoped tables are DELETE-able only through
the SECURITY DEFINER reporting_janitor_reap fn, which re-verifies
superseded/abandoned-past-retention eligibility inside the definer.
Cutover/rollback: security-operations.
Cross-link: canopy-reporting API Reference · Source: migrations/
Tables
| Table | Purpose |
|---|---|
|
FNS-388 SNAP monthly state activity report header (7 CFR 272.11). One row per (generation_id, report_month) — uniqueness moved onto the output generation by the #1202 run substrate; readers resolve the month’s PUBLISHED generation and serve only its row. Carries the aggregate household / individual / benefit counts, expedited + elderly/disabled splits, initial-cert vs recert split (classified by the LEGAL receipt month — never the UTC one, #1584), the calculated |
|
FNS-7176 Quality Control universe snapshot (7 CFR Part 275). One row per (generation_id, household, snapshot_date) — uniqueness moved onto the output generation by the #1202 run substrate; readers resolve the date’s PUBLISHED generation and filter by it. Carries the full deduction stack used in the QC review (earned-income / standard / dependent-care / medical / shelter / child-support) and the resulting |
|
ACF-199 monthly TANF case snapshot. One row per (generation_id, report_month, case_id) — uniqueness moved onto the output generation by the #1202 run substrate (MR6 cutover); readers resolve the month’s PUBLISHED generation (or the latest published, when no month is given) and serve only its rows. |
|
ACF-196 quarterly TANF financial report. Composite unique on |
|
Monthly Work Participation Rate calculation. One row per |
|
T-MSIS eligibility file rows (42 CFR 431 / 42 CFR 433). One row per (generation_id, report_month, enrollment_id) — uniqueness moved onto the output generation by the #1202 run substrate (MR6 cutover); readers resolve the month’s PUBLISHED generation (or latest) and serve only its rows. Encrypted at rest (#1256, ADR-004 A8a / Amendment 3): only the engine-evaluated keys are plaintext columns — |
|
The ADR-036 crypto-shred key store (#1256): KEK-wrapped per-subject DEKs. |
|
CMS-64 quarterly expenditure report. Composite unique on |
|
CMS-416 annual EPSDT report. Composite unique on |
|
The #1202/#1203 durable report-run substrate (plan report-run-generations D1–D3): permanent generation provenance rows with atomic staged→published promotion, the guarded token-fenced job table, and the materialized per-run universe. Generation lifecycle: |
|
Per-service transactional outbox (ADR-018), schema single-sourced in |
Cross-service FKs (ADR-001 boundary)
canopy-reporting is a downstream aggregator and holds no Postgres-level FKs to other services. Every UUID that points outside canopy_reporting is an application-level reference: snap_qc_universe.household_id / certification_id (canopy-persons, canopy-snap), tanf_acf199_snapshots.case_id (canopy-tanf), medicaid_tmsis_eligibility_extracts.person_id / enrollment_id (canopy-persons, canopy-enrollment), snap_monthly_reports.generated_by (worker id, no service-side enforcement). The aggregator pulls data via the program services' HTTP APIs (no cross-database joins) per ADR-001.
Retention
Federal-submission records — retention is per-federal-partner. SNAP records (7 CFR 272.1(f)): minimum 3 years from the end of the fiscal year. TANF records (45 CFR 92.42): 3 years from the date of submission. Medicaid records (42 CFR 433.32): 3 years from the date of submission of the final claim, longer if audit is open. CMS-64 / CMS-416: typically 7 years to cover federal audit cycles. In practice rows are retained indefinitely in the production DB; archive moves are operator-driven and gated by federal partner sign-off on the submission window.
Indexes
-
snap_monthly_reports_generation_month_uq(unique) — one row per (generation, report_month); replacedidx_snap_monthly_reports_monthwhen uniqueness moved onto the output generation (#1202 MR4 backfill migration) -
idx_snap_qc_universe_snapshot— per-snapshot listing -
idx_snap_qc_universe_household— per-household lookup -
snap_qc_universe_generation_household_snapshot_uq(unique) — one row per (generation, household, snapshot); replacedidx_snap_qc_universe_household_snapshotwhen uniqueness moved onto the output generation (#1202 MR4 backfill migration). Also the keyset ride for the generation-scoped paginated QC-universe reads (#1202 MR5): each page (WHERE generation_id = $1 [AND household_id > $3] ORDER BY household_id LIMIT $2) is a contiguous index range — seek, read one page, stop -
idx_snap_qc_universe_snapshot_household— the #1221(snapshot_date, household_id)keyset composite the pre-generation reads rode; retained for snapshot-scoped scans -
tanf_acf199_generation_month_case_uq(unique) — one case-snapshot per (generation, month, case); replacedtanf_acf199_month_casewhen uniqueness moved onto the output generation (#1202 MR4 backfill migration) -
tanf_acf199_month— per-month listing -
tanf_acf196_fy_quarter_cat(unique) — composite uniqueness on quarterly financial report key -
tanf_wpr_month(unique) — single WPR row per report_month -
medicaid_tmsis_generation_month_enrollment_uq(unique) — one extract row per (generation, month, enrollment); replacedmedicaid_tmsis_month_enrollmentwhen uniqueness moved onto the output generation (#1202 MR4 backfill migration) -
medicaid_tmsis_cms416_universe_idx— the(report_month, chip_indicator, person_id)composite the CMS-416 local-universe drain rides (half-open report-year range +chip_indicator = false+person_idkeyset order, EXPLAIN-pinned; #1202 MR6); replaced the prefix-redundant single-columnmedicaid_tmsis_month(dropped in20261101000004) -
medicaid_tmsis_person— per-person lookup -
medicaid_cms64_fy_quarter_cat_pop(unique) — composite uniqueness on CMS-64 row key -
medicaid_cms416_generation_year_age_uq(unique) — one row per (generation, report_year, age_group); replacedmedicaid_cms416_year_agewhen uniqueness moved onto the output generation (#1202 MR4 backfill migration) -
event_outbox_*— the three generated partial outbox indexes (drainer hot path, lease-aware claim, ADR-039 held-skip); documented once in the data-models index
Migration files
-
20260401000000_create_reporting_tables.sql— SNAP FNS-388 monthly reports + FNS-7176 QC universe snapshot -
20260409000000_tanf_medicaid_reporting_tables.sql— TANF ACF-199 monthly snapshots, ACF-196 quarterly financial, WPR calculations; Medicaid T-MSIS eligibility extracts, CMS-64 quarterly expenditures, CMS-416 annual EPSDT -
20261110000000_reporting_redaction_keys.sql— the ADR-036redaction_keysstore (#1256): per-generation DEKs + one-way tombstone + live-subject unique index -
20261110000001_seal_restricted_reporting_stores.sql— #1256 A8a: addserror_codevaluesadmin_reset/crypto_failure; the quiesced fresh-start reset (terminalize in-flight tmsis/cms_416 runs, supersede tmsis generations, clear their universe rows + derived CMS-64/416 rows); reshapesmedicaid_tmsis_eligibility_extracts(drop the 11 plaintext attribute columns, addrestricted_payload) -
20260508000000_create_event_outbox.sql+20260518004851_event_outbox_lease_columns.sql+20260713000000_event_outbox_hold.sql— the generated ADR-039 single-sourced outbox migrations (cargo xtask outbox-migrations --write); documented once in the data-models index -
20260723120000_qc_stub_fields_nullable.sql— #1155: the QC stub fields (expedited_service,work_registration_exempt_count,ievs_match_completed) nullable + historical values NULLed (unknown is NULL, never a fabricated false/0) -
20260820000000_qc_universe_keyset_index.sql— the(snapshot_date, household_id)keyset composite for the paginated QC-universe reads (#1221; transactional NOT CONCURRENTLY per the #1196 rationale) -
20261101000000_report_generations.sql+20261101000001_report_runs.sql+20261101000002_report_run_universe_and_legacy_backfill.sql— the #1202/#1203 durable report-run substrate (plan report-run-generations): thereport_generationspublication model, the guardedreport_runsjob table, the materializedreport_run_universe,generation_idon all five output tables with uniqueness moved to (generation_id, natural key), and the legacy published-generation backfill. Backfilled legacy generations are pure DATA —run_idNULL (surfaced asprovenance.run_id = null),build_version/params_hash'legacy'; thelegacy_generation_idwrite-path helper that stamped the synchronous extracts' rows was deleted in #1202/#1203 MR6 with its last consumers (every kind now writes through the run pipeline’s staged generations) -
20261101000003_abawd_household_unverified_legacy_null.sql— #1202 MR5 (plan D8):snap_qc_universe.abawd_householdnullable + DEFAULT dropped + ALL historical values NULLed — outage-fabricatedfalseis indistinguishable from sourcedfalse; NULL = "unverified legacy" -
20261101000004_cms416_universe_index.sql— #1202/#1203 MR6 (plan D2): addsmedicaid_tmsis_cms416_universe_idx (report_month, chip_indicator, person_id)for the CMS-416 local-universe drain and DROPS the prefix-redundantmedicaid_tmsis_month
All migrations are forward-only per ADR-016.