Jurisdiction Onboarding Runbook
On this page
Overview
Canopy is designed so that any US state, territory, tribe, or county can deploy any subset of benefit programs with zero code changes. This runbook walks through the steps to onboard a new jurisdiction, from initial configuration through UAT.
Per ADR-006, jurisdiction-specific configuration lives entirely in:
-
Rulesets — JDM files in
rulesets/{jurisdiction}/ -
Jurisdiction configuration —
rulesets/{jurisdiction}/jurisdiction.toml -
Environment variables — deployment-time settings
No Rust code changes are required. If a jurisdiction’s eligibility rules cannot be expressed in JDM rulesets and jurisdiction.toml configuration, that signals a gap in the ruleset schema that should be addressed at the platform level — not by forking or patching code.
Prerequisites
Before beginning onboarding:
-
Jurisdiction has identified which benefit programs to deploy (see ADR-005 for deployment profiles)
-
Jurisdiction has access to its current eligibility policy manual for each program
-
Federal parameters for the current fiscal year are available in
rulesets/federal/ -
Jurisdiction has infrastructure provisioned (PostgreSQL, RabbitMQ, Keycloak, S3-compatible storage)
-
Jurisdiction has obtained necessary data sharing agreements (CMAs for SSA SOLQ/BINDEX, IEVS MOUs, etc.)
Step 1: Create Jurisdiction Directory
Create rulesets/{jurisdiction}/ where {jurisdiction} is a lowercase identifier (e.g., georgia, texas, navajo-nation, los-angeles-county).
mkdir -p rulesets/{jurisdiction}
Step 2: Write jurisdiction.toml
Create rulesets/{jurisdiction}/jurisdiction.toml with program-specific state options and thresholds.
Use Georgia’s configuration as a reference:
# Jurisdiction configuration for {Jurisdiction Name}
# Per ADR-006: all jurisdiction-specific thresholds and policy options live here.
# Rulesets reference these values by name.
[jurisdiction]
name = "Jurisdiction Name"
fips_state_code = "XX" # 2-digit FIPS state code
fips_county_codes = [] # Empty = statewide; list county codes for county-level deployment
[jurisdiction.holidays] # #1158: REQUIRED — observed working-day holidays
coverage_years = [2026] # extend annually when the state calendar issues;
# EVERY covered year must list holidays (boot-refused otherwise)
dates = ["2026-01-01"] # full observed list, cited in citations.toml
[snap]
bbce_enabled = true # Broad-Based Categorical Eligibility
bbce_gross_income_limit_pct_fpl = 200
bbce_asset_test_eliminated = true
simplified_reporting = true
heat_and_eat_lua = true # LIHEAP/SNAP Heat & Eat linkage
standard_utility_allowance_monthly_cents = 43200 # Jurisdiction-specific SUA (monthly)
telephone_utility_allowance_monthly_cents = 5300 # Telephone-only SUA (monthly)
expedited_service_days = 7 # 7 CFR 273.2(i) deadline
abawd_waiver_active = false # True if jurisdiction has active ABAWD time limit waiver
abawd_waiver_areas = [] # County FIPS codes with area-specific waivers
[tanf]
income_limit_pct_fpl = 50
asset_limit_cents = 100000 # $1,000 in cents
time_limit_months = 48 # State time limit (federal max: 60)
work_requirement_hours_per_week = 30
work_requirement_age_min = 18
work_requirement_age_max = 59
[medicaid]
magi_adult_income_limit_pct_fpl = 138
magi_child_income_limit_pct_fpl = 222
magi_pregnant_income_limit_pct_fpl = 220
chip_income_limit_pct_fpl = 252
work_requirement_enabled = false
work_requirement_hours_per_month = 0
[caps]
income_limit_initial_pct_smi = 50
income_limit_continued_pct_smi = 85
copayment_schedule = "standard" # Reference to copayment table in CAPS ruleset
provider_rate_type = "market" # "market" or "cost"
[wic]
income_limit_pct_fpl = 185 # Federal standard; rarely varies
adjunctive_programs = ["snap", "medicaid", "tanf"]
Not all sections are required — include only the programs the jurisdiction is deploying.
Step 3: Create Program Rulesets
For each program the jurisdiction will deploy, create JDM ruleset files in rulesets/{jurisdiction}/.
Naming Convention
rulesets/{jurisdiction}/
├── jurisdiction.toml
├── snap-eligibility.json
├── snap-benefit-calculation.json
├── tanf-eligibility.json
├── tanf-benefit-calculation.json
├── tanf-work-requirements.json
├── medicaid-magi.json
├── medicaid-non-magi.json
├── medicaid-eligibility-hierarchy.json
├── chip-eligibility.json
├── caps-eligibility.json
└── wic-eligibility.json
Ruleset Development Process
-
Start from Georgia’s rulesets as a reference implementation
-
Modify thresholds to match the jurisdiction’s policy manual (most differences are threshold values that should be in
jurisdiction.toml, not in the ruleset itself) -
Add jurisdiction-specific provisions as structural additions to the JDM decision graph (e.g., state-specific exemptions, additional deduction types)
-
Reference federal parameters by name (e.g.,
"source": "federal/snap-deductions-2026") — do not hardcode federal values in jurisdiction rulesets -
Version the rulesets using
{fiscal_year}.{major}.{minor}format
Federal Parameter References
Rulesets reference shared federal parameters in rulesets/federal/:
rulesets/federal/
├── fpl-2026.json # Federal Poverty Level thresholds
├── snap-allotments-2026.json # Maximum SNAP allotments by household size
├── snap-deductions-2026.json # Standard deduction, excess shelter cap, etc.
├── snap-income-limits-2026.json # 130% / 100% FPL by household size
└── smi-2026.json # State Median Income (for CAPS)
Federal parameters are versioned by fiscal year (October 1 cutover). A jurisdiction’s rulesets reference the current fiscal year’s parameters. When federal parameters update, all jurisdictions must be re-tested.
Step 4: Configure Deployment
Environment Variables
Set deployment-time environment variables:
# Jurisdiction selection
CANOPY_JURISDICTION="{jurisdiction}" # Matches directory name under rulesets/
# Program deployment profile (ADR-005)
# Use: docker compose --profile snap-only up
# Or: docker compose --profile full up
# Keycloak realm configuration
CANOPY_AUTH__ISSUER_URL="https://auth.{jurisdiction}.example.gov/realms/canopy"
CANOPY_AUTH__AUDIENCE="canopy-api"
# Database URLs (one per service, per ADR-001)
CANOPY_PERSONS__DATABASE_URL="postgres://canopy:***@db/canopy_persons"
CANOPY_SNAP__DATABASE_URL="postgres://canopy:***@db/canopy_snap"
# ... one per deployed service
# Optional service URLs (ADR-005 capability flags)
CANOPY_EXCHANGE_URL="" # Empty = exchange integration disabled
CANOPY_PORTAL_URL="" # Empty = applicant portal disabled
Keycloak Realm
Create a Keycloak realm for the jurisdiction with:
-
Client credentials for each Canopy service
-
Realm roles matching the standard role set (admin, supervisor, eligibility_worker, intake_worker, fiscal_officer, readonly)
-
User federation configured for the jurisdiction’s identity provider (LDAP, SAML, etc.)
Use devstack/keycloak/canopy-realm.json as a starting template.
Database Initialization
Run devstack/postgres/init.sql against the jurisdiction’s PostgreSQL instance to create per-service databases.
Then run migrations for each deployed service:
for service in persons applications rules eligibility snap; do
sqlx migrate run \
--source services/canopy-${service}/migrations \
--database-url postgres://canopy:***@db/canopy_${service}
done
Step 5: Validate Rulesets
Before proceeding to integration testing:
# Set jurisdiction for testing
export CANOPY_JURISDICTION="{jurisdiction}"
# Validate all rulesets parse correctly
cargo nextest run -p canopy-rules --lib
# Run SNAP eligibility test scenarios against jurisdiction rulesets
cargo nextest run -p canopy-snap --lib
# Repeat for each deployed program
Create jurisdiction-specific test scenarios that exercise:
-
Income thresholds at boundary values (e.g., exactly at 130% FPL, 1 cent above)
-
Jurisdiction-specific policy options (e.g., BBCE enabled vs. disabled)
-
All program combinations the jurisdiction will deploy
-
Edge cases from the jurisdiction’s policy manual
Step 6: Integration Testing
With the devstack running:
export CANOPY_JURISDICTION="{jurisdiction}"
cargo xtask dev start
cargo nextest run --workspace --profile integration
Verify:
-
Applications can be submitted and routed to the correct program services
-
Determinations are evaluated using the jurisdiction’s rulesets
-
Determinations are signed and verifiable (ADR-002)
-
Events are published without restricted data (ADR-004)
-
Deployment profile starts only the intended services (ADR-005)
-
Health checks pass for all deployed services
Step 7: UAT Preparation
Seed Data
Use canopy-seed to generate deterministic test data for the jurisdiction:
cargo run -p canopy-seed -- --jurisdiction {jurisdiction} --programs snap,tanf
Seed data should cover:
-
Households at various income levels (below, at, and above thresholds)
-
All household compositions (single, married, with dependents, elderly/disabled)
-
All program-specific scenarios (ABAWD, categorical eligibility, simplified reporting, etc.)
-
Edge cases identified during ruleset validation
Step 8: Go-Live Checklist
-
All rulesets validated against jurisdiction policy manual
-
Integration tests pass with jurisdiction configuration
-
UAT scenarios completed successfully
-
Data sharing agreements in place (IEVS MOU, CMAs, FDSH onboarding)
-
Keycloak realm configured with production identity provider
-
Database backups configured
-
Monitoring and alerting configured (Prometheus/Grafana)
-
Incident response procedures documented
-
Federal reporting validated (FNS-388, FNS-7176, etc. as applicable)
-
Compliance documentation prepared (IRS Pub 1075 safeguard review if FTI)
Ongoing Maintenance
Federal Parameter Updates
Federal parameters (FPL, SNAP allotments, deductions) update annually on October 1 (federal fiscal year).
-
HHS publishes new FPL in January; FNS publishes SNAP parameters in September
-
Update
rulesets/federal/with new fiscal year files -
Update jurisdiction rulesets to reference new fiscal year parameters
-
Re-run all test suites with new parameters
-
Deploy before October 1
Policy Changes
When the jurisdiction changes eligibility policy:
-
Update
jurisdiction.tomlthresholds or ruleset JDM files as appropriate -
Version the ruleset change (
{fiscal_year}.{major}.{minor}) -
Run regression tests to verify no unintended side effects
-
Hot-reload rulesets via
PUT /v1/rulesets/{name}(no service restart required)
Audit Preparation
For IRS Pub 1075 safeguard reviews (if deploying TANF or Medicaid with FTI):
-
FTI audit logs are in canopy-tanf and canopy-medicaid databases
-
Verify audit log completeness: every FTI access has worker ID, timestamp, purpose, and data elements
-
Export audit logs for the review period
-
Review
compliance::irs-pub-1075-auditlabeled issues for any open findings