headers

package
v0.43.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: Apache-2.0, MIT Imports: 8 Imported by: 0

Documentation

Overview

Package headers names the HTTP / ext_proc header values that tapes-extproc reads from the upstream traffic Envoy hands it.

extproc is a passive ext_proc observer — it doesn't issue outbound HTTP requests, so it has no header-copying rules to encode (unlike the tapes proxy's proxy/header package, which manages client→proxy→upstream header forwarding in both directions). What extproc does have is a small set of header names that the state-machine reads at different phases of every turn, plus the rationale for each. Centralizing both here keeps string literals out of processor.go and gives any future header-shaped behavior change a single named site to land at instead of three scattered reads.

Index

Constants

View Source
const (
	Status = ":status"
	Method = ":method"
	Path   = ":path"
)

HTTP/2 pseudo-headers. Per RFC 9113 §8.3 these are lowercase on the wire; Envoy preserves that case when serializing into the ext_proc HeaderValue list. Get() is case-insensitive so the constants here are written in the form that minimizes surprise when grep'd alongside tcpdump or Envoy access logs.

View Source
const (
	// ContentType drives the reducer's reduceStream vs. reduceOneShot
	// dispatch — text/event-stream takes the SSE path, anything else
	// (typically application/json) takes the one-shot JSON path.
	ContentType = "content-type"

	// ContentEncoding gates request- and response-body decompression.
	// Reducers parse textual JSON / SSE, so any non-identity encoding
	// must be undone before captured bytes reach parsing or ingest.
	// If a new encoding shows up, this is the single read site to audit.
	ContentEncoding = "content-encoding"

	// ContentLength feeds pre-boundary request-size observation.
	// Envoy always delivers request headers, even when the body is
	// later rejected at the gRPC recv limit — so the declared length
	// read here at the headers phase is the only size signal that
	// covers requests too large to ever reach a body-phase callback.
	ContentLength = "content-length"
)

Standard request/response headers that extproc reads.

View Source
const (
	// AgentName tags the captured turn with the caller's agent
	// identity (Claude Code, opencode, etc.). Optional; tapes-ingest
	// stores it on the TurnEnvelope but does not gate on it.
	AgentName = "x-tapes-agent-name"

	// AIGSelectedBackend is the Envoy AI Gateway's per-request
	// indication of which AIServiceBackend it selected. extproc
	// prefers it for provider resolution because it survives path
	// rewrites and unusual routing configurations.
	AIGSelectedBackend = "x-ai-eg-selected-backend"

	// RequestID is the per-request correlation handle Envoy and
	// downstream logs use. extproc echoes it on every drop / accept
	// log line so an operator can join across components.
	RequestID = "x-request-id"
)

Extension headers (x-*) used by adjacent components.

View Source
const (
	// TapesEnvelopePrefix is the common prefix for every session
	// envelope header. envelope-stripping uses prefix matching so
	// new optional members (forward compatibility) drop out of the
	// upstream request automatically.
	TapesEnvelopePrefix = "x-tapes-"

	// TapesHarnessID identifies the harness flavor. Missing or empty
	// is parsed as harnessIDUnknown.
	TapesHarnessID = "x-tapes-harness-id"

	// TapesHarnessSessionID is the harness's session id (a UUID for
	// claude).
	TapesHarnessSessionID = "x-tapes-harness-session-id"

	// TapesHarnessVersion is opaque (e.g. claude version string).
	TapesHarnessVersion = "x-tapes-harness-version"

	// TapesCwd is the harness's working directory. UTF-8 path.
	TapesCwd = "x-tapes-cwd"

	// TapesSessionName is the user-given session label. Percent-
	// encoded UTF-8 on the wire (RFC 3986).
	TapesSessionName = "x-tapes-session-name"

	// TapesParentHarnessSessionID is the fork parent's harness session
	// id, when known. Same id-space as TapesHarnessSessionID.
	TapesParentHarnessSessionID = "x-tapes-parent-harness-session-id"

	// TapesHarnessMetadata is a base64url-encoded JSON object with
	// harness-specific metadata. The parser accepts whatever arrived
	// (size-wise) and surfaces malformed payloads as a non-fatal
	// drop of the field.
	TapesHarnessMetadata = "x-tapes-harness-metadata"
)

Session-tracking envelope headers. Lower-cased to match how Envoy hands them to ext_proc (HTTP/2 normalizes header names to lowercase on the wire). All members of this set are stripped from the request before it reaches the upstream LLM provider via the prefix-based match on TapesEnvelopePrefix.

View Source
const (
	// CodexSessionID is the Codex harness's root session id, present
	// on every Codex call.
	CodexSessionID = "session-id"

	// CodexThreadID is the Codex harness's thread id for this call:
	// equal to session-id on root turns, a distinct id on sub-thread
	// (spawned agent) turns.
	CodexThreadID = "thread-id"
)

Codex's native identity headers. Unlike Claude Code, Codex stamps thread-id on EVERY call — root turns carry thread-id == session-id, and only spawned sub-thread (child) turns carry a distinct thread-id. So presence alone doesn't mean "subagent"; the root guard in ThreadID compares the pair and resolves root turns to "". Getting this wrong is not cosmetic: a non-empty thread_id on a root turn misroutes the root spine into tapes derive's threadCall path and silently degrades the session's derived status (terminalMainSpan requires ThreadID=="").

Canonical home: tapes-harnesses src/envelope.rs (CODEX_THREAD_ID_HEADER / CODEX_SESSION_ID_HEADER); the lifecycle counterpart of the same identities is src/attribution/codex_app (session_id = the root session, agent_id = the child thread). The shared corpus at fixtures/thread/ pins the spellings and the pair rule cross-language (thread_corpus_test.go).

View Source
const (
	// PaperAuthOrgID carries the `org_id` JWT claim (the WorkOS token
	// names it org_id, not org).
	PaperAuthOrgID = "x-paper-auth-org-id"

	// PaperAuthSubject carries the `sub` JWT claim — the WorkOS user id,
	// persisted as the captured session's auth_subject.
	PaperAuthSubject = "x-paper-auth-subject"
)

Server-trusted identity headers, populated by the upstream gateway from validated JWT claims (Envoy's claim_to_headers feature). They are NOT part of the X-Tapes-* envelope and must not be sent by clients — that constraint is the gateway's responsibility to enforce. If the gateway is not configured to populate them, the fields arrive empty and propagate through to the dispatched session envelope as empty strings.

Variables

This section is empty.

Functions

func EnvelopeHeaderKeysFromRequest

func EnvelopeHeaderKeysFromRequest(hdrs *extprocv3.HttpHeaders) []string

EnvelopeHeaderKeysFromRequest scans hdrs for every header whose name starts with TapesEnvelopePrefix and returns the original keys (the case-as-received form). The processor passes the returned slice to Envoy as a HeaderMutation.remove_headers list so the entire envelope is stripped before the request reaches the upstream provider, including any forward-compatibility x-tapes-* additions that may arrive in the future.

Returned keys preserve the case Envoy delivered to ext_proc. Header removal in Envoy is case-insensitive, but echoing the as-received form makes operator-side packet captures grep cleanly when diagnosing why a header did or didn't get stripped.

func Get

func Get(hdrs *extprocv3.HttpHeaders, name string) string

Get returns the value of the named header from HttpHeaders, case-insensitive. Returns "" when not present. Prefers RawValue (the bytes Envoy received over the wire) and falls back to Value for older Envoy versions that didn't populate RawValue.

func StatusCode

func StatusCode(hdrs *extprocv3.HttpHeaders) int

StatusCode returns the parsed :status pseudo-header as an int. Returns 0 when the header is missing or unparseable — the caller is expected to treat 0 as "status unknown" and drop the turn rather than implying a 200.

func ThreadID

func ThreadID(hdrs *extprocv3.HttpHeaders) string

ThreadID resolves the harness-native sub-thread id for this request, or "" for a main-thread call (or a harness with no known mapping).

Types

type SessionEnvelope

type SessionEnvelope struct {
	Present bool

	HarnessID                string
	HarnessSessionID         string
	HarnessVersion           string
	Cwd                      string
	Name                     string
	ParentHarnessSessionID   string
	HarnessMetadata          map[string]any
	HarnessMetadataMalformed bool
}

SessionEnvelope is the parsed view of the X-Tapes-* headers on the inbound request. Field names match the JSON-on-the-wire names so the processor can hand the struct straight to the dispatcher with no further translation.

Present == true means at least one X-Tapes-* header was observed on the inbound request, including the case where only HarnessID was present. Present == false means no X-Tapes-* header arrived; the dispatcher must omit the entire session block from the envelope POST in that case.

HarnessID is always populated when Present == true: missing or empty harness-id is treated as "unknown" rather than rejected, so the parser substitutes "unknown" when the header is absent.

func ParseSessionEnvelope

func ParseSessionEnvelope(hdrs *extprocv3.HttpHeaders) SessionEnvelope

ParseSessionEnvelope reads every session-envelope header from hdrs. Returns a zero SessionEnvelope (Present=false) when no X-Tapes-* header is observed at all.

Presence detection uses the open TapesEnvelopePrefix (any `x-tapes-*` header flips Present=true), mirroring the strip path in EnvelopeHeaderKeysFromRequest. This keeps dispatch and strip symmetric: a future forward-compat envelope header (e.g. `x-tapes-trace-id`) gets both stripped AND dispatched, instead of stripped-but-silently-dropped from the ingest POST.

The pre-existing X-Tapes-Agent-Name header (see AgentName in headers.go) shares the x-tapes-* prefix and therefore also flips Present=true.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL