Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.26.1.
RS-OP-POSTURE: Relay operational posture
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 Section 2. Defined terms are used per 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
Section titled “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
Section titled “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. It does not define authoring, compilation, or packaging, which belong to RS-PR-RELAYCTL. It does not define the security model or the threat boundary, which belong to 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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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.
7. Startup and shutdown
Section titled “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
Section titled “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
Section titled “Evidence”This specification is verified: every requirement describes behavior shipped in the Relay V2
runtime and exercised by its tests.
- The route table and
RelayService::is_readyincrates/registry-relay-v2/src/server.rsfix the probe inventory and compose audit, source, and issuer readiness (Sections 2 and 3). - The
healthandreadyhandlers incrates/registry-relay-v2/src/api.rsreturn the fixed status bodies withno-store, and returnservice.not_readywithout naming a dependency (Section 2). crates/registry-relay-v2/src/audit.rsdefines audit readiness as a chain head plus a live sink probe, and the audit-failure branches inapi.rsreturnaudit.unavailableat every phase (Sections 3 and 4).verify_readiness_sourcesincrates/registry-relay-v2/src/sqlite_runtime.rsre-verifies snapshot immutability and schema fingerprints under the request timeout, and its readiness gate coalesces concurrent probes (Section 3).RelayRuntime::is_validandvalid_secret_referenceincrates/registry-relay-v2/src/contract.rsenforce the numeric bounds and the secret-reference forms (Section 5).QuotaLimiterincrates/registry-relay-v2/src/server.rsimplements the per-operation token bucket, andcrates/registry-relay-v2/src/problem.rsfixes the refusal codes and statuses (Section 5).operational_log_directiveincrates/registry-relay-v2/src/main.rsimplements the closedRELAY_LOGenum and the Relay-target scoping (Section 6).load_packageincrates/registry-relay-v2/src/package.rsperforms the re-derivation, andprepareincrates/registry-relay-v2/src/startup.rsorders 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.rsasserts both probe bodies against a realrelayprocess and asserts that graceful shutdown completes within its deadline (Sections 2 and 7).
- Operate Relay is the operator task guide for the behavior this specification fixes.
- Configure Relay documents the runtime document whose bounds Section 5 states.
- RS-PR-RELAY defines the data protocol, including the routes this specification leaves out of scope.
- RS-SEC-G defines the security model these operational refusals serve.