Skip to content

fix: invert the Responses instructions fingerprint into a codex/claude-only allowlist - #1107

Merged
ranxianglei merged 2 commits into
masterfrom
2026-09-22_fingerprint-allowlist
Sep 22, 2026
Merged

ranxianglei merged 2 commits into
masterfrom
2026-09-22_fingerprint-allowlist

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Fixes #1106

What

Inverts instructionsFingerprintExempt (#1104's trust list) into instructionsFingerprintApplies — an allowlist. The instructions fingerprint now participates in the compression-session key ONLY for:

  1. codex traffic — x-codex-turn-metadata present (any thread incl. root) or user-agent matches codex (Codex 集成:guardian 审批请求进入压缩管线,导致明确授权无法被识别 #150: task-level ids reused across tasks; modern subagent threads already isolate via thread-id verbatim, which bypasses this predicate)
  2. claude over Responses — x-claude-code-session-id wins the header walk (Subagent first token stalls 1-2min: withSessionLock covers full forward + slow flash upstream first byte #970: subagents share the main id; the Anthropic path splits them via claudeSubagentSplit, untouched)

Everyone else keys verbatim: opencode (#1102 — now covered by the default), grok/mcode, plugin conversation lanes, generic x-session-id / body session_id (the relay-station majority, #1106). For them, instructions drift mid-id = same conversation evolved (upgrade / plugin / AGENTS.md edit), and the old default forked an orphan |sub:<fp> session resetting all compression state.

The x-bili-plugin-instructions-mutable declaration is now vestigial — hosts keep stamping it for protocol compatibility with older proxies; PLUGIN.md updated to say so.

Why an allowlist, not a config knob

Same reasoning as #1104, inverted: the fingerprint only ever defended two verified id-sharing personas (codex, claude). A generic client that shares one id across personas and separates them ONLY by instructions is not known; if one appears, it joins the allowlist with evidence (issue documents this residual risk).

Changes

Pre-flight

  • npm run typecheck ✅
  • npm test ✅ 2172 pass / 0 fail (2 pre-existing E2E skips)
  • npm run build ✅

Review note

This flips a default on the identity path (wire/session keying, both modes reasoned) — per repo policy it stays human-merge.

@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-09-22_fingerprint-allowlist (a5d9173)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-1107

Each push to this PR publishes a new version under the pr-1107 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr1107.tgz
npm install -g package

Install instructions are refreshed on each push.

…e-only allowlist

The fingerprint's only real defendants are codex (task-id reuse across
personas, #150; modern codex subagent threads already isolate via
x-codex-turn-metadata -> thread-id verbatim) and claude-over-Responses
(#970 shared subagent ids). For everyone else - opencode (#1102), grok,
mcode, plugin lanes, and the generic x-session-id / body session_id
relay majority (#1106) - instructions drift mid-id means the same
conversation evolved (upgrade / plugin / AGENTS.md), and fingerprinting
forks an orphan |sub:<fp> session that resets all compression state.

instructionsFingerprintExempt -> instructionsFingerprintApplies: OFF by
default, ON only for x-codex-turn-metadata / codex user-agent /
x-claude-code-session-id. The x-bili-plugin-instructions-mutable
declaration is vestigial (kept in the protocol for older proxies).

Fixes #1106
@ranxianglei
ranxianglei force-pushed the 2026-09-22_fingerprint-allowlist branch from f68cb23 to ed38270 Compare September 22, 2026 02:37
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Reviewed this PR (it's already-open work, not redoing it). Findings so far:

Code review — logic verified correct

Diff hygiene — clean: 6 files, every line on-purpose, no whitespace churn, no version bump, no generated/lock-file noise.

Rebase + local gate on current master — the branch was behind by the persist-zstd merge (#1083). I rebased it onto baf6a53 (clean, zero file overlap) and re-ran the full gate locally on the rebased head ed38270:

  • npm run typecheck ✅
  • npm test ✅ 2189 pass / 0 fail / 2 skipped (the pre-existing E2E skips; count is higher than the PR body's 2172 because master gained tests after the branch point — persist-zstd suite)
  • npm run build ✅

Rebased head pushed to the PR branch (f68cb23 → ed38270, force-with-lease on the feature branch only).

CI — original head f68cb23: all 8 checks green (ubuntu/windows × node 22/24, version-guard, build-artifact, fake-codex e2e). CI on the rebased head ed38270 is running now — I'll confirm the final state in a follow-up comment.

Per repo policy (§7.4) this flips a default on the identity/session-keying path, so it stays human-merge regardless of gate results.

中文摘要:审核通过——instructions 指纹从豁免清单反转为 codex/claude 白名单的逻辑正确、diff 干净,已 rebase 到最新 master 并本地全量复验(typecheck/test/build 全绿),等 rebase 后 CI 跑完即可合并。

Review follow-up on the allowlist flip (#1107): the inline /codex/i regex was a second, case-insensitive definition of "codex traffic" alongside isCodexClient (the #645 convention used by every other codex path in server.ts). A case-insensitive match would pull non-codex relays with "Codex"-shaped UAs back into the instructions fingerprint and re-fork them mid-conversation - exactly what #1106 removes. Reuse isCodexClient so the process has one definition; also pin two test edges: a header-walk signal outranking x-claude-code-session-id takes claude out of the allowlist, and "Codex"-shaped UAs stay verbatim.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 CI now fully green on the rebased head (ed38270): all 8 checks pass — test ubuntu/windows × node 22/24, version-guard, build-artifact, zero-token fake-codex e2e, CODEX_HOME-overlay check. No further changes needed from the review; nothing was found that requires a code fix in this round.

Two notes for whoever merges:

中文摘要:rebase 到最新 master 后本地与 CI 全绿(typecheck / 2189 tests / build / 8 项 CI),代码审查无遗留问题,等待人工 review + 合并即可。

@ranxianglei
ranxianglei merged commit d3bb77d into master Sep 22, 2026
8 checks passed
ranxianglei added a commit that referenced this pull request Oct 3, 2026
…-sharing hosts

dsh stamps ONE x-bili-plugin-conversation id on every model request of a
session, INCLUDING the auto-review classifyRisk() calls (fixed
REVIEW_POLICY system + a freshly-flattened user blob, fired before every
tool call under the Auto permission tier). Those review requests walked
the full pipeline under the MAIN conversation id: they overwrote the
usage baseline (#1916), evicted remembered snapshots (#1307's guard was
a partial stopgap), and fed orphan-GC — while the review blob is
synthesized fresh every time, so compressing it has zero reuse value.

Key dsh traffic by id + system hash (kernel subagentNamespace anchor
semantics): the FIRST system seen under the id keeps the raw key (the
main turn claims it — dsh composes its main system once per session and
puts the volatile time-context in a USER message, so main turns never
fork mid-conversation), a different system forks onto <id>|sub:<fp>
(the review persona), and an empty system is non-anchoring (verbatim
key, system-less auxiliary calls keep riding the main session).

- src/session-id.ts: dshPersonaFingerprintApplies (evidence-permitlist,
  the #1104/#1107 discipline — do NOT enable wholesale, #1106) +
  openaiSystemTextForPersona.
- src/server.ts: fork applied on the openai and anthropic lanes;
  personaForked sessions record under their suffixed id
  (recordPluginSession) so the single-valued conversations map keeps
  the raw key owned by the main session (#970 discipline).
- AGENTS.md: Key Design Decisions #9 — the rule for future hosts that
  reuse one session id across personas.
- tests: fork/stability/allowlist/anthropic-lane coverage (peekSession
  — getSession is get-or-create and would mint the sessions we assert
  about).

Refs #1916 #1307 #1314
@ranxianglei
ranxianglei deleted the 2026-09-22_fingerprint-allowlist branch October 15, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant