Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
eac5260
fix(notary): use predicate dependency results
jeremi Jul 14, 2026
c515f2f
feat(solmara): federate child evidence by source
jeremi Jul 14, 2026
ca9eebe
feat(solmara): move Notary state to PostgreSQL
jeremi Jul 14, 2026
69ef991
feat(solmara): flatten child benefit household projection
jeremi Jul 14, 2026
efbd3da
feat(solmara): author six authority registry projects
jeremi Jul 14, 2026
b884726
feat(solmara): export authority runtime closures
jeremi Jul 14, 2026
4200ee5
feat(solmara): issue Relay workload identities
jeremi Jul 14, 2026
6d6bf2a
refactor(solmara): compose authority evidence in applications
jeremi Jul 14, 2026
5e76401
chore(solmara): refresh authority Notary configs
jeremi Jul 14, 2026
f0b77c7
feat(solmara): model typed authority projections
jeremi Jul 14, 2026
dc27ab2
refactor(home): present authority-owned Notaries
jeremi Jul 14, 2026
6b8c187
refactor(portal): compose authority evidence explicitly
jeremi Jul 14, 2026
fe5fadd
refactor(solmara): collect child benefit authority evidence
jeremi Jul 14, 2026
3de5842
chore(solmara): bound authority Notary CEL workers
jeremi Jul 14, 2026
d170e4f
chore(solmara): scan generated runtime for secrets
jeremi Jul 14, 2026
e332aa1
docs(solmara): describe authority-owned Notaries
jeremi Jul 14, 2026
0ed3e66
refactor(solmara): route examples to authority Notaries
jeremi Jul 14, 2026
2e0ad54
refactor(solmara): close authority secret contract
jeremi Jul 14, 2026
4caa9ca
refactor(solmara): pair each Relay with its Notary
jeremi Jul 14, 2026
caeb3be
test(solmara): prove authority Notary persistence
jeremi Jul 15, 2026
60321cf
chore(solmara): adopt Registry Stack v0.10.0
jeremi Jul 16, 2026
4d67967
fix(solmara): default release check to v0.10.0
jeremi Jul 16, 2026
71e9263
fix(solmara): pass child predicate evaluation date
jeremi Jul 16, 2026
17c9805
fix(solmara): close hosted release review gaps
jeremi Jul 16, 2026
948f475
fix(solmara): retain hosted secret scan
jeremi Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
150 changes: 86 additions & 64 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,100 @@

COMPOSE_PROJECT_NAME=solmara-lab-replace-me

REGISTRY_RELAY_AUDIT_HASH_SECRET=
REGISTRY_NOTARY_AUDIT_HASH_SECRET=
REGISTRY_NOTARY_REPLAY_REDIS_URL=redis://redis:6379/0
SOLMARA_RELAY_KEY_ACTIVE_WRITE_DEADLINE_UNIX_MS=4102444800000
SOLMARA_RELAY_AUDIT_EVENT_RETENTION_MS=2592000000
REGISTRY_ESIGNET_KYC_KEYSTORE_PASSWORD=
REGISTRY_ESIGNET_KYC_TOKEN_SECRET=
REGISTRY_ESIGNET_PSUT_SECRET=

SOLMARA_POSTGRES_USER=solmara_registry
SOLMARA_POSTGRES_PASSWORD=
SOLMARA_POSTGRES_DB=solmara_lab
SOLMARA_NIA_DATABASE_URL=postgres://solmara_registry:replace-me@postgres:5432/solmara_lab?sslmode=require
NIA_SOURCE_POSTGRES_READER_PASSWORD=
SIPF_SOURCE_POSTGRES_READER_PASSWORD=
SOLMARA_NIA_DATABASE_URL=postgres://solmara_source_nia_reader:replace-me@postgres:5432/solmara_lab?sslmode=require
SOLMARA_SIPF_DATABASE_URL=postgres://solmara_source_sipf_reader:replace-me@postgres:5432/solmara_lab?sslmode=require
SOLMARA_ESIGNET_POSTGRES_PASSWORD=

CRA_CHILD_BENEFIT_SOURCE_RAW=
CRA_CHILD_BENEFIT_SOURCE_HASH=
CRA_PENSION_SOURCE_RAW=
CRA_PENSION_SOURCE_HASH=
CRA_CITIZEN_SOURCE_RAW=
CRA_CITIZEN_SOURCE_HASH=
CRA_RELAY_AUDIT_HASH_SECRET=
CRA_RELAY_AUDIT_PSEUDONYM_EPOCH_1=
CRA_RELAY_POSTGRES_RUNTIME_PASSWORD=
CRA_RELAY_POSTGRES_KEYRING_MAINTENANCE_PASSWORD=
CRA_RELAY_POSTGRES_KEYRING_READER_PASSWORD=
CRA_NOTARY_AUDIT_HASH_SECRET=
CRA_NOTARY_POSTGRES_MIGRATOR_PASSWORD=
CRA_NOTARY_POSTGRES_RUNTIME_PASSWORD=
NIA_RELAY_AUDIT_HASH_SECRET=
NIA_RELAY_AUDIT_PSEUDONYM_EPOCH_1=
NIA_RELAY_POSTGRES_RUNTIME_PASSWORD=
NIA_RELAY_POSTGRES_KEYRING_MAINTENANCE_PASSWORD=
NIA_RELAY_POSTGRES_KEYRING_READER_PASSWORD=
NIA_NOTARY_AUDIT_HASH_SECRET=
NIA_NOTARY_POSTGRES_MIGRATOR_PASSWORD=
NIA_NOTARY_POSTGRES_RUNTIME_PASSWORD=
SRO_RELAY_AUDIT_HASH_SECRET=
SRO_RELAY_AUDIT_PSEUDONYM_EPOCH_1=
SRO_RELAY_POSTGRES_RUNTIME_PASSWORD=
SRO_RELAY_POSTGRES_KEYRING_MAINTENANCE_PASSWORD=
SRO_RELAY_POSTGRES_KEYRING_READER_PASSWORD=
SRO_NOTARY_AUDIT_HASH_SECRET=
SRO_NOTARY_POSTGRES_MIGRATOR_PASSWORD=
SRO_NOTARY_POSTGRES_RUNTIME_PASSWORD=
PROGRAMME_RELAY_AUDIT_HASH_SECRET=
PROGRAMME_RELAY_AUDIT_PSEUDONYM_EPOCH_1=
PROGRAMME_RELAY_POSTGRES_RUNTIME_PASSWORD=
PROGRAMME_RELAY_POSTGRES_KEYRING_MAINTENANCE_PASSWORD=
PROGRAMME_RELAY_POSTGRES_KEYRING_READER_PASSWORD=
PROGRAMME_NOTARY_AUDIT_HASH_SECRET=
PROGRAMME_NOTARY_POSTGRES_MIGRATOR_PASSWORD=
PROGRAMME_NOTARY_POSTGRES_RUNTIME_PASSWORD=
SIPF_RELAY_AUDIT_HASH_SECRET=
SIPF_RELAY_AUDIT_PSEUDONYM_EPOCH_1=
SIPF_RELAY_POSTGRES_RUNTIME_PASSWORD=
SIPF_RELAY_POSTGRES_KEYRING_MAINTENANCE_PASSWORD=
SIPF_RELAY_POSTGRES_KEYRING_READER_PASSWORD=
SIPF_NOTARY_AUDIT_HASH_SECRET=
SIPF_NOTARY_POSTGRES_MIGRATOR_PASSWORD=
SIPF_NOTARY_POSTGRES_RUNTIME_PASSWORD=
NAGDI_RELAY_AUDIT_HASH_SECRET=
NAGDI_RELAY_AUDIT_PSEUDONYM_EPOCH_1=
NAGDI_RELAY_POSTGRES_RUNTIME_PASSWORD=
NAGDI_RELAY_POSTGRES_KEYRING_MAINTENANCE_PASSWORD=
NAGDI_RELAY_POSTGRES_KEYRING_READER_PASSWORD=
NAGDI_NOTARY_AUDIT_HASH_SECRET=
NAGDI_NOTARY_POSTGRES_MIGRATOR_PASSWORD=
NAGDI_NOTARY_POSTGRES_RUNTIME_PASSWORD=

NIA_CHILD_BENEFIT_SOURCE_RAW=
NIA_CHILD_BENEFIT_SOURCE_HASH=
NIA_PENSION_SOURCE_RAW=
NIA_PENSION_SOURCE_HASH=
NIA_CITIZEN_SOURCE_RAW=
NIA_CITIZEN_SOURCE_HASH=
SOLMARA_ESIGNET_IDENTITY_RELEASE_RAW=
SOLMARA_ESIGNET_IDENTITY_RELEASE_HASH=

SRO_CHILD_BENEFIT_SOURCE_RAW=
SRO_CHILD_BENEFIT_SOURCE_HASH=
SRO_CITIZEN_SOURCE_RAW=
SRO_CITIZEN_SOURCE_HASH=

PROGRAMME_CHILD_BENEFIT_SOURCE_RAW=
PROGRAMME_CHILD_BENEFIT_SOURCE_HASH=
PROGRAMME_PENSION_SOURCE_RAW=
PROGRAMME_PENSION_SOURCE_HASH=
PROGRAMME_CITIZEN_SOURCE_RAW=
PROGRAMME_CITIZEN_SOURCE_HASH=

SIPF_PENSION_SOURCE_RAW=
SIPF_PENSION_SOURCE_HASH=
SIPF_CITIZEN_SOURCE_RAW=
SIPF_CITIZEN_SOURCE_HASH=

NAGDI_NOTARY_SOURCE_RAW=
NAGDI_NOTARY_SOURCE_HASH=
NAGDI_CITIZEN_SOURCE_RAW=
NAGDI_CITIZEN_SOURCE_HASH=

CHILD_BENEFIT_NOTARY_TOKEN=
CHILD_BENEFIT_CLIENT_TOKEN_HASH=
PENSION_NOTARY_TOKEN=
PENSION_CLIENT_TOKEN_HASH=
CHILD_BENEFIT_FEDERATOR_TOKEN=
CRA_CHILD_BENEFIT_CLIENT_TOKEN=
CRA_CHILD_BENEFIT_CLIENT_TOKEN_HASH=
NIA_CHILD_BENEFIT_CLIENT_TOKEN=
NIA_CHILD_BENEFIT_CLIENT_TOKEN_HASH=
SRO_CHILD_BENEFIT_CLIENT_TOKEN=
SRO_CHILD_BENEFIT_CLIENT_TOKEN_HASH=
PROGRAMME_CHILD_BENEFIT_CLIENT_TOKEN=
PROGRAMME_CHILD_BENEFIT_CLIENT_TOKEN_HASH=
CRA_PENSION_CLIENT_TOKEN=
CRA_PENSION_CLIENT_TOKEN_HASH=
CRA_CITIZEN_CLIENT_TOKEN=
CRA_CITIZEN_CLIENT_TOKEN_HASH=
NIA_CITIZEN_CLIENT_TOKEN=
NIA_CITIZEN_CLIENT_TOKEN_HASH=
SIPF_PENSION_CLIENT_TOKEN=
SIPF_PENSION_CLIENT_TOKEN_HASH=
NAGDI_NOTARY_TOKEN=
NAGDI_CLIENT_TOKEN_HASH=
CITIZEN_PORTAL_BFF_TOKEN_HASH=
PORTAL_CITIZEN_NOTARY_TOKEN=
PORTAL_RELAY_TOKEN=
PORTAL_RELAY_TOKEN_HASH=

CHILD_BENEFIT_NOTARY_ISSUER_JWK=
PENSION_NOTARY_ISSUER_JWK=
NIA_NOTARY_ISSUER_JWK=
SIPF_NOTARY_ISSUER_JWK=
NAGDI_NOTARY_ISSUER_JWK=
CITIZEN_NOTARY_ISSUER_JWK=
CITIZEN_ISSUER_NOTARY_ISSUER_JWK=
CITIZEN_ISSUER_NOTARY_ACCESS_TOKEN_JWK=
CITIZEN_ISSUER_ESIGNET_RP_JWK=
CITIZEN_ISSUER_ESIGNET_CLIENT_PRIVATE_KEY_B64=
CRA_RELAY_WORKLOAD_JWK=
NIA_RELAY_WORKLOAD_JWK=
NIA_ESIGNET_RELAY_WORKLOAD_JWK=
SRO_RELAY_WORKLOAD_JWK=
PROGRAMME_RELAY_WORKLOAD_JWK=
SIPF_RELAY_WORKLOAD_JWK=
NAGDI_RELAY_WORKLOAD_JWK=
PORTAL_SESSION_SECRET=
PORTAL_AUTH_PROVIDER=mock
PORTAL_ESIGNET_CLIENT_ID=solmara-portal
Expand All @@ -86,15 +110,13 @@ PORTAL_ESIGNET_REDIRECT_URI=http://127.0.0.1:4300/auth/callback
PORTAL_ESIGNET_SCOPE=openid profile
PORTAL_ESIGNET_SUBJECT_CLAIM=individual_id

CHILD_BENEFIT_NOTARY_URL=http://127.0.0.1:4321
PENSION_NOTARY_URL=http://127.0.0.1:4322
CHILD_BENEFIT_FEDERATOR_URL=http://127.0.0.1:4321
CRA_NOTARY_URL=http://127.0.0.1:4325
NIA_NOTARY_URL=http://127.0.0.1:4326
SRO_NOTARY_URL=http://127.0.0.1:4327
PROGRAMME_NOTARY_URL=http://127.0.0.1:4328
SIPF_NOTARY_URL=http://127.0.0.1:4322
NAGDI_NOTARY_URL=http://127.0.0.1:4323
PORTAL_CITIZEN_NOTARY_URL=http://127.0.0.1:4324
SOLMARA_CITIZEN_ISSUER_NOTARY_PORT=4325
PORTAL_CIVIL_RELAY_URL=http://127.0.0.1:4311
PORTAL_SOCIAL_RELAY_URL=http://127.0.0.1:4313
PORTAL_AGRI_RELAY_URL=http://127.0.0.1:4316
PORTAL_CERTS_RELAY_URL=http://127.0.0.1:4311
PORTAL_URL=http://127.0.0.1:4300
SCENARIO_RUNNER_URL=http://127.0.0.1:4302
STATIC_METADATA_URL=http://127.0.0.1:4331
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ jobs:
just metadata-publish
- name: Validate
run: |
just release-pins v0.8.4
just release-pins v0.10.0
just lint
just test
just compose
- name: Start stack
run: just up
- name: Smoke
- name: Smoke and PostgreSQL restart persistence
run: just smoke
- name: Portal live e2e
run: just portal-live-e2e
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ __pycache__/
.ruff_cache/
.mypy_cache/
dist/
projects/*/.registry-stack/
*.log
config/postgres/ssl/*
!config/postgres/ssl/.gitkeep
61 changes: 34 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ The first wave covers three journeys:
generated fixture checks.
- `geo/` contains the hand-authored Solmara geometry source used by the
generator.
- `ministries/` contains authority-owned Relay configs, manifest fragments,
crosswalks, and generated fixtures.
- `notaries/` contains purpose-oriented Notary configs.
- `ministries/` contains authority-owned source fixtures, manifest fragments,
and crosswalks.
- `projects/` contains the six authority-owned Registry project sources. Each
project generates one Relay config and one Notary config under `runtime/`.
- `metadata/` assembles the multi-authority metadata publication.
- `portal/` contains the citizen portal and BFF.
- `scenarios/`, `requests/`, and `perf/` carry guided scenarios, API examples,
Expand All @@ -62,10 +63,10 @@ just portal-compose-smoke # HTTP smoke against the Compose portal and live BFF
just portal-live-e2e # browser e2e against the running local stack
just hosted-smoke # public hosted health, endpoint, scenario, and portal checks
just up-esignet # local stack with eSignet-backed portal login
just smoke-esignet # eSignet discovery plus NIA UserInfo release smoke
just smoke-esignet # eSignet public discovery smoke
just down # stop the local Compose topology without deleting volumes
just reset # stop the local Compose topology and delete its volumes
just release-pins v0.8.4 # compare versions.env against published GHCR tags
just release-pins v0.10.0 # compare versions.env against published GHCR tags
just review # security and release-readiness checks
```

Expand All @@ -76,43 +77,48 @@ committed generated artifact.

`versions.env` is the root source for published image digests. The Compose
fallbacks mirror it so direct `docker compose` runs still use pinned Registry
Stack images. The current pins are the Registry Stack `v0.8.4` release digest
Stack images. The current pins are the Registry Stack `v0.10.0` release digest
assets. Because that release publishes amd64 images, Compose defaults
`REGISTRY_STACK_PLATFORM` to `linux/amd64`; override it only when the release
publishes an image for another platform.

The NIA population Relay is intentionally Postgres-backed. `just gen-secrets`
also creates local Postgres TLS material under `config/postgres/ssl/`, and the
generated NIA connection string includes `sslmode=require`, matching Registry
Stack `v0.8.4` Relay requirements.
Every authority runs one Relay and one Notary. Relay consultation state and all
Notary correctness state are PostgreSQL-backed. `just gen-secrets` creates
local PostgreSQL TLS material and distinct runtime and migrator passwords for
each authority. See
[`docs/notary-postgresql-state.md`](docs/notary-postgresql-state.md) for the
database map, diagnosis, backup, recovery, and upgrade workflow.

## Hosted Deployment

See [`docs/hosted-deployment.md`](docs/hosted-deployment.md) for the full
runbook. Coolify uses one hosted compose file for the lab edge plus one compose
file per pseudo-government authority:
runbook. Coolify uses one hosted Compose file for the lab edge plus four
ministry-grouped authority applications:

- `compose.coolify.yaml` for the Visitor Center, portal, scenario runner, and
static metadata.
- `compose.coolify.interior.yaml` for CRA, NIA, and the NIA Postgres store.
- `compose.coolify.yaml` for the Visitor Center, portal, scenario runner,
child-benefit evidence composition, and static metadata.
- `compose.coolify.interior.yaml` for the CRA and NIA Relay and Notary pairs
and their PostgreSQL databases.
- `compose.coolify.esignet.yaml` for eSignet, eSignet UI, and its backing
Postgres/Redis/seed services.
- `compose.coolify.social-development.yaml` for SRO, MoSD programme MIS, and
the child benefit notary.
- `compose.coolify.labour-pensions.yaml` for SIPF and the pension notary.
- `compose.coolify.agriculture.yaml` for NAgDI and the agriculture notary.
- `compose.coolify.citizen-services.yaml` for the citizen services notary.
- `compose.coolify.social-development.yaml` for the SRO and Programme Relay
and Notary pairs and their PostgreSQL databases.
- `compose.coolify.labour-pensions.yaml` for the SIPF Relay and Notary pair and
its PostgreSQL databases.
- `compose.coolify.agriculture.yaml` for the NAgDI Relay and Notary pair and
its PostgreSQL databases.

The hosted compose files remove host port bindings and avoid repo bind mounts
because Coolify does not seed bind-mount sources from the Git checkout. They do
not define custom Docker networks; cross-authority calls use the public
`*.solmara.registrystack.org` TLS endpoints. Authority compose files preserve
Relay audit/cache volumes and Notary audit state volumes, with a small
`volume-permissions` sidecar that makes those volumes writable by the product
runtime users.
authority-owned PostgreSQL state, persistent Relay snapshot caches, and
workload credentials. Notary containers do not use Redis or a writable state
directory.

Run `uv run scripts/render-hosted-configs.py` after editing Relay or Notary
configs so the hosted config overlays stay current.
Run `just registry-projects-sync` after editing an authority project, then
`just registry-projects-runtime-check` to verify the local and hosted Relay and
Notary closures are deterministic.

Run `just hosted-smoke` after each hosted deploy from a trusted shell with the
demo tokens available in `.env` or the process environment. It checks public
Expand All @@ -132,8 +138,9 @@ workflow summary for Coolify env vars:

For local eSignet testing, run `just up-esignet` instead of `just up`, then
sign in through the portal with Elena's fixture `legacy_nid` and static OTP
`111111`. Run `just smoke-esignet` to verify eSignet discovery and the NIA
`solmara-nia-userinfo` attribute-release profile.
`111111`. This sign-in is the end-to-end check of the NIA
`solmara-nia-userinfo` attribute-release profile and its rotating eSignet
workload identity. Run `just smoke-esignet` for the public discovery checks.

Set `UMAMI_WEBSITE_ID` in the hosted environment to enable analytics for the
Visitor Center through the Registry Stack Umami instance.
Expand Down
Loading
Loading