Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/v/0.38.0/llms.txt
Full corpus: https://docs.registrystack.org/v/0.38.0/llms-full.txt

# Disclosure modes and computed answers

> Why Evidence Gateway answers a question about a subject with a computed value whose shape is fixed in advance, instead of handing back the source record.

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

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.

## What actually controls disclosure

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 twelve forms and the
set is closed (`products/evidence/contracts/supported-value-forms.yaml`).

| Value form | What the caller learns |
|------------|------------------------|
| `boolean` | one true or false, and nothing about how it was reached |
| `controlled-code`, `controlled-code-list` | exact membership in a reviewed codelist; labels and source codes are not disclosed |
| `controlled-category` | exact membership in a reviewed closed category set |
| `bounded-identifier` | a source-owned identifier with an exact governed ASCII prefix, a constrained nonempty ASCII suffix, and a total length of at most 1024 bytes |
| `bounded-integer`, `bounded-decimal` | a number inside declared bounds, at a declared scale |
| `date-bucket`, `time-bucket` | which reviewed bucket applies; the underlying date, instant, or duration is not disclosed |
| `audience-scoped-entity-reference`, `entity-reference-list` | an opaque reference meaningful to the named audience and to no other; unavailable to a holder-bound requirement, which has no audience |
| `reviewed-structured-value` | a 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 questions, four answer shapes

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

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`). An environmental
authority, 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 a facility inspection
is required.

## There is no redacted answer

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.

## A refusal is not an existence oracle

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

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

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

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

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

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.

## Limits

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](#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](../known-limitations/) for
the full inventory.

## Related

- [Security overview](../../security/): the full posture this fits into
- [Evidence Gateway security model](../../security/evidence/): the invariants named on this page
- [Known limitations and non-guarantees](../known-limitations/): where the edges are
- [Records stay home](../records-stay-home/): what stays inside the institution and what crosses out
- How a requirement and its disclosure are defined:
  [RS-PR-EVIDENCE](../../spec/rs-pr-evidence/)