Skip to content

docs: add "Records stay home" explanation pilot page#169

Open
jeremi wants to merge 14 commits into
mainfrom
claude/handoff-review-ayqb7d
Open

docs: add "Records stay home" explanation pilot page#169
jeremi wants to merge 14 commits into
mainfrom
claude/handoff-review-ayqb7d

Conversation

@jeremi

@jeremi jeremi commented Jun 28, 2026

Copy link
Copy Markdown
Member

Summary

Adds the trust-spine pilot explanation at docs/site/src/content/docs/explanation/records-stay-home.mdx — the quality exemplar for the documentation effort. Owning area: docs (Trust & Security spine).

The page explains how an institution proves facts from registries it already holds without the records leaving: what stays inside the boundary, what crosses it, the three disclosure modes (value / predicate / redacted), and an honest statement of what the design does and does not guarantee (not air-gapped, not zero-knowledge, default result is provenance-tagged rather than signed, no revocation/erasure flow).

Two small frontmatter fixes applied while landing it:

  • owner set to registry-docs (resolves the TODO-set-maintainer placeholder; matches the convention used by sibling explanation pages such as architecture.mdx).
  • last_reviewed quoted ("2026-06-28") for house-style consistency.

Checks

  • Frontmatter (check:content): verified by hand against scripts/check-doc-frontmatter.mjs — all 9 required keys present; status: draft, doc_type: explanation, locale: en are valid; standards_referenced: [] passes; owner now non-placeholder.
  • Internal links: the /spec/* links in Related (rs-sec-g, rs-pr-relay, rs-pr-notary, rs-dm-claim) all resolve to existing pages; the not-yet-built Trust & Security pages are referenced as plain text, not links, so nothing dangles.
  • Full npm run check not run locally: docs/site/node_modules is not installed in this environment, so CI on this PR is the validating gate for Vale, markdownlint, build, and link checks.

Notes

Deferred follow-ups (tracked, not blockers for this page's content):

  • Escalate the Mermaid trust-boundary diagram to a draw.io editable SVG per the diagrams convention (it currently renders with an adjacent text equivalent).
  • Optionally add an audience taxonomy value — the vocabulary already exists in the frontmatter checker (integrator / operator / …).

No release, security, migration, or compatibility concerns — this is a net-new explanation page on a new, stable slug.

DCO

  • Every commit includes a Signed-off-by trailer.
  • I reviewed the submitted changes and am responsible for the contribution.

Generated by Claude Code

Adds the trust-spine pilot explanation at
explanation/records-stay-home.mdx — the quality exemplar for the
documentation effort. It explains how an institution proves facts from
registries it already holds without the records leaving: what stays
inside the boundary, what crosses it, the three disclosure modes
(value / predicate / redacted), and an honest statement of what the
design does and does not guarantee.

Resolves the shippable frontmatter TODO by setting owner to the
registry-docs area (matching sibling explanation pages) and quotes
last_reviewed for house-style consistency.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdd153146c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +176 to +179
- **No revocation or erasure flow is defined.** This version specifies issuance,
disclosure, presentation, and verification, but not credential revocation or
data-subject erasure. A key rotated out may remain published so existing results stay
verifiable — that is not a revocation mechanism.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correct the credential revocation limitation

In deployments that enable credential status, Registry Notary already has a revocation/lifecycle flow: products/notary/docs/credential-lifecycle-status.md:32-37 describes enabling status for revocation, products/notary/docs/credential-lifecycle-status.md:101-108 defines revoked, and products/notary/docs/api-reference.md:46-47 lists the status read/update routes. Keeping this as “no revocation flow” will mislead operators and verifiers into ignoring the supported status path; narrow the limitation to no aggregated/external revocation-list profile or to erasure only.

Useful? React with 👍 / 👎.

Comment on lines +141 to +143
time beyond what its configuration grants. Only liveness probes and the public verification
keys are reachable without authentication; anything that touches a record or a claim
requires it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include all unauthenticated service exceptions

This overstates the protected surface. RS-SEC-G says readiness probes are also unauthenticated (docs/site/src/content/docs/spec/rs-sec-g.mdx:98), and Notary's API reference lists unauthenticated well-known exceptions beyond verification keys, including OID4VCI issuer metadata and SD-JWT VC type metadata (products/notary/docs/api-reference.md:59-63). In deployments exposing those routes, this page gives auditors and operators an incomplete allowlist; include these exceptions or scope the sentence to claim/record routes.

Useful? React with 👍 / 👎.

Comment on lines +189 to +191
- The security model and protocol contracts: [RS-SEC-G](/spec/rs-sec-g/),
[RS-PR-RELAY](/spec/rs-pr-relay/), [RS-PR-NOTARY](/spec/rs-pr-notary/),
[RS-DM-CLAIM](/spec/rs-dm-claim/)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use relative links for archive builds

These root-relative links are the only ](/...) links under src/content/docs, and they bypass DOCS_BASE in archived builds. The archive build sets a base path (scripts/build-archives.mjs:32), and the built-link checker rejects archive pages whose root-relative href points outside /v/<version>/ (scripts/check-built-links.mjs:94-101), so this page can make npm run check fail once archives are built. Use relative links like ../spec/rs-sec-g/ instead.

Useful? React with 👍 / 👎.

last_reviewed: "2026-06-28"
doc_type: explanation
locale: en
standards_referenced: []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge List the SD-JWT VC standard in frontmatter

standards_referenced is empty, but this page makes SD-JWT VC claims at lines 94 and 126, and the docs style guide requires this field to list each mentioned standard using ids from src/data/standards.yaml (sd-jwt-vc is defined there). Leaving it empty keeps the standards/adoption tracking from seeing this external-standard claim; add at least sd-jwt-vc.

Useful? React with 👍 / 👎.

---
title: Records stay home
description: How an institution proves facts from registries it already holds — without the records leaving.
status: draft

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide the draft page from published builds

This marks the page as status: draft, but the build uses Starlight's draft: true flag to suppress draft pages: the existing draft explanation and decision pages both set draft: true, while status is only a custom field validated by content.config.ts and the frontmatter checker. Without the flag, this pilot page still builds at /explanation/records-stay-home/ and gets Markdown/search affordances before approval.

Useful? React with 👍 / 👎.

Comment on lines +80 to +81
- **Private signing keys never leave the issuer.** The institution publishes the *public*
half of its signing key so anyone can verify a result; the private half stays inside.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit key-verification claims to signed artifacts

The public key does not let anyone verify every result. RS-SEC-G scopes public-key verification to credentials or signed responses, and this same page later says plain evaluation results are provenance-tagged rather than signed; as written, operators could read this as saying default claim-result JSON is cryptographically verifiable. Say the public half verifies signed credentials and signed federation/response artifacts, not all results.

Useful? React with 👍 / 👎.

Make the Related links archive-safe (../../spec/... instead of root-
relative /spec/...), unblocking the check:links:built CI gate that
rejects archived pages linking outside their version.

Tighten two Tier-C security claims to match RS-SEC-G and the page's own
honesty thesis:
- the published public key verifies a signed credential or signed
  result, not "a result" in general (default results are provenance-
  tagged, not signed);
- describe the unauthenticated surface accurately (liveness/readiness
  probes, public verification keys, credential-issuance discovery
  metadata) while keeping the strong rule that anything touching a
  record or claim requires authentication.

Declare the SD-JWT VC standard in standards_referenced.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 077dcb043d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +114 to +115
The mode is policy-bound, not caller's choice: a claim defines an `allowed` set and a
`default`, the service refuses a mode outside the allowed set, and every result records

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clarify callers can request allowed disclosure modes

For requests that deliberately set disclosure, this overstates policy control: the request model has an optional disclosure field (crates/registry-notary-core/src/model.rs:727), and RS-PR-NOTARY-009 says Notary refuses only modes outside the claim's allowed set and uses the default when none is requested (docs/site/src/content/docs/spec/rs-pr-notary.mdx:138). Saying the mode is not the caller's choice can make integrators miss supported mode negotiation; phrase it as caller-selected within the claim policy.

Useful? React with 👍 / 👎.

Comment on lines +17 to +19
about those records** — *is this person alive? is this household eligible?* — and return
a result another system can trust, while the records themselves are **read where they
already live, never written back, and never handed over**.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope the no-record-transfer promise to evidence flows

For deployments that expose Registry Relay consultation routes, this guarantee is too broad: RS-PR-RELAY-008 defines Relay's public surface as record collection and single-record retrieval routes, so authorized Relay clients can receive source records by design. As written, this frames Registry Stack as never handing records over; scope the promise to Notary claim/evidence flows rather than Relay's record-bearing API.

Useful? React with 👍 / 👎.

Comment on lines +130 to +132
field the holder does not present stays hidden. It is **holder-bound** — tied to the
holder's key, and not presentable without the matching private key — and the holder chooses
which fields to reveal to which verifier. Anyone can verify it against the issuer's

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify holder binding by credential profile

In profiles where holder_binding.mode is none, Registry Notary can issue a credential without a holder DID: issue() only requires holder confirmation when the profile mode is not none, and the conformance profile says sub is the holder DID only for holder-bound credentials, otherwise it is the evaluation subject reference. Calling every credential holder-bound can make verifiers assume proof-of-possession protection that is absent for unbound profiles.

Useful? React with 👍 / 👎.

Comment on lines +174 to +175
default a matching failure collapses to a single public reason, so the lookup surface
cannot be used as an existence oracle.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit existence-oracle claim to matching errors

This guarantee only holds for default matching-error collapse, not for all lookups. The page itself recommends an exists predicate that returns true or false, and RS-DM-CLAIM also allows bindings with no matching policy to fall back to identifier-only resolution and permits controlled deployments to disable collapse; in those contexts, authorized calls can intentionally reveal record existence. Scope this sentence to granular matching failures rather than the whole lookup surface.

Useful? React with 👍 / 👎.

…clear em dashes

Five load-bearing claims are corrected to match the RS-* specifications
(the specification oracle) and the Rust implementation, each verified
against a REQ-ID or source line:

- Scope the headline promise: records are "never returned as the answer"
  rather than "never handed over". Registry Relay is itself a scoped,
  authenticated read API that returns source records to authorized
  callers (REQ-PR-RELAY-006/008), so the stack-wide absolute was wrong;
  the read-in-place, no-write-back, retained-custody promise holds.
- Holder binding is profile-conditional, not unconditional. The default
  binding mode is `none`, which issues an unbound credential, and shipped
  notary profiles use it; selective disclosure holds regardless.
- The disclosure mode is policy-bound but caller-requestable: a caller
  may request a mode, which policy then bounds to the claim's `allowed`
  set, applying the `default` when none is requested (REQ-PR-NOTARY-009).
- The existence-oracle protection is scoped to the "matching surface",
  the spec's term (REQ-DM-CLAIM-005); "lookup surface" over-generalized
  and contradicted the page's own exists-as-predicate example.
- Revocation is framed accurately: the specs define no revocation,
  credential status, or erasure, while the implementation ships an
  optional, default-disabled credential-status surface (IETF Token
  Status List) that an operator can enable; erasure is absent everywhere.

Also replace 17 em dashes with house-style punctuation so the exemplar
satisfies RegistryDocs.EmDash, including the rendered diagram label and
the disclosure table.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8d2c4d2a1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +71 to +73
- **Source data is read in place.** Relay reads sources as batch snapshots or table scans;
there is no write-back to the source registry, and runtime services expose no
data-mutation routes. The source keeps running as it always has.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify the no-write-back guarantee for sidecar sources

For deployments where Registry Notary reads sources through source_adapter_sidecar, this promise is broader than the implementation: the sidecar supports configured http_json.method: POST and Rhai source.post_json(...) calls for JSON POST searches (products/notary/docs/script-rhai-source-adapter-guide.md:102, crates/registry-notary-source-adapter-sidecar/src/sidecar.rs:4876). Those calls are intended for search-before-read, but if pointed at a mutating endpoint the stack does not enforce “no write-back”; scope this guarantee to Relay's read-only connectors or to non-mutating source-adapter configurations so operators do not treat it as universal.

Useful? React with 👍 / 👎.

Comment on lines +114 to +116
The mode is policy-bound: a caller may request a mode, but a claim defines an `allowed`
set and a `default`, the service refuses a requested mode outside the allowed set and
applies the default when the caller requests none, and every result records which mode was

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Account for disclosure downgrade policies

When a claim configures disclosure.downgrade as default or redacted, a caller requesting a mode outside allowed is not refused: validate_requested_disclosure_before_source() accepts the request if the downgraded mode is allowed, and rendering records the downgraded mode. In those deployments this tells integrators to expect a rejection when they actually get a redacted/default success, so describe the downgrade policy instead of unconditional refusal.

Useful? React with 👍 / 👎.

Comment on lines +87 to +88
Only a computed answer crosses out, never the source row. The answer takes one of a few
shapes:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope the no-row promise to reviewed claim models

For deployments with object/list-valued claims, this is stronger than the runtime enforces: Notary accepts value.type: object/array in validate_claim_value_type(), exposes source objects to CEL, and value disclosure emits the evaluated value except for configured object redaction. A broadly modeled extract/CEL claim can therefore return row-shaped data, so qualify this as a property of reviewed claim definitions rather than a stack guarantee.

Useful? React with 👍 / 👎.

Comment on lines +30 to +32
A caller never sends the value it is asking about and never receives the underlying
record. It sends a subject identifier and the id of a *claim* (a single, pre-modelled
question) and receives one of a few narrow shapes of answer: a yes/no, a single value, a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow for required request attributes

For claims whose bindings use target.attributes.*, requester.identifiers.*, or relationship attributes, callers must send more than a bare subject identifier: the request model carries target/requester attributes and the claim spec explicitly allows those lookup paths (docs/site/src/content/docs/spec/rs-dm-claim.mdx:87, crates/registry-notary-core/src/model.rs:784). As written, integrators following this page can omit the attributes needed for matching or purpose-gated reads; narrow this sentence to say callers do not supply the evaluated value, not that they only send a subject id.

Useful? React with 👍 / 👎.


| Mode | Discloses | Withholds |
|------|-----------|-----------|
| `value` | the full evaluated value | nothing about the value |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include policy redaction in value disclosure

In governed object-claim deployments, value mode can still withhold configured fields: when the PDP/matching policy returns redaction fields, build_claim_result_view() removes those object fields even while the effective disclosure remains value (crates/registry-notary-server/src/runtime.rs:5537). Saying value mode withholds nothing will make callers and auditors expect raw full-value responses where the service intentionally returns a field-redacted object; add that exception or scope this table to claims without policy redaction.

Useful? React with 👍 / 👎.

Comment on lines +152 to +154
- **Every person-level request is audited.** An audit record captures at least the caller,
the scopes exercised, a request id, and the declared purpose where one was supplied. A
deployment can run audit fail-closed, so a request whose audit record cannot be written

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not promise scopes in Notary audit records

This is true for Relay, but not for Notary audit events: EvidenceAuditEvent carries the principal hash, path, status, claim hash, purposes, and correlation hash, but has no scopes_used/scopes field (crates/registry-notary-core/src/model.rs:1591). In Notary deployments this overstates what an auditor can reconstruct from the audit sink; either scope the sentence to Relay or describe Notary's claim/source policy fields instead of exercised scopes.

Useful? React with 👍 / 👎.

claude added 2 commits June 28, 2026 14:00
Add `auditor` and `decision-maker` to the enforced `audience` set in
check-doc-frontmatter.mjs (union, not replacement), so Decide and
Trust & Security pages can declare their reader audience without failing
the frontmatter gate. `audience` stays optional and is validated only
when present, so existing pages are unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>
… read surface

Address a third review pass plus the approved boundary-diagram gap. Each
change is verified against the RS-* specifications and the Rust code.

- Request inputs: a caller sends the claim id and the inputs the claim's
  matching policy needs (a subject identifier, and where required, target or
  requester attributes, further identifiers, or relationship attributes), not
  only a subject id (RS-DM-CLAIM Section 3, REQ-DM-CLAIM-004).
- Row shape: recast "never the source row" as a modelling discipline, since
  object/array claims and CEL extracts can return row-shaped data under
  `value` disclosure (RS-DM-CLAIM; runtime value path).
- value mode: note that policy redaction removes object fields even under
  `value`, and that `value` is not constrained to a scalar.
- Disclosure downgrade: describe the `downgrade` policy (deny refuses;
  default/redacted substitute an allowed fallback) instead of unconditional
  refusal (REQ-PR-NOTARY-009 plus the runtime downgrade path).
- Audit: describe the captured field as the scope or claim the request
  exercised, accurate for both Relay (scope) and Notary (claim).

The boundary diagram and caption now show Registry Relay's own external,
scoped, audited record-consultation surface (REQ-PR-RELAY-006/008) alongside
the Notary answer flow, keeping the read-in-place, no-write-back,
distributed-custody thesis intact.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7be8ecaf1c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// layers, audience the reader roles.
const validLayer = new Set(['metadata', 'consultation', 'evaluation', 'credential', 'federation', 'administration', 'operations']);
const validAudience = new Set(['integrator', 'operator', 'maintainer', 'specification editor', 'tooling']);
const validAudience = new Set(['integrator', 'operator', 'maintainer', 'specification editor', 'tooling', 'auditor', 'decision-maker']);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep audience vocabulary in sync with the schema

This now lets check:content accept auditor and decision-maker, but the formal RS-TERMS audience vocabulary still defines only integrator, operator, maintainer, specification editor, and tooling, and src/content.config.ts uses the same five-value Zod enum. If a page follows this checker and adds either new role, the frontmatter check passes while the documented vocabulary and Astro build disagree; update RS-TERMS and the Starlight schema with the new values, or keep the checker restricted to the existing set.

Useful? React with 👍 / 👎.

Comment on lines +141 to +145
A credential is not a copy of the record. It is an **SD-JWT VC**: the signed body carries a
SHA-256 *digest* of each selectively disclosable field rather than the field value, so a
field the holder does not present stays hidden. Holder binding is set by the credential profile. A holder-bound profile ties the credential
to the holder's key so it is not presentable without the matching private key; the default
`none` mode issues an unbound credential. Either way, the holder chooses which fields to

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Describe SD-JWT disclosure at claim granularity

In deployments that issue object-valued claims, this overstates what holders can selectively reveal: disclosures_for_results() builds one SD-JWT disclosure per ClaimResultView (or per configured OID4VCI projection claim), so fields inside an object value are bundled into the same disclosure rather than independently hidden or revealed. Credential designers following this text could model several attributes inside one object claim expecting per-field holder choice, but a verifier who receives that disclosure gets the whole object value that survived policy redaction; scope the statement to claim/output disclosures or document the projection requirement for field-level choice.

Useful? React with 👍 / 👎.

Comment on lines +96 to +97
authorized caller: a governed, audited read under the `dataset:rows` permission, never a
bulk export. Registry Notary returns the answer a rule computes rather than the source row;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not rule out paginated record export

For Relay deployments that expose entity collection routes, dataset:rows authorizes a paginated record collection: the handler returns a page plus next_cursor, and clients can iterate those cursors up to the configured filters and limits. Calling this “never a bulk export” can make operators treat dataset:rows as non-harvestable when it can be used to collect all reachable rows over multiple requests; phrase this as bounded/paginated/scoped access rather than a categorical no-bulk guarantee.

Useful? React with 👍 / 👎.

claude added 2 commits June 28, 2026 14:25
…larity

Two precision fixes from a fourth review pass, each verified against the code.

- Relay `dataset:rows` reads are paginated: the entity collection route
  returns a cursor, so an authorized caller can iterate reachable rows.
  Describe the read as governed, audited, paginated, and bounded by scope,
  filters, and limits rather than "never a bulk export".
- SD-JWT selective disclosure is per claim output: the issuer builds one
  disclosure per claim result (or projection output), so an object-valued
  output is revealed as a unit, not field by field within the object.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>
This reverts the audience-vocabulary change in 9b774ae. The audience set is
defined in three places that must agree: the RS-TERMS specification, the
Starlight Zod schema in src/content.config.ts, and this checker. Extending
only the checker lets check:content accept `auditor` and `decision-maker`
while the Astro build (a five-value Zod enum) and RS-TERMS still reject them,
so a page using either value passes the content check but fails the build.

Restore the five-value set until the vocabulary is extended across the spec,
the schema, and the checker together.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a340c561c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- **No revocation or erasure flow is specified; revocation is an optional operator surface.**
The `RS-*` specifications define no credential revocation, credential status, or
data-subject erasure. The implementation ships an optional credential-status surface (an
IETF Token Status List with states `valid`, `suspended`, `revoked`, and `expired`),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register Token Status List before citing it

This page now cites the IETF Token Status List profile, but the frontmatter only lists sd-jwt-vc and docs/site/src/data/standards.yaml has no Token Status List entry to reference. The docs style guide requires standards_referenced to list each standard mentioned using IDs from that register, so standards/adoption tracking will miss this status-list claim unless the register and this frontmatter are updated, or the wording stops presenting it as a referenced standard.

Useful? React with 👍 / 👎.

that evaluates claims, applies disclosure policy, and issues credentials. Registry Relay
turns an existing file or database table into a read-only, access-controlled API without
replacing the source, and publishes restricted, scoped consultation routes that return
source records to authorized callers holding the `dataset:rows` permission. Notary is the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use concrete dataset row scope names

For Relay record reads, the enforced scope is not the literal dataset:rows; RS-PR-RELAY-006 and the Relay configs use a concrete dataset prefix such as social_registry:rows (<dataset_id>:rows). Operators following this text can grant or audit for dataset:rows, which will not satisfy Relay's scope checks for any real dataset, so make this a placeholder or example with an actual dataset id.

Useful? React with 👍 / 👎.

Comment on lines +129 to +130
instead substitutes that fallback mode when the fallback is itself allowed. The default mode
applies when the caller requests none, and every result records which mode was applied. A privacy-sensitive claim is expected to default to the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope default disclosure to single-claim requests

When an evaluation request omits disclosure but includes multiple claims, Notary does not apply each claim's own default: requested_disclosure() selects the first requested claim's default, then validates or downgrades that same mode for every result. In mixed-default requests this sentence can make integrators expect a later claim's more private default to apply when the earlier claim's less restrictive default may be used, so scope this statement to single-claim requests or document the first-claim default behavior.

Useful? React with 👍 / 👎.

| Mode | Discloses | Withholds |
|------|-----------|-----------|
| `value` | the evaluated value, less any object fields the policy redacts | nothing beyond policy-redacted object fields |
| `predicate` | only the true/false satisfaction | the underlying value |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify predicate mode for non-boolean claims

When a claim that returns a string, object, or array allows predicate, the runtime does not synthesize a true/false answer: build_claim_result_view() uses result.value.as_bool() for both the predicate value and satisfied, so non-boolean values leave both fields absent. This table tells callers they will learn true or false for every predicate result, which can make integrators model an extract/object claim with predicate and receive no outcome; limit this guarantee to boolean-valued claims or document the non-boolean behavior.

Useful? React with 👍 / 👎.

…, predicate scope

Three accuracy fixes from a fifth review pass, each verified against the
specifications and code:

- The record-read scope is `<dataset_id>:rows`, not a literal `dataset:rows`
  (REQ-PR-RELAY-006); a literal grant satisfies no real dataset's scope check.
  Corrected in the diagram, the caption, and the prose.
- Describe the optional credential-status surface as a status list rather than
  naming a formal standard the page does not list in standards_referenced.
- Predicate disclosure yields a true/false only for a claim whose rule yields a
  boolean; the runtime leaves the outcome absent for a non-boolean value.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e64122e8a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +161 to +163
requires authentication; the routes reachable without it are limited to operational and
discovery surfaces: liveness and readiness probes, the public verification keys, and
credential-issuance discovery metadata.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge List the public OID4VCI and status routes

In deployments with OID4VCI pre-authorized-code or credential status enabled, this public-route allowlist is incomplete: Notary's auth exemption list includes /oid4vci/credential-offer, /oid4vci/nonce, /oid4vci/offer/start, /oid4vci/offer/callback, /oid4vci/token, and /v1/credentials/{credential_id}/status (crates/registry-notary-server/src/standalone.rs:4526-4547), and OpenAPI marks the callback/token routes public while minting a pre-authorized code or access token (crates/registry-notary-server/src/openapi.rs:505-559). Auditors following this sentence could block or overlook required public issuance/status endpoints, so scope it to claim/record routes or include these OID4VCI/status exceptions.

Useful? React with 👍 / 👎.

claude added 2 commits June 28, 2026 14:56
The "limited to" list understated what is reachable without authentication.
Per is_auth_exempt_path (crates/registry-notary-server/src/standalone.rs), the
middleware-exempt routes also include the OID4VCI issuance-flow routes (offer,
token, nonce, callback, credential-offer), the credential-status route, and the
API and credential-type metadata routes, not only liveness and readiness, the
public keys, and issuer discovery metadata. None return a record or claim result
on their own, and the issuance and status routes run their own flow checks.
Reword so an auditor scoping the unauthenticated surface is not misled.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>
Add explanation/disclosure-and-minimization.mdx, the in-depth companion to
the "Records stay home" overview. It explains the disclosure model as a
mechanism and minimization as a modelling discipline, each claim traced to an
RS-* requirement or a line of Rust:

- the three modes in depth: value (with object-field redaction and non-scalar
  values), predicate (only for a boolean-yielding rule), redacted (withholds
  value and outcome);
- how policy binds a mode (default/allowed/downgrade; caller-requestable but
  bounded; the recorded mode is the effective one after forcing or downgrade);
- selective disclosure in the credential at claim-output granularity (an
  object output is revealed whole), with profile-conditional holder binding;
- disclosing non-existence (matching-failure collapse) and across a federation
  boundary (a signed, scoped evaluation result);
- an honest limits section (not zero-knowledge, modelled not automatic,
  provenance not signature, alignment not conformance).

Wire the overview's Related section to the new page.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b953f8479

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

carry the underlying source value and not convey the satisfaction outcome: the body withholds
both the value and the predicate. The runtime matches this: the disclosed value is absent, and
the `satisfied` outcome is populated only for `value` and `predicate` (by reading the value as a
boolean) and is absent for `redacted` (`runtime.rs`). A redacted answer carries no value and no

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify redacted results for CCCEV rendering

For callers that request CCCEV JSON-LD, this "no yes/no" guarantee is not what the implementation returns: view_claim() leaves satisfied unset for redacted, but render_cccev_evidence_node() serializes cccev:isConformantTo with result.satisfied.unwrap_or(false), so every redacted CCCEV response still contains a boolean outcome field, interpreted as false. Scope this statement to the claim-result JSON shape or document the CCCEV exception so integrators do not treat redacted CCCEV renders as outcome-free.

Useful? React with 👍 / 👎.

---
title: Disclosure and minimization
description: How Registry Notary bounds what a claim answer reveals through three disclosure modes, policy-bound mode selection, and a minimization discipline the claim author models.
status: draft

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide the new draft explanation page

This marks the page with the custom lifecycle status, but that field is only validated by the docs frontmatter/schema and does not suppress Starlight output; existing hidden drafts such as explanation/publishing-pipeline.mdx also set draft: true. Without the Starlight draft flag, this new pilot page still builds as a public route and can be indexed before review approval.

Useful? React with 👍 / 👎.

Comment on lines +11 to +12
standards_referenced:
- sd-jwt-vc

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge List every referenced standard in frontmatter

The page now references additional registered standards beyond SD-JWT VC, including OID4VCI, did:jwk/DID, JSON-LD, and CCCEV, but standards_referenced lists only sd-jwt-vc. The docs style guide uses this field for standards/adoption tracking, so these claims will be missed unless the matching register IDs (oid4vci, w3c-did, json-ld, cccev) are added or the prose stops citing them as standards.

Useful? React with 👍 / 👎.

E2 (disclosure-and-minimization): the page references OID4VCI, did:jwk (W3C
DID), JSON-LD, and CCCEV beyond SD-JWT VC; declare them in standards_referenced
so the standards register tracks them. Scope the redacted "no yes/no" guarantee
to the claim-result JSON shape: the CCCEV JSON-LD render serializes
cccev:isConformantTo from satisfied with a false fallback (runtime.rs), so a
redacted CCCEV node still carries a boolean outcome field.

Pilot (records-stay-home): declare oid4vci, now referenced by the
unauthenticated-route sentence.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c4dc126e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +148 to +149
own disclosure mechanism, and the two should not be conflated. The credential is an SD-JWT VC
(`typ` header `dc+sd-jwt`, signed with EdDSA over Ed25519, `REQ-PR-NOTARY-013`) that gives the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not document credential signing as EdDSA-only

In deployments whose credential profile uses an ES256/P-256 signing key, this statement is inaccurate: validate_signing_key_alg_usage() allows credential-profile signing keys to use EdDSA or ES256, and the config tests accept an ES256 credential-profile key. Verifiers or integrators following this page could reject valid ES256 credentials or assume P-256 profiles are unsupported, so describe the SD-JWT VC as signed with the credential profile's configured/allowed algorithm rather than EdDSA-only.

Useful? React with 👍 / 👎.

Comment on lines +149 to +152
object-valued output is revealed as a unit, not field by field within the object. Anyone can
verify it against the issuer's
published public keys, served without authentication so a verifier needs no credential of
its own. The issued credential carries no full record payload.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope the no-record-payload credential claim

Even with the preceding object-output caveat, this final sentence is still categorical for profiles that issue value or projected object claims: disclosures_for_results() copies result.value (or the projected whole value) into an SD-JWT Disclosure, and direct issuance returns both the compact credential and disclosures. The issuer-signed JWT body has digests, but the issued credential response can still carry record-shaped values, so scope this to reviewed/minimized claim outputs or specifically to the issuer-signed JWT body.

Useful? React with 👍 / 👎.

Comment on lines +69 to +71
*Two surfaces cross the boundary, both governed.* Registry Notary evaluates one modelled
question against a source and returns a shaped, minimized answer; it is the only component
that evaluates claims, applies disclosure policy, and issues credentials. Registry Relay

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify Notary as the SD-JWT VC issuer

In Relay deployments with provenance enabled, Relay can attach its own VC-JWT signed response credentials to entity-record or aggregate responses (RS-PR-RELAY-013 and api/provenance_issuance.rs), distinct from Notary SD-JWT VCs. Calling Notary the only component that issues credentials can make operators miss Relay's separate issuer keys, DID document, schemas, and audit events; qualify this as Notary being the only component that issues claim/evidence SD-JWT VC credentials.

Useful? React with 👍 / 👎.

Comment on lines +159 to +160
- **Scope-before-source, deny-by-default.** A service checks the caller's scope *before* it
reads any source or evaluates any claim, and does not widen a caller's reach at request

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify scope-before-source for self-attestation

For self-attestation deployments with scope_policy: disabled (or optional policy with no scopes presented), Notary does not check a caller scope before source access: require_evaluation_access() skips source-scope checks for self-attestation principals, and self_attestation_scope_policy_allows() returns true when the policy is disabled. The read is still gated by self-attestation token/client/allow-list policy, but this wording will make auditors expect scopes on every Notary claim read; say scope or configured trust-context policy is checked before source access.

Useful? React with 👍 / 👎.

Comment on lines +170 to +172
- **Every person-level request is audited.** An audit record captures at least the caller,
a request id, the scope or claim the request exercised, and the declared purpose where
one was supplied. A

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Describe hashed Notary audit identifiers

For Notary requests this over-promises raw audit fields: EvidenceAuditEvent carries principal_id_hash, correlation_id_hash, and claim_hash, and audit_event_from_response() populates the correlation hash rather than a raw request_id. Relay audit records do have principal_id, scopes_used, and request_id, but operators reviewing Notary evidence will not find the raw caller/request id promised here; scope this sentence by component or name the hashed Notary fields.

Useful? React with 👍 / 👎.

Comment on lines +209 to +212
state, purpose, profile, and audience (`REQ-PR-NOTARY-018`). Federation is static-peer only:
peers are loaded from configuration at startup, a request from an unconfigured peer is rejected,
and dynamic discovery, shared replay storage, and federated credential issuance are out of scope
(`REQ-PR-NOTARY-019`). The federated answer is the same kind of minimized, mode-governed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not treat Redis federation replay as out of scope

For multi-instance federation deployments, shared replay storage is implemented and documented: federation calls require_replay_insert() against the runtime replay store, ReplayStores::from_config(&config.replay) supports Redis, and the federation operator guide says multiple serving instances need that shared store. Saying shared replay storage is out of scope will lead operators to avoid or overlook the Redis hardening path; limit the out-of-scope statement to dynamic trust discovery or the legacy federation.replay shape if that is the intended boundary.

Useful? React with 👍 / 👎.

Add explanation/trust-and-security.mdx, the third trust-spine page: a single
map of the security and trust posture for a reviewer or operator. Built ground,
draft, adversarial-verify; every posture-table row and authorization/audit claim
traces to an RS-* requirement or a line of Rust.

- A posture table (authentication, authorization/scope, audit, disclosure,
  credential signing, holder binding, revocation, federation, key custody,
  network egress, transport/tenant isolation) with a stack/operator/configured
  "Provided by" column.
- Exact scope enumerations: six data-plane suffixes (including identity_release
  and the parameterized evidence_verification:<suffix> form) and three
  service-level scopes.
- A Known gaps section stating the two spec/code conflicts honestly as
  spec-says-X, implementation-does-Y, under review: the holder-binding default
  none vs REQ-PR-NOTARY-015, and the missing scopes field on the Notary
  EvidenceAuditEvent vs REQ-SEC-G-008.

Also correct credential signing to the profile's configured algorithm (EdDSA
over Ed25519 by default, ES256 over P-256 supported) on the disclosure page, and
wire the trust-spine Related links across the three pages.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f9602c845

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

---
title: Trust and security
description: A single map of the Registry Stack trust posture, the stack-versus-operator responsibility split, and the honest gaps a reviewer should weigh.
status: draft

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide the draft trust page

This page is marked with the custom lifecycle status: draft, but Starlight only suppresses authored pages when they also set draft: true; the repo's existing hidden draft pages use that flag, while status is just extended frontmatter metadata. Without it, /explanation/trust-and-security/ can still be built and indexed before the trust overview is approved, so add draft: true or make the page current.

Useful? React with 👍 / 👎.

| Property | Current posture | Provided by |
|------|------|------|
| Authentication | A runtime service runs exactly one authentication mode (static credentials/API key or OIDC) and authenticates every route returning person-level records or claim results before producing a response (`REQ-SEC-G-002`, `REQ-PR-RELAY-005`). | Stack (primitive); mode configured |
| Authorization / scope | Scope-based and deny-by-default, with scope-before-source: the required scope is enforced before any source read or claim evaluation, so a caller lacking the scope is refused before source access (`REQ-SEC-G-005`, `REQ-PR-RELAY-006`). | Stack (primitive); scopes configured |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope source authorization beyond scopes

For self-attestation claims configured with scope_policy: disabled (or optional policy with no scope signal), Notary does not enforce a caller scope before source access: require_evaluation_access() returns early for self-attestation principals and self_attestation_scope_policy_allows() accepts disabled policy. As written, auditors will expect a required scope before every Notary source read; qualify this row to scope checks or the configured self-attestation trust-context policy.

Useful? React with 👍 / 👎.

Comment on lines +46 to +47
claim evaluation, disclosure policy, and credential issuance; Registry Relay must not perform claim
evaluation or issue credentials (`REQ-ARC-G-007`). That boundary does not prohibit Relay from

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify Relay credential issuance

In Relay deployments with provenance.enabled, Relay can attach a W3C VCDM VC-JWT signed response credential to entity-record or aggregate responses (RS-PR-RELAY-013 and crates/registry-relay/src/provenance/jwt_vc.rs), so saying Relay must not issue credentials makes operators miss a second issuer/key surface. Fresh evidence in this commit is that the new Trust page repeats the credential-issuance boundary; scope it to Notary claim/evidence SD-JWT VC credentials and call out Relay response credentials separately.

Useful? React with 👍 / 👎.

Comment on lines +157 to +158
Dynamic trust-chain discovery, shared replay storage, and federated credential issuance are out of
scope for this version and must not be implied by a conformance claim (`REQ-PR-NOTARY-019`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not rule out shared replay storage

For multi-instance federation deployments, shared replay storage is implemented and expected: top-level replay.storage: redis is built into the runtime replay store and federation replay checks call require_replay_insert() against that store, while the federation operator guide says multiple serving Notary instances need a shared replay store. Fresh evidence in this commit is that the new Trust page also treats shared replay as out of scope; narrow this to dynamic trust discovery/federated issuance or to the legacy federation.replay shape.

Useful? React with 👍 / 👎.

Comment on lines +11 to +14
standards_referenced:
- sd-jwt-vc
- oid4vci
- w3c-did

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Declare every standard cited here

This frontmatter omits standards the page cites in the body: it discusses a W3C VC JSON-LD envelope in the credential-signing rows and names the OpenAPI document in the unauthenticated-route section, but only lists SD-JWT VC, OID4VCI, and DID. The docs style guide uses standards_referenced for standards/adoption tracking, so add the matching registered IDs such as verifiable-credentials, json-ld, and openapi, or avoid presenting those as referenced standards.

Useful? React with 👍 / 👎.

Comment on lines +96 to +99
A service must not widen a caller's reach at request time beyond what configuration grants. Only
liveness and readiness probes and public verification-key discovery (the issuer JWKS and, where
published, the `did:web` document) are served without authentication; every claim-bearing and
record-bearing route requires authentication (`REQ-SEC-G-006`). On the Notary listener the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include Relay provenance public routes

For Relay deployments with signed response credentials enabled, this public-route allowlist is incomplete: build_app_with_provenance_metadata_and_metrics() merges the schema and JSON-LD context routers, and in gateway issuer mode the DID router, into the unauthenticated public router; Relay OpenAPI can also be public when server.openapi_requires_auth is false. Security reviewers following this sentence could block required verifier-support routes, so qualify it by component or list the Relay provenance/OpenAPI exceptions too.

Useful? React with 👍 / 👎.

| Revocation / credential status | An optional, default-disabled status-list surface; `CredentialStatusConfig.enabled` defaults to `false`. No revocation flow is specified by the `RS-*` set. | Operator (optional surface) |
| Federation | Static-peer only: peers loaded from config at startup, an unconfigured peer rejected; the answer is a compact signed JWT carrying a scoped evaluation result, never a credential (`REQ-PR-NOTARY-018`, `REQ-PR-NOTARY-019`). | Stack; peers configured |
| Key custody | The stack never serves the private half (the `PublicJwk` type rejects private members); storage, injection, and rotation of keys are the operator's (`REQ-SEC-G-007`, `REQ-SEC-G-013`, `RS-SEC-G` Section 9). | Stack (public-only surface) + operator |
| Network egress | An outbound SSRF-guard primitive constrains destinations (scheme allowlist, deny private ranges, deny cloud-metadata), applied at `SHOULD` level; edge rate limiting and egress limits are the operator's (`REQ-SEC-G-012`, `RS-SEC-G` Section 9). | Stack (primitive) + operator |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Account for private-network egress opt-outs

In deployments that explicitly set allow_insecure_private_network on Notary source connections or federation peers, the fetch policy allows private-network HTTP destinations while still denying cloud-metadata addresses (source_fetch_url_policy() and the federation peer validation tests cover this escape hatch). Saying the primitive denies private ranges without that qualifier makes operators think private-network egress is always blocked, so note the configured escape hatch or scope this to the default strict policy.

Useful? React with 👍 / 👎.

Five grounded corrections from review, each verified against spec and code, so
an auditor reading the security posture is not misled:

- Relay issuer surface: scope the "Relay must not issue credentials" boundary to
  claim credentials, and note Relay's own signed response credentials (VCDM 2.0
  VC-JWT) and did:web issuer surface with provenance enabled (REQ-PR-RELAY-013,
  REQ-PR-RELAY-014, REQ-SEC-G-007).
- Self-attestation authorization: scope-before-source applies to scope-authorized
  callers; a self-attestation principal is gated by the trust-context policy
  (self_attestation.scope_policy, default Required, can be Disabled), not a
  per-source scope check.
- Network egress: note the allow_insecure_private_network opt-in re-enables
  private-network HTTP destinations while still denying cloud-metadata.
- Unauthenticated surface: add the Relay listener's auth-exempt routes, including
  the provenance verifier-support routes (/schemas, /contexts, /.well-known/did.json)
  and OpenAPI.
- Federation replay: federation reuses the deployment replay store (Redis for
  multi-instance); the shared replay storage REQ-PR-NOTARY-019 places out of scope
  is cross-peer replay coordination, not a deployment's own Redis store.

Also scope the same cross-peer-replay point on the disclosure page.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 380ae045f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

---
title: Records stay home
description: How an institution proves facts from registries it already holds, without the records leaving.
status: draft

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide the draft records page

For a page kept at status: draft, this does not prevent publication: status is only custom frontmatter validated in docs/site/src/content.config.ts, while the existing hidden draft explanation pages also set Starlight's draft: true flag. Without that flag, /explanation/records-stay-home/ can still build as a public route and be indexed before the draft is approved; either add draft: true or make the page current.

Useful? React with 👍 / 👎.

Comment on lines +134 to +137
This is the mechanism behind "prove a fact without sharing the record". To check whether a
person has a registered record, model the question as an *existence* rule and disclose it
as a `predicate`: the caller learns `true` or `false`, and the row never crosses the
boundary. To check eligibility without exposing an income figure, derive the decision with

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't promise false for missing existence records

When the configured source binding does not resolve a row, load_sources() returns SourceNotFound and, with the default matching-collapse policy, surfaces evidence.not_available before the exists rule can return a Boolean. In the missing-record case this example makes integrators expect a clean false, but the runtime intentionally hides non-existence as an unavailable/error outcome; qualify the example to records that match or describe the not-available behavior.

Useful? React with 👍 / 👎.

- **A machine-readable evaluation result**: a claim-result document carrying *provenance
metadata*: which evaluation produced it, under which policy, across how many sources. This
provenance lets a receiving system trace the result; it is not a cryptographic signature.
- **A holder-bound credential**: an SD-JWT VC the subject can store in a wallet and present

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't label default credentials holder-bound

For credential profiles left at the default holder-binding mode, the issued SD-JWT VC is not holder-bound: HolderBindingConfig defaults to mode: "none", and issuance only requires holder material when the profile mode is not none. This summary will make integrators expect proof-of-possession for credentials that the default profile intentionally issues unbound; call this an issued credential or qualify it by profile.

Useful? React with 👍 / 👎.

An existence predicate does not return a clean false for a missing record:
under the default matching-failure collapse, an unresolved record surfaces a
single not-available reason rather than a false, the same collapse the limits
section describes as the existence-oracle protection. State that a resolved
record returns true and an unresolved one collapses to not-available, so the
example agrees with the page's own existence-oracle limit.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gfv6Eurtn4CzfnxLpNL2gP
Signed-off-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants