Expand description
Where a persisted AI request came from: the client that produced it, the inbound wire protocol it spoke, and how strongly the client is evidenced.
All three halves are closed enums so ai_requests.client_kind,
ai_requests.wire_protocol and ai_requests.client_attestation are
CHECK-constrained columns, never free text. This module is the normative
specification of client attribution; the hosted gateway documentation
restates it for operators.
§Evidence tiers
classify answers “which client sent this request?” by walking a ladder
of evidence, strongest first. The winning tier sets client_kind and
client_attestation; everything the wire carried is kept in
ClientEvidence so a classification can be audited or corrected later.
| tier | header / signal | trust |
|---|---|---|
host-token | bridge principal with x-systemprompt-client-attestation: host-token; the bridge verified a per-host HMAC token on its loopback and stamped x-systemprompt-client itself | cryptographic on the device, channel-bound to the bridge |
bridge-secret | bridge principal with x-systemprompt-client-attestation: bridge-secret; the caller presented the raw loopback secret, so the host is taken from the lower tiers | channel verified, host not |
declared | x-systemprompt-client: <kind> from any principal (or passed through on the secret path) | client-asserted, closed vocabulary |
native-marker | a structural marker of one harness in the body (NativeMarker): Claude Code’s billing entrypoint or metadata.user_id grammars, Codex turn metadata | structural, unforged in practice |
user-agent | the exact first product token of User-Agent (ua_product) | weakest tier kept |
none | nothing matched; client_kind is other | honest fallback |
internal and unknown are never produced from live gateway traffic:
internal pairs with server-side producers and unknown with rows written
before attribution existed.
A conflict between tiers (a host token naming opencode under a
claude-cli User-Agent) is never rejected: the strongest tier wins and the
evidence row records the rest. Two inputs are rejected with 400 instead:
an x-systemprompt-client value outside the vocabulary, and an
x-systemprompt-client-attestation header from anything but the bridge.
The one wire vocabulary is ClientKind::as_str. The bridge’s own host
ids (codex-cli) are baked into HMAC labels and rendered host configs, so
they stay bridge-internal and map through
ClientKind::from_bridge_host_id / ClientKind::bridge_host_id.
Copyright (c) systemprompt.io — Business Source License 1.1. See https://systemprompt.io for licensing details.
Structs§
- Classification
Input - Raw inputs to
classify.principal_is_bridgeis the only fact that comes from authentication rather than the request itself. - Classified
conflictingis set when a weaker signal named a different client than the winning tier; the caller logs it, nothing rejects it.- Client
Evidence - Which tier named
client_kind, and what the wire carried. - Request
Origin - Client, wire protocol and attestation of one request, carried together so a producer cannot record one without the others.
- Stainless
Headers - The
x-stainless-*headers every Stainless-generated SDK sends.
Enums§
- Classification
Rejection - A request the gateway refuses with
400. The evidence gathered before the rejection rides along so the rejected row still records what was sent. - Client
Attestation - How strongly the recorded client is evidenced; see the module head for
the ladder. Ordered strongest-first so
Ordmatches precedence. - Client
Kind - The client that produced an AI request.
- Inbound
Wire Protocol - The protocol a request arrived on.
Internalpairs withClientKind::Internal;Unknownpairs withClientKind::Unknown. - Native
Marker - A structural marker of one harness found in the request body.
- Origin
Parse Error