Skip to content

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.

Only two rejection types can be overridden:

DecisionReason codeWhen it fires
REJECT_STATEGAMMA_BELOW_FLOORGamma fell below the configured floor
REJECT_ACTIONACTION_PREVIEW_UNSAFEAction preview triggered a gate

These are not overrideable:

DecisionWhy
REJECT_BASIN_COLLAPSELoss events are non-negotiable safety boundaries
REJECT_PARADOXDual-actor collisions require policy resolution, not overrides
REJECT_LICENSEInvalid license is a trust boundary violation
ERRORSystem errors require investigation, not bypass

The protocol separates verification into two independent stages:

The Substrate runtime verifies the token’s cryptographic integrity and binding constraints before consulting the coordinator:

  1. Schema version check (schemaVersion == 1)
  2. Key ID lookup against policy authorities
  3. RSA-PSS signature verification
  4. Payload deserialization (strict, deny_unknown_fields)
  5. Timestamp validation (RFC 3339)
  6. Expiry check (with 30-second clock-skew tolerance)
  7. TTL within policy maximum (expiresAt - issuedAt <= maxTokenTtlMs)
  8. Policy version match
  9. License ID match
  10. Actor ID match (required in multi-actor sessions)
  11. Operator ID match
  12. Request hash match (canonical SHA-256)

Any failure preserves the original rejection (fail-closed).

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 only

Override tokens use a two-layer envelope: an outer wrapper with the signature, and an inner payload with binding claims.

{
"schemaVersion": 1,
"keyId": "operator-1",
"payload": "{...JSON string...}",
"signature": "base64url-no-padding"
}
FieldDescription
schemaVersionAlways 1 — future-proofing for envelope format changes
keyIdIdentifies which authority key signed the token
payloadSerialized JSON string of the inner payload
signatureRSA-PSS signature over the payload bytes, base64url-encoded without padding
{
"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"
}
FieldDescription
tokenIdUUID v4 — unique identifier for replay detection
operatorIdHuman operator who authorized the override
requestHashSHA-256 of the canonical evaluation request (binding mechanism)
policyVersionMust match the active deployment policy version
licenseIdMust match the runtime’s license
actorIdTarget actor — required when session has multiple agents
issuedAtToken creation timestamp (RFC 3339)
expiresAtToken expiry — must be within maxTokenTtlMs of issuedAt
justificationOptional free-text note from the operator

All fields use strict deserialization (deny_unknown_fields) — unknown fields cause rejection.

PropertyValue
AlgorithmRSA-PSS
HashSHA-256
Key formatPKCS#8 PEM (private and public)
Signature encodingBase64url without padding (URL_SAFE_NO_PAD)
Signature inputUTF-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.

The requestHash binds a token to a specific evaluation request. The hash is computed over a deterministic canonical form:

Field pathPurpose
envelopeVersionProtocol version
requestIdRequest identity
snapshot.timestampObservation time
snapshot.signatureMetric signature
snapshot.metricsAll metrics (sorted by key via BTreeMap)
action.typeAction type
action.targetAction target
action.payloadAction payload (deep-sorted recursively)
actorIdTarget actor

The overrideToken field is excluded from the hash — it cannot be included in the hash that it must itself contain (chicken-and-egg).

  1. Collect fields into a canonical structure with camelCase serialization
  2. Sort metrics by key (BTreeMap provides deterministic ordering)
  3. Deep-sort action payload keys recursively
  4. Serialize to compact JSON (no pretty-printing)
  5. Compute SHA-256 over the UTF-8 bytes
  6. Encode as lowercase hexadecimal

The hash is stable regardless of metric insertion order or payload key ordering in the original request.

Three independent layers prevent token reuse:

The requestHash ties the token to the exact evaluation request. A different request produces a different hash, resulting in RequestHashMismatch.

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_tokens
SET redeemed_at = ?
WHERE token_id = ? AND redeemed_at IS NULL

If rows_affected == 0, the token was already redeemed → REPLAY_DETECTED.

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

When an override token is present in the evaluation request, the response includes an overrideOutcome field.

{
"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.

{
"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.

ReasonCause
SchemaVersionUnsupportedschemaVersion is not 1
UnknownKeyIdkeyId not found in policy authorities
InvalidSignatureRSA-PSS signature verification failed
MalformedPayloadPayload JSON parse error or invalid timestamps
TokenExpiredCurrent time past expiresAt (with 30s tolerance)
TokenTtlExceededTTL exceeds maxTokenTtlMs from policy
PolicyVersionMismatchToken’s policyVersion doesn’t match runtime
LicenseMismatchToken’s licenseId doesn’t match runtime
ActorMismatchToken’s actorId doesn’t match request
OperatorMismatchToken’s operatorId doesn’t match authority
RequestHashMismatchCanonical hash doesn’t match token’s requestHash
ReplayDetectedToken already redeemed (coordinator or in-session)
CoordinatorUnavailableCoordinator unreachable (5-second timeout)

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.