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

# RS-OP-POSTURE: Relay operational posture

> The normative operational contract for a Relay deployment: the liveness and readiness probes, how readiness composes audit, source, and issuer state, the audit sink as a serving precondition, runtime limits and quotas, bounded operational logging, and startup verification of the sealed package.

This document defines what an operator can observe about a running Relay deployment and what the
runtime guarantees while it serves. It covers the two operational probes, the state readiness
composes, the audit sink as a precondition of serving rather than a side effect of it, the runtime
limits and quotas a deployment configures, the closed operational log level, and the verification
the process performs before it opens a listener.

The key words in this document are interpreted per [RS-DOC](../rs-doc/) Section 2.
Defined terms are used per [RS-TERMS](../rs-terms/).

Relay exposes no administrative surface, no posture document, and no metrics endpoint. Everything
an operator can observe over HTTP is stated in Section 2, and it is deliberately small: two probes
that answer with a fixed status word and nothing else. The observable detail a previous Relay
returned in a posture document now lives in the audit chain and in the process log, both of which
are read from the deployment host rather than fetched over the service port.

## Version history

| Version | Date | Status | Change |
| --- | --- | --- | --- |
| 0.1.0 | 2026-07-19 | draft | Initial posture-document contract for `registry.ops.posture.v1`. |
| 0.1.1 | 2026-07-19 | draft | Added restricted Relay per-resource refresh health before the 1.0 schema freeze. |
| 0.1.2 | 2026-07-20 | draft | Replaced waiver reason text with validated operator references and optional summaries before the 1.0 schema freeze. |
| 0.1.3 | 2026-07-23 | draft | Reserved credential-shaped waiver-reference prefixes before the 1.0 schema freeze. |
| 0.1.4 | 2026-07-24 | draft | Separated portable waiver-schema checks from contextual semantic validation before the 1.0 schema freeze. |
| 0.1.5 | 2026-08-03 | draft | Recorded that Registry Notary is retired and that no supported deployment produces a `registry-notary` document, while the frozen v1 shape and the consumer validation path for one are unchanged. Stated that Evidence Gateway and Registry Mint expose no posture endpoint. |
| 1.0.0 | 2026-08-11 | draft | Rewritten for the Relay V2 runtime that replaced the V1 gateway. REQ-OP-POSTURE-001 through REQ-OP-POSTURE-011 are retired in full and MUST NOT be reused: they defined the `registry.ops.posture.v1` document returned from `GET /admin/v1/posture`, its component pairing, its sensitivity tiers, its deployment-waiver metadata, and Relay per-resource refresh health. The current runtime has no administrative surface, emits no posture document, and performs no source refresh, so none of those requirements has a successor. The operational contract that does exist is numbered from REQ-OP-POSTURE-101 so that no retired identifier carries a second meaning. |

## 1. Scope and surface

This specification covers the operational behavior of one `relay` process serving one sealed
package: what it exposes for liveness and readiness checks, what it refuses to do while a
dependency is unavailable, the bounds a runtime document sets on it, what it writes to its log, and
what it verifies before it accepts traffic.

It does not define the data protocol. The route set, the response envelope, access and disclosure
profiles, pagination, and the error vocabulary belong to [RS-PR-RELAY](../rs-pr-relay/). It does
not define authoring, compilation, or packaging, which belong to
[RS-PR-RELAYCTL](../rs-pr-relayctl/). It does not define the security model or the threat boundary,
which belong to [RS-SEC-G](../rs-sec-g/).

Evidence Gateway is out of scope. It exposes liveness and readiness probes of
its own, and neither is governed by the requirements in this specification.

An operational probe is an observation, not an authorization decision, a conformance claim, or a
statement about the correctness of the data a source holds. A ready deployment is one that can
answer a consultation now; it is not a deployment whose records are current, complete, or fit for a
given purpose.

## 2. Operational routes

A Relay deployment serves a fixed route set. Four of its routes are unauthenticated in every
deployment, and two of those are the operational probes this specification governs. A contract that
publishes its resource metadata or its generated artifacts lets further routes answer without a
credential; RS-PR-RELAY describes those rather than this specification.

| Route | Operation identifier | Answers |
| --- | --- | --- |
| `GET /health` | `relay.health` | Whether the process is alive and serving |
| `GET /ready` | `relay.ready` | Whether the activated package can be served now |

`GET /openapi.json` and `GET /v2` are also unauthenticated, and both are described by RS-PR-RELAY
rather than by this specification. The per-deployment OpenAPI description lists `relay.health` and
`relay.ready` as operations with empty security, which is how an integrator discovers that neither
probe takes a credential.

REQ-OP-POSTURE-101: A deployment MUST expose exactly two operational probes, `GET /health` and
`GET /ready`, both unauthenticated.
It MUST NOT expose an administrative route, a posture route, a metrics route, or any other
operational surface on the service port.
A probe response MUST carry `Content-Type: application/json` and `Cache-Control: no-store`, and its
body MUST be exactly `{"status":"ok"}` for `/health` and exactly `{"status":"ready"}` for `/ready`.
A probe body MUST NOT carry deployment identity, build or version detail, configuration values,
source paths, record counts, or the identity of any failing dependency.
The deployment's OpenAPI description MUST declare both operations with empty security.

REQ-OP-POSTURE-102: `/health` MUST report process liveness only.
It MUST NOT consult the audit sink, a configured source, or the token issuer, and it MUST return
`200` for as long as the process is serving.
An operator MUST NOT read a `/health` success as evidence that the deployment can answer a
consultation. The two probes exist because those are different questions: a process can be alive
and correctly refusing every request.

REQ-OP-POSTURE-103: `/ready` MUST report whether the deployment can answer a consultation now.
It MUST evaluate the composition in Section 3 on each request rather than return a value cached at
startup.
When the composition does not hold, the response MUST be a problem document with code
`service.not_ready` and HTTP status `503`, and that document MUST NOT identify which dependency is
unready.
An operator that needs to know which dependency failed MUST read the process log on the deployment
host.

The `relay` binary also ships a `healthcheck` subcommand for container orchestration.

REQ-OP-POSTURE-104: `relay healthcheck` MUST probe liveness, not readiness. Its default URL is
`http://127.0.0.1:8080/health`.
It MUST reject a URL carrying credentials, a query, or a fragment before sending anything, MUST
bypass any ambient proxy configuration, MUST follow no redirect, MUST time out after 5 seconds, and
MUST read at most 128 response bytes.
It MUST succeed only on `200` with `application/json` and a body equal to `{"status":"ok"}`, and it
MUST exit non-zero on every other outcome.
A deployment that wants an orchestrator to withhold traffic until a package is servable MUST probe
`/ready` directly rather than rely on this subcommand.

## 3. Readiness composition

Readiness is a conjunction of three independent observations, evaluated concurrently.

REQ-OP-POSTURE-105: A deployment is ready exactly when audit readiness, source readiness, and
issuer readiness all hold.
Audit readiness MUST require both a resolvable hash-chain head and a live probe of the concrete
sink, so that a replaced, removed, or unwritable audit destination makes the deployment unready.
Source readiness MUST re-verify every configured source within the runtime's request timeout: a
snapshot source MUST be confirmed unchanged, and every source's observed schema fingerprint MUST
still equal the fingerprint the sealed package recorded.
Issuer readiness MUST reflect the authenticator's own state when the runtime configures an issuer,
and MUST hold trivially when it configures none.
A source verification that exceeds the request timeout MUST resolve as unready rather than block
the probe.

Source readiness is therefore a drift check, not a liveness ping. A source file edited, replaced, or
migrated under a running deployment makes it unready rather than making it serve records the sealed
package did not describe.

REQ-OP-POSTURE-106: Concurrent readiness probes MUST share one in-flight source verification.
A probe arriving while a verification is running MUST await that result rather than start a second
one, and a verification that reached its deadline MUST hold the flight until its worker observes
the same deadline and exits.
Readiness probing is thereby bounded work: a probe interval shorter than the verification cost
cannot multiply source reads.

REQ-OP-POSTURE-107: A deployment MUST NOT begin serving in an unready state.
Startup MUST evaluate readiness after constructing the service and again immediately before binding
the listener, and MUST fail with a startup error rather than bind a listener when either evaluation
does not hold.

## 4. Audit as a serving precondition

Relay writes a hash-chained, value-free audit record for every consultation it attempts, refuses,
or completes. That record is a precondition of the response, not a trace left behind it.

REQ-OP-POSTURE-108: When an audit append fails at any phase, including the attempt record written
before a source is read, a refusal record, and the terminal record written before a prepared body is
returned, the request MUST fail with problem code `audit.unavailable` and HTTP status `503`.
The runtime MUST NOT return a response body it had already prepared once its terminal audit record
failed, and MUST NOT degrade to serving without audit.
A refusal MUST be recorded with the same durability as a successful read, so that an unavailable
audit sink cannot turn a refusal into an unrecorded event.

The operational consequence is that audit availability is a serving dependency of the same rank as
the source. An operator sizing or mounting the audit path is sizing the deployment's availability,
not its observability.

## 5. Limits and quotas

A runtime document binds one sealed package to one host. Its operational fields are bounded, and
the bounds are refusals rather than clamps.

REQ-OP-POSTURE-109: A runtime document MUST set `limits.requestTimeoutMilliseconds` between 1 and
120000 inclusive and `limits.concurrentQueries` between 1 and 256 inclusive.
When present, `cursor.maximumAgeSeconds` MUST be between 1 and 86400 inclusive, `quotas.burst`
MUST be between 1 and 100000 inclusive, `quotas.requestsPerMinute` MUST be non-zero, and
`shutdown.gracePeriodMilliseconds` MUST be non-zero.
The runtime MUST refuse to start on a document that violates any of these bounds, and MUST NOT
silently clamp an out-of-range value to a supported one.
`cursor` and `shutdown` are optional sections, and every field inside a present section is required.
The runtime defaults the cursor maximum age to 300 seconds when the whole `cursor` section is
absent, and the shutdown grace period to 30 seconds when the whole `shutdown` section is absent. A
present section that omits a field is a rejected document, not a defaulted one.
A document MUST set `cursor` when its contract binds any list or search operation, or when more
than one resource can appear in metadata; the runtime refuses to start without it.

REQ-OP-POSTURE-110: `limits.concurrentQueries` MUST bound admission at the source boundary: every
query MUST acquire an admission permit before it runs, and `limits.requestTimeoutMilliseconds` MUST
bound the total of the wait for that permit and the query itself.
A request that cannot acquire a permit within the timeout MUST be refused rather than queued
indefinitely.

REQ-OP-POSTURE-111: When a runtime configures `quotas`, the quota key MUST be the compiled operation
identifier, and the bucket MUST refill continuously at `requestsPerMinute` divided by 60 per second
up to `burst`.
A denied consultation MUST be refused with `consultation.rate_limited` and a denied aggregate-data
request with `aggregate-data.rate_limited`, both at HTTP status `429`.
A quota denial MUST be audited as a refusal before the response is returned, and an audit failure
during that refusal MUST take precedence, producing `audit.unavailable`.
The bucket is shared by every caller of the operation, so a deployment MUST NOT rely on quotas as a
per-caller, per-client, or per-tenant fairness control. They bound the load one operation can place
on a source.
Quotas MUST NOT apply to `/health` or `/ready`.

REQ-OP-POSTURE-112: A request URI longer than 16384 bytes MUST be refused with
`internal.uri_too_long` and HTTP status `414` before the runtime resolves a route, a profile, or a
credential.

REQ-OP-POSTURE-113: A runtime document MUST NOT carry inline secret material.
Every secret MUST be an indirect reference of the form `secret:env/NAME`, where `NAME` is upper
snake case, or `secret:file/name`, where `name` uses lower-case letters, digits, `.`, `_`, and `-`,
each at most 128 bytes.
The runtime MUST refuse to start when a referenced secret cannot be resolved, rather than start
with an unauthenticated or unkeyed configuration.

## 6. Operational logging

Relay's log is the operational detail its HTTP surface deliberately withholds. It is written where
only the deployment host can read it, which is what allows it to be more specific than a probe
response.

REQ-OP-POSTURE-114: The runtime MUST write structured JSON log records to standard error.
`RELAY_LOG` selects the level from the closed set `off`, `error`, `warn`, `info`, `debug`, and
`trace`, defaulting to `info`.
An unrecognized value MUST be treated as `info` and MUST NOT be passed through as a log filter
directive.
The configured level MUST apply to Relay-owned log targets only, so that dependency events, which
can carry request URLs and headers, remain off at every level.
A Relay-owned log record MUST carry fixed messages and value-free dimensions, and MUST NOT carry
record values, credentials, or token contents.

`RELAY_LOG` and `RELAY_RUNTIME`, which supplies the runtime document path, are the environment
variables the `relay` process reads by name, alongside `RELAY_HEALTHCHECK_URL` for the healthcheck
subcommand. It also reads whatever variable a `secret:env/<NAME>` reference in the runtime document
names. The `relayctl` authoring tool defines no product-specific environment-variable configuration;
its fixture runner may use only the host's temporary-directory selection for internal materialization, per
[RS-PR-RELAYCTL](../rs-pr-relayctl/).

## 7. Startup and shutdown

Startup is where a deployment's trust in its package is established. Nothing else opens until that
verification passes.

REQ-OP-POSTURE-115: The runtime MUST verify the sealed package before it opens an issuer, audit,
source, or listener resource.
Verification MUST re-derive the package rather than trust its manifest: it MUST recompute the digest
of every listed file, recompute the manifest's `packageRevision` over the canonical JSON of the
manifest's own unsigned content, re-canonicalize the compiled registry and compare it byte for byte
with the packaged bytes, and re-derive the compiled contract from the packaged `registry.yaml`,
governed files, and recorded source schemas.
The package directory's file set MUST equal the manifest's file list exactly, so an added or removed
file fails verification.
A package containing a symlink, a non-regular file, or an unsafe permission bit MUST be refused.

REQ-OP-POSTURE-116: After package verification, the runtime MUST observe each configured source and
MUST refuse to start unless the observed schemas equal the schemas the package recorded, source for
source.
A deployment therefore cannot start against a source whose shape has drifted from the one the
contract was compiled against, and cannot start with a source the package does not describe.

REQ-OP-POSTURE-117: A startup failure MUST be terminal and value-free. The runtime MUST exit
non-zero with one of its fixed failure reasons, and MUST NOT continue in a degraded mode, retry
against a different package, or serve any route.

REQ-OP-POSTURE-118: On `SIGINT`, or on `SIGTERM` where the platform provides it, the runtime MUST
stop accepting new connections and allow in-flight requests to complete within the configured grace
period.
Exceeding that grace period MUST be reported as a shutdown failure rather than a clean exit.

## Conformance

A runtime conforms to this specification when it exposes exactly the two operational probes with
their fixed bodies and no administrative, posture, or metrics surface; evaluates readiness as the
live conjunction of audit, source, and issuer state; refuses to serve when audit is unavailable;
enforces its configured limits and quotas as refusals; keeps its log bounded to Relay-owned targets
at a closed set of levels; ships a liveness-only healthcheck subcommand with the stated safety
constraints; re-derives the sealed package before opening any other resource; and shuts down within
its grace period (REQ-OP-POSTURE-101 through REQ-OP-POSTURE-118).

A deployment conforms when it probes liveness and readiness as separate questions, reads
dependency-level detail from the process log rather than from a probe body, probes `/ready`
directly when an orchestrator must withhold traffic, and does not treat quotas as a per-caller
control. Those duties are the operator-facing halves of REQ-OP-POSTURE-102, REQ-OP-POSTURE-103,
REQ-OP-POSTURE-104, and REQ-OP-POSTURE-111.

## Evidence

This specification is `verified`: every requirement describes behavior shipped in the Relay V2
runtime and exercised by its tests.

{/* Evidence: crates/registry-relay-v2/src/server.rs defines the fixed route set and the readiness
    conjunction; crates/registry-relay-v2/src/api.rs holds the probe handlers, the fixed status
    bodies, the audit-failure paths, and quota refusal. */}

- The route table and `RelayService::is_ready` in `crates/registry-relay-v2/src/server.rs` fix the
  probe inventory and compose audit, source, and issuer readiness (Sections 2 and 3).
- The `health` and `ready` handlers in `crates/registry-relay-v2/src/api.rs` return the fixed status
  bodies with `no-store`, and return `service.not_ready` without naming a dependency (Section 2).
- `crates/registry-relay-v2/src/audit.rs` defines audit readiness as a chain head plus a live sink
  probe, and the audit-failure branches in `api.rs` return `audit.unavailable` at every phase
  (Sections 3 and 4).
- `verify_readiness_sources` in `crates/registry-relay-v2/src/sqlite_runtime.rs` re-verifies
  snapshot immutability and schema fingerprints under the request timeout, and its readiness gate
  coalesces concurrent probes (Section 3).
- `RelayRuntime::is_valid` and `valid_secret_reference` in
  `crates/registry-relay-v2/src/contract.rs` enforce the numeric bounds and the secret-reference
  forms (Section 5).
- `QuotaLimiter` in `crates/registry-relay-v2/src/server.rs` implements the per-operation token
  bucket, and `crates/registry-relay-v2/src/problem.rs` fixes the refusal codes and statuses
  (Section 5).
- `operational_log_directive` in `crates/registry-relay-v2/src/main.rs` implements the closed
  `RELAY_LOG` enum and the Relay-target scoping (Section 6).
- `load_package` in `crates/registry-relay-v2/src/package.rs` performs the re-derivation, and
  `prepare` in `crates/registry-relay-v2/src/startup.rs` orders it before every other resource and
  checks readiness twice (Sections 3 and 7).
- The out-of-process test `crates/registry-relay-v2/tests/process_http.rs` asserts both probe
  bodies against a real `relay` process and asserts that graceful shutdown completes within its
  deadline (Sections 2 and 7).

## Next

- [Operate Relay](../../operate/relay/) is the operator task guide for the behavior this
  specification fixes.
- [Configure Relay](../../configure/relay/) documents the runtime document whose bounds Section 5
  states.
- [RS-PR-RELAY](../rs-pr-relay/) defines the data protocol, including the routes this specification
  leaves out of scope.
- [RS-SEC-G](../rs-sec-g/) defines the security model these operational refusals serve.