Skip to content

seal: sealing is provider-locked in the composition root, not in the port #96

Description

@LKSNDRTMLKV

The problem

Sealing is provider-agnostic in the port and provider-locked in the composition
root. A self-hoster gets one specific QTSP or a ghost, and nothing in the design
requires that.

The port is already neutral. dpp_domain::ports::seal::SealPort says nothing
about any vendor. SealRequest, SealedEnvelope and SealFormat are
provider-neutral, and SealFormat already carries Cades, Xades, Pades and
Jades.

The wiring is not. QtspSealAdapter has exactly two states — the eID Easy
backend, or GhostSeal. dpp-node/src/main.rs selects between them by matching
on EideasyConfig::from_env(). There is no selector and no second backend.

So the constraint is roughly 200 lines of composition-root wiring, not an
architectural commitment.

Why this matters

A qualified certificate is issued to a legal person after identity
verification under eIDAS Art. 24(1). A self-hoster therefore cannot ride anyone
else's certificate — they must be their own subscriber. That makes "self-host
the software, then go contract with one specific vendor before you can publish a
compliant passport" the actual onboarding story, which is not a self-host story.

It is also worse for operators who already hold a relationship with a national
QTSP: they would be asked to re-procure through an aggregator that resells the
same underlying providers.

And it undercuts the positioning where it is strongest. The product is
operator-controlled infrastructure with no vendor lock-in; a hardcoded trust
provider is the one dependency that carries legal weight, and therefore the one
a compliance reviewer will notice first.

Shape

Cloud Signature Consortium (CSC) is the right interoperability target for a
second backend, rather than a third bespoke adapter.

  • CSC API v2.2 defines the endpoints this needs: /credentials/list,
    /credentials/info for capability discovery, /signatures/sign/hash,
    /signatures/timestamp, and OAuth2 token issuance.
  • Critically, it defines service-level authorisation for electronic seals
    unattended signing with no per-signature activation step. That is the mode a
    passport-sealing node needs; the attended flows are designed for human
    signatures.
  • It covers CAdES, XAdES, PAdES and JAdES, and distinguishes returning a raw
    signature from returning a full AdES envelope.

Sketch:

  • Core — no change. The port is already right.
  • dpp-seal — a provider selector plus a CSC backend alongside the existing
    one.
  • Engine — near-zero. main.rs selects a backend instead of matching one
    config type; the sealing_live guard and the trust tier are unchanged.

What is not known

Per-vendor detail the specification cannot answer, and no public page states for
any candidate:

  • which CSC version each QTSP actually serves;
  • whether their service-authorisation mode is enabled for seals rather than
    only for attended signatures;
  • their per-seal commercial terms.

Each of these needs a sandbox account per vendor to establish.

One thing to not carry forward

The csc/ module deleted alongside the eID Easy work is not a head start.
It modelled CSC v1.0, with a JAdES default this design would not use, against a
specification now several minor versions on. Reviving it means rewriting it —
the deletion stands.

Scope

This issue is the design and its cost, not a commitment to build it. It should
not start before the qualified-seal path is proven end-to-end against at least
one real provider, since a second backend written before the first one has ever
completed a live seal is a second untested thing rather than a hedge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions