Requests for comment · RFC 0003
Secrecy (sekretess) as a pillar
A mechanical contract is more exposing than a paper one by default - structured, distributed, and quoted in every receipt. This RFC argues secrecy is not a v2 bolt-on but a pillar the form is uniquely able to deliver, and works through the design: a signed disclosure policy, scoped receipts, receipted reads, and cryptographic selective disclosure.
- STATUS
- Open for comment
- TOPICS
- Confidentiality, selective disclosure, field-level sealing, receipted reads, escalation privacy, OSL
This is a request for comment, not part of the specification. The syntax is a strawman and much of it is pseudo-code; it exists to be argued with, not adopted by default.
The thesis: secrecy is a pillar, not a patch
The standard’s current posture files secrecy honestly as an open problem parked for a later version. This RFC argues the opposite framing is the right one: confidentiality is a first-class property the mechanical contract is uniquely positioned to deliver - and it should be designed as a pillar, beside determinism and “everything declared,” not bolted on afterward.
The reason is an inversion. A paper contract can only be disclosed in full, or redacted by hand with a marker pen. A structured contract can do minimal disclosure by construction - publish the port manifest (capabilities are rarely secret), seal the parameters (the rates), disclose per audience, with the confidentiality assessment written into the contract as a signed policy rather than improvised per request. Done right, the mechanical contract is the first contract form that can comply with a field-level secrecy regime precisely - clause by clause, field by field - instead of approximately. That is not damage control. That is a capability no prior contract form has.
Why it bites harder for us than for a PDF
Secrecy has to be a pillar precisely because the form is more exposing than paper by default, on four fronts:
- Structured. A rate is a queryable field, not a needle in 40 pages.
- Distributed. Execution spreads state - buyer, supplier, platform each hold the log.
- Receipts quote the contract. A rejection diff citing
rates.senior = 895 x indexleaks the rate card to whoever holds the receipt. - Escalation summons outsiders. A steering group, a mediator, a court - escalation hands contract internals to third parties.
And the law makes this concrete. In Swedish public procurement, contracts are public documents by presumption, but a supplier’s business circumstances are sealed on a harm test - and in practice detailed price information, hourly and unit rates, is the typically-sealed material (Upphandlingsmyndigheten guidance). Read against the language: the rate card - the exact field the rules cite in every receipt - is the exact field most often under secrecy. Field-level secrecy is not a nice-to-have; it is the legal shape of the domain.
The design, in four layers
1. A signed disclosure policy - the assessment, declared
The confidentiality assessment lives in the contract itself, pre-declared and signed at genesis, as per-audience views. Not improvised each time someone asks.
disclosure:
view public # what any citizen may see (a public-document view)
show ports.* # the capability manifest is rarely secret
hide rates.* # unit / hourly prices sealed
hide parties.*.org-nr
view counterparty grant buyer, supplier
show all # the parties see everything they signed
view mediator grant referee
show disputed-branch # only the clause under dispute, with values
view court grant external-authority
show per order.scope # exactly what the order compels, and no more
seal:
rates.senior digest salted # the field is replaced by a hash of salt + value;
rates.junior digest salted # disclosed per view, verifiable against the signature
Absent policy = the default posture, which a profile sets (a public-sector profile presumes a public view with sealed rates; a private profile presumes counterparty-only).
2. Scoped receipts - name the clause, not the value
A receipt names the clause IDs it applied. The full diff, with values, goes only to the parties; the third-party and escalation variants carry digests instead. The hash chain stays intact while the receipt quotes nothing.
receipt on invoice.submit:
cites §7.2 rates.senior # the clause id it applied - never the number
to parties full-diff, values # buyer + supplier see the arithmetic
to others digests-only # escalation / audit variant leaks no value
This is cheap now and hard to retrofit, so it is proposed from day one.
3. Receipted reads - who looked, and when
Access itself becomes an event, through a read port. “Who looked at the sealed fields, and when” is then a fold over access receipts - a query no contract system can answer today. (Generalized from the health domain’s access log, where the citizen has a right to the list of everyone who read their record.)
ports:
in rates.read from auditor payload signed-request # access is an EVENT, not a side-channel
rule on rates.read:
disclose rates.* per view.of(reader) # scoped to the reader's declared view
accept # the read is receipted; the access log is a fold
4. Escalation never widens disclosure by itself
The question that makes secrecy hard - how do you keep contents secret when a dispute drags in outsiders - has a crisp answer under this model: each escalation level carries a declared disclosure scope, and escalating never widens it automatically.
- Level 1: the parties only.
- Level 2: a steering group, on digested receipts.
- Level 3: a mediator, the disputed branch disclosed with values.
- Level 4 / court: per court order, and the salted-digest structure lets the contract comply exactly - the disputed branch and its receipt sub-chain, mathematically bound to the signed whole, nothing else.
Who saw what is itself receipted.
A contract, end to end
The four layers in one contract - a public-sector consultancy framework, showing both faces at once: secrecy (the rate card a field-level regime seals) and privacy (personal data with a declared retention window).
parties:
region org 232100-0032 role buyer sign eIDAS-seal
supplier org 556677-8899 role supplier sign eIDAS-seal
forvratt authority role external-authority sign eIDAS-seal # court/agency key
artifacts:
table rates: # the parameters most often under sekretess
senior 1_450 SEK/h
specialist 1_950 SEK/h
junior 850 SEK/h
volume.ceiling 70_000_000 SEK # the takvolym
# ── SEKRETESS: the disclosure policy, signed at genesis ────────────
disclosure: # the confidentiality assessment, DECLARED - not improvised per request
view public # a public-document view: what any citizen may request
show ports.* # the capability manifest is public (rarely secret)
show volume.ceiling # the ceiling is public (procurement transparency)
hide rates.* # field-level secrecy - unit/hourly prices sealed
seal-until 2029-01-01 # contract-term secrecy runs for a bounded window
view counterparty grant region, supplier
show all # the parties see everything they signed
view mediator grant referee
show disputed-branch # only the clause in dispute, with values
view court grant forvratt
show per order.scope # exactly what an order compels, and no more
seal:
rates.senior digest salted # field -> hash(salt + value), signed;
rates.specialist digest salted # disclosed per view, verifiable against the
rates.junior digest salted # signature without trusting the platform
# ── PRIVACY: personal data, minimised + retained, not forever-by-default ──
personal-data:
consultant.personnummer category national-id basis contract # a lawful basis; specially protected
minimise # never in receipts - digest only
retain until contract.close + 24 months # DECLARED retention, then purge-eligible
# ── receipted reads: access is an EVENT, not a side-channel ────────
ports:
in rates.read from region.auditor or forvratt payload signed-request
rule on rates.read:
disclose rates.* per view.of(reader) # scoped to the reader's declared view
accept # the read is receipted -> access log = fold(read receipts)
# ── receipts quote clause-ids, never values ───────────────────────
receipt on timesheet.approve:
cites §7.2 rates.senior # names the CLAUSE it applied
to parties full-diff, values # region + supplier see the arithmetic
to others digests-only # audit / escalation variant leaks no number
The cryptographic path
Layers 1-4 are legally sufficient with the platform as a trusted referee holding full state. The next step removes that trust:
| Approach | What it buys | Read | Maturity |
|---|---|---|---|
| Salted-hash selective disclosure (SD-JWT) | Each field becomes hash(salt + value), the digests are signed; disclose only chosen fields + salts, verifiable against the signature without trusting the platform. The direct fit for “public in structure, sealed in parameters.” |
IETF SD-JWT | Standardized, production - the EU digital-identity wallet stack uses it |
| BBS+ signatures | Selective disclosure plus predicate proofs - prove “rate < X” without revealing the rate - and unlinkability. | BBS+ vs SD-JWT | Standardizing |
| Need-to-know distribution (Canton / Daml) | Each party receives only their slice of a transaction at the protocol level - one side sees the cash leg, the other the securities leg. | Canton protocol | Production, in regulated finance |
| Zero-knowledge compliance proofs | Prove “this invoice complies with the sealed contract” while revealing nothing - even the referee need not see the terms. | zk-Agreements | Research-grade; watch, do not build |
| Confidential computing (TEE) | The literal black box - evaluation inside an attested enclave; parties get receipts plus attestation, the operator sees nothing. | vendor docs (SGX, Nitro Enclaves) | Production tech, adds operational + vendor-trust cost |
The staging: access-control views and scoped receipts first (no new cryptography, legally sufficient); SD-JWT-style salted digests next (verifiable disclosure without platform trust); zero-knowledge and enclaves stay on the watchlist, adopted only when a buyer demands referee-blind validation.
Testing the theory against determinism
Secrecy cannot be argued into a standard whose first covenant is replay reproduces state bit-for-bit. The two look like they collide: replay wants every replica to reproduce the same state from the same inputs, and sealing means some replicas do not hold the inputs. So the theory was run as an executable test against that covenant - and it holds, but only because the test forced two rules into the open that a prose argument would have missed.
The resolution is that “replay” splits in two. The full-state evaluator (the referee holding real values) reproduces state bit-for-bit - determinism lives here, untouched. A digest-only holder cannot re-evaluate a sealed computation, but can verify the receipt and the signed commitment cryptographically. Evaluate needs values; verify needs only digests. Sealing separates the two; it does not break either.
That split only works under two rules the test made non-negotiable:
- Rule A - salts are fixed at signing. Each sealed field’s digest is
hash(salt + value)with a salt minted once and bound into the signed genesis. Re-rolling a salt changes the digest and breaks the signature - which is exactly why salts cannot be generated fresh per replay. Determinism forbids the randomness a naive sealing scheme would reach for; the salt has to be a signed constant. - Rule B - erasable implies non-computational. This is the honest resolution of the erasure-versus-replay tension. A field a rule branches on (a rate) may be sealed - hidden from a viewer while the evaluator keeps the value - but it may never be purged, because purging it makes replay produce nonsense and the bit-for-bit property dies. So a right-to-erasure can only be honored on fields that no rule computes over (identity data,
minimise). The linter enforces it: a contract that declares a computational field erasable does not pass. Where the law demands erasing a value the contract computes on, the contract must be restructured so it no longer does - or the erasure cannot be honored mechanically, and the standard should say so plainly rather than pretend.
And erasure itself is a signed event (drop the salt and value, keep the digest so the chain still verifies), with retention expressed as an event-time predicate, never a wall-clock read - consistent with the rest of the machine.
Under those rules the crux checks out end to end: a redacted view and a disclosed view both verify against one signature; a disclosed value re-derives its committed digest while a forged value does not; two replays over real values are identical to the minor unit; and purging a non-computational field leaves the signed root and the whole chain valid while the value is genuinely gone. The test is small and reproducible, and it is the kind of thing the standard should carry as a golden fixture, not a paragraph of confidence.
Honest creaks
- Determinism versus erasure - resolved, but with a real limit. The tension (append-only replay versus a value that must be gone) is handled by Rule B above: erasable implies non-computational, enforced by the linter. The residual, honest limit is that this is a genuine constraint, not a free lunch - you cannot erase a field the contract computes on. If a regulator demands erasing a value that a rule branches on, the contract has to be restructured so it no longer does, or the erasure cannot be honored inside the machine. That is a real boundary the standard must state, not paper over.
- The referee still sees everything, until the crypto lands. Layers 1-4 protect contents from third parties and the public, not from the platform holding full state. That is honest access control, not confidentiality from the operator. Only selective disclosure or an enclave closes that gap - and the standard should say which it delivers, plainly.
- A disclosure policy is only as good as its authoring. An author who seals nothing publishes everything. As with entrenchment, the profile must mandate the floors - a public-sector profile must seal what the law seals - or the pillar is decorative.
- Escalation leakage is a design surface, not a solved problem. Getting the per-level scopes right, and proving a mediator saw only the disputed branch, is genuine work; the receipted-reads mechanism makes leakage auditable, which is not the same as impossible.
Open questions for comment
Per compose, don’t extend, disclose and seal should resolve as named compositions - a disclosure is a scoped borrow plus a receipted copy - rather than new core verbs.
- Secrecy is now named as property 6 (the five properties became six). Is that the right altitude, or should it live as a strong section without a headline slot?
- Is the
disclosurepolicy block first-class genesis, with a closed view/seal vocabulary and profile-mandated floors? - Are scoped receipts (clause-ids to third parties, values to parties) mandatory from v1, given they are hard to retrofit?
- Are receipted reads a core mechanism, and does every sealed field require a
readport to be legible? - Where is the line between v1 access-control secrecy and cryptographic selective disclosure - what forces the upgrade?
- Does the salted-digest model genuinely reconcile append-only replay with a right-to-erasure, or only approximate it?