Docs for SCORPIOX CODE @ 77c49df

Session Identity Headers

One config key stamps every OpenAI-compatible request with a per-session GUID and a per-conversation-lineage GUID - yours to inspect, rename, extend, or switch off.

Every request SCORPIOX CODE sends to an OpenAI-compatible endpoint can carry a small set of identity headers: one for the session you are in, one for the conversation lineage that session belongs to, plus anything extra you choose to add. Providers use them for request grouping, session affinity, cache correlation, and their own analytics — and because you control exactly what goes out, nothing is hidden and nothing is forced.

The whole feature is one config key: OPENAI_EXTRA_HEADERS. It ships with a sensible default (two X-Sx-* headers naming your session and thread), it accepts any additional headers you want, and it is the single switch for turning identity headers off completely.

Docs for SCORPIOX CODE @ 77c49df.

The whole idea in one line: with PROVIDER=openai, SCORPIOX CODE stamps every outgoing request with X-Sx-Session-Id and X-Sx-Thread-Id — a per-session GUID and a per-conversation-lineage GUID — and lets you add, rename, or remove all of it through one key.


What gets sent by default

Out of the box, an OpenAI-compatible request leaves your machine with these headers attached:

X-Sx-Session-Id: 8f14e45f-ceea-467f-a1c5-5f0e2d3f9a1b
X-Sx-Thread-Id:  c4ca4238-a0b9-4e2a-a7c9-33e0f0d5a1d2

Both values are UUIDs generated on your machine. Nothing is fetched, nothing is registered, and the values have no meaning outside your own session folder and whatever the receiving endpoint does with them.

HeaderWhere the value comes fromHow long it stays the same
X-Sx-Session-IdA UUIDv4 generated when the session is created, stored in that session's meta.jsonOne session — a new session folder means a new value
X-Sx-Thread-IdA UUIDv4 naming the conversation lineage the session belongs toThe whole lineage — see Session vs. thread

Both GUIDs live in the session's meta.json next to the model, provider, and profile, so a session resumed later carries the same identity it had when it was created. If you open a session that predates this feature, SCORPIOX CODE generates GUIDs for it once and writes them back, so older sessions get stable identity too.


The one key: OPENAI_EXTRA_HEADERS

OPENAI_EXTRA_HEADERS is a comma-separated list of Name:value pairs. It applies to the OpenAI provider only (PROVIDER=openai — the provider for llama.cpp, vLLM, SGLang, Ollama, LM Studio, Azure, and any other OpenAI-compatible endpoint; see Using the OpenAI Provider). No other provider reads it.

OPENAI_EXTRA_HEADERS=X-Sx-Session-Id:{session_guid},X-Sx-Thread-Id:{thread_guid}

That is the shipped default: two headers, named exactly as above, with their values filled in per request. Change the names, add more pairs, or empty the key — the format stays the same either way.

Placeholders

Three placeholders are supported, each substituted per request, just before the request is sent. Anything else in braces passes through untouched:

PlaceholderSubstitutes toNotes
{session_guid}The UUIDv4 for the current sessionAlways present for a valid session. This is the value behind X-Sx-Session-Id.
{thread_guid}The UUIDv4 of the current conversation lineageAlways present for a valid session. This is the value behind X-Sx-Thread-Id.
{session_id}The session's human-readable ID, the same name as the session folder (for example 2026_10_02_quiet_tesla)Populated when session file logging has been initialized for the run.
(unknown tokens)Left as literal text{anything_else} is not a known placeholder and is sent verbatim.

A pair whose value ends up empty is dropped, so a header you configured to a placeholder that resolves to nothing is not sent as Header: with an empty value — the pair disappears quietly.

Literal-token trap. Unknown placeholders are passed through as literal text. If you use {session_id} in an environment where the session ID has not been populated, the header is sent with the literal string {session_id} as its value — recognizable at the receiving end, but probably not what you intended. Prefer {session_guid} and {thread_guid}, which are always populated for a valid session.

Custom headers

Add anything you like after the defaults — static values or placeholder-bearing values:

OPENAI_EXTRA_HEADERS=X-Sx-Session-Id:{session_guid},X-Sx-Thread-Id:{thread_guid},X-Team:platform,X-Cost-Center:{session_guid}

Common reasons to add a pair:

The list is processed left to right. Whitespace around a pair is tolerated (Name: value works), so both X-Team:platform and X-Team: platform are fine.


Session vs. thread: why there are two IDs

Two identifiers sound redundant until you watch a long task run. They answer different questions:

The practical difference shows up in what a provider sees:

EventX-Sx-Session-IdX-Sx-Thread-Id
New session startedNew UUIDNew UUID (new lineage)
Auto- or manual compaction (/compact)New UUID (new session folder)Unchanged — the lineage carries over
/resume of an earlier sessionNew UUID for the resumed sessionUnchanged — the lineage carries over
/clearNew UUIDNew UUID (fresh lineage)

So a provider that groups by session sees many short sessions; a provider that groups by thread sees one continuous work stream per task, even when the session underneath it has been swapped out several times. That is what makes the thread GUID the right key for per-task correlation, and the session GUID the right key for per-session bookkeeping.

Both values are plain UUIDs generated locally with a cryptographically-seeded generator. They contain no username, no machine name, no path, and no project name.


Hardening: what the header path refuses to do

Every header in the list passes through validation before it is attached. The checks are strict on purpose:

The behavior is fail-safe in the boring direction: anything questionable is dropped or refused, and the request still goes out with the headers that did validate.


Opting out: sending no identity headers at all

Setting the key to empty disables the feature entirely — no X-Sx-Session-Id, no X-Sx-Thread-Id, no custom headers. The request goes out with exactly what the endpoint requires and nothing more.

The quick way

Add one line to your project config:

# .scorpiox/scorpiox-env.txt
OPENAI_EXTRA_HEADERS=

An empty value means "send nothing extra" — the key's comment in scorpiox-env.txt says exactly that: Empty = send nothing (opt out of session tracking).

The durable way

Put the same line in the tier that matches how long you want the opt-out to last. See Configuration and Profiles for the full cascade — highest tier wins per key:

You want…Put OPENAI_EXTRA_HEADERS= in
This project only.scorpiox/scorpiox-env.txt (or .claude/scorpiox-env.txt) in the project
Every project on this machine, for you~/.claude/scorpiox-env.txt
Every user of this installscorpiox-env.txt next to the installed binaries
A named profile you flip on and offscorpiox-env/<name>.txt profile overlay

You can also set it as a real OS environment variable, which outranks every file:

OPENAI_EXTRA_HEADERS= ./scorpiox

And you can edit the key interactively with scorpiox-config (the TUI editor) or non-interactively:

scorpiox-config --set OPENAI_EXTRA_HEADERS ""            # writes to the project tier
scorpiox-config --set OPENAI_EXTRA_HEADERS "" --level user

Profile switching is the tidy pattern for temporary opt-outs: keep a profile whose only delta is OPENAI_EXTRA_HEADERS=, and flip to it with /profile <name> (persistent) or /use <name> (this session only). Switch back the same way. See Configuration and Profiles.

What changes at the provider when you opt out

A profile (or any higher tier) can silently re-enable it. If you set the key empty in your project file but an active profile sets it to something non-empty, the profile wins and the headers go out. When headers "won't turn off," run scorpiox-config --verbose and check which tier is supplying the value.


What a request looks like on the wire

For a session running with the default configuration:

POST /v1/chat/completions HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Authorization: Bearer <your key, when configured>
X-Sx-Session-Id: 8f14e45f-ceea-467f-a1c5-5f0e2d3f9a1b
X-Sx-Thread-Id: c4ca4238-a0b9-4e2a-a7c9-33e0f0d5a1d2

The identity headers travel alongside the standard ones, after them, and are yours to inspect: point OPENAI_BASE_URL at any endpoint that echoes request headers (a local debug server, or a logging reverse proxy in front of your engine) and the two X-Sx-* lines appear in the captured request with the UUIDs your session's meta.json holds.


How other providers handle identity

The openai provider is the only one that takes its headers from OPENAI_EXTRA_HEADERS. Other providers manage identity themselves — fixed header names, values generated or configured per provider:

ProviderIdentity sent with every requestWho controls it
openaiX-Sx-Session-Id:{session_guid}, X-Sx-Thread-Id:{thread_guid} by default; anything else you configureYou — via OPENAI_EXTRA_HEADERS, including the opt-out
grokFixed session-affinity headers: x-grok-session-id and x-grok-conv-id (the same generated UUID, held for the provider's lifetime) plus x-grok-turn-idx, and the same UUID as the prompt_cache_key field in the body to pin requests onto a warm KV cacheThe provider — fixed names and values, no config surface
opencodeRequired session headers: x-opencode-session (required on inference; auto-generated ses_... id, or your OPENCODE_SESSION_ID override), x-opencode-request (per-call), x-opencode-client: cli, x-opencode-project: globalThe provider, with one override key (OPENCODE_SESSION_ID)

Two things are worth taking from that table:

If you need uniform identity headers across providers — the same two names on every backend — that is what OPENAI_EXTRA_HEADERS gives you on the openai provider, and why the default names are deliberately neutral (X-Sx-*) rather than tied to one vendor.


Configuration reference

KeyTypeDefaultDescription
OPENAI_EXTRA_HEADERStextX-Sx-Session-Id:{session_guid},X-Sx-Thread-Id:{thread_guid}Extra HTTP headers attached to every outgoing request when PROVIDER=openai. Comma-separated Name:value pairs. Placeholders: {session_guid}, {thread_guid}, {session_id}. Empty = send nothing (opt out of session tracking).

It lives in the Provider & Authentication section of the config editor and is visible when PROVIDER=openai. As with every key, it can be set in any cascade tier, in a profile overlay, or as an OS environment variable — the highest tier that sets it wins.


Gotchas