burrow/evolution/proposals/BEP-0005-daemon-ipc-and-apple-boundary.md
JettChenT d1638726ca Add proxy subscription runtime support
Add daemon RPCs, Apple and GTK import flows, packet proxy runtime support, diagnostics, and BEPs for proxy subscription handling.

Redact subscription URL secrets from fetch errors before they reach logs or UI surfaces.
2026-06-01 15:23:17 +08:00

5.1 KiB

BEP-0005 - Daemon IPC and Apple Boundary

Status: Draft
Proposal: BEP-0005
Authors: gpt-5.4
Coordinator: gpt-5.4
Reviewers: Pending
Constitution Sections: II, III, IV, V
Implementation PRs: Pending
Decision Date: Pending

Summary

Burrow should formalize one Apple/runtime boundary: Apple clients speak only to the daemon over gRPC on the app-group Unix socket, and the daemon owns all external control-plane, helper-process, and runtime coordination work. This prevents UI code from accreting side HTTP paths or ad hoc control-plane integrations that bypass the system Burrow is supposed to own.

Motivation

  • The current Tailnet work already showed the failure mode: Swift UI code started reaching around the daemon boundary to talk to helper HTTP endpoints directly.
  • Apple-specific process ownership is easy to blur between the app, the network extension, and helper daemons unless the contract is explicit.
  • If Burrow wants a durable multi-runtime architecture, the daemon must remain the only orchestration boundary between clients and control/data-plane behavior.

Detailed Design

  • Apple UI and Apple support libraries may call only daemon gRPC methods over the declared Burrow Unix socket.
  • Direct Swift calls to external control-plane HTTP APIs, localhost helper HTTP servers, or runtime-specific subprocesses are forbidden.
  • The daemon is responsible for:
    • discovery of Tailnet authorities and related metadata
    • control-plane session setup and tracking
    • login/session lifecycle brokering
    • runtime start/stop/reconcile
    • translating helper or bridge processes into stable daemon RPCs
  • Apple uses two daemon sockets with one shared app-group database:
    • the app-facing control socket, burrow.sock, is for UI management RPCs such as network list, import, preview, refresh, node selection, account/login control, and other non-packet workflows
    • the packet-tunnel socket, burrow-packet-tunnel.sock, is opened by PacketTunnelProvider and owns active tunnel runtime RPCs such as TunnelStart, TunnelConfiguration, TunnelPackets, and TunnelStop
  • The Network Extension must host or connect to the packet-tunnel daemon socket while a VPN session is active. The app must not hot-swap packet runtime state underneath an already-running Apple NEPacketTunnelProvider.
  • UI-side network mutations may persist desired state through the control daemon. If those mutations affect active packet-tunnel settings or runtime, the Apple client must restart or otherwise reassert the Network Extension so settings and packet runtime are installed from the same selected network state.
  • burrow/src/control/ owns transport-neutral control-plane semantics such as discovery, authority normalization, and request/response shaping.
  • Apple UI owns presentation only:
    • forms
    • local state
    • presenting returned auth URLs or statuses
    • surfacing daemon availability and errors
  • Any new Apple-facing runtime capability requires a daemon RPC first.

Security and Operational Considerations

  • Keeping control-plane I/O out of Swift UI reduces accidental secret, token, and callback sprawl across app code.
  • The daemon boundary makes testing and kill-switch behavior tractable because runtime integration is localized.
  • Apple daemon lifecycle ownership must be explicit: the app owns the control daemon used for presentation workflows, and the Network Extension owns the packet daemon used for active tunnel runtime. Both use the app-group database as the durable desired-state store.
  • Non-Apple presentation clients should follow the same daemon-first lifecycle pattern: connect to a managed daemon when present, or start a user-scoped embedded daemon before issuing RPCs, without adding platform-local control-plane paths.

Contributor Playbook

  • Before adding a new Apple-side workflow, identify the daemon RPC that should own it.
  • If the RPC does not exist, add the protocol shape in proto/burrow.proto, implement it in the daemon, and only then wire Swift UI.
  • Verify that no Swift UI or support code calls external control-plane HTTP endpoints directly.
  • For Tailnet and similar flows, test:
    • daemon unavailable behavior
    • successful RPC path
    • error propagation through the UI
  • Keep Linux GTK and Apple clients visually and functionally aligned around the same daemon-backed home surface: Networks, Accounts, Tunnel, and add flows should remain corresponding views over the daemon API.

Alternatives Considered

  • Let Apple UI call control-plane endpoints directly for convenience. Rejected because it creates parallel orchestration paths and breaks the daemon contract.
  • Allow one-off exceptions for login helpers. Rejected because those exceptions become the architecture.

Impact on Other Work

  • Governs the Tailnet refactor and future Apple runtime work.
  • Governs Linux GTK daemon startup parity where the same daemon API is reused from a user-scoped presentation process.
  • Interacts with BEP-0002 control-plane bootstrap and BEP-0003 transport refactoring.

Decision

Pending.

References

  • Apple/UI/
  • Apple/Core/
  • Apple/NetworkExtension/
  • burrow/src/daemon/
  • burrow/src/control/