canopy-tanf Data Model
On this page
Cross-link: canopy-tanf API Reference · Source: migrations/
Tables
| Table | Purpose |
|---|---|
|
TANF application intake records. One row per application received from canopy-eligibility. Carries the orchestrator’s |
|
Income records relevant to TANF determination. Includes both self-reported and FTI-verified income. |
|
Federal Tax Information received from IRS. All access MUST be wrapped with FTI audit logging. Carries |
|
SSA SOLQ/BINDEX match results (TANF CMA). |
|
Per-person federal + state time-limit tracking (42 USC 608(a)(7)). |
|
Per-person work-requirement state. |
|
Per-week work activity log. |
|
Signed TANF determinations. |
|
Immutable determination input snapshots (T2-4). One row per determination (PK = FK |
|
Per-value DEK store for crypto-shred redaction. One row per per-determination DEK: |
|
Lump-sum ineligibility windows. Nonrecurring income ≥ 100% FPL = lump sum; |
|
GRG (Grandparents Raising Grandchildren) payments. |
|
Per-person Personal Responsibility requirement tracking. |
|
TANF verification discrepancies — discriminator: |
|
FTI access audit log. Maintained separately from the application audit log; available for IRS on-site inspection independently. Carries |
|
Retention archive table ( |
|
Per-program overpayment claims. Byte-identical schema across all five program services. |
|
One or more repayment plans per claim. |
|
Append-only ledger of recoupment events. |
|
Append-only principal corrections (signed |
|
Per-service transactional outbox (ADR-018), schema single-sourced in |
|
Per-service consumer inbox (#433 / ADR-018 amendment). |
Cross-service FKs (ADR-001 boundary)
Per ADR-001, canopy-tanf holds no Postgres-level foreign keys to other services. DB-level FKs are intra-database only: every tanf_application_id column references tanf_applications(id); determination_snapshots.determination_id → tanf_determinations(id); tanf_work_activities.work_requirement_id → tanf_work_requirements(id); repayment_plans.overpayment_claim_id → overpayment_claims(id); recoupment_ledger.overpayment_claim_id → overpayment_claims(id) + repayment_plan_id → repayment_plans(id). Every other UUID column referencing a foreign service — household_id / person_id / applicant_person_id / head_of_household_person_id / grandparent_person_id / grandchild_person_id (canopy-persons), application_id on tanf_applications (canopy-applications), overpayment_claims.determination_id (this service’s tanf_determinations, unconstrained for back-rebill flows) — is application-level only.
Retention
canopy-tanf holds FTI (IRC §6103(l)(7); IRS Publication 1075) in fti_tax_data. FTI audit access is logged to fti_audit_log per Pub 1075 §4; minimum 7-year retention applies to the audit log per Pub 1075 AU-11 (ADR-004 Amendment 2; rows aged out of fti_audit_log migrate to fti_audit_log_archive with the ADR-014 hash chain extending across the boundary). SSA SOLQ/BINDEX data in ssa_match_results is retained per the TANF Computer Matching Agreement terms. TANF case records overall follow the HHS uniform-administrative-requirements retention floor (45 CFR 75.361, 3-year minimum — TANF has no dedicated retention CFR analogous to SNAP’s 7 CFR 272.1(f)) and the Georgia state records-retention schedule. The longest applicable floor governs. Archive moves and FTI-expiry purges are operator-driven (ADR-016 forward-only — no destructive migrations).
Indexes
-
idx_tanf_applications_{application,household}— application lookups -
idx_tanf_income_application— income lookup per application -
idx_fti_tax_data_{application,person}— FTI lookup (audit-wrapped) -
idx_ssa_match_results_application— SSA match lookup -
idx_tanf_time_limits_person— time-limit lookup -
idx_tanf_work_requirements_person— work-requirement lookup -
idx_tanf_work_req_sanctioned(partial,(sanction_level, sanction_expires_at) WHERE sanction_level >= 1, migration 20260816000000) — supervisor sanctions-rollup panel (#1233); the rollup query carries a matchingWHERE sanction_level >= 1so the aggregate scans only the sanctioned subset via this index instead of seq-scanning the level-0-dominated table (predicate must stay in lockstep with the query; pinned by an EXPLAIN regression test) -
idx_tanf_determinations_application— determination lookup -
idx_tanf_determinations_determined_at_id((determined_at DESC, id DESC), migration 20260817000000) — the keyset cursor backing the paginatedGET /v1/determinationslist (#1195); serves the newest-first page (and the ACF-199 extractor’s month-scoped page-loop) as an index scan with no top-N sort -
idx_determination_snapshots_as_of— input-snapshot lookup by evaluation date (ADR-028) -
idx_tanf_lump_sum_person— lump-sum ineligibility windows -
idx_tanf_grg_grandparent,idx_tanf_grg_grandchild— GRG payment lookups -
idx_tanf_pr_application,idx_tanf_pr_person— Personal Responsibility lookups -
idx_tanf_discrepancies_household,idx_tanf_discrepancies_status(partial on pending) — discrepancy worklist -
idx_fti_audit_{accessed_at,accessed_by,purpose_code}— FTI audit query shapes -
idx_fti_audit_created_at—(created_at)for the ADR-014 §9 in-lock predecessor-hash lookup (ORDER BY created_at DESC LIMIT 1, held under the per-originating_systemadvisory lock on every FTI-bearing determination commit) andverify_chain’s ascending walk; #1197, migration 20260811000000. Closes the ADR-014 §9 doc/schema drift (the §9 budget asserted a `created_at DESCindex that did not exist — the pre-#1197 indexes are onaccessed_at/accessed_by/purpose_code) -
idx_fti_audit_event_hash,idx_fti_audit_archive_event_hash— ADR-014 hash-chain verification -
overpayment_claims_status,repayment_plans_by_claim,recoupment_ledger_by_claim— overpayment lifecycle -
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 -
event_inbox_unprocessed_idx(partial,WHERE processed_at IS NULL) — replay / janitor hot path
Migration files
-
20260325000000_create_tanf_tables.sql— original schema (9 tables: applications, household_snapshots, income, fti_tax_data, ssa_match_results, time_limits, work_requirements, work_activities, determinations) -
20260325000001_create_fti_audit_log.sql— Pub 1075 §4 audit log + archive table (LIKE … INCLUDING ALL) -
20260407000000_add_lump_sum_grg_personal_resp.sql—tanf_lump_sum_periods(PAMMS 1650),tanf_grg_payments(PAMMS 1210),tanf_personal_responsibilities(PAMMS 1345-1370) -
20260422000000_add_denial_reason_code.sql—tanf_determinations.denial_reason_codefrom JDMo-denial-codeoutput (no backfill — pre-fix rows use the legacy substring-categorized basis) -
20260425000000_add_fti_audit_hash_chain.sql— ADR-014previous_hash+event_hashonfti_audit_log+ archive -
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 -
20260510000000_drop_federal_time_limit_default.sql— dropsDEFAULT 60fromtanf_time_limits.federal_limit_monthsso value flows fromjurisdiction.tomlper ADR-003 / #441 -
20260510000001_create_overpayments.sql— canonical overpayment schema per 42 USC 609(a)(1); 45 CFR 263.11 -
20260727000000_claim_provenance_adjustments.sql— #1104: claim provenance + partial UNIQUEs, the void path, andclaim_adjustments(canonical copy incrates/canopy-overpayments/migrations/; byte-parity crate-tested) -
20260511000000_add_sanction_lifecycle.sql— #416sanction_imposed_at/sanction_expires_at/sanction_reasonontanf_work_requirements -
20260512000000_create_tanf_discrepancies.sql— #448 worker-portal verification discrepancies -
20260516000000_create_event_inbox.sql— #433 consumer-side inbox (ADR-018 amendment) -
20260603120000_fti_audit_append_only_guard.sql— statement-level append-only guard onfti_audit_log+ archive (GUCcanopy.audit_maintenance) -
20260623000000_create_determination_snapshots.sql—tanf_determinations.snapshot_hashcolumn + the immutabledetermination_snapshotstable + its append-only trigger (T2-4 #685; ADR-028) -
20260624140000_create_redaction_keys.sql— the per-serviceredaction_keystable + its one-way-tombstone trigger (T2-6 #687, ADR-036) -
20260630000000_drop_tanf_household_snapshots.sql— drops the superseded write-onlytanf_household_snapshotstable (created in20260325000000); the verdict-affecting household composition is frozen indetermination_snapshotsinstead (D9 #883, ADR-016 expand→contract) -
20260811000000_fti_audit_created_at_idx.sql—idx_fti_audit_created_atfor the ADR-014 §9 in-lock predecessor lookup + verify walk (#1197, scale audit H13; closes the §9 doc/schema drift). Transactional (NOTCONCURRENTLY) for the same reason as the #1196 eligibility index migration -
20260816000000_sanctions_rollup_idx.sql—idx_tanf_work_req_sanctionedpartial index for the supervisor sanctions-rollup panel (#1233, scale audit L1). Transactional (NOTCONCURRENTLY), same rationale -
20260817000000_tanf_determinations_list_keyset_idx.sql—idx_tanf_determinations_determined_at_id (determined_at DESC, id DESC)backing the keyset-paginatedGET /v1/determinationslist (#1195, scale audit C1). Transactional (NOTCONCURRENTLY), same rationale -
20260902000000_overpayment_claims_keyset_idx.sql— #1222 (scale audit M11):overpayment_claims_keyset (created_at DESC, id DESC)serving the roll-up keyset page; transactionalCREATE INDEX(notCONCURRENTLY— the sqlx migrator’s advisory lock deadlocks againstCONCURRENTLY’s snapshot wait); canonical copy in `crates/canopy-overpayments/migrations/, byte-parity asserted by the crate’s tests -
20260910000000_chain_v2_substrate.sql— #1246 MR-2 (ADR-014 Amendment 6): the dormant chain-v2ftisubstrate — see the chain-v2 section below
All migrations are forward-only per ADR-016.
chain-v2 substrate (dormant, #1246 / ADR-014 Amendment 6)
Installed by 20260910000000_chain_v2_substrate.sql — the fti-family copy
(canopy-tanf is a chain SOURCE; the anchor store + C6 verification state live only
in canopy_security). Dormant until the #1279 cutover.
| Table | Purpose |
|---|---|
|
The shared registry substrate (identical DDL to the canopy-security copy): instance history, active pointer, fenced epochs, the source registry |
|
The FTI family’s strict-from-row-one event store (same constraint set as the audit copy). Hashed business columns are DERIVED from the payload — including the row |
Ownership: every object is owned by NOLOGIN canopy_chain_owner_tanf;
the canopy_tanf_app runtime role appends ONLY through the SECURITY
DEFINER function (C8). Details: ADR-014
Amendment 6.