The seal side now has a local backend faithful enough to stand in for a provider: it emits the whole B-LTA structure, so a sandbox exercises the same shape a real seal has. The registry side has no equivalent. GhostRegistrySync discards, and the only thing that speaks the registry's wire contract is an axum server inside a test file (dpp-node/src/infra/registry/tests.rs), which nothing outside cargo test can reach.
So a sandbox environment can publish, seal and resolve, and cannot register. That is the last gap before the whole loop runs on our own infrastructure.
What it would serve
The three routes the adapter calls, plus the token exchange in front of them:
|
route |
today |
| register |
POST {base}/registrations |
in-test only |
| poll |
GET {base}/registrations/{id}/status |
in-test only |
| transfer |
POST {base}/registrations/{id}/transfer |
in-test only |
| token |
the OAuth2 client-credentials exchange in registry::client::get_token |
in-test only |
The in-test double already implements all four against the real adapter, so this is mostly promotion, not invention: lift it out of tests.rs into something with a main, a port and a container image, and keep the test using it so the two cannot drift.
The thing this must not be allowed to imply
The Commission has published no API specification. #90 records what that means, and it is worth repeating here because a running "registry" is exactly the artefact that makes people forget it:
- the paths above are invented —
/api/v1 was observed on the registry's own web client, not read from a spec;
api_version "1.0" is provisional;
- the authentication model is structurally wrong. The adapter models an OAuth2 client-credentials exchange; registry identity is a qualified electronic seal or a qualified electronic attestation of attributes (IR 2026/1778 Arts. 4–5). That is a wrong mechanism, not a wrong URL.
So this mimics our adapter's assumptions, not the registry. It proves our outbox, drain, backoff, polling, verdict handling and operator-facing rollup work end to end. It proves nothing about whether we match the Commission, and the day the real specification lands, this simulator is what has to change first — not the thing that reassures anyone it already works.
That should be said in the service itself: a name that cannot be mistaken for the real one, a banner on every response, and a refusal to run against anything but a local/dev profile. The seal backend's convention is a good one to copy — its certificate literally says NOT A QUALIFIED SEAL, so the envelope carries the caveat rather than the README.
Worth deciding while building it
- How faithful should the verdicts be? Registration is asynchronous: an accepted submission is polled. A simulator that always accepts immediately exercises the happy path and nothing else. Being able to make it reject, hold in
submitted, or fail intermittently is where the drain's backoff and exhaustion actually get tested.
- Operator verification lapse. The drain holds without consuming attempts when verified-operator status has lapsed (Art. 4(4), at most three years). A simulator that cannot express a lapsed operator cannot exercise that hold, which is a silent-queue path worth being sure about.
- Does it persist? A restart that forgets every registration makes the polling path untestable across restarts, which is one of the things a sandbox is for.
- Enrolment is a different artefact. Organisation enrolment counter-seals an EC-generated PDF declaration and wants PAdES embedded in the PDF; the seal adapter emits detached CAdES. Simulating enrolment would need that second artefact, so it is probably out of scope for a first cut — but say so deliberately rather than discovering it.
What it unlocks
Publish → seal (local, B-LTA) → register (local registry) → poll → resolve, entirely on our own infrastructure, with no QTSP credential and no Commission endpoint. That is the whole loop under test for the first time.
Related
#90 (what the real integration is blocked on, and why) · odal-node/dpp-core#98 (the registry types) · the in-test double at crates/dpp-node/src/infra/registry/tests.rs · the seal backend's local stand-in, which is the pattern to copy.
The seal side now has a local backend faithful enough to stand in for a provider: it emits the whole
B-LTAstructure, so a sandbox exercises the same shape a real seal has. The registry side has no equivalent.GhostRegistrySyncdiscards, and the only thing that speaks the registry's wire contract is an axum server inside a test file (dpp-node/src/infra/registry/tests.rs), which nothing outsidecargo testcan reach.So a sandbox environment can publish, seal and resolve, and cannot register. That is the last gap before the whole loop runs on our own infrastructure.
What it would serve
The three routes the adapter calls, plus the token exchange in front of them:
POST {base}/registrationsGET {base}/registrations/{id}/statusPOST {base}/registrations/{id}/transferregistry::client::get_tokenThe in-test double already implements all four against the real adapter, so this is mostly promotion, not invention: lift it out of
tests.rsinto something with amain, a port and a container image, and keep the test using it so the two cannot drift.The thing this must not be allowed to imply
The Commission has published no API specification. #90 records what that means, and it is worth repeating here because a running "registry" is exactly the artefact that makes people forget it:
/api/v1was observed on the registry's own web client, not read from a spec;api_version"1.0"is provisional;So this mimics our adapter's assumptions, not the registry. It proves our outbox, drain, backoff, polling, verdict handling and operator-facing rollup work end to end. It proves nothing about whether we match the Commission, and the day the real specification lands, this simulator is what has to change first — not the thing that reassures anyone it already works.
That should be said in the service itself: a name that cannot be mistaken for the real one, a banner on every response, and a refusal to run against anything but a local/dev profile. The seal backend's convention is a good one to copy — its certificate literally says
NOT A QUALIFIED SEAL, so the envelope carries the caveat rather than the README.Worth deciding while building it
submitted, or fail intermittently is where the drain's backoff and exhaustion actually get tested.What it unlocks
Publish → seal (local,
B-LTA) → register (local registry) → poll → resolve, entirely on our own infrastructure, with no QTSP credential and no Commission endpoint. That is the whole loop under test for the first time.Related
#90 (what the real integration is blocked on, and why) · odal-node/dpp-core#98 (the registry types) · the in-test double at
crates/dpp-node/src/infra/registry/tests.rs· the seal backend's local stand-in, which is the pattern to copy.