Skip to content

capture: port the shell's packet-capture subsystem as a governed sidecar#85

Merged
mdheller merged 1 commit into
mainfrom
capture-sidecar
Jul 23, 2026
Merged

capture: port the shell's packet-capture subsystem as a governed sidecar#85
mdheller merged 1 commit into
mainfrom
capture-sidecar

Conversation

@mdheller

Copy link
Copy Markdown
Contributor

The native macOS shell had a full network-visibility subsystem — packet capture (BBPacketCapture, tshark/tcpdump), a live connection map (BBNetworkMonitor), and a per-domain firewall (BBFirewall) — that never made it into the production Gecko build. This ports the whole thing as capture-sidecar/, a loopback-only Rust sidecar (cloned from the iot-sidecar shape), so it works on Linux/Windows/macOS.

Surfaces

  • Packet capture — engine detect (dumpcap › tshark › tcpdump, incl. Windows Wireshark paths), whole-session capture on any, live line stream over /events WS, save pcap (dumpcap writes real pcapng; text engines buffered).
  • Connection map — eTLD+1 + tracker/analytics/CDN classifier ported verbatim from the shell; the browser feeds observations via POST /map.
  • Firewall — per-domain Ask/Allow/Block, persisted to JSON.

Governance (why a sidecar, not ObjC)

Every side effect goes through the canonical agent-control-bridge.py --surface capture. Packet-capture START and pcap EXPORT are the exfiltration vectors, so they're prohibited by default and reclassify to gated only on an explicit cockpit user gesture (actor=user AND userGesture=true). An injected/agent planner can never silently record or export traffic. Fail-closed on any gate error, mirroring the iot sidecar's one-engine invariant.

Also fixes a pre-existing bridge bug

CLI --param values weren't coerced, so userGesture=true arrived as a string and gesture-gated conditions could never permit through the CLI — the exact path the Rust sidecar gates use (only the in-process API the tests used worked). Now coerced via _parse_flow, so the enforced path matches the tested path. The iot containment test stays 20/20.

Verified

  • capture-sidecar unit tests (eTLD / classify / firewall round-trip) pass
  • test_capture_injection_containment.py 16/16 — agent capture/export BLOCKED, forged gesture BLOCKED, user-gesture+token PERMITTED, reads allowed
  • live binary smoke — detects real dumpcap, classifies doubleclick.net=tracker, POST /capture/start(agent) → HTTP 403, loopback-only bind
  • build-capture-sidecar.sh — cargo build + 3-way smoke (refuses ungoverned start, /health 200, agent capture 403)

Not in this PR (next commit)

The cockpit panel UI + auto-launch wiring (assemble-cockpit.sh staging, bearbrowser-cockpit-up launch + port, runtime/cockpit-config.js base). This PR is the proven engine + governance; the visible-in-browser surface layers on next — same split the iot-sidecar already followed.

The native macOS shell had a full network-visibility subsystem (BBPacketCapture
+ BBNetworkMonitor + BBFirewall) that never made it into the Gecko build. This
ports it as capture-sidecar/ — a loopback-only Rust sidecar (cloned from the
iot-sidecar shape) providing the same three surfaces cross-platform:

- Packet capture: engine detect (dumpcap > tshark > tcpdump, +Windows paths),
  whole-session capture on 'any', live line stream over /events WS, save pcap.
- Connection map: eTLD+1 + tracker/analytics/cdn classifier ported verbatim
  from the shell; browser feeds observations via POST /map.
- Firewall: per-domain Ask/Allow/Block, persisted to JSON.

Governance (the reason this is a sidecar, not ObjC): every side effect is
classified by the canonical agent-control-bridge --surface capture. Packet
capture START and pcap EXPORT are the exfiltration vectors, so they are
prohibited by default and reclassify to gated ONLY on an explicit cockpit user
gesture (actor=user AND userGesture=true) — an injected/agent planner can never
silently record or export traffic. Fail-closed on any gate error.

Also fixes a pre-existing bridge bug: CLI --param values weren't coerced, so
'userGesture=true' arrived as a string and gesture-gated conditions could NEVER
permit via the CLI path (the path the Rust gates use) — only via the in-process
API the tests used. Now coerced via _parse_flow, so the enforced path matches
the tested path. iot containment test still 20/20.

Proven:
- capture-sidecar unit tests (eTLD/classify/firewall) pass
- test_capture_injection_containment.py 16/16: agent capture/export BLOCKED,
  forged gesture BLOCKED, user-gesture+token PERMITTED, reads allowed
- live binary smoke: detects dumpcap, classifies doubleclick.net=tracker,
  agent capture-start -> HTTP 403, loopback-only bind
- build-capture-sidecar.sh: cargo build + 3-way smoke (refuses ungoverned,
  /health 200, agent capture 403)

Not yet wired: cockpit panel UI + auto-launch in assemble-cockpit/cockpit-up +
runtime/cockpit-config base. Next commit.
@mdheller
mdheller merged commit cf68f7a into main Jul 23, 2026
18 checks passed
@mdheller
mdheller deleted the capture-sidecar branch July 23, 2026 15:06
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.

1 participant