#322 added cades::certificate_standing: the seal certificate's validity window judged against an attested sealing time, and revocation read from the CRLs the seal carries. It is served on GET /api/v1/dpp/{dppId}/seal and in the evidence dossier.
Nothing decides anything with it. Every surface that produces a verdict still asks only SealBinding — does the seal cover this passport's signature — and that question is indifferent to whether the certificate behind it was revoked or expired when the seal was made.
What that means concretely
A passport sealed with a certificate the CA had already revoked reports, today:
| surface |
reports |
GET /api/v1/seal → audit.sound |
counted as sound |
the stored-seal audit's broken |
0 |
evidence dossier qualified_seal check |
Pass |
POST .../seal/repair |
refused — "this seal verifies" |
GET /api/v1/dpp/{id}/seal → certificate |
revoked |
So the one surface that names the problem is the one an operator has to already suspect something to look at. The audit is the surface built for finding what nobody is looking at, and it cannot see this.
SealValidationStatus::of(binding, certificate) already composes both into the right answer — totalFailed / revoked against an attested time, indeterminate / revokedNoPoe without one. It is called on the read route and in the dossier's validation field, and by nothing that classifies.
Where it would go
seal_drain::audit_seals_once classifies on inspector.binding(...) alone. SealAudit would need a fifth bucket, and the walk would need certificate_standing per row — which is a second CAdES parse per seal, so the batch/interval arithmetic (SEAL_AUDIT_BATCH, SEAL_AUDIT_INTERVAL_SECS) moves with it.
verify::engine::qualified_seal_status matches binding and returns Pass for CoversThisSignature. The dossier already carries certificate, so the verifier has the material without re-reading the seal.
- The repair route's refusals are binding-shaped. A revoked certificate is not repairable by re-sealing anyway — a replacement would be bought from the same backend under the same certificate — so it wants its own refusal saying that, not admission to the repair path.
Why it is not urgent, and when it stops being deferrable
Every seal this workspace can produce is self-issued by the local backend, whose certificate is minted fresh, never revoked, and valid for its own configured window. The condition is unreachable here.
It becomes reachable the moment a real QTSP credential exists, and the failure is silent in the direction that matters: a passport whose seal is worthless in law counted as sound, in the number an operator reads to decide there is nothing to do.
Worth deciding at the same time
Whether sound keeps its name. It currently means "covers this signature"; with the certificate folded in it would mean "covers this signature and the certificate stood up", and a count that quietly changes meaning between releases is worse than a new one beside it.
#322addedcades::certificate_standing: the seal certificate's validity window judged against an attested sealing time, and revocation read from the CRLs the seal carries. It is served onGET /api/v1/dpp/{dppId}/sealand in the evidence dossier.Nothing decides anything with it. Every surface that produces a verdict still asks only
SealBinding— does the seal cover this passport's signature — and that question is indifferent to whether the certificate behind it was revoked or expired when the seal was made.What that means concretely
A passport sealed with a certificate the CA had already revoked reports, today:
GET /api/v1/seal→audit.soundbrokenqualified_sealcheckPassPOST .../seal/repairGET /api/v1/dpp/{id}/seal→certificaterevokedSo the one surface that names the problem is the one an operator has to already suspect something to look at. The audit is the surface built for finding what nobody is looking at, and it cannot see this.
SealValidationStatus::of(binding, certificate)already composes both into the right answer —totalFailed/revokedagainst an attested time,indeterminate/revokedNoPoewithout one. It is called on the read route and in the dossier'svalidationfield, and by nothing that classifies.Where it would go
seal_drain::audit_seals_onceclassifies oninspector.binding(...)alone.SealAuditwould need a fifth bucket, and the walk would needcertificate_standingper row — which is a second CAdES parse per seal, so the batch/interval arithmetic (SEAL_AUDIT_BATCH,SEAL_AUDIT_INTERVAL_SECS) moves with it.verify::engine::qualified_seal_statusmatchesbindingand returnsPassforCoversThisSignature. The dossier already carriescertificate, so the verifier has the material without re-reading the seal.Why it is not urgent, and when it stops being deferrable
Every seal this workspace can produce is self-issued by the local backend, whose certificate is minted fresh, never revoked, and valid for its own configured window. The condition is unreachable here.
It becomes reachable the moment a real QTSP credential exists, and the failure is silent in the direction that matters: a passport whose seal is worthless in law counted as sound, in the number an operator reads to decide there is nothing to do.
Worth deciding at the same time
Whether
soundkeeps its name. It currently means "covers this signature"; with the certificate folded in it would mean "covers this signature and the certificate stood up", and a count that quietly changes meaning between releases is worse than a new one beside it.