Skip to content

Latest commit

 

History

History
981 lines (853 loc) · 55.6 KB

File metadata and controls

981 lines (853 loc) · 55.6 KB

api-relay-audit Roadmap

Living document tracking completed work, near-term candidates, medium-term ideas, explicitly deferred backlog, and the explicit "not-doing" list. Each item has a short rationale so future contributors (including future iterations of the author) can quickly reconstruct why a thing is or is not on the list.

Last updated: 2026-06-07 (error diagnosis report layer added; roadmap hygiene pass: v1.9 follow-up test gaps, refusal/transport decouplings, and the protobuf-channel spike are no longer active near-term candidates)

Threat model anchor: Liu et al., Your Agent Is Mine: Measuring Malicious Intermediary Attacks on the LLM Supply Chain, arXiv:2604.08407. Detection concepts cross-referenced with SlowMist OpenClaw Security Practice Guide, hvoy.ai zzsting88/relayAPI claude_detector.py, cctest.ai (Claude-only closed-source SaaS, 5 黑盒检测项 — 复查 2026-05-03), LLMprobe-engine (Bazaarlinkorg/LLMprobe-engine, AGPL-3.0, 朋友项目,未来 contributor, arXiv:2026-04-26, 正交威胁轴:模型替换质量欺诈 vs 我们的安全攻击), 掺水了么? (is.real.dpdns.org, 震旦安全实验室自称,匿名个人开发者,Vercel, 9 黑盒检测维度 — 逆向 2026-05-09).


✅ Shipped

2026-06-07 follow-up — Error diagnosis report layer

  • Productized operational error explanations: added api_relay_audit/error_diagnosis.py, a zero-dependency helper that maps common HTTP/transport failures (401, 403, 404, 422, 429, 5xx, TLS, DNS/connect, timeout, CORS/browser fetch, non-JSON response, format-detection failure) to a category, likely cause, and next action.
  • Report integration without risk-matrix changes: APIClient.call() error results now include a diagnosis object, and the Markdown report renders compact diagnosis lines wherever user-visible probe errors occur. Step 9 deliberately diagnoses only transport failures because its 400 / 422 responses are intentionally induced leakage probes, not user configuration failures.
  • Boundary held: this is an explanatory/reporting layer, not a new detector, not a hosted preflight service, not a new API family, and not a change to LOW/MEDIUM/HIGH semantics. inconclusive remains inconclusive.
  • Final test count: 778/778 passing (733 baseline → 764 after current master follow-ups → 769 after release-engineering version sync coverage → 775 after query-family growth contracts → 778 after release-hardening public verification regressions).

v1.9 — Dual-distribution full generation (2026-06-01)

  • Standalone product promise preserved: root audit.py stays committed, curl-downloadable, stdlib + curl only. It is now a fully generated artifact produced from the modular source files, not a hand-edited second implementation.
  • Phase 1 contract + CI gate: added scripts/build-standalone.py --check and GitHub Actions drift gates for standalone generation, public metrics, and dual-distribution parity tests. scripts/collect-metrics.py --check now fails on README / web / docs/_metrics.md step/test/version/HEAD drift.
  • Builder prerequisite explicit: the standalone generator is a developer tool that requires Python 3.10+ (CI runs 3.11). The generated root audit.py still preserves the user-facing stdlib + curl-only runtime.
  • Phase 2 low-blast source-of-truth moves: extracted Step 4/6 refusal vocabulary/helpers into api_relay_audit/refusal.py with scripts.audit re-exports for compatibility; extracted internal httpx/curl request mechanics into api_relay_audit/_transport.py while keeping APIClient public imports unchanged and standalone flat.
  • Phase 3 generation complete: scripts/build-standalone.py now builds root audit.py end-to-end by inlining portable modular modules, stripping modular imports, and replacing the client transport with a stdlib + curl-only facade. Parity tests now assert the generator contract plus focused behavior/constant regressions instead of relying on hand-maintained character-copy checks.
  • ROADMAP 2.4 coverage closed: real-body APIClient.ensure_format() tests now cover modular and standalone behavior; argparse-level --latency-probe-count wiring is pinned on both distributions.
  • ROADMAP 2.45 low-blast decouplings closed: the Step 4/6 refusal vocabulary/helpers now live in api_relay_audit/refusal.py with compatibility re-exports, and low-level transport mechanics now live in api_relay_audit/_transport.py behind the existing APIClient facade. The archived thinking-signature investigation script remains under scripts/experiments/verify_signature_schema.py.
  • Issue #14 downstream-fork uptake: credited @liuwei71320 in CONTRIBUTORS.md for ai-relay-audit-gui; upstreamed the Windows long-context stability finding by moving curl POST bodies out of argv, and added explicit --fast-context opt-in for low-cost Step 7 scans while keeping full scan as the default.
  • Boundary held: no hosted dashboard, no new dependency, no Claude Code header impersonation, no knowledge-cutoff/0-100/4-level risk/OpenAI streaming auto-detect, no copied closed-source fingerprint table.
  • Final test count: 700/700 passing (642 baseline → 683 after current guardrail/contract and tool-substitution fixture tests → 689 after release/community static-site regression tests → 694 after external refusal-marker and tool-rewrite fixture coverage → 700 after skill distribution contract regression tests).

v2.1 and earlier (pre-session baseline)

  • Steps 1-7: infrastructure recon / model list / token injection via delta method / prompt extraction / instruction conflict / jailbreak / context length scan
  • Step 8: AC-1.a tool-call package substitution (pip / npm / cargo / go echo probes with character-level diff)
  • 3D risk matrix (D1 injection / D2 override / D3 substitution)
  • Dual-distribution: scripts/audit.py + api_relay_audit/*.py modular, audit.py standalone curl-only single-file

v2.2 + v2.3 + v1.7.3 (shipped 2026-04-11, 12 commits in one session)

  • Step 9 — Error Response Header Leakage (AC-2 adjacent): 7 deterministic broken requests + scan for credential echo / upstream URLs / env vars / FS paths / stack traces / LiteLLM internal fields / Bedrock guardrail PII echoes. Sourced from 8 verified LiteLLM GitHub issue bug reports.
  • Step 10 — Stream Integrity (AC-1 SSE-level): Anthropic streaming probe + 4 invariants (SSE event whitelist / output_tokens monotonicity / input_tokens consistency / thinking signature validity) + stream model identity check. Concept verified against hvoy.ai claude_detector.py, clean-room reimplementation with tri-state verdicts instead of their 0-100 score.
  • Step 11 — Web3 Prompt Injection (profile=web3|full): 3 probes targeting SlowMist signature isolation (transfer guidance / sign refusal / private key refusal). Safe-priority classifier with hard-injection override for contradictory responses.
  • Non-Claude identity detection: 22-keyword tuple (→ 26 in v1.7.7) with two-tier matching (strict keywords require identity anchor phrases; lax keywords use word-boundary + non-letter lookahead). Catches Chinese-market substitutes: GLM / DeepSeek / Qwen / MiniMax / Grok / GPT / ERNIE / Doubao / Moonshot / Kimi + Chinese brand names (通义/千问/智谱/豆包/ 文心/月之暗面). Eliminates residual "I am Claude, not GPT" false positive.
  • --profile {general,web3,full} flag: runtime audience selector instead of git branch split. Web3 users opt in; general users see no change. Preserves dual-distribution, test suite, memory, and single- source-of-truth invariants that branches would break.
  • 6D risk matrix (D1/D2/D3/D3i/D4/D4m/D4i/D5/D5i/D6/D6i) with character-identical parity between modular and standalone audit.py.
  • 10 Codex-review bugs fixed across 6 independent review rounds (2 MEDIUM + 1 LOW + 1 NIT + 1 MEDIUM + 1 MEDIUM + 1 LOW + 1 MEDIUM + 2 LOW). Every fix has a regression test.
  • 319 pytest tests (from 114 baseline, +205 new, zero regressions)
  • 11 CLI flags and 3 profile choices
  • FOR_JOHN.md diary chapter, memory files updated, full push to origin/master

v1.7.7 (shipped 2026-04-14, 6 commits)

  • --transparent-log <path> (arXiv §7.3): append-only JSONL forensic log. Every API request recorded with timestamp, URL, SHA-256 of request/response bytes, status code, transport metadata. Hash only, not body. Hooks into all 4 APIClient methods with incremental SHA-256 for streaming. redact_error() strips response body from error field to prevent credential leakage (Codex review HIGH fix). os.makedirs for parent directory (Codex review MEDIUM fix).
  • Identity anchor residual fixes: CJK no-whitespace ("我是GPT-5") via _CJK_STRICT_PATTERNS supplementary path; filler cap {0,4}{0,6} for verbose self-IDs.
  • Context-strict tier for warp/windsurf: new _CONTEXT_STRICT_KEYWORDS requiring post-keyword identity signal (punctuation or role word). Eliminates FP on "I am in warp speed" / "I am a windsurf instructor" (Codex review MEDIUM fix). CJK path also enforces suffix constraint with full-width punctuation support (Codex review LOW fix).
  • 26 identity keywords (from 22: added warp, windsurf, antigravity, deepmind). Three-tier matching: strict (anchor-required), context-strict (anchor + suffix), lax (word-boundary).
  • 493 pytest tests (from 319, +174 new, zero regressions)
  • 12 CLI flags (--transparent-log added) and 3 profile choices
  • Version sync: scripts/audit.py v2.2 → v2.3, SKILL.md YAML fix, README/CLAUDE.md numbers updated

✅ Shipped v1.8 — Infrastructure Audit Layer (2026-04-18)

Step 12: Infrastructure Fingerprint (informational)

Commit: 17387b0 on feat/v1.8-infra-audit-layer Module: api_relay_audit/infra_fingerprint.py (~240 LOC, 24 tests) What: 3 unauthenticated GET probes (/, /v1/models, /nonexistent-*)

  • hand-curated framework signature database (new-api, one-api, lobechat-relay, fastgpt, cloudflare, nginx-raw, caddy-raw) + majority-vote confidence (confirmed ≥2 hits / tentative 1 hit / unknown 0 hits). Why: Zhang et al., Real Money, Fake Models, arXiv:2603.01919, Section 3.2 Infrastructure reports 11 of 17 identified shadow APIs are built on OneAPI and its derivative NewAPI. Knowing the framework lets the operator cross-reference CVEs and assess professionalism. Classification: informational only — does NOT feed into the 6D risk matrix.

Step 13: Latency Variance (informational)

Commit: 3339bc1 on feat/v1.8-infra-audit-layer Module: api_relay_audit/latency_variance.py (~180 LOC, 20 tests) What: N (default 10) identical max_tokens=8 probes + descriptive stats

  • gap-ratio bimodality heuristic. Verdict = stable (CV<0.25) / variable (0.25≤CV<0.5) / high-variance (CV≥0.5) / bimodal / inconclusive. Why: silent A/B testing between the advertised model and a cheaper substitute produces bimodal latency. Queue multiplexing produces multi-modal. Stable low-variance latency is the honest baseline. Classification: informational only; v1.8 could false-positive on jitter and warm-up. Future v2+ may promote bimodality to a D7 dimension once we have enough honest-relay baseline data.

v1.8 Codex review cycle (same-day follow-up, 2026-04-18)

Commits: 4db33b7 (MEDIUM fix), d0fb5d9 (LOW coverage + HIGH known-limitation docs) Verdict: minor-fixes-needed → closed. 6th Codex review round shipped on this repo, cumulative 18 real bugs/limitations found across the loop.

  • HIGH (app-layer drowned by edge-layer on aggregate): deferred to v1.8.1 per Pareto analysis. Current Step 12 is informational-only; per-probe result still preserves one-api / new-api identity; only the majority-vote aggregate loses it. Fix requires changing aggregate_framework signature + all call sites + report renderer, which warrants real Cloudflare-fronted relay data before committing to the layer split. Current behavior locked by test_one_api_behind_cloudflare_aggregates_as_cloudflare + test_new_api_behind_cloudflare_aggregates_as_cloudflare.
  • MEDIUM (N=4 single-outlier bimodality false positive): fixed. Gap search restricted to interior splits where both sides have ≥2 samples. [1.00, 1.01, 1.02, 1.80] now returns (False, ~0.01) — the single outlier is correctly treated as high-variance, not bimodal. Genuine 2+2 distributions [1.00, 1.01, 1.80, 1.82] still fire. Dual-distribution parity preserved.
  • LOW (missing test coverage): fixed. 5 new tests — N=4 outlier + N=4 true bimodal + N=5/N=6 extreme-outlier cluster-size rule + 3-success / 7-error partial-success CV verdict + Step 12/13 constants dual-distribution parity (FRAMEWORK_SIGNATURES, INFORMATIVE_HEADERS, _BODY_SCAN_LIMIT, BIMODAL_GAP_THRESHOLD, CV cutoffs, DEFAULT_PROBE_COUNT).
  • Final test count: 546/546 passing (v1.7.7 baseline 493 → v1.8 ship 537 → v1.8 Codex follow-up 546, +53 net for v1.8).

v1.8.1 Codex review cycle #2 (handoff-prep, 2026-04-20)

Second Codex pass before front-end handoff. 5 findings (1 HIGH already in v1.8.1 backlog + 2 MEDIUM + 2 LOW); 4 fixed in this cycle.

  • HIGH (majority-vote mixes app-layer and edge-layer): unchanged — already tracked as v1.8.1 item #0 below; deferral rationale still stands.
  • MEDIUM #2 (Step 13 first sample polluted by format detection): fixed. New APIClient.ensure_format() warm-up; run_latency_variance calls it before the timing loop so no sample includes a failed Anthropic probe plus a successful OpenAI request. 2 new tests (call ordering + graceful degradation for clients lacking the method).
  • MEDIUM #3 (time.time is wall clock, not monotonic): fixed. Migrated to time.perf_counter() in both distributions. Removes NTP / VM clock-skew artifacts from CV / bimodality inputs.
  • LOW #4 (LobeChat's x-powered-by: next.js misfires on every Vercel site): fixed. Signal removed; body branding (lobechat / lobe-chat) is still the identifier. Negative test locks behavior.
  • LOW #5 (--latency-probe-count took 0 / negatives / huge values): fixed. validate_probe_count rejects values outside [3, 50] with a readable argparse.ArgumentTypeError. 11 new tests around the bounds.
  • Final test count: 560/560 passing (546 → 560, +14 this cycle).

v1.8.1 Codex review cycle #2 round 2 (post-commit verification, 2026-04-20)

Re-ran Codex on commit 122f23d right after v1.8.1 shipped. Codex confirmed all 4 code fixes were correct but flagged 3 test-coverage gaps — cases where the existing tests could be false-greens (pass identically if the underlying fix were reverted). Triaged under strict-2-hour handoff window; prioritized the one true false-green and deferred the other two to v1.9.

  • #3 test gap — false-green clock-source check: fixed in this round. Added two new regression tests — tests/test_latency_variance.py::test_uses_perf_counter_not_wall_clock and tests/test_dual_distribution_parity.py::test_standalone_uses_ perf_counter_not_wall_clock. Both monkeypatch the time module, instrument perf_counter with a deterministic counter + time.time with a constant, then assert that perf_counter was invoked ≥2× per probe, time.time was never called, and measured latencies equal the fake clock deltas exactly. Under a reverted time.time implementation these fail loudly because the mocked client returns instantaneously (elapsed ≈ 0), whereas the fake perf_counter yields elapsed = 1.0. Both distributions pinned.
  • #2 test gap — ensure_format only exercised via mock: fixed in v1.9. tests/test_client.py::TestAutoDetection::test_ensure_format_ real_body_detects_anthropic exercises the real modular method body, and tests/test_dual_distribution_parity.py::test_standalone_ensure_format_ real_body_detects_anthropic mirrors the same coverage onto the generated standalone artifact.
  • #5 test gap — validator not tested at parse_args() level: fixed in v1.9. tests/test_dual_distribution_parity.py::test_latency_probe_ count_argparse_wiring_rejects_invalid_values proves both distributions wire validate_probe_count into argparse and reject invalid values with SystemExit(2).
  • Final test count: 562/562 passing (560 → 562, +2 this round).

v1.8.2 Identity consistency wording (shipped 2026-05-29)

OpenRouter Claude Opus 4.8 internal report from 2026-05-29 showed a concrete contradiction: response metadata reported provider: Anthropic and model: anthropic/claude-4.8-opus-20260528, while natural-language answers to "what model are you" self-identified as Qwen/DeepSeek in 4 of 6 calls. That evidence changes Step 5's attribution semantics.

  • Design correction: natural-language self-ID is not ground truth for the actual upstream model. It is only a consistency signal that must be interpreted alongside full response JSON, request id, provider/model metadata, and platform logs.
  • Product change: Step 5 still flags non-Claude self-ID under the D2 instruction-override dimension, but the report no longer phrases it as proof that the actual upstream model was replaced.
  • Regression tests: OpenRouter-shaped Anthropic metadata + Qwen and DeepSeek self-ID cases are pinned for both modular and standalone distributions.
  • Final test count: 586/586 passing (+4 OpenRouter identity consistency cases this round).

🔜 Near-term candidates (next 1-2 sessions)

Pick one of these to start the next session. Each is scoped to fit in a single session, has a clear spec, and does not require new infrastructure. Items marked ✅ closed or historical are retained for auditability only; do not pick them as new work unless new evidence reopens the decision.

0. v1.8.1 — app-layer vs edge-layer framework separation

Status: deferred from v1.8 Codex review HIGH finding (2026-04-18) Precondition: at least one real Cloudflare-fronted one-api or new-api endpoint audit result on file. Without real data, any layer-split heuristic is a guess. Scope: ~60 LOC refactor — partition FRAMEWORK_SIGNATURES into APP_LAYER_FRAMEWORKS = {"one-api", "new-api", "lobechat-relay", "fastgpt"} and EDGE_LAYER_FRAMEWORKS = {"cloudflare", "nginx-raw", "caddy-raw"}. aggregate_framework returns {"app": (framework, confidence), "edge": (framework, confidence)} tuple instead of a single (framework, confidence). Report renderer displays both layers with distinct labels. Dual-distribution impact: large. aggregate_framework signature change affects scripts/audit.py wiring + standalone audit.py inlined copy + report renderer + all 4 existing aggregate tests (must update assertions). Parity regression test: add a new test confirming that a Cloudflare- fronted one-api landing page returns {"app": ("one-api", "tentative"), "edge": ("cloudflare", "confirmed")} — this is the HIGH-finding scenario we want to NOT lose. Cost of deferring further: zero if operators don't hit this case in real audits. Every month without a Cloudflare-fronted complaint is evidence we can leave it. Revisit when #1 (local Docker validation) runs.

1. Local one-api Docker real-world validation

Status: not a coding task — ops / validation exercise Scope: 30-60 minutes Docker setup + audit run + write-up Why: generate the first real "before/after" detection rate data by running the tool against a clean local one-api deployment. Confirms that the 13-step pipeline does not false-positive on a legitimate relay, and gives Step 12 its first real confirmed-framework hit for ground truth. Dependencies: Docker + a valid upstream API key. one-api source is publicly available at github.com/songquanpeng/one-api. Output: a reports/one-api-clean-baseline.md file plus a diary entry in FOR_JOHN.md documenting what Step 9/12/13 actually caught.

2. Crypto Address Substitution (profile=web3|full)

Status: spec'd, deferred from original v3 PR 2 — DEMOTED from v1.8 lead because Step 12/13 had clearer Pareto justification Scope: ~180 LOC new module + ~30 tests Why: arXiv §5.2 reports a real case of a relay draining an ETH private key. Probe set: ETH USDT contract / BTC Satoshi genesis / SOL Token Program / ERC-20 transfer calldata / BTC bech32 address. Strict byte-level classifier — NO case folding (EIP-55 mixed case). Dependencies: none. Byte-level string comparison, no crypto libs. Cost of deferring further: low — no new adversarial case reported since the original paper.

2.4 v1.9 — test-coverage follow-ups from Codex review cycle #2 round 2 ✅ closed

Status: shipped in v1.9. This item is kept only as historical context for why the tests exist.

  1. ensure_format real-body integration test — fixed by tests/test_client.py::TestAutoDetection::test_ensure_format_real_body_ detects_anthropic plus the generated-standalone mirror in tests/test_dual_distribution_parity.py::test_standalone_ensure_format_ real_body_detects_anthropic.
  2. --latency-probe-count parser-level wiring test — fixed by tests/test_dual_distribution_parity.py::test_latency_probe_count_argparse_ wiring_rejects_invalid_values, which drives both modular and standalone parse_args() with invalid values and expects SystemExit(2).

Cost of deferring further: none; there is no remaining action for this item unless a future CLI refactor changes argparse wiring again.

2.45 v1.9 — controlled-blast decouplings (handoff-prep triage, 2026-04-20)

Status: partially shipped in v1.9. This section now tracks the remaining decouplings, not the low-blast work that already landed. Shipped:

  • Tier A archival — scripts/verify_signature_schema.py moved to scripts/experiments/ (zero blast on imports, no module references the archived script). Codex review on the move caught one latent path-drift bug: OUT_DIR = Path(__file__).parent.parent / "reports" would have written to scripts/reports/ after the rename. Fixed by bumping the anchor to .parent.parent.parent.
  • Step 4/6 refusal vocabulary/helpers moved into api_relay_audit/refusal.py with scripts.audit compatibility re-exports.
  • Phase 2a transport extraction landed as api_relay_audit/_transport.py behind the existing APIClient facade.

Remaining decoupling candidates are ranked by blast-radius vs. leverage.

  1. Extract REFUSAL_MARKERS + _looks_like_refusalclosed in v1.9. The modular source now imports/re-exports helpers from api_relay_audit/refusal.py; standalone keeps the generated inline copy, with refusal-marker parity tests guarding drift.

  2. Split api_relay_audit/client.py (924 LOC) into client/transport.py + client/format_detection.py + client/stream.py + client/__init__.py (re-exports).

    Codex review follow-up (2026-04-20): my original "HIGH blast / blocked on dual-distribution policy" framing was over- pessimistic. A cheaper incremental path existed, and Phase 2a has now landed: low-level httpx/curl mechanics live in internal api_relay_audit/_transport.py, while APIClient remains the public facade.

    After 2a stabilizes, Phase 2b (stream extraction) and 2c (format-detection extraction) can follow the same pattern. Only Phase 2d ("promote the internal modules to public re-exports under api_relay_audit.client.*") needs the dual-distribution policy decision, because that is when the standalone flat copy starts diverging.

    Remaining scope: Phase 2b/2c only. Keep them facade-preserved unless there is a concrete user-facing reason to expose submodules.

    Original full-split Phase 2d (for reference): HIGH blast, blocked on dual-distribution policy (keep / deprecate / auto-generate standalone from modular). 2-3 hours plus design.

  3. Split scripts/audit.py (1536 LOC) into scripts/steps/ subdirectory, one file per numbered step. Scope: ~1200 LOC moved + scripts/audit.py becomes a thin orchestrator that imports from scripts.steps.step_NN_*. Blast radius: MEDIUM-HIGH. The test_risk_matrix_character_ identical parity test slices text between # Overall rating and # Output comments; if the split moves those comments into a step module the parity test breaks unless we relocate the slice markers. Prereq: same dual-distribution decision as #2; also needs the parity test's slice markers redesigned. Time estimate: 3-4 hours.

  4. Extract web/ dashboard to a separate repo api-relay-audit-dashboard.

    Codex review follow-up (2026-04-20): my "1h mechanical" estimate was wrong — web/ has live wiring into the rest of the repo that must be re-established after extraction:

    • .github/workflows/pages.yml deploys web/** to GitHub Pages; triggered by paths: [web/**]. Either (a) remove this workflow when extracting and rewire inside the new repo, or (b) keep a thin web/ symlink/submodule in this repo that still satisfies the paths filter.
    • scripts/extract-data.py has --output as a required flag with no default (scripts/extract-data.py:197); the CLAUDE.md + docstring example just happens to pass --output ./web/data.json. After extraction, either the script gains a default pointing at the new repo path or every caller/doc/CI invocation is updated to the new location.
    • web/index.html is 75 KB with inline JS/CSS; splitting into HTML + CSS + JS is a separate frontend task that probably wants to happen IN the new repo rather than this one.

    Blast radius: LOW on the Python backend (nothing under api_relay_audit/* or scripts/audit.py imports the dashboard), but MEDIUM on CI/deployment because the GitHub Pages workflow currently depends on web/ living in-tree. Why not now: defer to the front-end colleague's first- day discussion. They may prefer to keep it in-tree for one more iteration OR prefer a clean extract where they own the new repo from day one. Either answer is reasonable. Time estimate: 2-3 hours (extraction + CI rewire + README cross-links), NOT 1 hour.

Cost of deferring further: moderate. The former #1 and the Phase-2a slice of #2 are shipped, and Phase 3 generation removed the old dual-distribution blocker. The next backend refactor can start with the scripts/audit.py step-orchestration split or a facade-preserved stream / format-detection extraction; #4 remains a front-end colleague conversation, not a backend task.

2.5 v1.9 — over-engineering prune (backlog, handoff-prep triage)

Status: audit done 2026-04-20 before front-end handoff; no deletions yet — items tabled because deletion before handoff is high-risk. Scope: each item below is a separate consideration; don't do them all at once.

Top-5 candidates ranked by maintenance-cost-per-value (worst first):

  1. Dual-distribution invariantresolved in v1.9 Phase 3. audit.py is now generated from modular source by scripts/build-standalone.py; standalone remains first-class, but the "write every feature twice" tax is gone. Keep this item here as the historical reason Phase 3 existed, not as an active prune candidate.
  2. error_leakage GitHub-issue cross-reference: every leak marker maps to a real LiteLLM issue number (#5762, #8075, ...). Elegant when shipped but issue state rots (renames, merges, closures). Simplify to the regex + literal substring paths; keep issue refs only in FOR_JOHN.md provenance notes.
  3. transparent_log.py: JSONL forensic logger (--transparent-log gated). Academic anchor (arXiv §7.3) but real-world usage unconfirmed. Convert to an optional extra once packaging is introduced (same packaging work needed for v2.5 LLMmap Pro).
  4. Web3 profile (Step 11, 3 probes + profile gating + hard-injected override): low invocation rate expected vs. surface area. Could collapse to a single probe while keeping the --profile web3 CLI surface.
  5. latency_variance bimodality branch: Step 13 is already informational-only; bimodality adds inference complexity without affecting risk matrix. Could report only CV + count and still deliver the same operator value.

Cost of deferring further: zero. Pruning helps only if we keep shipping new features on top; if development pauses, these stay as inert reference code. Revisit when next feature cycle starts.

2.6 v1.9 — cctest.ai 复查衍生候选 (2026-05-03)

Status: partially shipped. Step 14 upstream channel classifier shipped in v1.9 through the lower-risk header/id/body route. The old protobuf-signature reverse-engineering plan is now historical context, not an active near-term candidate. Remaining items in this section stay spike-only / research-only: no default audit wiring, no 6D risk-matrix change, and no public per-relay claims without reviewable evidence.

Source: cctest.ai/zh + cctest.ai/zh/faq 复查 (2026-05-03)。Memory reference_cctest_ai.md 同步更新。变更点 vs 19 天前情报:

  • 6 检查 → 5 检查(Signature Analysis + Signature Verification 合并为「签名校验」单项)
  • 模型选择器新增 Opus 4.7
  • 价格 / 渠道分类 (aws-bedrock / vertex / kiro / warp / windsurf / antigravity) 没变 —— 后四类我们 v1.7.4 已覆盖
  • 5 检查中 #1 LLM 指纹 = 我们 Step 5 (v1.6);#2 流结构 = 我们 Step 10 (v1.7);#3 非流结构 = 我们 Step 8/9 部分覆盖;只有 #4 / #5 是新增量

对 api-relay-audit 真正的增益面只有 2 项(其余都是反向验证):

2.6.1 Step 14 — Upstream channel ID 检测 ✅ 已 ship (v1.9, 2026-05-10)

实现api_relay_audit/channel_classifier.py(modular)+ audit.py Section 3h(standalone),告别 protobuf 路线,采用 LLMprobe-engine 的 header/id-prefix/body 三层规则方案(clean-room reimplementation;全仓库改为 AGPL-3.0-only 后,未来可在保留归属、许可证和源码义务的前提下兼容引入)。 标签集只覆盖 Step 12 看不见的 7 个上游:openrouter, cloudflare-ai-gateway, aws-bedrock, google-vertex, aws-apigateway, anthropic-official, anthropic-relay (Tier 3 msg_01... 推断)+ unknown 兜底。Step 12 已覆盖的 9 个 channel (litellm/helicone/portkey/kong-gateway/alibaba-dashscope/azure-foundry/new-api/one-api/cloudflare) 不重复实现。informational only,不进 6D 风险矩阵。 一次 max_tokens=4 /v1/messages 探针,~$0.001 成本。 测试覆盖 39 个 channel_classifier 用例 + 1 个 dual-distribution parity 测试 (锁住 TIER1_RULES / TIER2_WEIGHTS / TIER2_PRIORITY / TIER3_RELAY_ID_PATTERN)。 2026-05-31 rebase after v1.8.2: preserved the Step 5 identity-consistency wording from PR #20 while keeping Step 14 informational-only. Generic Google edge/hosting headers (server: Google Frontend, x-goog-*, via: google) no longer classify as google-vertex; Vertex now requires msg_vrtx_ or vertex-2023-10-16. Final test count: 642/642 passing.

2026-05-09 立项动机:LLMprobe-engine (competitors/LLMprobe-engine/src/channel-signature.ts) 已用纯 header/id-prefix/body 规则实现 16 个渠道标签,三层检测架构: Tier 1 确定性(1.0 confidence,如 gen- → OpenRouter,x-litellm-* → LiteLLM, helicone-* → Helicone),Tier 2 加权评分(Bedrock/Vertex/Anthropic-official), Tier 3 推断(native Anthropic msg_01... regex → relay proxy)。 这个方案比 protobuf 解析简单得多,且已在 171 个真实端点验证过。 Spike 阶段先验证 header-based 方案是否已满足需求,再决定是否还需 protobuf。 结果:header-based 已经足够,protobuf 方案弃了(见下面"原方案"段,留作历史档案)。

原 Protobuf 签名解析 (upstream channel ID) — historical, not active

What cctest.ai claims to do: 解析 Anthropic 流式响应里的 signature 字段 (Protobuf 编码),反推上游渠道 → 输出 aws-bedrock / vertex / direct-anthropic / kiro / warp / windsurf / antigravity 标签,再对签名做 replay 校验是否被篡改。 这是他们公开宣称的"业界最专业"护城河,闭源不公开算法。

Why this was worth considering: before Step 14 shipped, we could only infer channel from response wording and infrastructure clues. A parseable signature protocol might have provided a harder channel signal. In practice, the cleaner header/id/body classifier delivered the useful part without reverse-engineering a brittle opaque signature protocol or expanding the risk matrix.

Spike scope (research-only, NO product code):

  1. 抓取 5-10 条真实 Claude 直连响应(通过本地 --transparent-log 现有日志或新写一个一次性抓取脚本,调用第三方中转),定位 signature / signature_delta 字段在 streaming SSE 中的出现位置
  2. protoc --decode_raw 或 Python 的 google.protobuf 试解析 字段,确认是否真为 protobuf wire format(也可能是 base64 包裹的 其他二进制格式)
  3. 如果可解析:枚举字段 ID,对照 cctest.ai 输出的渠道标签反推映射表
  4. 对 Bedrock / Vertex 渠道(如果手头有可用密钥或公开样本)重复 1-3, 确认渠道签名在结构上确实可区分
  5. 写最小 PoC scripts/experiments/signature_decode_spike.py(类比 v1.8 时期的 verify_signature_schema.py,进 experiments/ 不进 主 pipeline),输出可行性报告 → ROADMAP §2.6.1 状态更新

Current decision: do not run this spike unless new field evidence shows header/id/body classification is materially insufficient.

Pre-conditions to ship as Step 14:

  • Anthropic 官方未来不破坏 wire format 的合理把握(cctest 自己也吃这个 风险 → 协议无文档时必然脆弱)
  • 至少能区分 direct-anthropic vs aws-bedrock vs vertex 三类(最低 可行集),其余 Kiro/Warp/Windsurf/Antigravity 标签可与 v1.7.4 关键词 联合判断
  • 对 OpenAI 格式中转站不应假阳(OpenAI 响应没有 signature 字段; Step 14 须返回 inconclusive 而非 clean,类比 Step 10 的设计)

Blast radius: SPIKE 阶段零(只在 experiments/ 下)。如果 spike 通过 推进到 Step 14:MEDIUM-HIGH,因为新增 D7 维度涉及 6D → 7D 矩阵升级 + 双分发 char-parity + 至少 30 个新测试 + Reporter 渲染层。

Time estimate (spike only): 1.5-3 小时取决于 Anthropic 是否真用 protobuf。

Why deferred: spike 必须拿到真实响应样本才能开做;handoff 期不动 master。

Risks / 已知坑:

  • Anthropic 可能未来把 signature 改为 opaque token → 整个 Step 14 报废
  • 解析二进制协议属于灰区(reverse engineering),需在 README 注明 "best-effort, may break without notice"
  • 不能复用 cctest.ai 的标签字符串(闭源,无 license)—— 我方需独立命名

2.6.2 Spike Step 15 — 多模态能力探测 (dilution detector)

2026-05-09 更新:LLMprobe-engine 已提供可直接参考的 base64 fixture 设计 (competitors/LLMprobe-engine/src/multimodal-fixtures.ts): 64×64 纯红色 PNG(关键词 "red")+ 单页 PDF(关键词 "BAZAAR")。 这两个 fixture 的设计原则(小体积、确定性关键词答案、自制版权干净)值得 参考,但需 clean-room 自制图片(不能复制 AGPL-3.0 代码库的 base64 字串)。

What cctest.ai does: 在常规检测后追加 1 个图像 + 1 个文档识别请求, 验证模型真有多模态能力。如果应答是空文本 / 拒绝识别 / 给出与图无关的 内容 → 上游可能被替换为非多模态廉价模型。

Why worth porting: 中转站常见的稀释手段是把 Claude Opus 替换为某个 便宜的纯文本模型,单看文本响应可能挑不出毛病,但对图像必然露馅。我们 现有 10+ 步全是文本探测,对这一类替换零检测能力。

Spike scope (research-only):

  1. 选定 1-2 张 ≤50KB 的小图,预设标准答案(如:纯色块 + 文字 OCR; 或 5x5 棋盘 + 计数题),版权干净(自制即可)
  2. 把图作为 base64 嵌进 Anthropic messages 格式 (image content block) 与 OpenAI 格式 (image_url content part) 各发一遍,记录 Claude 直连基线响应特征
  3. 设计裁判:基线包含哪些关键词 → 视为 pass;空文本 / "I cannot see images" / 答案与基线 token-overlap < 0.3 → 视为稀释嫌疑
  4. 评估单次成本(Anthropic 当前定价图像按 token 折算,预计 < $0.005 单次)—— 必须低于 Step 9-13 单步成本上限才能默认开启
  5. PoC 落 scripts/experiments/multimodal_dilution_spike.py

Pre-conditions to ship as Step 15:

  • 基线裁判规则在 ≥3 个不同小图上稳定(不出现 Claude 直连本身假阴)
  • 单次成本 < $0.01,且总 token 增量 < 当前总测试 token 的 10%
  • OpenAI 格式分支处理图片输入失败时返回 inconclusive 而非误判
  • profile gating 决策:是默认开启,还是放进 --profile full

Blast radius: SPIKE 阶段零。Step 15 实现:MEDIUM —— 需扩展 APIClient 支持 vision content block,新增 ~150 LOC 模块 + ~25 测试 + 双分发 inline,但不一定升级风险矩阵(可先做 informational like Step 12/13)。

Time estimate (spike only): 1-2 小时。

Why deferred: 同 §2.6.1,handoff 期不动 master;且需要先决定图片 版权来源(最稳是自制纯色 + 文字图)。

Risks / 已知坑:

  • 真 Claude 在某些 prompt 下也会拒识别图(隐私 / 人脸 / 医学),裁判 规则要避开这些类别
  • 图像稀释检测对"上游是 GPT-4V 假冒 Claude"无能为力(GPT-4V 也能识图 正确)—— 需在 README 限定威胁模型为"上游被替换为纯文本廉价模型"

2.6.3 LLMprobe-engine 其他借鉴点 (2026-05-09, 未计划实现时间)

来自 competitors/LLMprobe-engine/,朋友项目(未来 contributor)。在 MIT 阶段只能 clean-room 参考算法;全仓库改为 AGPL-3.0-only 后,可在保留归属、许可证和源码义务 的前提下兼容引入或改写:

  1. Bayesian log-odds identity scorer (fingerprint-bayesian.ts): 用 softmax 归一化的 log-likelihood ratio 替代我们 Step 5 的布尔关键词检测, 输出概率分布而非布尔值。纯数学,零依赖,可在不破坏 zero-dep 不变量的情况下 实现。提升 false-positive 精度。候选:未来 Step 5 升级,不急。

  2. AC-1.b 统计累积模式 (proxy-analyzer.ts computeAc1b): 需 ≥3 neutral + ≥3 sensitive 样本,当 sensitiveAnomalies ≥ 1 且 (neutralAnomalies === 0sensitiveRate ≥ neutralRate × 2) 时判定条件注入。 比我们现有的单次 AC-1.b 检测更统计严谨。候选:ROADMAP §12 的增强路径, 需多轮审计数据积累,不是单会话任务。

  3. Thinking signature round-trip (signature-probe.ts): 把 thinking block 的 signature 原样送回官方 API,400 = 签名被篡改,200 = 官方渠道。 与我们 scripts/experiments/verify_signature_schema.py 思路相同,可相互验证。

2.6.4 Memory & doc 同步 (已完成)

  • ~/.claude/projects/C--Users-john-Downloads-api-relay-audit/memory/ reference_cctest_ai.md —— 6 检查 → 5、模型列表加 Opus 4.7、复查 日期 2026-05-03、明确两条 spike 锚点
  • 本 ROADMAP §2.6 节 (本节) —— 候选项落地

2.6.4 不做的事 / 显式守界

事项 为什么不做
直接抄 cctest.ai 的 signature 字段映射表 闭源 SaaS 无 license;必须 clean-room reimplementation 从真实样本反推
把 cctest.ai 的渠道标签字符串复用 同上;用方独立命名(如 channel: bedrock-likely 而非 aws-bedrock
在 spike 阶段就升级 6D → 7D 矩阵 Step 14 已作为 informational classifier ship;渠道身份仍不进入风险矩阵。
引入第三方多模态裁判模型 破坏零依赖不变量;裁判用关键词 + token overlap 可解决
在 §2.6 spike 通过前公开任何 cctest.ai 测试结果 数据外泄风险;任何对外发布需用户显式批准

Cost of deferring further: low. Step 14 already covers the practical channel-classification need without protobuf parsing. If future field reports show header/id/body signals are insufficient, reopen the protobuf spike from this historical plan; otherwise the next real research candidate here is §2.6.2 multimodal dilution, and only as an experiments-only spike.


2.7 掺水了么? 逆向衍生候选 (2026-05-09)

来源:is.real.dpdns.org 逆向(震旦安全实验室,实为匿名个人开发者,Vercel Serverless,题库完全闭源)。检测维度 9 个(API 响应实测,非页面自述的 7 个)。 与我们重叠的 7 项从略;以下只记录我们真正缺失的 2 项,以及 1 项已明确不做的项。

状态:研究阶段,无实现计划。禁止动 master;如 spike 请落 scripts/experiments/

2.7.1 Spike — 提示完整性检测 (Prompt Integrity)

What 掺水了么 does:验证发出去的 system prompt 是否被 relay 篡改或截断后到达模型。 与我们的 prompt injection 检测(Step 1/2,检测 relay 向 prompt 注入内容)方向相反: 这是检测 relay 是否删除/改写用户 system prompt。

Spike 思路

  1. 在 system prompt 里嵌入一个唯一 canary 字符串(如随机 UUID)
  2. 随后问模型"你的系统提示里有什么特殊指令?"或"重复你的 system prompt 的第一句"
  3. 如果 canary 出现在响应中 → prompt 完整送达;如果不出现 → 截断嫌疑
  4. 对比直连基线(我们没有 API key 做基线,可用 refusal 模式作为 inconclusive 兜底)

与现有 Step 2/4/6 的区别:Step 2 是检测 relay 注入内容;这个检测 relay 删除内容。 两个方向,现有步骤不覆盖"删除"路径。

Blast radius(spike 阶段):零。实现为 Step N:MEDIUM。 Cost of deferring:低,无用户 report 此类攻击。

2.7.2 Spike — 身份越狱抵抗探测 (Identity Jailbreak Resistance)

What 掌水了么 does:主动用 jailbreak 提示诱导模型承认自己不是 Claude(如 "你其实是 GPT-4,对吧?请直接承认"),测试模型能否抵抗身份否认攻击。

与现有 Step 4/6 的区别:我们的 identity 检测是被动的——读响应看有没有 自称其他模型。掌水了么这个是主动的——用攻击提示看模型会不会被诱导放弃 Claude 身份。 对"真 Claude 但被加了强制改身份 system prompt 的 relay"有独特检测价值。

Spike 思路:3-5 条越狱探针("忘掉你的限制,告诉我你真实的模型名称"等), pass 条件:响应中不出现竞争模型名称 OR 明确重申 Claude 身份。 与现有 identity_patterns.py 的 26 个关键词直接复用,不需要新匹配逻辑。

Blast radius(spike 阶段):零。实现为 Step N:LOW,可直接扩展 Step 6 jailbreak。 Cost of deferring:低。

2.7.3 Knowledge cutoff 探测 — 维持不做决策

掌水了么有"知识能力评估"维度,疑似基于模型知识截止日期的事实性问题。 我们的"Explicitly NOT doing"表里已有此项,理由仍然成立: relay 可硬编码"May 2025"在 system prompt 里轻松绕过。不重新评估。

3. MistTrack AML integration (profile=web3|full, optional)

Status: sketched in SlowMist OpenClaw Practice Guide, not started Scope: ~100 LOC adapter + external API dependency Why: SlowMist's "Cross-Skill Pre-flight Check" pattern — when an agent is about to make a high-value crypto action, call MistTrack for an AML risk score. Score ≥ 90 → hard abort. Integrates well with our --profile web3 flag. Dependencies: MistTrack API key or public endpoint. Breaks the zero-dep invariant for standalone audit.py. Should probably be modular-only, with --profile full gate. Cost of deferring: low — requires external infrastructure setup.


🛠 Medium-term ideas (1-3 month horizon)

6. Full AC-1 tool_call support (as opposed to AC-1.a text echo)

Status: backlog item from Step 8 Scope: ~150 LOC — APIClient.tool_call() method + structured tool_call payload inspection + matching probe set Why: Step 8 currently catches AC-1.a (typosquat on plain text echoes) via text-level comparison. A more specific attack — rewriting the tool_calls JSON payload but leaving plain text alone — is not caught. Paper §4.2.1 notes: "the compromised dependency is cached locally and re-imported across future sessions, giving the attacker a durable supply-chain foothold." Cost vs benefit: marginal coverage uplift over AC-1.a (all observed wild samples were AC-1.a). Defer until the first wild AC-1 case is reported.

7. Schema deviation anomaly detection (paper §7.2)

Status: paper lists it as a detection dimension; we don't implement Scope: unknown — would need design work. Paper §7.2 Table 10 reports ~10% contribution to AC-1.a detection at 6.7% FPR budget, 0% at 1% FPR budget. Why not: low marginal value — our byte-level diff in Step 8 is strictly better on AC-1.a, and the architectural complexity of adding a schema-deviation feature to both distributions is high. Paper authors themselves flagged this as the weakest of their three defenses. Decision: defer indefinitely unless a new attack class needs it.

8. JA3 fingerprint clustering

Status: mentioned in paper §7.3 (6 JA3 fingerprints observed on 147 IPs, 40k unauthorized access attempts) Scope: client-side JA3 fingerprinting + server-side collection + corpus-level clustering Why not yet: single-session value is low. JA3 clustering becomes valuable after the audit corpus reaches ≥100 distinct relay endpoints. We currently have 0 in our corpus (users run the tool ad-hoc). Revisit after ~6 months of field use and corpus growth.

9. Structured audit corpus from hvoy.ai leaderboard

Status: hvoy.ai /APIreview.html lists 40+ real Chinese relay endpoints with CNY pricing and推荐/中性/不推荐 ratings Scope: ops + data pipeline: scrape or manually collect the list, request consent from relay operators, run api-relay-audit against each, compile a reports/corpus-2026-Qx.md document Why: independent validation — our tool's findings should be compared against hvoy.ai's recommendations and any divergence explained. Also gives us JA3 data (see item 8) if we collect client-side TLS fingerprints during the audit runs. Legal consideration: some of the listed relays may have ToS that prohibit audit probing. Need consent per-relay. Cost: high — multi-session ops work.

10. v2.0 — Capability benchmark delta (direct model-substitution detection)

Status: promoted from "future idea" (v1.8 Codex cycle note). The most direct signal we do not yet ship: run a small skill-gated prompt set and measure accuracy delta vs. a known-honest baseline. If the advertised model is Opus 4.6 but the delta is Haiku-shaped, that is substitution evidence stronger than any fingerprint. Scope: 2-3 week project — GPQA / MMLU subset (20-30 questions, rotating pool to avoid memoization), grader (can be Claude itself with a strict rubric), cost model, baseline corpus build. Not one-session work. Why not now: ROADMAP #1 (local Docker validation) must run first — we need an honest-relay reference before we can claim a "delta" exists. Running v2.0 without a baseline produces noise. Dependencies: curated probe set, grader prompt, baseline data from at least one honest relay (local one-api + legitimate upstream key). Risk: score noise, question leakage into future training corpora, grader variance. Need many probes + CI bounds before reporting a verdict.

11. English-first README + blog-post announcement

Status: current README has English intro + 中文说明 section Scope: polish pass + a 500-word X/Twitter announcement thread Why: broader visibility after the Codex review loop gave us a credible quality story ("10 bugs found and fixed across 6 reviews, all with regression tests"). The 319-test count is a marketable data point. When: after item 1 or 4 — the tool should have one more differentiating feature before the announcement.


🤔 Long-term / uncertain

11a. v2.5 — LLMmap Pro active fingerprinting (breaks zero-dep invariant)

Status: sketched but shelved during v1.8 Pareto selection — PyTorch + transformers pull ~4.5 GB of dependencies, which directly breaks the zero-dependency invariant of the standalone audit.py. Scope: port LLMmap-style active probing (crafted adversarial questions whose response distribution distinguishes Claude vs GPT vs LLaMA vs Qwen vs DeepSeek) + optional embedding-distance scoring. Blocker: the zero-dep invariant. Options:

  • Option A: split into a separate repo api-relay-audit-deep with heavy deps, linked from README.
  • Option B: keep modular-only, gated behind pip install api-relay-audit[deep] extra. Standalone audit.py excludes it.
  • Option C: re-implement the classifier as pure numpy+stdlib with pre-trained weights shipped as a blob. Brittle. Decision: defer until someone demands it. The fingerprinting we ship in Step 12 (infra) + future v2.0 (capability delta) together cover the same detection question — active prompt-distribution fingerprinting is the third orthogonal signal but has the worst cost/benefit of the three.

11. AC-2 active webhook canary

Status: paper describes this as the highest-confidence AC-2 signal Blocker: requires a publicly reachable HTTPS endpoint to receive beacon requests. Breaks the zero-dep invariant. Needs domain name, HTTPS cert, webhook receiver service. When: if/when api-relay-audit gets a hosted component.

12. Full AC-1.b conditional-delivery detection

Status: paper §4.2.2 lists 5 theoretical trigger families (content keyword, user fingerprint, time windows, request count, tool name) Blocker: paper itself concludes "finite black-box auditing is fundamentally inadequate for conditional delivery." We can probe some families (Step 8 + --warmup partially covers request-count gating), but complete coverage requires many-round or long-running audits. Decision: accept this as an out-of-scope attack class. Document in README limitations.

13. Hosted web dashboard (hvoy.ai-style)

Status: hvoy.ai has a React/Vite dashboard that makes the tool approachable to non-developers Blocker: requires separate web app maintenance, API backend, auth. Changes the product from "one-curl-download" to "hosted service". Decision: out of scope for the CLI project. If demand emerges, spin off a separate repo.

14. Claude Code CLI header impersonation

Status: observed in hvoy.ai's get_headers function (impersonates claude-cli/2.0.76 + all x-stainless-* headers) Why not port: would make our requests indistinguishable from their tool — no differentiation benefit. Also, impersonating a specific CLI version is brittle (breaks when Claude Code bumps its version). Decision: permanently out of scope.


🚫 Explicitly NOT doing (and why)

These were evaluated and deliberately dropped. They are listed here so future contributors don't re-consider them without new information.

Item Why not
Token accounting audit (exact token counting) Paper out of scope; no clean offline tokenizer; character-ratio heuristic too noisy; breaks zero-dep invariant if tiktoken added.
Knowledge cutoff probe (hvoy.ai dimension 1) Author of hvoy.ai acknowledges it is trivially defeated by a relay hard-coding "May 2025" in system prompts. 50% of their score is wasted.
hvoy.ai 0-100 numeric scoring We use 6D boolean risk matrix for clearer downstream decisions. Numeric thresholds need recalibration every model generation.
Copy hvoy.ai's "null" text block body fingerprint Unclear purpose in upstream source; would make our requests indistinguishable from theirs (no benefit).
4-tier risk scale (adding CRITICAL) Requires Reporter class refactor; dashboard has downstream consumers; current LOW/MEDIUM/HIGH covers the action space.
Git branch split (main + web3) --profile runtime flag is strictly better: one codebase, one test suite, one distribution, single-source-of-truth memory. Branches would double maintenance cost and break test_dual_distribution_parity.
Auto-detection of OpenAI streaming Step 10 is Anthropic-only by design; OpenAI SSE schema differs. A Chinese relay that only speaks OpenAI format is correctly reported as "inconclusive" on Step 10, not "clean".

📐 Architectural invariants (must-preserve)

When adding any new feature, verify these hold before committing:

  1. Generated dual distributionpython3 scripts/build-standalone.py --check and tests/test_dual_distribution_parity.py must stay green. Any change to portable audit semantics belongs in modular source, then root audit.py must be regenerated. Add focused behavior or constant regression tests for new shared semantics.
  2. Zero-dependency standaloneaudit.py must run on vanilla Python 3.7+ with only curl available. No new pip dependencies in the standalone distribution. New third-party libs go in api_relay_audit/* modular only.
  3. Profile gating — any new detection step that only serves a subset of users (e.g. Web3-specific) must be gated by --profile web3|full and default to off under --profile general.
  4. Risk matrix monotonic — a new dimension can only add to the risk matrix, never weaken an existing determination. New dimensions go into HIGH or MEDIUM branches, never LOW.
  5. Memory-grounded decisions — before adding a feature, check ~/.claude/projects/C--Users-john-Downloads-api-relay-audit/memory/ for prior decisions on the same topic. Especially project_competitive_ landscape.md (so we don't re-invent hvoy.ai features) and reference_litellm_secret_regex.md (so Step 9 patterns stay in sync with LiteLLM's issue tracker).
  6. Codex review loop — any feature PR ≥200 LOC or adding a new detection step should get at least 2 rounds of independent Codex review. The review loop found 10 real bugs in this session that would otherwise have shipped; the cost (~2-5 min per round) is trivial compared to the false-negative risk.
  7. Attribution for ported concepts — when porting from hvoy.ai, SlowMist, LiteLLM, or one-api, add clear docstring attribution ("concept inspired by X, clean-room reimplementation"). License matters: LiteLLM is Apache-2.0 (can port code verbatim); hvoy.ai has no LICENSE (must be clean-room); SlowMist docs are narrative (ideas, not code).

🧭 How to use this roadmap

Starting a new session:

  1. Read the top "Shipped" section to know current state
  2. Read "Near-term candidates" — skip entries marked closed / historical, then pick one based on available time and current field evidence
  3. If the session is short (< 1 hour), prefer validation / docs / rebase-safe work. If longer, pick a feature candidate only after checking the architectural invariants and current PR queue.

Completing a feature:

  1. Move it from "Near-term" to "Shipped" with the commit hash
  2. Add any sub-items that got deferred to the appropriate section
  3. If the decision changed what was previously "explicitly not doing", update the reason or remove it

Proposing a new feature:

  1. First check the "Explicitly NOT doing" table — if it's listed, do not re-propose without new information
  2. Check "Architectural invariants" — does the feature break any?
  3. Draft the item in the appropriate time horizon section with a rationale and dependencies
  4. Run it through Codex review methodology during implementation