Specify candidate signed time samples and trust lifecycle

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbc-910e-77a0-b336-3130194350b3
This commit is contained in:
tegwick 2026-09-14 15:13:44 +02:00
parent 8f2d17ae06
commit 7af595b2cc
7 changed files with 299 additions and 0 deletions

View file

@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "protected-header-v0.1 review candidate v0.1",
"type": "object",
"additionalProperties": false,
"required": [
"alg",
"kid",
"typ"
],
"properties": {
"alg": {
"const": "ES256"
},
"kid": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"typ": {
"const": "railiance-clock-sample-v0.1"
}
}
}

View file

@ -198,3 +198,6 @@ dependencies, not placeholders to bypass while shipping.
- Authenticated NTP option for infrastructure review (not v0.1 wire protocol): https://www.rfc-editor.org/rfc/rfc8915
- Corporate domain time: https://learn.microsoft.com/en-us/windows-server/networking/windows-time-service/how-the-windows-time-service-works
- Internal ownership links: ../docs/ecosystem.md.
Detailed wire/trust review candidate: [sample profile](sample-profile-v0.1.md).
This supplies proposed field/encoding choices; owner admission remains RCLK-WP-0002.

View file

@ -0,0 +1,148 @@
# Time sample profile — review candidate v0.1
Status: proposed under RCLK-WP-0002-T02/T03, 2026-09-14. No trust owner has
adopted this profile. It refines the base specification for review; conflicting
or unresolved requirements prevent implementation admission.
## Transport and signature candidate
HTTPS POST `/v1/time-samples` accepts the request schema, at most 1 KiB of UTF-8
JSON, and returns at most 8 KiB of ASCII JWS Compact Serialization with
`Content-Type: application/jose`. No redirects, compression or detached payloads.
Set `Cache-Control: no-store` on responses. HTTP failure, wrong media type,
oversize or an unsigned error never establishes time. No bearer credential is
needed by this candidate sample request; endpoint access/exposure remains an
owner decision. TLS validation must already work through an admitted path.
Choose JWS Compact Serialization as the candidate envelope: its protected header
and exact payload bytes are covered by the signature. Verify the original encoded
segments; never parse and reserialize before signature verification.
[Envelope standard: RFC 7515](https://www.rfc-editor.org/rfc/rfc7515).
Candidate algorithm is `ES256`, restricted to P-256 keys and SHA-256, with the
JWS 64-byte R||S signature encoding. Use a maintained JOSE/crypto library, never
handwritten ECDSA. Owner/library compatibility review is still required.
[Algorithm definition: RFC 7518 §3.4](https://www.rfc-editor.org/rfc/rfc7518#section-3.4).
Protected header contains exactly `alg`, `kid`, and
`typ: railiance-clock-sample-v0.1`. This typ is an application discriminator,
not a registered media type. Reject all other headers, including embedded keys,
remote key URLs, `crit`, and alternate payload encoding. No algorithm negotiation,
`none`, MAC profile, or token/key reuse. `kid` selects only within the locally
admitted authority/environment trust binding, never a global key search.
JSON is UTF-8 without BOM, duplicate member names, nonfinite numbers or trailing
content. Enforce these at decoding, before a JSON Schema validator loses lexical
information. Limit nesting to 4. Base64url segments are unpadded, canonical and
nonempty; decoding and re-encoding must match. Fixed signatures decode to 64 bytes.
The signature binds bytes, so JSON member ordering is not a security ambiguity;
no additional JSON canonicalization scheme is required. Unknown fields/versions
are refused rather than interpreted through a newer schema silently.
## Fields, units and correlation
See the adjacent request, sample and protected-header schemas. The sample schema
applies to the decoded authenticated payload, not to the JWS envelope.
- `profile` fixes wire semantics; `authority_id` and `environment` must equal
locally configured expectations and the outstanding request.
- `nonce` is 32 random bytes encoded as 43 base64url characters. The requester
generates it with a cryptographic RNG. Schema length alone cannot prove entropy
or canonical encoding; the decoder must also verify those requirements.
- `epoch` is an opaque continuity identifier. A UUID-shaped label is not a
monotonic generation, a freshness proof, or rollback authorization.
- `policy_id` names an immutable owner-adopted policy revision. Unknown revisions
are refused. The policy fixes leap handling, quality/error model, timer model,
trust freshness and local lifetime/RTT limits.
- `receive_unix_ns` and `send_unix_ns` are decimal strings of nonnegative signed
64-bit POSIX nanoseconds since 1970-01-01T00:00:00Z. `error_ns` is a nonnegative
bound covering server send-time error, including acquisition resolution and
health observation aging. `max_age_ns` is a positive lifetime ceiling measured
from client request send, not from eventual response receipt.
- `quality` is `bounded` for candidate usable samples. No unhealthy sample with
a fabricated numeric bound is permitted. Diagnostics are a separate response.
A signed `bounded` assertion still requires the locally admitted policy/model.
All decimal values must be <= 9223372036854775807, with no sign, exponent, decimal
point or leading zeros. Schema checks lexical shape only; implementations must
check numerical limits, send >= receive, and checked arithmetic separately.
There is no JWT `exp` evaluated against a misaligned local wall clock.
POSIX nanoseconds do not encode a distinct leap second. This profile therefore
cannot claim protected use during a leap ambiguity: the admitted policy must
specify detection, exclusion and reacquisition behavior before deployment.
Ordinary RFC3339 display is derived from the accepted interval; display precision
is not an accuracy claim. Local timezone display conversion is separate.
## Acceptance sequence and lifetime
1. Before sending, require admitted transport/key/policy context and a supported
clock domain. Store nonce, request fields and monotonic send instant. Bound
outstanding requests and local timeout; limits come from local policy.
2. At receipt, capture monotonic receive time, reject domain/suspend changes and
expired requests, enforce envelope/JSON limits, select the pinned key and verify
signature/type/algorithm. Do not trust payload values until verification.
3. Validate the authenticated payload schema and semantic limits; match nonce,
authority/environment/profile, policy and continuity. Atomically retire the
nonce before installing the mapping. Only one response wins. Failed/expired
exchanges retire their nonce and retry with a new one. Resource exhaustion
may deny service but must never result in accepting a weaker sample.
4. Obtain conservative real elapsed bounds [Rmin,Rmax] from the reviewed timer
model. Accept the server-send error bound e only under admitted health policy.
At receipt use L=s3-e and U=s3+e+Rmax, as in the base specification. Checked
arithmetic must reject underflow/overflow. Server processing is not subtracted.
5. Age limits count conservative elapsed time since request send, including transit,
verification and any later processing. The smallest local, signed, trust and
policy lifetime limit applies. A slow response cannot restart the age budget.
Require the upper bound on age to be strictly below the applicable limit.
6. At each read, propagate from the receipt anchor by reviewed elapsed bounds
[Dmin,Dmax]: [L+Dmin,U+Dmax]. Check interval width, age, trust, continuity and
lifecycle before returning usable. The API does not execute a protected action;
consumers still check validity and action binding at the side effect.
Arithmetic example, synthetic only: s3=1000000000 ns, e=2000000 ns and
Rmax=20000000 ns yield [998000000,1022000000]. A later elapsed interval
[9990000,10010000] yields [1007990000,1032010000]. These numbers establish no
production oscillator, RTT, uncertainty or freshness threshold.
No protected holdover is admitted initially. Repeated reads of a live mapping
still require a proven bounded timer/lifecycle model and its finite age policy.
The schema alone cannot prove any of these operational assumptions.
## Trust, epoch and restart rules
The trust owner must supply an authoritative binding of authority/environment,
key ID, algorithm/public key, policy revision and validity/revocation state.
A signed sample cannot supply its own trust admission. Protected verification
requires independently admitted current trust evidence; if its freshness/validity
cannot be established with the available bootstrap context, return untrusted.
No use of the sample being evaluated to bootstrap its own key validity.
Owner review must name the actual trust delivery/revocation mechanism and prove
its operation with wrong OS time. Until that exists, only isolated fixtures are
possible. HTTPS that cannot validate remains a bootstrap failure; never disable
certificate checks to obtain the first sample.
Rotation requires the new key binding to be admitted before use. Revoke all
cached mappings signed by a revoked key; an old nonce/signature or code rollback
cannot restore it. Require a fresh exchange after process restart or epoch change.
Persisted rollback floors must have owner-approved integrity/recovery semantics.
If a new interval is wholly older than a trustworthy prior lower bound, refuse;
an overlapping interval does not prove continuity. Missing/corrupted persistent
state, restored VM snapshots or unknown epoch changes require reviewed recovery,
not automatic floor reset. The concrete storage/recovery design is still open.
## Review evidence still required
T02: signing library/custody compatibility; native trust bootstrap/revocation and
rotation; authoritative key bindings; epoch/floor recovery; leap policy adoption.
T03: measured server error model; Linux/Windows/WSL timer and suspend detection;
verified age/width thresholds; delayed/asymmetric path and arithmetic tests.
T04: consumer-specific validity boundaries, atomic consume/check-to-use and audit
attribution, including wrong-principal/tenant negatives.
Schema fixtures test structural rejection only. They are unsigned, synthetic and
not a cryptographic, native transport, lifecycle or UTC-quality acceptance proof.
Future conformance must include duplicate JSON keys, noncanonical base64url,
wrong JWS key/algorithm/header, bad signature, replay, unknown policy, numerical
overflow, stale trust, delayed replies and suspend during verification.

View file

@ -0,0 +1,29 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "time-request-v0.1 review candidate v0.1",
"type": "object",
"additionalProperties": false,
"required": [
"profile",
"authority_id",
"environment",
"nonce"
],
"properties": {
"profile": {
"const": "railiance-clock-v0.1"
},
"authority_id": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"environment": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"nonce": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}(?![\\s\\S])"
}
}
}

View file

@ -0,0 +1,63 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "time-sample-v0.1 review candidate v0.1",
"type": "object",
"additionalProperties": false,
"required": [
"profile",
"authority_id",
"environment",
"nonce",
"epoch",
"policy_id",
"receive_unix_ns",
"send_unix_ns",
"error_ns",
"max_age_ns",
"quality"
],
"properties": {
"profile": {
"const": "railiance-clock-v0.1"
},
"authority_id": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"environment": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"nonce": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}(?![\\s\\S])"
},
"epoch": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"policy_id": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}(?![\\s\\S])"
},
"receive_unix_ns": {
"type": "string",
"pattern": "^(0|[1-9][0-9]{0,18})(?![\\s\\S])"
},
"send_unix_ns": {
"type": "string",
"pattern": "^(0|[1-9][0-9]{0,18})(?![\\s\\S])"
},
"error_ns": {
"type": "string",
"pattern": "^(0|[1-9][0-9]{0,18})(?![\\s\\S])"
},
"max_age_ns": {
"type": "string",
"pattern": "^[1-9][0-9]{0,18}(?![\\s\\S])"
},
"quality": {
"const": "bounded"
}
}
}

View file

@ -0,0 +1,26 @@
{
"request": {
"profile": "railiance-clock-v0.1",
"authority_id": "fixture-authority",
"environment": "isolated-test",
"nonce": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
},
"header": {
"alg": "ES256",
"kid": "fixture-key",
"typ": "railiance-clock-sample-v0.1"
},
"sample": {
"profile": "railiance-clock-v0.1",
"authority_id": "fixture-authority",
"environment": "isolated-test",
"nonce": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"epoch": "fixture-epoch",
"policy_id": "fixture-policy-1",
"receive_unix_ns": "999000000",
"send_unix_ns": "1000000000",
"error_ns": "2000000",
"max_age_ns": "100000000",
"quality": "bounded"
}
}

View file

@ -64,3 +64,10 @@ Review started 2026-09-14: `docs/identity-and-utc-review.md` pins the IAM/layer
source baseline, separates actors/workloads/permissions/Ansible artifacts, and
identifies source-selection, leap and error-bound gates. Owner assent and measured
policy remain pending; no review task is complete from the proposal alone.
Sample/trust candidate added: `specs/sample-profile-v0.1.md`, three closed
JSON schemas and unsigned fixtures. Schema validation: three positive fixtures
and twenty structural negatives; no crypto/lifecycle/UTC acceptance claimed.
Trust delivery, measured timer/error policy, leap and rollback recovery remain
owner review gates. Custodian response 03014307-20b6-47d3-81f4-7f12fcaed014
confirms discovery only and does not close admission tasks.