HITL Override Protocol
The HITL (Human-in-the-Loop) override protocol allows credentialed operators to cryptographically authorize a single rejected evaluation to proceed — without modifying the deployment policy.
Overrideable Decisions
Section titled “Overrideable Decisions”Only two rejection types can be overridden:
| Decision | Reason code | When it fires |
|---|---|---|
REJECT_STATE | GAMMA_BELOW_FLOOR | Gamma fell below the configured floor |
REJECT_ACTION | ACTION_PREVIEW_UNSAFE | Action preview triggered a gate |
These are not overrideable:
| Decision | Why |
|---|---|
REJECT_BASIN_COLLAPSE | Loss events are non-negotiable safety boundaries |
REJECT_PARADOX | Dual-actor collisions require policy resolution, not overrides |
REJECT_LICENSE | Invalid license is a trust boundary violation |
ERROR | System errors require investigation, not bypass |
Two-Stage Protocol
Section titled “Two-Stage Protocol”The protocol separates verification into two independent stages:
Stage 1: Local Verification (Runtime)
Section titled “Stage 1: Local Verification (Runtime)”The Substrate runtime verifies the token’s cryptographic integrity and binding constraints before consulting the coordinator:
- Schema version check (
schemaVersion == 1) - Key ID lookup against policy authorities
- RSA-PSS signature verification
- Payload deserialization (strict,
deny_unknown_fields) - Timestamp validation (RFC 3339)
- Expiry check (with 30-second clock-skew tolerance)
- TTL within policy maximum (
expiresAt - issuedAt <= maxTokenTtlMs) - Policy version match
- License ID match
- Actor ID match (required in multi-actor sessions)
- Operator ID match
- Request hash match (canonical SHA-256)
Any failure preserves the original rejection (fail-closed).
Stage 2: Coordinator Redemption
Section titled “Stage 2: Coordinator Redemption”If local verification passes and a redemption client is configured, the runtime calls the coordinator’s /v1/override-tokens/redeem endpoint for atomic single-use enforcement:
Runtime ──verify──► Local checks pass │ ├──redeem──► Coordinator │ ├── ACCEPTED → override applied │ ├── REPLAY_DETECTED → original rejection preserved │ └── error → original rejection preserved │ └── (no client configured) → override applied after local checks onlyToken Structure
Section titled “Token Structure”Override tokens use a two-layer envelope: an outer wrapper with the signature, and an inner payload with binding claims.
Outer Envelope
Section titled “Outer Envelope”{ "schemaVersion": 1, "keyId": "operator-1", "payload": "{...JSON string...}", "signature": "base64url-no-padding"}| Field | Description |
|---|---|
schemaVersion | Always 1 — future-proofing for envelope format changes |
keyId | Identifies which authority key signed the token |
payload | Serialized JSON string of the inner payload |
signature | RSA-PSS signature over the payload bytes, base64url-encoded without padding |
Inner Payload
Section titled “Inner Payload”{ "tokenId": "550e8400-e29b-41d4-a716-446655440000", "operatorId": "alice", "requestHash": "1c712b22c149a35a740398a70c02b99b3831931a452efb9d1bf8eec59b8caea2", "policyVersion": 1, "licenseId": "lic_abc123", "actorId": "agent-1", "issuedAt": "2026-03-21T12:00:00Z", "expiresAt": "2026-03-21T12:05:00Z", "justification": "Authorized by ops lead after manual review"}| Field | Description |
|---|---|
tokenId | UUID v4 — unique identifier for replay detection |
operatorId | Human operator who authorized the override |
requestHash | SHA-256 of the canonical evaluation request (binding mechanism) |
policyVersion | Must match the active deployment policy version |
licenseId | Must match the runtime’s license |
actorId | Target actor — required when session has multiple agents |
issuedAt | Token creation timestamp (RFC 3339) |
expiresAt | Token expiry — must be within maxTokenTtlMs of issuedAt |
justification | Optional free-text note from the operator |
All fields use strict deserialization (deny_unknown_fields) — unknown fields cause rejection.
RSA-PSS Signing
Section titled “RSA-PSS Signing”| Property | Value |
|---|---|
| Algorithm | RSA-PSS |
| Hash | SHA-256 |
| Key format | PKCS#8 PEM (private and public) |
| Signature encoding | Base64url without padding (URL_SAFE_NO_PAD) |
| Signature input | UTF-8 bytes of the JSON payload string |
The signing infrastructure reuses the kairos-license RSA verification stack, ensuring a single cryptographic implementation across the system.
Canonical Request Hashing
Section titled “Canonical Request Hashing”The requestHash binds a token to a specific evaluation request. The hash is computed over a deterministic canonical form:
Included Fields (Allow-List)
Section titled “Included Fields (Allow-List)”| Field path | Purpose |
|---|---|
envelopeVersion | Protocol version |
requestId | Request identity |
snapshot.timestamp | Observation time |
snapshot.signature | Metric signature |
snapshot.metrics | All metrics (sorted by key via BTreeMap) |
action.type | Action type |
action.target | Action target |
action.payload | Action payload (deep-sorted recursively) |
actorId | Target actor |
Excluded Fields
Section titled “Excluded Fields”The overrideToken field is excluded from the hash — it cannot be included in the hash that it must itself contain (chicken-and-egg).
Algorithm
Section titled “Algorithm”- Collect fields into a canonical structure with
camelCaseserialization - Sort metrics by key (BTreeMap provides deterministic ordering)
- Deep-sort action payload keys recursively
- Serialize to compact JSON (no pretty-printing)
- Compute SHA-256 over the UTF-8 bytes
- Encode as lowercase hexadecimal
The hash is stable regardless of metric insertion order or payload key ordering in the original request.
Replay Protection
Section titled “Replay Protection”Three independent layers prevent token reuse:
Layer 1: Request Binding (Primary)
Section titled “Layer 1: Request Binding (Primary)”The requestHash ties the token to the exact evaluation request. A different request produces a different hash, resulting in RequestHashMismatch.
Layer 2: Time Binding (Primary)
Section titled “Layer 2: Time Binding (Primary)”The expiresAt timestamp enforces a hard TTL. The coordinator sets expiresAt = now + min(requestedTtl, maxTokenTtlMs). Expired tokens return TokenExpired (with 30-second clock-skew tolerance).
Layer 3: Coordinator Redemption (Authoritative)
Section titled “Layer 3: Coordinator Redemption (Authoritative)”The coordinator’s redeem endpoint performs an atomic SQL update:
UPDATE issued_tokensSET redeemed_at = ?WHERE token_id = ? AND redeemed_at IS NULLIf rows_affected == 0, the token was already redeemed → REPLAY_DETECTED.
Layer 4: In-Session Guard (Best-Effort)
Section titled “Layer 4: In-Session Guard (Best-Effort)”The SubstrateSession maintains an in-memory set of redeemed token IDs. This is best-effort:
- Does not survive process restarts
- Not the primary defense — the coordinator is authoritative
- Prunes expired entries before each check
Override Outcome
Section titled “Override Outcome”When an override token is present in the evaluation request, the response includes an overrideOutcome field.
Applied
Section titled “Applied”{ "overrideOutcome": { "status": "Applied", "keyId": "operator-1", "tokenId": "550e8400-e29b-41d4-a716-446655440000", "operatorId": "alice", "expiresAt": "2026-03-21T12:05:00Z", "failureReason": null, "originalDecision": "REJECT_STATE", "originalReasonCode": "GAMMA_BELOW_FLOOR" }}When applied: decision becomes PASS, reasonCode becomes NONE, and escalation is cleared.
Rejected
Section titled “Rejected”{ "overrideOutcome": { "status": "Rejected", "keyId": "operator-1", "tokenId": null, "operatorId": null, "failureReason": "RequestHashMismatch", "originalDecision": "REJECT_STATE", "originalReasonCode": "GAMMA_BELOW_FLOOR" }}The original decision and reason code are preserved.
Failure Reasons
Section titled “Failure Reasons”| Reason | Cause |
|---|---|
SchemaVersionUnsupported | schemaVersion is not 1 |
UnknownKeyId | keyId not found in policy authorities |
InvalidSignature | RSA-PSS signature verification failed |
MalformedPayload | Payload JSON parse error or invalid timestamps |
TokenExpired | Current time past expiresAt (with 30s tolerance) |
TokenTtlExceeded | TTL exceeds maxTokenTtlMs from policy |
PolicyVersionMismatch | Token’s policyVersion doesn’t match runtime |
LicenseMismatch | Token’s licenseId doesn’t match runtime |
ActorMismatch | Token’s actorId doesn’t match request |
OperatorMismatch | Token’s operatorId doesn’t match authority |
RequestHashMismatch | Canonical hash doesn’t match token’s requestHash |
ReplayDetected | Token already redeemed (coordinator or in-session) |
CoordinatorUnavailable | Coordinator unreachable (5-second timeout) |
Design Principles
Section titled “Design Principles”Fail-closed: Every verification failure preserves the original rejection. An operator can never accidentally bypass a gate — only explicit, cryptographically valid authorization succeeds.
Request-bound: Tokens are tied to a specific evaluation request by hash. Approving one request does not authorize any other.
Time-bound: Tokens expire. The coordinator enforces maxTokenTtlMs from the deployment policy.
Single-use: The coordinator’s atomic redemption ensures each token is used exactly once.
Non-escalating: Override tokens cannot override basin collapse or paradox rejections. These safety boundaries are absolute.