feat(native-bridge): native-messaging bridge spike (0289 Option C)#442
Merged
Conversation
The pure-web PWA (no bundled Electron host) can reach a local model with no loopback HTTP port, no CORS, and no DNS-rebinding surface (the 1Password pattern). Follow-up to exploration 0289; keeps the shipped pairing-code bridge (Option B) as the default. Private, tested POC in packages/native-bridge-extension: - MV3 extension: externally_connectable allowlist + fixed key -> stable ID - background.js relay: page <-> native host, sender.origin re-checked - native host (xnet-bridge-host.mjs): 4-byte-LE stdio framing -> backend, drains in-flight replies before exit; cli backend spawns claude/codex with NO port, daemon backend forwards to the hardened bridge + token - install-host.mjs pins native-host allowed_origins to the derived extension ID (crx-id.mjs); page connector mirrors the ChatAgent contract - 28 tests (framing, relay + both backends incl. a round-trip through the real hardened daemon, page protocol, ext-ID derivation, real-process e2e) Registered under the vitest integration project; checks off the last 0289 item with a UX comparison (B stays default, C is an opt-in upgrade). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Contributor
|
✓ Changelog fragment found — thanks! |
Contributor
|
Preview removed for PR #442. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to exploration 0289 ("Securely connecting the browser to a local model") — the one remaining unchecked item: Option C, a browser-extension + native-messaging bridge so the pure-web PWA (
https://app.xnet.fyi, no bundled Electron host) can reach a local model with the strongest origin binding — no loopback HTTP port, no CORS, no DNS-rebinding surface (the 1Password pattern).B stays the default. The shipped pairing-code bridge (Option B) remains the default trust path; this is a spike + UX comparison, proposed as an opt-in upgrade — not wired into the deployed app.
What's here —
packages/native-bridge-extension/(private, no build, no changeset)externally_connectableorigin allowlist + a fixedkey→ deterministic ID;background.jsrelays page ↔ native host and re-checkssender.origin.xnet-bridge-host.mjs) — 4-byte-LE stdio framing → backend; drains in-flight replies before exit. Two backends:clispawns the user'sclaude/codex(no port anywhere),daemonforwards to the hardened bridge over loopback carrying its pairing token (reuse MCP tools //run/ Ollama proxy).install-host.mjs) pins the native-host manifestallowed_originsto the derived extension ID (crx-id.mjs) — the OS enforces that only this extension may launch the host.web/extension-connector.mjs) mirrors theChatAgentcontract so it slots into the connector ladder as anextensiontier (documented drop-in; doesn't touch the churnydetect.ts).Two allowlists — the extension's
externally_connectableand the native-host manifest'sallowed_origins— are the entire trust story. Inclimode no loopback port faces the browser, so the DNS-rebinding/drive-by surface Option B defends with a token simply doesn't exist.Tests — 28, registered under the vitest integration project
Framing codec (partial/multi/oversize frames, multibyte UTF-8), relay + both backends including a round-trip through the real hardened bridge daemon with its pairing token, the page protocol against a fake
chrome, extension-ID derivation, and a real-process end-to-end that spawns the host and drives it with native-messaging frames. Found & fixed one real bug: the host exited on stdin-endand dropped an in-flight async reply — now drains first.UX finding (full table in the package README)
Recommendation: keep B as default; pursue C as an opt-in "install the xNet bridge extension" upgrade for a browser-only install with no bundled daemon.
Checks off the final item in the 0289 doc and adds a Spike Outcome section.
🤖 Generated with Claude Code