Skip to content
Registry StackDocsv0.25.0

Disclosure modes and computed answers

View as Markdown

You are weighing whether Registry Stack actually keeps a sensitive registry record private while still answering questions about the subjects in it. The short answer is that a caller asks a question and receives a computed answer, not the record. This page explains how that works, what each kind of answer does and does not reveal, and where the privacy claim has edges. You will see one product term used throughout: a requirement, which is one predefined, reviewed question about one set of subject roles, together with the exact set of values its answer may carry. A requirement is deliberately narrow and reusable. A requirement that returned a whole record would over-collect, and a requirement that embedded one consumer’s eligibility or action rules could not be reused as neutral evidence.

Evidence Gateway has no caller-selectable disclosure modes

Section titled “Evidence Gateway has no caller-selectable disclosure modes”

The most important difference is a subtraction. An Evidence Gateway request carries four required members: a per-request nonce, the requirement identifier, the purpose, and one selector per declared subject role. The only optional member is holderKeys, an array of holder public keys, each echoed into the cnf claim of the credential issued for it. Under a holder-bound requirement at least one key is required and each key’s RFC 7638 thumbprint scopes that credential’s subject binding; the keys still never reach authorization, selectors, scripts, source requests, or audit. The request schema is closed and rejects anything else (products/evidence/contracts/request.schema.yaml). What the keys never do is select a binding mode or a disclosure level: the requirement declares the mode, and no caller input changes it. There is no disclosure mode, no redaction field list, no allowed set, and no downgrade policy, because there is nothing for a caller to choose.

Evidence Gateway answers a predefined requirement and returns that requirement’s answer. It does not run a disclosure policy engine over an arbitrary record, and it has no notion of “the same answer, revealed at three levels of detail.” How much a requirement reveals is fixed when the requirement is authored and reviewed, and it is identical for every caller and every purpose authorized to invoke it.

That is deliberately narrow. It moves the disclosure decision from request time, where a caller could argue for more, to authoring and review time, where an operator has to justify it once and can be held to it.

A requirement declares one to sixteen concepts. Each concept names the fact being disclosed and fixes the exact value form that fact takes. The answer carries a value for each declared concept and nothing else; the assertion schema rejects additional properties, and subject selector profiles and selector values never appear in it (products/evidence/contracts/evidence.schema.yaml).

The value form is the minimization control, because the form decides what the number, code, or date behind the answer is allowed to become before it leaves the service. There are eleven forms and the set is closed (products/evidence/contracts/supported-value-forms.yaml).

Value formWhat the caller learns
booleanone true or false, and nothing about how it was reached
controlled-code, controlled-code-listexact membership in a reviewed codelist; labels and source codes are not disclosed
controlled-categoryexact membership in a reviewed closed category set
bounded-integer, bounded-decimala number inside declared bounds, at a declared scale
date-bucket, time-bucketwhich reviewed bucket applies; the underlying date, instant, or duration is not disclosed
audience-scoped-entity-reference, entity-reference-listan opaque reference meaningful to the named audience and to no other; unavailable to a holder-bound requirement, which has no audience
reviewed-structured-valuea small structure whose closed JSON Schema forbids arbitrary JSON; the widest form, and the one to justify hardest

Two of these deserve emphasis. A controlled code answers with a reviewed disclosure code rather than the register’s own code, so the register’s internal coding never leaves. A date bucket answers with a bucket rather than the date, which is how a question about time can be answered without releasing a birth date or an expiry date.

The size ceilings are part of the control: at most sixteen concepts per requirement, and a total serialized value budget of 65536 bytes.

Four acceptance definitions carry equal weight in the Evidence Gateway contract. None is privileged, none is a built-in type, and none has a special route (AGENTS.md, products/evidence/CONCEPT.md). They are worth reading together because they show the same mechanism producing four differently shaped answers.

  • Adult status declares one boolean concept, derived from a date the caller never sees, with the age threshold supplied as a reviewed derivation parameter rather than by the caller. The caller learns whether the threshold is met, not the date or the age.
  • Residence region declares one controlled-code concept bound to a reviewed codelist at a pinned version. Several register codes map onto one disclosed region, so the answer is coarser than the register’s own value by construction. A register code with no reviewed mapping leaves the requirement unresolved rather than guessing.
  • Professional licence status declares two concepts: a boolean for whether the licence is active, and a controlled-category for an expiry bucket. The caller learns “active, expiring within 90 days” without learning the expiry date.
  • Legal-parent relationship declares one boolean concept over two subject roles, a child and a candidate parent that the caller already holds a reference to. It confirms or denies the relationship the caller asked about, and never enumerates the child’s parents.

The fixtures for all four are in products/evidence/fixtures/acceptance/.

Evidence Gateway is not a consumer decision

Section titled “Evidence Gateway is not a consumer decision”

Registry Relay source-access and adaptation policy controls how an authoritative source is read and normalized. Evidence Gateway authorization and requirement policy control which requirement is evaluated, who can request it, for what purpose, and exactly which values leave the service. The evidence consumer determines how the evidence is used, while the decision owner remains accountable for requirements, decisions, and actions.

A derivation script can compute an evidence value, such as whether a threshold is met or which reviewed bucket applies. It is not a general-purpose consumer eligibility or decision engine, and Version 1 has no general policy engine (products/evidence/contracts/README.md). A public-health programme, for example, can act as both evidence consumer and decision owner when it combines those answers with its own thresholds, priorities, and case state to decide whether outreach or follow-up is required.

Evidence Gateway cannot return an answer that carries neither a value nor a satisfaction outcome, and this is structural rather than an omission: an assertion must carry at least one supported value, so an assertion with nothing in it cannot be produced (products/evidence/contracts/evidence.schema.yaml).

An Evidence Gateway request that cannot be answered does not return an empty assertion. It returns an application/problem+json document with a closed member set, and no assertion is produced at all (products/evidence/contracts/problem-contract.yaml).

What used to be modelled as a redacted answer is modelled in Evidence Gateway as a different requirement, or as a coarser value form on the same requirement, and is reviewed as such.

Collapsing an unresolved outcome is not optional and not a mode. Every requirement declares existenceDisclosure: collapse-unresolved, and that enum has exactly one permitted value (products/evidence/contracts/bundle.schema.yaml).

No match, ambiguity, a missing required fact, and an unresolved derivation input all collapse into one public problem, evidence.unavailable with status 422, carrying the same title and the same body shape. A caller cannot tell which of the four happened, so it cannot use the response to learn whether a record exists (products/evidence/contracts/problem-contract.yaml, invariant V1-I16 in products/evidence/contracts/security-invariant-matrix.yaml). Existence may be disclosed only as a separately authorized declared concept, never as error detail.

Internally the outcomes stay distinct in the audit record. Collapsing is a property of what the caller sees, not of what the operator can review.

Preserve negative, unknown, and unavailable evidence

Section titled “Preserve negative, unknown, and unavailable evidence”

Requirement authors must keep these source meanings distinct:

  • true means the reviewed source contract positively establishes the fact.
  • false means the reviewed source contract positively establishes its negative.
  • An absent value means a subject matched but the declared source value is unknown, absent, or not recorded.
  • No match means the exact selector did not resolve a subject.
  • Ambiguity means the selector resolved more than one admissible subject.
  • A source failure means the source could not provide a reliable result.

A derivation must not silently coerce an absent value, no match, ambiguity, or source failure to false. Missing evidence is not a negative fact. An explicitly named existence concept is the narrow exception: a reviewed derivation can treat “no admissible match” as a negative only when the concept means exactly that one admissible match exists, and ambiguity or source failure never becomes false.

Subjects do not travel, and do not correlate

Section titled “Subjects do not travel, and do not correlate”

A subject appears in an assertion only as a role-bound opaque binding, derived with a keyed function over a binding scope, the purpose, role, selector profile, binding-key version, operator trust domain, and the complete canonical selector bundle. The binding scope is the only part that varies, and the requirement declares which of two closed values it takes.

By default the scope is the audience. The same person asked about by two relying parties, or under two purposes, yields two unrelated bindings, so assertions cannot be joined into a profile (invariant V1-I17). That property is also why audience-scoped-entity-reference is a usable value form: an assertion can identify a related entity to the audience that asked without minting an identifier anyone else can match on.

A requirement can instead declare the holder-bound scope, where the derivation takes the RFC 7638 thumbprint of the holder key and no audience at all. That is a deliberate trade rather than a stronger guarantee. The credential becomes presentable to several verifiers, and the same holder key under the same purpose, role, selector profile, and selector values yields the same binding for every one of them, so a holder who reuses a key is correlatable across the verifiers that see it. The service cannot prevent or detect that reuse; what it does is refuse the two entity-reference value forms for such a requirement, because both project a seed through an HMAC that takes the audience as an input and would degenerate without one. Issuing a batch of members under distinct holder keys lets a holder hand each verifier a different binding, which removes the deterministic key-based link and not linkability in general: the members still share an issuance timestamp, purpose, requirement, Evidence Type, configuration revision, and disclosed values.

Response formats are not disclosure levels

Section titled “Response formats are not disclosure levels”

Evidence Gateway can serialize the same assertion in up to four formats: a signed flattened JWS, an SD-JWT VC under a frozen local profile, a separately typed unsigned envelope, and a batch envelope carrying one holder-bound credential per presented holder key. signed-jws is mandatory in every bundle and is the default (products/evidence/contracts/bundle.schema.yaml). The formats a given request may select are the intersection of the bundle’s permission, the one matched grant’s permission, and the requirement’s binding mode allowlist: a holder-bound requirement serves only the SD-JWT VC serialization and the batch envelope, and the whole intersection is enforced with one denial that never says which layer withheld the format.

These are integrity and encoding choices, not privacy choices. All of them carry the same declared values. The SD-JWT VC serialization emits one root disclosure per supported value by default. A governed reviewed-structured-value can instead expose an always-visible object whose direct fields are independent disclosures. Nested field values remain atomic. The issuer appends every disclosure, so the response the caller receives reveals the same set as the JWS. Holder-side omission at presentation time is outside Version 1 (products/evidence/contracts/sd-jwt-vc-profile.yaml). The service performs no presentation-side verification in either binding mode: it appends no key-binding JWT, issues no challenge, and holds no presentation state. Verifying a holder-bound presentation is the relying party’s act, done with the portable verifier or the evidence verify-presentation command (products/evidence/contracts/holder-bound-profile.yaml). The unsigned envelope carries no integrity protection, is released only when both the reviewed configuration and the caller’s matched grant permit it, and is never later-verifiable evidence.

One requirement is not the whole disclosure surface

Section titled “One requirement is not the whole disclosure surface”

Each requirement declares a disclosureGuard naming the value families it touches, and two requirements that are each defensible alone can reconstruct a protected value between them. The complete enabled configuration is therefore reviewed as one disclosure surface rather than requirement by requirement, and that review is an operator duty the runtime cannot perform (invariant V1-I03, products/evidence/OPERATOR-CONTRACT.md).

Disclosure control is one safeguard among several

Section titled “Disclosure control is one safeguard among several”

Value forms are one design-time safeguard within a broader posture that also includes authentication, a single authorization decision taken before any source is contacted, immutable startup configuration, and audit at two durable gates. They are not the whole privacy guarantee. A few protections are not the software’s to make at all: secret and key custody, audit retention, tenant isolation, transport, and rate limiting are your deployment’s job, not built-in guarantees.

A few boundaries matter when you evaluate this design:

  • Minimization is modelled, not automatic: a requirement reveals exactly what its concepts declare. A requirement authored with reviewed-structured-value returns that structure. Least disclosure is a choice the requirement makes, not a property the runtime imposes on every answer.
  • Purpose does not narrow the answer: a requirement returns the same concepts in the same forms for every purpose authorized to invoke it. Purpose gates whether a caller may ask; a purpose that justifies a coarser answer needs its own requirement.
  • The caller cannot inject a value, but matching strictness is configured: the computed answer rests on Evidence Gateway deriving it from one reviewed source consultation. A lookup resolves to exactly one match, no match, or ambiguity, and matching is only as strict as that reviewed source contract.
  • This is not zero-knowledge: a boolean answer is computed inside the service from data the service read, and SD-JWT selective disclosure is digest omission. Neither is a zero-knowledge proof.
  • An audience-scoped assertion is not a general-purpose credential: its subject binding is scoped to the audience named in it, so it is meaningful to that relying party and to no other. A holder-bound credential is presentable to several verifiers, and pays for that with the correlation described in Subjects do not travel, and do not correlate.
  • Verifying a presentation is not preventing replay: a key-binding JWT proves the presenter held the confirmation key’s private key when it was signed, over exactly those bytes. Comparing the challenge nonce is not consuming it, the same presentation verifies again, and retiring a nonce belongs to the relying party’s own challenge lifecycle.

These forms also sit inside boundaries that are not specific to disclosure design: how the source consultation is authorized and pinned, how subject selectors are scoped, and what “aligned with a standard” does and does not mean. Weigh those alongside the value forms; see the known limitations hub for the full inventory.