Skip to content

feat: in-app cross-device LAN reachability (circle key + LAN toggle)#89

Merged
bahadirarda merged 4 commits into
mainfrom
feat/lan-cross-device
May 26, 2026
Merged

feat: in-app cross-device LAN reachability (circle key + LAN toggle)#89
bahadirarda merged 4 commits into
mainfrom
feat/lan-cross-device

Conversation

@bahadirarda
Copy link
Copy Markdown
Contributor

Summary

Makes cross-device peering work end-to-end from the desktop app — no CLI. Two layers, both now one-click in Network → Cross-device:

  • Trust (circle key): Generate / Join / Copy / Leave (shipped in v0.22.169).
  • Transport (LAN reachability): a new toggle. The managed daemon is loopback-only by design; flipping the toggle rebinds it to 0.0.0.0 with --allow-lan + token auth so circle peers can read this device's agent list.

Security model

  • A non-loopback bind is never --no-auth and always carries a token (unit-tested invariant). Code-executing endpoints stay bearer-only; only the read-only circle endpoints are LAN-reachable, gated by the circle key.
  • Loopback-trust: when LAN-exposed, requests from 127.0.0.1/::1 bypass the token, so local consumers (codex / claude-code / gemini) keep working with no token and no reprovisioning. LAN callers still need the token (full) or circle key (read-only). Gated on --allow-lan, so relay / explicit token-only deployments are unchanged.
  • First enable installs the platform firewall rules via serve's --allow-lan hook (Windows UAC; macOS no-op).
  • Default off — loopback/no-auth behaviour is byte-for-byte unchanged until the operator opts in.

Changes

  • internal/daemon: LanExposurePath/Enabled/SetLanExposure + config-driven daemonServeArgs (pure, tested).
  • internal/server: trustLoopback in authMiddleware/circleOrBearer + isLoopbackRequest (tested).
  • internal/cli: clawtool daemon lan <on|off|status>.
  • cmd/clawtool-installer: LanStatus/LanEnable/LanDisable + the Network card LAN toggle.

Test plan

  • make ci-fast (fmt/vet/build/version-sync/test -race) green
  • Unit: security invariants (LAN bind never no-auth, always token), loopback bypass vs LAN token enforcement, isLoopbackRequest, LAN toggle, daemonServeArgs
  • installer builds + frontend JS valid
  • Real-machine validation (needs two devices): enable the toggle + set a shared circle key on Mac + Windows, confirm each lists the other's agents and local codex/claude-code keep working

Add an opt-in LAN-exposure marker (LanExposurePath / LanExposureEnabled /
SetLanExposure). When set, Ensure binds the managed daemon to 0.0.0.0 with
--allow-lan + a token file instead of the 127.0.0.1 --no-auth loopback
default, so circle peers can reach the read-only A2A endpoints. Security
invariant (unit-tested): a non-loopback bind is NEVER --no-auth and always
carries a token, so code-executing endpoints stay bearer-gated against the
network; only the circle-key read-only endpoints are LAN-reachable. Default
off — loopback/no-auth behaviour is byte-for-byte unchanged.

Foundation only: enabling still needs the app toggle + a loopback-trust
middleware tweak so local consumers keep working without a token.
…umers

When the daemon is LAN-exposed (--allow-lan, token enforced), requests
originating from loopback (127.0.0.1/::1) now bypass the token — the
machine is still its own trust boundary, so codex / claude-code / gemini
keep reaching the daemon over loopback with no token and no reprovisioning.
LAN callers still need the bearer token (code-exec) or circle key (read-only
enumeration). Gated on AllowLAN, so relay / explicit token-only deployments
are unchanged (loopback still requires the token there). Unit-tested:
loopback bypass, LAN token enforcement, and the no-trust path.
Wraps daemon.SetLanExposure + a daemon restart so Ensure rebinds: `on`
exposes the daemon to LAN circle peers (0.0.0.0 + token; loopback stays
token-free), `off` returns it to loopback. `status` prints on/off for the
desktop app to read. The first `on` boot installs the platform firewall
rules via serve's --allow-lan hook.
The Network → Cross-device card gains a "Reachable on your LAN" switch
alongside the circle key. Flipping it on runs `daemon lan on` (rebinds the
daemon to 0.0.0.0 + token, loopback stays token-free, firewall auto-prompt
on first enable) so circle peers can read this device's agent list; off
returns to loopback. Backed by LanStatus/LanEnable/LanDisable App methods.
Completes the in-app cross-device flow: shared circle key (trust) + LAN
reachability (transport), no CLI.
@bahadirarda bahadirarda merged commit 32d5573 into main May 26, 2026
7 checks passed
@bahadirarda bahadirarda deleted the feat/lan-cross-device branch May 26, 2026 10:33
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