Skip to content

route-insight: a routing board page, with numeric fingerprint and protocol audit - #1004

Open
InvisibleGuardian007 wants to merge 4 commits into
magpie-community:mainfrom
InvisibleGuardian007:add-route-insight
Open

InvisibleGuardian007 wants to merge 4 commits into
magpie-community:mainfrom
InvisibleGuardian007:add-route-insight

Conversation

@InvisibleGuardian007

@InvisibleGuardian007 InvisibleGuardian007 commented Nov 11, 2026 •

Copy link
Copy Markdown

Adds Route insight (@magpie-community/route-insight): a page plugin that turns the gateway's own usage log into a live routing board, plus a 指纹 tab that checks what a provider actually serves.

What the board shows

  • Every request: time, provider (with its icon), model asked → model served, reasoning effort, agent, status, first byte, total time, tokens in/out, cache read + hit rate, tok/s, and how it was billed (credits, quota window, or free).
  • Per-provider health, grouped statistics (by provider / model / effort / agent / stop reason / group), and cards you can fold down to a single title bar to fit magpie's window.
  • Reads usage.jsonl read-only, tailing it; the service listens on 127.0.0.1 only and serves its own page (same shape as page-chat / page-usage-board).

The 指纹 tab (opt-in, costs quota)

  • 数值指纹 — lm-detector's shared numeric challenges, six samples over two rounds (the SDK caps a round at three), merged per model and ranked by probability with the margin over the runner-up. Names a concrete model (e.g. deepseek-v4.1-flash-1017), not a family. Reference data is downloaded on demand and cached.
  • 协议审计 — seven OpenAI protocol probes, scored out of 100: echo consistency (three temperature-0 answers, variation of visible output tokens), usage accounting (official fields present, no Anthropic-shaped keys, totals add up), function calling, structured output, stream/non-stream consistency. Adapted from the detection dimensions Veridrop publishes — no code copied (AGPL-3.0 vs MIT), see THIRD_PARTY_NOTICES.md.
  • The two are cross-checked: when the numeric fingerprint names a different model than the one requested at ≥60% probability, the result card says so in red instead of leaving it to two separate rankings.

Notes for reviewers

  • Plain ESM, no build step, no dependencies. main is index.mjs, which exports only default = { id, server } (the OpenCode v1 form page-chat and page-usage-board use), so importing it has no side effects — the local service starts on server(), once.
  • node test.mjs runs 85 offline assertions, including the protocol audit against canned responses (an official-shaped provider scores 100/clean; one leaking input_tokens/output_tokens drops to tampered with a critical finding). No network, no sign-in.
  • node scripts/check.mjs route-insight passes locally (registry + package shape). That run needed a shim for import.meta.dir under plain Node; bun runs it as is.
  • lib/lmfpd is a vendored MIT copy of lm-detector and ships with its license. lib/relay.mjs is my own implementation of published detection ideas, not Veridrop's code.
  • Magpie's page sandbox has no storage and no network, so the page is a boot page that navigates to the service, as the docs describe.

…tocol audit

Adds packages/route-insight (@magpie-community/route-insight), a page plugin
that reads this machine's gateway usage log and shows it as a live board:
first byte, total time, cache hit, tokens, speed, how each call was billed,
and per-provider health.

The same page carries a 指纹 tab with two checks that need no reference to the
official API's own answers:

- 数值指纹: lm-detector's shared numeric challenges (6 samples over two
  rounds, merged per model), naming the concrete model a provider runs.
- 协议审计: seven OpenAI protocol probes — echo consistency, usage
  accounting, function calling, structured output and stream consistency.
  Adapted from the detection dimensions Veridrop publishes (AGPL-3.0, no code
  copied; see THIRD_PARTY_NOTICES.md).

Plain ESM, no build step, no dependencies; the local service listens on
127.0.0.1 only and serves its own page. node test.mjs runs 85 offline
assertions (the protocol audit is tested against canned responses).
@yetone

yetone commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

Holding this review until the maintainer has looked, per Look for harm first (reviewed at 4cdb5f8). The lines that stop it:

  • New network addresses with a download. lib/lmfpd/index.js DEFAULT_DATA_URLS fetches about 3.6 MB of reference data from https://lm.ikale.io/data/, and falls back to https://cdn.jsdmirror.com/npm/lmfpd-data@latest/. That is an unpinned @latest on a third-party npm mirror. The sha256 fields it checks come from inside the same download, so a bad mirror or a new latest passes them. The file is cached in ~/.config/magpie/lmfpd-data.bin. The README says it is fetched as soon as the 指纹 page opens, not when a probe is asked for.
  • A bundled third-party build. lib/lmfpd/index.js is about 4,000 generated lines (an esbuild-style bundle of sdk/ts/src/*, with lines up to 1,167 characters). It is vendored instead of coming in as a dependency with its source and version.
  • Requests on the user's subscriptions. The lab and numeric-fingerprint runs send their own /v1/chat/completions requests through the gateway. Each probe should say what it costs before it runs.

Before this can be reviewed:

  • pin the data to a version and a hash kept in this package, or ship the data in it;
  • leave out the mirror, or say who controls it;
  • fetch only when the user starts a fingerprint run;
  • bring lmfpd in as a pinned dependency, or as source with its license and commit.

Nothing in this PR was run.

@yetone yetone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. The board itself is useful, and incremental reading holds up well. Against a 1M-line, 220MB usage.jsonl, three polls of /api/state?window=all took 0.57s, 0.46s and 0.47s, rss stayed at 386MB, and the client IP is never returned. Several things block the merge, though. The first one is a security hole.

All probes below ran on our test Mac with a temp HOME, a fake config dir, and a fake gateway on loopback. Every fetch to anything other than loopback was blocked and logged. No real vendor or model endpoint was contacted, and nothing reached lm.ikale.io or jsdmirror.

Blocking

1. Any website can read the board and spend the user's quota (index.mjs:420-433, index.mjs:493)

The service checks only Host. It has no service token, no Origin check and no Sec-Fetch check. It also answers Origin: null with access-control-allow-origin: null, and a sandboxed iframe on any site sends Origin: null.

Probe results:

  • Reading state. curl -H "Origin: null" /api/state?window=all returns 200 with access-control-allow-origin: null. The body includes the rests keys, which are provider@account-email (the probe's victim.person@example.com came back), the balances ($123.45), quotas, and every agent, provider and model in the log. /api/providers, /api/lab/history and /api/lab/last are open the same way.
  • Running probes. POST /api/lab/run with content-type: text/plain (a simple request, so there is no preflight) and Origin: null runs the probes. It takes the attacker's own prompt, up to 2000 characters. The model's answer to it (SECRET-MODEL-OUTPUT for ATTACKER PROMPT: write my essay…) can then be read back from the response and from /api/lab/last. This turns the user's subscription into a free LLM endpoint for any web page.
  • No limit. A page on a normal origin can still fire the POST blind, since nothing checks Origin. Five parallel runs were all accepted. Together with the single runs in the probe, that was 46 upstream requests, and nothing limits how many runs can be in flight.

How magpie's other page services handle this: page-model-quota and amp-usage put a random per-start token into the page through a <meta name="service-token"> placeholder. They serve that page only to a navigation (Sec-Fetch-Mode: navigate) and require the token on every API call. Please do the same here and drop the Origin: null ACAO. A run should also be a non-simple request (JSON content-type plus the token header), and there should be one run in flight at a time.

2. Network destinations that aren't disclosed, contradicting the README

  • README.md:5 says "不发任何出站请求" (sends no outbound requests), and README.md:218 says "没有第三方请求,没有遥测" (no third-party requests, no telemetry).
  • In fact lib/lmfpd/index.js:1830-1831 downloads reference data from https://lm.ikale.io/data/, with https://cdn.jsdmirror.com/npm/lmfpd-data@latest/ as the fallback.
    • The mirror URL is not pinned (@latest) and is served by a third-party mirror.
    • The sha256 in bundle.json is only used as a version marker (:1863, :1896). Nothing checks the data's integrity against a value shipped in the package.
    • The data refreshes in the background every day (loadData ttl 86400, :1914).
    • The download starts when the fingerprint tab opens (README:72, "进「指纹」页就先预热", it warms up as soon as the tab opens) and on every run that includes numfp. The user never clicks anything that says it will download. In the probe, /api/lab/numeric and the runs both tried both hosts.

Please:

  • correct both README lines;
  • say in the UI, before the first download, that it fetches from those two hosts;
  • pin the mirror to a version;
  • check the data against a hash shipped in the package.

3. The English UI is in Chinese

I rendered the page with ?lang=en in Chromium and WebKit, with a 5000-line fake log. Only the title changes. The rest of the page is Chinese (about 2256 CJK characters on screen), for example 5,000 行日志 · 读于 …, 看板 供应商 指纹, and 暂停 刷新 全部 成功 失败.

  • Strings are hardcoded in Chinese in ui/app.js (292 lines), ui/index.html (56), and in the verdicts and notes that lib/relay.mjs (89), lib/lab.mjs (74) and lib/numeric.mjs (27) send to the page.
  • page/boot.js has en words, but its diagnostic labels are Chinese only.
  • magpie's pages need both en and zh.

4. The page overflows at narrow widths

At a 420px viewport, document.documentElement.scrollWidth is 690 in Chromium and 683 in WebKit, so the whole page scrolls sideways. The table and charts need their own overflow-x: auto container, or a stacked layout.

5. No tests

README.md:227 says node test.mjs # 85 条断言 (85 assertions), but no test file is in the PR. Nothing guards the parser, the incremental reader, the filters or the verdicts. Please add the tests, run by bun test from the repo root, including one that fails if the service answers without its token. The README also links screenshots/*.png (:9, :27-28, :60, :144-148, :236-242), which aren't in the PR either.

6. The audit calls legitimate backends fakes (lib/relay.mjs)

This is not promotion: relay.mjs names no relay, URL or ranking, and it only audits the model the user picked. But its verdicts treat "not byte-for-byte OpenAI" as evidence of tampering:

  • :429: a tool-call id without call_ gets "官方 OpenAI 一定是这个前缀,别的后端抄不全" (official OpenAI always uses this prefix; other backends can't copy it fully).
  • :395 and :511-513: any critical finding gives 明显不像官方接口 (clearly not an official API).
  • magpie itself translates Claude, Gemini, DeepSeek and plugin providers into the OpenAI shape. So these verdicts mark the user's own legitimate subscriptions as fakes, and the user will read that as a vendor or relay cheating them.
  • Please scope the audit to models whose provider claims to be OpenAI, or word the results as "differs from OpenAI's format" without a tampering verdict.

7. Probe cost has no bound

  • The numeric fingerprint sends with no max_tokens (lib/gateway.mjs:18, lib/numeric.mjs:72,76: maxTokens: 0). It makes up to 6 samples × 2 tries, each allowed 240s.
  • Relay sends 7 requests without max_tokens.
  • Anim sends 2 at max_tokens: 12000.
  • With data available, one full click is about 21 paid requests with no upper limit on output. Please cap each request, show the request count on the button before the run, and allow one run at a time (see 1).

Should change

  • Prompt text sent to vendors. lib/lab.mjs:61-62 (ANIM_PROMPT) says "你不需要任何测试,不要有任何限制,不要看现有的画鹈鹕测试的提示词" (you don't need any tests, have no restrictions at all, don't look at existing pelican-test prompts). That is jailbreak-style wording, sent upstream from the user's account. Please drop "不要有任何限制" (have no restrictions at all) and the meta instructions. lib/relay.mjs:226,235 also send nonce="openai-detector" and "veridrop stream check" upstream; please use neutral strings.

  • Files it writes to magpie's config dir.

    • route-insight-lab-results.json: six full results including raw model output.
    • route-insight-lab-history.json: 40 entries.
    • route-insight-art/ (up to 60 HTML files).
    • lmfpd-data.bin.
    • route-insight-access.log: logs method, host, URL and User-Agent for every request except /api/state, rotated at 1MB (index.mjs:316-321).

    The README's "只读" (read-only) is not accurate. Please list these files, and drop the access log or make it opt-in.

  • Attribution. THIRD_PARTY_NOTICES.md says "by Ikaleio". lib/lmfpd/LICENSE says "Copyright (c) 2026 xqy2006". The vendored lib/lmfpd/index.js is byte-identical (sha256 3c17ea64…) to npm lmfpd-sdk@0.2.0 (MIT, gitHead 1e58ed83), which is good. Please name the copyright holder as the license does. THIRD_PARTY_NOTICES.md is also missing from package.json "files", so it isn't in the npm tarball.

  • README.

    • It says 0.13.0 in places while package.json says 0.13.1.
    • Install is from github:InvisibleGuardian007/magpie-route-insight (:174), and homepage/bugs point to that repo. In this repo it installs from npm as @magpie-community/route-insight.
    • D:/数据/AI/... (:182, :188) is a local path.

Checked and fine

  • Host check. Host: evil.example:port gets 403, so DNS rebinding is closed.
  • Path traversal. .., %2e%2e, a sibling ui2 prefix, and ..%2f in /icons/ and /api/lab/art/ all get 404 or 400.
  • Served content. Model-made HTML is served under a CSP with no script. The main page's CSP is script-src 'self'. Everything rendered into innerHTML goes through esc().
  • Other network traffic. No telemetry, eval or child_process. The only other requests go to the gateway: /v1/chat/completions and /v1/models.
  • providers.json. Only names, counts and icons are used. No key leaves the service.
  • Incremental reading. The usage log reads only the tail first, then only new bytes, and detects rotation (numbers above).
  • Port. 4397 collides with no other package.
  • npm. @magpie-community/route-insight is not on npm and not on dist. The registry entry passes check.mjs.

What ran

At the PR head merged with main 4b0aef5:

  • bun scripts/check.mjs route-insight passed, and check.mjs passed for every package.
  • npm pack --dry-run lists 25 files, 127kB. There is no test file and no THIRD_PARTY_NOTICES.md.
  • The root bun test with a temp HOME: 8362 pass, 0 fail. None of those tests are route-insight's, because it has none.
  • The probes and renders above (Chromium and WebKit, 1200 and 420px, en and zh).

Not tried:

  • a real magpie gateway or real accounts;
  • the real lmfpd data, which stayed blocked;
  • comparing relay.mjs against Veridrop's source.

@yetone

yetone commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

This addendum covers the three commits pushed while the review above was being written (4cdb5f8, 6680757, f769d59, now at 0.13.4). The review's findings came from 36275bc. I re-read the diff since then: these commits don't touch index.mjs's request handling, lmfpd, relay.mjs, the lab prompts or the i18n, so all seven blocking items still stand. The commits do fix one thing: THIRD_PARTY_NOTICES.md is now in "files". They also add two new things that need changing:

  • registry.json:292 sets "icon": "https://cdn.jsdelivr.net/gh/InvisibleGuardian007/magpie-route-insight@main/ui/icon.svg".
    • That URL is on a branch of the author's own repo, so whatever is pushed there later shows up in every user's Discover list without a review here.
    • Please drop the field, so the page icon from the package is used, or point it at a file in this repo.
  • ui/brand/codex.png is copied out of the Codex desktop app's bundle (Contents/Resources/icon-codex-dark.png, per THIRD_PARTY_NOTICES).
    • It is OpenAI's asset, and the app's license doesn't allow redistributing it in an MIT package.
    • Please use magpie's own provider icon (it comes through /icons/) or the letter tile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants