Module opencode_session
Expand description
x-opencode-session — OpenCode relay session-affinity header.
The OpenCode relay (opencode.ai, serving both the Zen and Go endpoints)
uses x-opencode-session to pin the requests of one conversation to the
same upstream backend, which is what keeps that backend’s prompt cache warm
across the turns of a conversation. Upstream documents the header under its
guidance for keeping an account from being flagged, and at least one Go
model (deepseek-v4-flash) rejects header-less requests outright with
HTTP 400 Model is unavailable.
Every OpenCode inference request, on both wires and on both streaming and
non-streaming paths, resolves its value through session_token so the
header cannot drift per code path. Model-catalog and warmup requests
(GET /models) do not carry it: they are not a conversation turn, so there
is no backend to pin.
§Value derivation
Upstream specifies no format, length, or lifetime for the value; its only
normative statement is that a tool “includes the x-opencode-session
header so we can optimize prompt caching”. The one datum on accepted values
is that a random UUID is sufficient to turn the 400 above into a 200. This
module therefore emits a 128-bit lowercase hex digest: opaque, fixed-length,
unambiguously safe as an HTTP header value, and non-reversible.
The digest is taken over the ambient conversation scope
(zeroclaw_api::TOOL_LOOP_SESSION_KEY) rather than over a cached random
token, so the value is resolved from the canonical source at use time and
this module holds no per-session lookup table.
§Why the scope is hashed rather than sent
Session keys embed channel and user identifiers — sanitize_session_key
covers inputs shaped like whatsapp_123@g.us_alice and
slack_C123_1.2_user one. Forwarding one verbatim would hand a third-party
relay a cross-service-linkable per-user identifier, which the privacy
contract in docs/book/src/contributing/privacy.md forbids. Hashing keeps
the affinity behavior while sending nothing but an opaque token.
The digest is domain-separated but unsalted, so it is deterministic across restarts — which is what preserves cache affinity for a conversation that spans a daemon restart. The tradeoff is explicit: this prevents plaintext exposure of channel and user identifiers, but it is not a defense against a party who knows ZeroClaw’s session-key format brute-forcing a low-entropy key space. Defeating that would require a per-install salt, which would cost the cross-restart affinity this is for; it is deliberately out of scope here.
Constants§
- OPENCODE_
SESSION_ HEADER - The affinity header OpenCode reads to pin a conversation to one backend.
Functions§
- is_
opencode_ target - True when
base_urladdresses the OpenCode relay. - operator_
pinned_ session - True when
extra_headerspins anx-opencode-sessionvalue that will actually reach the wire. - redirect_
policy - Redirect policy for clients whose requests may carry the session header.
- restrict_
redirects - Apply
redirect_policytobuilderwhenendpointis an OpenCode target. Every other provider keeps reqwest’s default redirect handling. - session_
token - Affinity token for the calling conversation, or
Nonewhenbase_urlis not an OpenCode target.