Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The reset has two evidence cadences:
- CRA civil links and death status, MoSD enrolment, SIPF pension and survivor
cases, and NAgDI agriculture requirements use named Relay V2 exact lookups.

Six Evidence cells have distinct providers, issuers, signing keys, JWKS, audit
Six Evidence gateways have distinct providers, issuers, signing keys, JWKS, audit
sinks, subject-binding secrets, and endpoints. Five Relays expose only the
named non-enumerating operations needed by the lab. NIA's Relay is reserved for
the optional eSignet UserInfo profile; NIA Evidence reads its own extract.
Expand Down Expand Up @@ -60,11 +60,12 @@ Local entry points after startup are:

- Visitor Center: `http://127.0.0.1:4301`
- Programme portal: `http://127.0.0.1:4300`
- Authority Evidence gateway: `https://localhost:4341/evidence/{authority}`
- Local TLS front door: `https://localhost:4341/evidence/{authority}`
- Static metadata: `http://127.0.0.1:4331`

The gateway strips `/evidence/{authority}` before forwarding. Application code
uses six authority base URLs and never assumes a national Evidence host.
The front door strips `/evidence/{authority}` before forwarding, so one local
port reaches all six gateways. Application code uses six authority base URLs and
never assumes a national Evidence host.

## Architecture

Expand Down Expand Up @@ -103,7 +104,7 @@ uv run python -m solmara_lab.publisher --root .. publish-extract \

The live MoSD mutation is visible on the next Relay-backed Evidence request
without a restart. A newly named SRO extract is not visible until only the SRO
Evidence cell is rebound and restarted. Invalid, stale, or overwritten extracts
Evidence gateway is rebound and restarted. Invalid, stale, or overwritten extracts
fail closed. Run lifecycle smoke in isolated volumes or restore the deterministic
fixture state afterward.

Expand Down Expand Up @@ -131,9 +132,9 @@ Relay/Evidence authoring inputs are tracked.

- `relays/` contains five authority-governed Relay V2 projects.
- `evidence/cells/` contains six authority Evidence bundle templates and
runtime bindings.
runtime bindings, one per gateway.
- `generator/solmara_lab/publisher.py` publishes deterministic SQLite sources.
- `scenarios/` and `scenario-runner/` route requirements to authority cells,
- `scenarios/` and `scenario-runner/` route requirements to authority gateways,
verify multiple JWKS, and compose application outcomes.
- `portal/` and `home/` display safe authority, issuer, and source-type labels.
- `metadata/` and `requests/registry-lab/` publish discovery and request
Expand Down
18 changes: 14 additions & 4 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
A small dated log of what changed in the visitor center and the lab topology.
Newest entry first.

## 2026-08-20 Evidence gateways replace "cells"

Every visitor-facing surface now calls an authority's Evidence deployment an
Evidence gateway. "Cell" was a private coinage of the reset and was never
defined for a reader; "gateway" is the noun Registry Stack itself uses for the
service that answers a requirement over an authority's own source. Nothing in
the topology changed. Directory names, hostnames, volume paths, and the
`solmara-authority-cells` catalogue identifier keep their existing spelling
because they are identities rather than descriptions.

## 2026-08-20 Registry Stack v0.23.0

Solmara pins Registry Stack v0.23.0 across `versions.env`, the release-pin
Expand All @@ -13,10 +23,10 @@ topology is unchanged. The reason to move is security: v0.23.0 upgrades `h2`
to 0.4.17, past RUSTSEC-2026-0258, where a peer could queue unlimited empty
DATA frames on the HTTP/2 path that Relay and Evidence both serve over.

The Evidence deployment contract gained three required fields, so the six cell
The Evidence deployment contract gained three required fields, so the six gateway
bundles are authored rather than only re-pinned. `service.publicOrigin` names
the one routed origin RFC 9728 discovery answers on, which is each cell's own
`<cell>-evidence.solmara.registrystack.org`. Every requirement and every
the one routed origin RFC 9728 discovery answers on, which is each gateway's own
`<authority>-evidence.solmara.registrystack.org`. Every requirement and every
concept now carries a `handle`, the stable key a client reads a result under;
each is authored to the last segment of the identifier it names. v0.23.0 also
adds Registry Discovery and published Discovery client packages, neither of
Expand Down Expand Up @@ -53,7 +63,7 @@ digest input.

## 2026-08-12 Authority-owned Relay and Evidence reset

Solmara now authors six independent Evidence cells and five Relay V2 projects.
Solmara now authors six independent Evidence gateways and five Relay V2 projects.
CRA, NIA, and SRO publish immutable SQLite extracts for direct Evidence use;
CRA, MoSD, SIPF, and NAgDI expose only named exact Relay lookups, with NIA's
Relay reserved for the optional eSignet UserInfo profile. The programme code
Expand Down
33 changes: 17 additions & 16 deletions docs/hosted-deployment.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Hosted deployment

Deploy the authority-cell reset only from the exact Registry Stack release
Deploy the authority-owned reset only from the exact Registry Stack release
recorded in `versions.env`. Relay, Evidence, and Mint must use their official
Registry Stack OCI images pinned by digest. Solmara-owned images are also
digest-pinned, while the local Relayctl helper is assembled from its
Expand Down Expand Up @@ -41,7 +41,7 @@ Do not reuse an unrelated package or deploy a mutable tag.
## Authority topology

The hosted topology contains five Relay V2 services, six independently signed
Evidence cells, one shared lab Mint, and the programme application. The optional
Evidence gateways, one shared lab Mint, and the programme application. The optional
eSignet profile uses the NIA Relay lookup.

Evidence hosts are:
Expand All @@ -53,8 +53,8 @@ Evidence hosts are:
- `sipf-evidence.solmara.registrystack.org`
- `nagdi-evidence.solmara.registrystack.org`

Each host is also the cell's own identity. The signed bundle carries it as
`service.publicOrigin`, and the cell answers with it as the `resource` and the
Each host is also the gateway's own identity. The signed bundle carries it as
`service.publicOrigin`, and the gateway answers with it as the `resource` and the
`jwks_uri` prefix of its RFC 9728 protected-resource metadata and in the
`WWW-Authenticate` challenge it returns. `SOLMARA_<CELL>_EVIDENCE_PUBLIC_HOST`
moves the route without moving that identity, so a deployment that sets one
Expand All @@ -72,16 +72,16 @@ paths are:
|---|---:|---|
| Shared Mint | 3 | Runtime, secrets, and Transit socket |
| Five Relays | 10 | One runtime and one mutable source path per authority |
| Six Evidence cells | 21 | Runtime, secrets, and Transit socket per cell, plus the CRA, NIA, and SRO immutable-extract paths |
| Six Evidence gateways | 21 | Runtime, secrets, and Transit socket per gateway, plus the CRA, NIA, and SRO immutable-extract paths |

Each application also joins a Coolify-managed container network that carries the
ingress proxy, alongside the private runtime network its services address. A
listener bound only to its private runtime address refuses that proxy and never
answers its public route, so Mint and every Evidence cell bind every interface.
answers its public route, so Mint and every Evidence gateway bind every interface.
Evidence records this as `networkExposure: container-private`, the exposure its
runtime requires before it accepts a wildcard bind. Isolation rests on network
membership rather than on the bind address: an application's networks carry only
its own containers and the proxy, so no authority reaches another's cells.
its own containers and the proxy, so no authority reaches another's gateways.

Joining two networks makes a container's routable address ambiguous. Traefik
reads the address from the first network it iterates when no network is named,
Expand Down Expand Up @@ -111,7 +111,7 @@ to two different empty directories. Co-locating the provision, signer, core, and
four authority applications is a deployment precondition.

Each ministry runs its own signer application, which creates and owns the
Transit socket paths of its cells and runs one isolated signer per cell:
Transit socket paths of its gateways and runs one isolated signer per gateway:

| Application | File | Signers |
|---|---|---|
Expand Down Expand Up @@ -156,7 +156,7 @@ least-authority source clients, each limited to its named operation:
- `nia-esignet`

The ninth client, `solmara-demo`, belongs to the programme application and is
used to request assertions from the authority Evidence cells. The shared Mint
used to request assertions from the authority Evidence gateways. The shared Mint
is a lab convenience, not production tenancy guidance.

## Secret boundary
Expand All @@ -170,7 +170,7 @@ Compose file, repository file, build log, or delivery record:
- authority-specific Relay audit HMAC keys, plus cursor HMAC keys for the Relay
contracts that require cursors;
- Mint and Evidence audit HMAC keys;
- one subject-binding HMAC key for each Evidence cell;
- one subject-binding HMAC key for each Evidence gateway;
- the programme federator token;
- when eSignet is enabled, its database credential, KYC-token and PSUT HMAC
secrets, KYC keystore credentials, and portal OIDC client key.
Expand All @@ -195,14 +195,14 @@ hosted provisioner creates the initial checked publication and reuses the exact
active filename on a restart. It never overwrites an active extract in place.

Reuse does not re-apply the extract's serving age. `maximumExtractAgeSeconds` is
a policy each Evidence cell applies to live requests, so re-checking it while
a policy each Evidence gateway applies to live requests, so re-checking it while
re-adopting an already published extract would only make the provision
application refuse to redeploy a day after it last ran. Every other binding,
metadata, schema, and integrity check still applies, and an extract published in
the future is still refused. Publishing a newer checkpoint remains the deliberate
`publish-extract` operation below.

To publish a later checkpoint, override the matching direct-cell provisioner
To publish a later checkpoint, override the matching direct-gateway provisioner
service command with:

```text
Expand All @@ -211,8 +211,9 @@ publish-extract --target <cra|nia|sro>-evidence --assets /opt/solmara-hosted-ass

The operation validates the current binding, appends the checked publication
under a fresh immutable filename, preserves the old file, and atomically
rebinds only that cell's runtime configuration. Restart only the matching
Evidence cell after the operation succeeds. The running cell continues to read
rebinds only that gateway's runtime configuration. Restart only the matching
Evidence gateway after the operation succeeds. The running gateway continues to
read
the old mounted extract until that restart. A malformed, metadata-mismatched,
or non-newer publication fails closed.

Expand Down Expand Up @@ -248,7 +249,7 @@ Deploy the reset alongside the existing deployment in this order:
`compose.coolify.social-development.yaml`,
`compose.coolify.labour-pensions.yaml`, and
`compose.coolify.agriculture.yaml`. Confirm all five Relays and all six
Evidence cells are ready on the private routes.
Evidence gateways are ready on the private routes.
6. Run the hosted programme, denial, JWKS, source-label, redaction, and UI smoke
against the new routes before changing public routing.
7. If required for this deployment, add `compose.coolify.esignet.yaml` and run
Expand All @@ -274,7 +275,7 @@ requires:
- wrong-purpose and unauthorized calls to fail generically;
- the portal to prove authority URL selection, per-authority JWKS verification,
source-type labels, and redaction;
- the Visitor Center to show the publisher, six Evidence cells, five Relays,
- the Visitor Center to show the publisher, six Evidence gateways, five Relays,
shared Mint, and programme application;
- when the optional profile is deployed, eSignet login to complete through the
NIA Relay V2 lookup.
Expand Down
4 changes: 2 additions & 2 deletions docs/purposes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Status: normative for the authority-owned Evidence reset.

Evidence requests carry one closed purpose code in the signed request body.
For Relay-backed requirements, the Evidence cell authenticates to Registry Mint
For Relay-backed requirements, the Evidence gateway authenticates to Registry Mint
with a dedicated private-key JWT client. Mint issues a token whose registration
fixes the full purpose IRI, exact Relay scope, and `solmara-runtime` audience.
The caller cannot select or override downstream Relay authority.
Expand All @@ -30,4 +30,4 @@ Responses and logs never include selectors, tokens, source rows, secrets,
private keys, audit material, or sensitive dependency details.

The programme application verifies every authority JWS and owns the final
cross-authority outcome. No Evidence cell returns an application decision.
cross-authority outcome. No Evidence gateway returns an application decision.
25 changes: 13 additions & 12 deletions docs/solmara.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Solmara lab architecture

Solmara is a fictional institutional lab. Its core boundary is an authority
cell: an authority publishes a governed source, operates its Evidence service,
signs its assertions, and owns its audit trail. A programme consumes assertions
from several cells and owns the programme decision.
Solmara is a fictional institutional lab. Its core boundary is the authority:
each one publishes a governed source, operates its own Evidence gateway, signs
its assertions, and owns its audit trail. A programme consumes assertions from
several authorities and owns the programme decision.

## Runtime topology

Expand All @@ -16,9 +16,9 @@ from several cells and owns the programme decision.
| SIPF | live read-only SQLite | pension payment and survivor exact lookups |
| NAgDI | live read-only SQLite | voucher and livestock exact lookups |

There are six Evidence cells and five Relays. A Relay exists only for governed
live consultation. It is not a façade over an Evidence cell's immutable local
extract.
There are six Evidence gateways and five Relays. A Relay exists only for
governed live consultation. It is not a façade over an Evidence gateway's
immutable local extract.

Every Relay operation has one dedicated Mint client registration, exact scope,
fixed purpose claim, access profile, and disclosure profile. Relays do not
Expand All @@ -27,10 +27,11 @@ offer list, search, cursor, or generic record routes. The shared

## Evidence authority

Each cell has a unique provider IRI, authority DID issuer, ES256 signing key and
JWKS, audit destination, subject-binding secret, runtime binding, and hostname.
The application chooses the cell from a closed requirement directory, fetches
only that cell's JWKS, verifies the exact requirement, evidence type, concepts,
Each gateway has a unique provider IRI, authority DID issuer, ES256 signing key
and JWKS, audit destination, subject-binding secret, runtime binding, and
hostname. The application chooses the gateway from a closed requirement
directory, fetches only that gateway's JWKS, verifies the exact requirement,
evidence type, concepts,
purpose, nonce, issuer, provider, audience, and validity window, then retains a
safe presentation projection.

Expand All @@ -57,7 +58,7 @@ profile.
Wrong scope, wrong purpose, malformed selectors, invalid source rows,
unavailable source, and unavailable audit fail closed. No-match, ambiguous
match, and policy-hidden matches use the same data-free unresolved class. An
Evidence cell does not infer a negative assertion from an unresolved Relay
Evidence gateway does not infer a negative assertion from an unresolved Relay
consultation.

## Programme stories
Expand Down
2 changes: 1 addition & 1 deletion docs/stories/birth-to-child-benefit.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ unregistered birth, duplicate enrolment, and wrong purpose. An unregistered
birth is a valid CRA record with no BRN and produces signed false, not an
unresolved consultation. The mutable MoSD source changes on the next request;
the three immutable sources change only after a reviewed replacement is bound
and the owning cell is restarted.
and the owning gateway is restarted.

The response surface contains safe authority, issuer, provider, source type,
and verified concept values. It does not contain selectors, tokens, source
Expand Down
2 changes: 1 addition & 1 deletion docs/stories/death-to-pension-survivor.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The pension application combines a CRA death assertion and a SIPF active
payment assertion under `pension-payment-review`. The application, not either
Evidence cell, derives whether payment should stop.
Evidence gateway, derives whether payment should stop.

CRA calls only `civil-person/death-by-uin` and discloses the governed death
status. SIPF calls only `pension-payment/by-pensioner-uin` and discloses active
Expand Down
6 changes: 3 additions & 3 deletions home/e2e/home.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ test('problem-codes anchors resolve, including current Evidence authorization de
await expect(page.locator('[id="malformed_request"]')).toBeVisible();
});

test('anatomy lists five Relay V2 projects and six authority Evidence cells with config links', async ({ page }) => {
test('anatomy lists five Relay V2 projects and six authority Evidence gateways with config links', async ({ page }) => {
await page.goto('/anatomy');
await expect(page.locator('#relays .entity')).toHaveCount(5);
for (const id of ['cra-evidence', 'nia-evidence', 'sro-evidence', 'mosd-programme-evidence', 'sipf-evidence', 'nagdi-evidence']) {
Expand Down Expand Up @@ -241,10 +241,10 @@ test('purpose lens: the live review reveals evidence and the wrong-purpose chall
await page.goto('/');

await expect(
page.locator('#proof .proof-grid > div').filter({ hasText: 'authority Evidence cells' }).locator('strong')
page.locator('#proof .proof-grid > div').filter({ hasText: 'authority Evidence gateways' }).locator('strong')
).toHaveText('6');
await expect(
page.locator('#solmara-preview .country-facts > div').filter({ hasText: 'Evidence cells' }).locator('dd')
page.locator('#solmara-preview .country-facts > div').filter({ hasText: 'Evidence gateways' }).locator('dd')
).toHaveText('6');

// The boundary challenge appears only after a successful live evidence run.
Expand Down
2 changes: 1 addition & 1 deletion home/src/lib/components/ProofStrip.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<div class="content proof-grid">
<div><strong>1</strong><span>synthetic country</span></div>
<div><strong>{authorities ?? 'Unavailable'}</strong><span>live authorities</span></div>
<div><strong>{evidenceCells ?? 'Unavailable'}</strong><span>authority Evidence cells</span></div>
<div><strong>{evidenceCells ?? 'Unavailable'}</strong><span>authority Evidence gateways</span></div>
<div><strong>{journeys ?? 'Unavailable'}</strong><span>guided policy journeys</span></div>
<div><strong>0</strong><span>real resident records</span></div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion home/src/lib/components/SolmaraPreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</p>
<dl class="country-facts">
<div><dt>Live authorities</dt><dd>{authorities ?? 'Unavailable'}</dd></div>
<div><dt>Evidence cells</dt><dd>{evidenceCells ?? 'Unavailable'}</dd></div>
<div><dt>Evidence gateways</dt><dd>{evidenceCells ?? 'Unavailable'}</dd></div>
<div><dt>Real records</dt><dd>0</dd></div>
</dl>
<a class="button-link secondary-link" href="/country">Explore Solmara and its full synthetic cast</a>
Expand Down
Loading
Loading