Skip to content
Registry StackDocsv0.20.0

Trusted context constraints

View as Markdown

Trusted context constraints are policy facts whose meaning must survive the boundary between a caller, the product that answers the caller, and the source registry behind it: why the operation is permitted, what purpose it serves, who may receive the result, and what safeguards apply.

Read one thing before the rest: Registry Relay and Evidence Gateway do not share an authorization model, a policy vocabulary, or a source path. No service in this stack authorizes a caller and then consults another service on its behalf. Each product enforces its own boundary against its own configured sources, under its own closed contract names.

There is no shared runtime policy vocabulary

Section titled “There is no shared runtime policy vocabulary”

Earlier versions of this page described a shared policy vocabulary in a registry-platform-pdp crate, covering legal basis, consent, jurisdiction, assurance, and source freshness, which Registry Relay consumed at request time. That is no longer the model. Registry Relay carries no policy decision point, evaluates no external policy, and reads none of that vocabulary. The retired crate has been removed.

What replaced it is narrower and easier to review: each product declares its own constraints in its own governed contract, and each enforces only what its own runtime can actually check. Where the two products use the same word, treat it as a coincidence of English rather than a shared control.

Relay records governance in the contract and enforces purpose in the token

Section titled “Relay records governance in the contract and enforces purpose in the token”

Registry Relay separates two things that a shared vocabulary used to blur.

Declared governance. A registry contract carries a processingDescriptions block: one entry per governed processing activity, each naming an identifier, the operations it covers, a purpose, a recipient class, a reference to a legal-basis document, a reference to a DPV processing profile, and a list of safeguards. These are closed, required fields of the contract, so a contract cannot compile without them and cannot carry an unrecognized extra field. They are a reviewed declaration sealed into the package, not a check the runtime performs on a request. Their visibility on the API is set by metadataVisibility.processing, which is public, operation-bound, or operator-only.

Enforced purpose. Separately, an operation’s access profile may declare a purpose constraint: a claim name plus a closed list of allowed values. Relay reads that claim from the verified access token and denies the request when the value is absent or outside the list. The caller does not declare a purpose in the request, so a purpose is authority the issuer granted rather than a label the caller chose. The same is true of the authority row binding: where an operation declares one, the value that pins every returned row comes from a token claim or from the token’s principal identifier, never from a request field.

Two constraints that used to be Relay’s are simply gone, and no replacement exists: there is no consent-reference check and no jurisdiction check in the runtime. A contract can declare a jurisdiction in its authoritative scope and can name a consent-related safeguard in a processing entry, but those are review material for a human, not a gate the process applies.

Relay owns its source, and its source is a local file

Section titled “Relay owns its source, and its source is a local file”

Relay owns source identity, the reviewed views it may read, the compiled statements it may run, and the properties a disclosure profile may return. In this version that ownership is much narrower than it used to be: the source is one read-only SQLite file per binding, opened in place, so Relay holds no source credential, negotiates no protocol, and adapts to no external system. See Records stay home for how that boundary is enforced.

There is no source-observation contract and no observed_at semantics. A Relay response carries the values the reviewed view returns and states nothing about when the underlying record was observed.

Evidence Gateway authenticates the caller itself. It validates the bearer token against exactly one configured issuer with exact audience, token type, and algorithm allowlists, and it reads the principal from one configured claim with no client_id, azp, header, or request fallback. Unsigned headers and caller request fields never substitute for authenticated authority, even behind a gateway (invariant V1-I04, Evidence Gateway operator contract).

Authorization is a single decision, not a series of separately satisfied checks. One exact entitlement match binds requester, optional actor, requirement revision, purpose, every role/selector-profile/value-origin tuple, subject authority, and audience together, and it happens before audit, credential resolution, or source access. A request that does not match completely contacts no source at all (invariant V1-I05).

Two consequences of that decision are worth stating separately, because they are the places where authorization is most often assumed rather than enforced:

  • A selector is a provider-lookup input, never proof of authority. Selector validation happens inside an already matched subject-authority path, so holding an identifier or a demographic tuple does not entitle a caller to ask about that subject (invariant V1-I06).
  • A consent, approval, or grant reference supplied by the caller never creates authority. Grant identifiers are accepted only from authenticated context and must be bound to the complete entitlement (invariant V1-I07).

Evidence Gateway has no notion of a Relay operation or a compiled evidence mode selecting one. It declares a closed single or search-then-fetch acquisition over HTTP sources with fixed origins, methods, fixed or tagged selector or prior-fact-bound paths, fixed non-secret headers, denied redirects, and client-side response projection. The core, not a script or response, executes the sequence and enforces one request per configured stage (invariants V1-I09 and V1-I40). The source boundary belongs entirely to Evidence Gateway and its authoritative sources.

Freshness has no counterpart in either product

Section titled “Freshness has no counterpart in either product”

An assertion carries observedAt, which is when Evidence Gateway evaluated the requirement. It is not a source-declared observation time, and Evidence Gateway Version 1 has no source freshness family: there is no maximum accepted observation age, no freshness field, and no configuration for one. The request nonce is uninterpreted correlation data and is explicitly not a freshness proof (products/evidence/contracts/cccev-field-mapping.yaml).

Registry Relay has no freshness family either. A snapshot source is pinned to the exact bytes captured at startup, so its answers are as current as the file an operator placed there and no more; a live-read-only source reflects the file as other processes commit to it. Neither profile attaches an observation time to a response, and neither bounds how stale the underlying data may be.

A deployment that needs an age bound on the underlying record has to define and enforce it outside these products: in the authoritative source contract for Evidence Gateway, and in the operational procedure that produces the SQLite file for Relay. Neither response carries one.

Relay has no diagnostics command and no configuration report. What is inspectable about a running deployment is what its own metadata routes serve, under the visibility its contract compiled, plus its audit log. The processing entries described above are the deployment’s declaration of legal basis, purpose, recipient class, and safeguards, and an operator can choose to publish them, bind them to the operations they cover, or withhold them entirely.

Relay’s audit records the resource, the operation, the contract revision, the access and disclosure profile names, the transform identifiers applied, and the principal kind. It records no row value, no selector value, and no raw identifier, and the same discipline applies to operational logs, where each request URI is mapped onto a closed set of literal route shapes before anything is written.

Presence and allow-list checks remain distinct concepts even though only the allow-list form is enforced here. Requiring that a purpose claim be present is not the same as approving every value it might carry, which is why the purpose constraint carries an explicit allowed list rather than a presence flag. Authentication-derived authority also remains distinct from an assurance value asserted somewhere in a token: Relay checks the claims its contract names and attributes no meaning to any other claim.

  • Does the contract’s processingDescriptions block match what the deployment actually does, and has a human reviewed the legal-basis and DPV documents it references?
  • Is metadataVisibility.processing set deliberately, rather than left at whatever the starter contract produced?
  • Does every operation that should be purpose-bound carry a purpose constraint, and does the issuer actually populate that claim?
  • Does Evidence Gateway deny an unauthorized request before it contacts any source?
  • Is every dimension of the Evidence Gateway entitlement matched as one decision rather than one at a time?
  • Are Registry Relay and Evidence Gateway sources, credentials, authorization decisions, and audit trails kept separate?
  • Where the deployment needs a freshness bound, is it enforced outside these products, and is that written down?