# v0.3.0 - Host-network bridge: browse any remote host's localhost

## TL;DR
The containerized Firefox can now reach a dev app running on **any other host's `localhost`** - your laptop, a DinD container, a client VM - no matter what network either side sits on. Two new tools (`connect_host_network` / `disconnect_host_network`, behind `--enable-bridge`) plus a tiny companion package, `@luutuankiet/firefox-bridge`, that you run on the remote host. One shared secret, no inbound ports on either end, no relay server to babysit.

## Why
The browser only sees its own host's network. When an app serves on some *other* host's `localhost:3000` (a DinD container, your Mac, a client box), the browser can't reach it - so you can't screenshot or verify that frontend. This closes that gap at the browser level, so the browser relocating onto a different network later doesn't break it.

## Highlights
| Capability | Detail |
|---|---|
| Zero-ingress both ends | Connection is established purely outbound; neither side opens an inbound port. Works behind NAT/firewalls. |
| Symmetric secret identity | Both ends derive the *same* ed25519 keypair from one shared secret (`sha256("firefox-bridge/v1\n" + secret)`). The server firewalls to exactly that key. No SSH keys, no key exchange. |
| No self-owned infra in the data path | HyperDHT does introduction + NAT holepunching only (public commons bootstrap, self-hostable). Data path is a direct, Noise-encrypted P2P stream, with a decentralized blind-relay fallback for symmetric-NAT-both-ends. |
| Browser-level, live | Flips Firefox's SOCKS proxy prefs at runtime (privileged context) - `localhost` in the browser becomes the remote host's `localhost` with `socks_remote_dns` + `allow_hijacking_localhost`. No container restart. |
| One bridge at a time | Global pref flip; connect captures the baseline and disconnect restores it. |

## How it works
1. On the remote host: `npx @luutuankiet/firefox-bridge --secret <secret>`. It announces a HyperDHT server under the secret-derived key and runs an in-process SOCKS5 exit.
2. In firefox-mcp: `connect_host_network` with the same secret. It probes reachability, starts a local dumb-pipe listener, opens one Noise stream per browser connection to the remote bridge, and points Firefox's SOCKS proxy at the listener.
3. Because the SOCKS5 exit runs on the remote host, DNS and `localhost` resolve *there*. Navigate to `http://localhost:3000` and you hit the remote dev app.
4. `disconnect_host_network` restores the captured proxy prefs and tears the tunnel down.

## Config
- New flag: `--enable-bridge` (env `ENABLE_BRIDGE=true`). Requires `--enable-privileged-context` (and `MOZ_REMOTE_ALLOW_SYSTEM_ACCESS=1` in the Firefox container).
- New dependency: `hyperdht` (MIT), loaded via `createRequire` and kept external in the bundle (same pattern as gifenc/pngjs).

## Companion package
`@luutuankiet/firefox-bridge` - a lightweight, zero-build ESM package (only `hyperdht` at runtime, no selenium) published alongside this release. It's the piece you run on the remote host.

## Files changed
- New: `src/bridge/client.ts`, `src/bridge/prefs.ts`, `src/tools/bridge.ts`
- New package: `packages/firefox-bridge/`
- Registration: `src/index.ts`, `src/tools/index.ts`, `src/cli.ts`, `tsup.config.ts`, `package.json`
- CI: `publish.yml` now publishes both packages with OIDC provenance
