Architecture

On this page
NOTE

The full system-architecture narrative — service topology, database topology (default vs --shared-db), the event bus / outbox, Typst document generation, shared crates, and the ADR rationale — lives in the Developer Guide. The canonical per-service ports/databases catalog is the Service Catalog; shared crates are in Shared Crates Reference. This page is a bounded architecture overview — it must not re-accrete the full catalog.

Canopy is a multi-service Rust workspace implementing an integrated eligibility system for Georgia DHS (SNAP, TANF, Medicaid/CHIP, CAPS, WIC). Program services are legally isolated (each owns its PostgreSQL database per ADR-001) to satisfy federal data tenancy; infrastructure services share one PostgreSQL instance. All services communicate over the RabbitMQ topic exchange canopy.events. ADRs live at docs/modules/ROOT/pages/adrs/adr-{NNN}-*.adoc.

ADR index (number → topic)

  • ADR-001 — Program service isolation (own PostgreSQL DB per program; no cross-program DB access; Amendment 1 = sanctioned bulk-read contracts — keyset universe reads + :batchGet + projection + async report-run job model)

  • ADR-002 — Black-box determination contract (program services return signed JWS, never raw data; Amendment 1 = async/bulk determination variant — queued determination.requested, unchanged JWS in the system-initiated path, stable idempotency keys + driver-owned checkpoints, one-pending-slot deferral, + reuse of the signed previous_determination_id (ADR-028 §57) as the initial-vs-re-determination signal)

  • ADR-003 — Ruleset-as-data (eligibility logic in versioned JDM files, evaluated by canopy-rules)

  • ADR-004 — Legally-scoped data tenancy (FTI / IEVS / SSA SOLQ-BINDEX / FDSH isolated to authorized services; Amendment 1 = canopy-reporting authorized as a restricted-data consumer for the person-level T-MSIS PHI extract (CMS-416 held de-identified), minimum-necessary — its own Pub 1075 §4 audit log; Amendment 2 = tamper-evidence mechanism re-homed to ADR-041, retention corrected to 7 years (Pub 1075 AU-11), A6 preserved, A7 chain-v2 attachment withdrawn)

  • ADR-005 — Modular deployment profiles (a Docker Compose profile picks the program subset)

  • ADR-006 — Jurisdiction-agnostic rulesets (federal params + per-jurisdiction rules + jurisdiction.toml)

  • ADR-007 — CLI/API/UI parity (every API operation has a canopy CLI subcommand)

  • ADR-008 — Applicant portal architecture (Dioxus, reference-number auth)

  • ADR-009 / ADR-026 — PostgreSQL-backed sessions with Redis LRU cache for BFFs; canopy-portal is Postgres-free with Redis-primary sessions

  • ADR-010 / ADR-029 — Typst document generation (canopy-typst wraps typst-as-lib); general signed-document renderer (POST /v1/documents/render)

  • ADR-011 — Policy-to-rules traceability (citations.toml → PAMMS)

  • ADR-012 / ADR-017 — Layered YAML config + SOPS/age-encrypted secrets at rest

  • ADR-014 — FTI audit hash-chain integrity (amends ADR-004). Superseded by ADR-041 (Amendment 12): the FTI hash chain + the chain-v2 line are being retired (gated behind #1304) for a general logging + redaction facility. Historical chain-v2 lineage: Amendment 5 = chain-v2 (hash-bound sequence + durable head, sharded, externally notarized-anchored); Am 6 = the substrate byte-level contract; Am 7 = the append-transport bindings; Am 8 = the verifier bindings; Am 9 = the verifier revisions (unified /v1/security/chain/* namespace, family lease, durable verify jobs, JSON number fence); Am 10 = the external anchor authority (enumerable transparency frontier); Am 11 = the anchor reframed on a WORM capability tier (supersedes Am 10). All of the above are superseded by ADR-041; the surviving non-chain C7/C8 obligations are re-ratified there.

  • ADR-016 — Forward-only schema migrations

  • ADR-018 — Persistent outbox (event_outbox table + OutboxDrainer)

  • ADR-019 — Service identity and on-behalf-of (OIDC at service boundaries)

  • ADR-021 / ADR-022 / ADR-024 — Worker-portal composability (plugin model, override storage, user-delta schema)

  • ADR-025 — Cross-service referential integrity (cargo xtask seed-verify auditor pattern)

  • ADR-027 / ADR-028 — Worker fact-authoring + determination input snapshot (epic &56)

  • ADR-030 — Code-quality gating (strict lint posture + quality-budgets ratchet)

  • ADR-031 / ADR-032 — Policy coverage assurance + two-tier scenario corpus

  • ADR-033 / ADR-034 / ADR-035 — Generative seed harness; per-program / per-subject determination context-mapping

  • ADR-037 — Signing-key-aware service-token acquisition (sender self-heals on a rotated/deleted IdP token-signing key; amends ADR-019)

  • ADR-038 — Concurrency-safe, recoverable applicant finalization (persons transactional receipt + draft-row-locking saga + crypto-shred compensation + held events; amends ADR-026 §5/§6)

  • ADR-039 — Single-source outbox schema (canonical in canopy-mq + generator + parity gate) + first-class event-hold (amends ADR-018)

  • ADR-040 — Build-once, gate-complete artifact promotion (immutable SHA staging refs + digest retag behind a full test-stage barrier; guarded latest; per-image SBOMs; ci-config-lint static gate)

  • ADR-041 — Configurable structured logging + jurisdiction-owned field redaction (supersedes ADR-014’s hash chain; amends ADR-004). Per-field redaction in every service, fully jurisdiction-overridable (canopy = mechanism, deployment = policy); an unfilterable versioned audit-export channel carrying a complete-row digest + policy version; external tamper-evidence + retention delegated to the deployment logging facility

  • ADR-042 — Upload scan quarantine (clamd backend, async promotion). Applicant uploads land durable at scan_status='pending'; a fenced promotion worker settles verdicts bound to content identity (a verdict follows the bytes, not the row); serving and review gate on viewability — clean, or scan-skipped with an audited supervisor override

  • ADR-043 — OIDC program amendments to the immutable ADR-023 (citizen-path credential, exchange semantics, frozen rejection contract). The citizen path runs on a dedicated narrow IdP service account (no citizen token exists to exchange); GA exchange semantics fixed (sub preserved, azp visible, act unused); 401/403 responses frozen; a denied user-context exchange never downgrades to the broad service token

  • ADR-044 — Worker program scope is a required IdP claim (Pub 1075 AC-6). A token with no usable primary_programs is refused at admission — role-agnostic, no canopy-side override (the override is the claim mapper); the claim parses once into a structurally non-empty WorkerProgramScope, so the fail-open see-all branches have nothing left to branch on. Explicitly a BFF control: it does not supersede the upstream actor-claim work

The complete, current ADR list is in the site navigation under Architecture & Design.

Service topology at a glance

  • Infrastructure services share the postgres instance (port 5432): rules, persons, applications, eligibility, verification, enrollment, renewals, notices, exchange, appeals, reporting, security, plus the canopy_web BFF database. canopy-portal is Postgres-free per ADR-026.

  • Program services have isolated PostgreSQL databases (ADR-001): snap (5433), tanf (5434), medicaid (5435), caps (5436), wic (5437).

  • BFFs: canopy-web (worker portal) and canopy-portal (applicant portal — a Dioxus app per ADR-008, built via dx). canopy-web’s container-internal listen port is fixed; its host port is OS-ephemeral — discover it with cargo xtask dev status.

Edit this page · default