344 lines
8.5 KiB
JSON
344 lines
8.5 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://hermes.bstein.dev/contracts/hux/v1/common.schema.json",
|
|
"title": "HUX shared definitions",
|
|
"description": "Cross-surface primitives reused by every HUX contract. Identity, time, provenance, sensitivity and evidence are defined once here so Chat, Worker, Telegram, voice and future clients agree on them.",
|
|
"$defs": {
|
|
"id": {
|
|
"type": "string",
|
|
"description": "Opaque, prefix-typed identifier. Prefix names the record kind (evt, mem, prj, conv, art, pol, apr, rcpt, src, psg, cit, nb, sug, rel).",
|
|
"pattern": "^[a-z]{2,6}_[A-Za-z0-9._-]{4,80}$"
|
|
},
|
|
"user_ref": {
|
|
"type": "string",
|
|
"description": "Hashed Keycloak subject as already used by the chat router. Never a raw subject, email, or Telegram id.",
|
|
"pattern": "^usr_[0-9a-f]{16,64}$"
|
|
},
|
|
"timestamp": {
|
|
"type": "string",
|
|
"description": "RFC 3339 UTC timestamp with a trailing Z.",
|
|
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,6})?Z$"
|
|
},
|
|
"sha256": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
},
|
|
"surface": {
|
|
"type": "string",
|
|
"enum": [
|
|
"chat",
|
|
"worker",
|
|
"telegram",
|
|
"voice",
|
|
"api"
|
|
]
|
|
},
|
|
"provider": {
|
|
"type": "string",
|
|
"description": "Provider class, never a vendor model name. Switchyard resolves the class to a concrete target.",
|
|
"enum": [
|
|
"codex",
|
|
"claude",
|
|
"local"
|
|
]
|
|
},
|
|
"effort": {
|
|
"type": "string",
|
|
"enum": [
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh"
|
|
]
|
|
},
|
|
"sensitivity": {
|
|
"type": "string",
|
|
"description": "public: safe to show anywhere; personal: user-owned but not sensitive; sensitive: health/finance/legal/relationships; restricted: credentials, minors, biometric, location traces.",
|
|
"enum": [
|
|
"public",
|
|
"personal",
|
|
"sensitive",
|
|
"restricted"
|
|
]
|
|
},
|
|
"redaction": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"level"
|
|
],
|
|
"properties": {
|
|
"level": {
|
|
"type": "string",
|
|
"enum": [
|
|
"none",
|
|
"partial",
|
|
"full"
|
|
]
|
|
},
|
|
"reason": {
|
|
"type": "string",
|
|
"maxLength": 200
|
|
}
|
|
}
|
|
},
|
|
"actor": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"type",
|
|
"id"
|
|
],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"enum": [
|
|
"user",
|
|
"assistant",
|
|
"tool",
|
|
"system",
|
|
"operator"
|
|
]
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 120
|
|
},
|
|
"display": {
|
|
"type": "string",
|
|
"maxLength": 120
|
|
}
|
|
}
|
|
},
|
|
"route": {
|
|
"type": "object",
|
|
"description": "What was asked of Switchyard and what it resolved. requested is a friendly mode or a route id; resolved_target is the Switchyard target name.",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"requested"
|
|
],
|
|
"properties": {
|
|
"requested": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 120
|
|
},
|
|
"resolved_target": {
|
|
"type": "string",
|
|
"maxLength": 120
|
|
},
|
|
"provider": {
|
|
"$ref": "#/$defs/provider"
|
|
},
|
|
"effort": {
|
|
"$ref": "#/$defs/effort"
|
|
}
|
|
}
|
|
},
|
|
"build": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"commit": {
|
|
"type": "string",
|
|
"pattern": "^[0-9a-f]{40}$"
|
|
},
|
|
"image_digest": {
|
|
"$ref": "#/$defs/sha256"
|
|
}
|
|
}
|
|
},
|
|
"provenance": {
|
|
"type": "object",
|
|
"description": "Who produced a record, on which surface, under which session/run, through which route, from which build.",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"surface",
|
|
"actor",
|
|
"recorded_at"
|
|
],
|
|
"properties": {
|
|
"surface": {
|
|
"$ref": "#/$defs/surface"
|
|
},
|
|
"actor": {
|
|
"$ref": "#/$defs/actor"
|
|
},
|
|
"recorded_at": {
|
|
"$ref": "#/$defs/timestamp"
|
|
},
|
|
"session_id": {
|
|
"type": "string",
|
|
"maxLength": 120
|
|
},
|
|
"conversation_id": {
|
|
"$ref": "#/$defs/id"
|
|
},
|
|
"message_id": {
|
|
"type": "string",
|
|
"maxLength": 120
|
|
},
|
|
"run_id": {
|
|
"type": "string",
|
|
"maxLength": 120
|
|
},
|
|
"route": {
|
|
"$ref": "#/$defs/route"
|
|
},
|
|
"build": {
|
|
"$ref": "#/$defs/build"
|
|
}
|
|
}
|
|
},
|
|
"evidence_ref": {
|
|
"type": "object",
|
|
"description": "Pointer to the thing that justifies a record. Never inline the payload here; the UI expands it through the owning API.",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"kind",
|
|
"id"
|
|
],
|
|
"properties": {
|
|
"kind": {
|
|
"type": "string",
|
|
"enum": [
|
|
"message",
|
|
"tool_call",
|
|
"tool_result",
|
|
"artifact_version",
|
|
"source",
|
|
"passage",
|
|
"memory",
|
|
"approval",
|
|
"run",
|
|
"url",
|
|
"file",
|
|
"build",
|
|
"flux",
|
|
"pod",
|
|
"receipt"
|
|
]
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 200
|
|
},
|
|
"uri": {
|
|
"type": "string",
|
|
"maxLength": 2000
|
|
},
|
|
"hash": {
|
|
"$ref": "#/$defs/sha256"
|
|
}
|
|
}
|
|
},
|
|
"tags": {
|
|
"type": "array",
|
|
"maxItems": 32,
|
|
"uniqueItems": true,
|
|
"items": {
|
|
"type": "string",
|
|
"pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
|
|
}
|
|
},
|
|
"tenant_slot": {
|
|
"type": "string",
|
|
"description": "Router-assigned tenant process slot.",
|
|
"pattern": "^slot-[0-9]{1,3}$"
|
|
},
|
|
"trust": {
|
|
"type": "string",
|
|
"description": "How identity was asserted: router, relay, worker, or the loopback-only release evidence producer.",
|
|
"enum": [
|
|
"router",
|
|
"relay",
|
|
"worker",
|
|
"evidence"
|
|
]
|
|
},
|
|
"identity": {
|
|
"type": "object",
|
|
"description": "Resolved caller identity. Every record path and authorization check derives from tenant_slot + subject.",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"tenant_slot",
|
|
"subject",
|
|
"surface",
|
|
"trust"
|
|
],
|
|
"properties": {
|
|
"tenant_slot": {
|
|
"$ref": "#/$defs/tenant_slot"
|
|
},
|
|
"subject": {
|
|
"$ref": "#/$defs/user_ref"
|
|
},
|
|
"surface": {
|
|
"$ref": "#/$defs/surface"
|
|
},
|
|
"trust": {
|
|
"$ref": "#/$defs/trust"
|
|
}
|
|
}
|
|
},
|
|
"revision": {
|
|
"type": "integer",
|
|
"description": "Optimistic concurrency counter. Mutations send If-Match: <revision>; a mismatch is 409.",
|
|
"minimum": 1
|
|
},
|
|
"idempotency_key": {
|
|
"type": "string",
|
|
"description": "Client-chosen key; a repeat with the same key returns the original record instead of creating a duplicate.",
|
|
"pattern": "^[A-Za-z0-9._:-]{8,120}$"
|
|
},
|
|
"contract_version": {
|
|
"type": "string",
|
|
"pattern": "^1\\.[0-9]+\\.[0-9]+$"
|
|
},
|
|
"audit_outcome": {
|
|
"type": "object",
|
|
"description": "Result of an authorization decision, written for every read and mutation.",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"at",
|
|
"identity",
|
|
"action",
|
|
"resource",
|
|
"outcome"
|
|
],
|
|
"properties": {
|
|
"at": {
|
|
"$ref": "#/$defs/timestamp"
|
|
},
|
|
"identity": {
|
|
"$ref": "#/$defs/identity"
|
|
},
|
|
"action": {
|
|
"type": "string",
|
|
"pattern": "^[a-z_]+\\.[a-z_]+$"
|
|
},
|
|
"resource": {
|
|
"type": "string",
|
|
"maxLength": 200
|
|
},
|
|
"outcome": {
|
|
"type": "string",
|
|
"enum": [
|
|
"allow",
|
|
"deny",
|
|
"not_found",
|
|
"conflict",
|
|
"flag_off"
|
|
]
|
|
},
|
|
"reason": {
|
|
"type": "string",
|
|
"maxLength": 200
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|