feat(discovery): bonjour beacons + bridge presence
This commit is contained in:
64
docs/bonjour.md
Normal file
64
docs/bonjour.md
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
summary: "Bonjour/mDNS discovery + debugging (Gateway beacons, clients, and common failure modes)"
|
||||
read_when:
|
||||
- Debugging Bonjour discovery issues on macOS/iOS
|
||||
- Changing mDNS service types, TXT records, or discovery UX
|
||||
---
|
||||
# Bonjour / mDNS discovery
|
||||
|
||||
Clawdis uses Bonjour (mDNS / DNS-SD) as a **LAN-only convenience** to discover a running Gateway and (optionally) its bridge transport. It is best-effort and does **not** replace SSH or Tailnet-based connectivity.
|
||||
|
||||
## What advertises
|
||||
|
||||
Only the **Node Gateway** (`clawd` / `clawdis gateway`) advertises Bonjour beacons.
|
||||
|
||||
- Implementation: `src/infra/bonjour.ts`
|
||||
- Gateway wiring: `src/gateway/server.ts`
|
||||
|
||||
## Service types
|
||||
|
||||
- `_clawdis-master._tcp` — “master gateway” discovery beacon (primarily for macOS remote-control UX).
|
||||
- `_clawdis-bridge._tcp` — bridge transport beacon (used by Iris/iOS nodes).
|
||||
|
||||
## TXT keys (non-secret hints)
|
||||
|
||||
The Gateway advertises small non-secret hints to make UI flows convenient:
|
||||
|
||||
- `role=master`
|
||||
- `lanHost=<hostname>.local`
|
||||
- `sshPort=<port>` (defaults to 22 when not overridden)
|
||||
- `gatewayPort=<port>` (informational; the Gateway WS is typically loopback-only)
|
||||
- `bridgePort=<port>` (only when bridge is enabled)
|
||||
- `tailnetDns=<magicdns>` (optional hint; may be absent)
|
||||
|
||||
## Debugging on macOS
|
||||
|
||||
Useful built-in tools:
|
||||
|
||||
- Browse instances:
|
||||
- `dns-sd -B _clawdis-master._tcp local.`
|
||||
- `dns-sd -B _clawdis-bridge._tcp local.`
|
||||
- Resolve one instance (replace `<instance>`):
|
||||
- `dns-sd -L "<instance>" _clawdis-master._tcp local.`
|
||||
- `dns-sd -L "<instance>" _clawdis-bridge._tcp local.`
|
||||
|
||||
If browsing shows instances but resolving fails, you’re usually hitting a LAN policy / multicast issue.
|
||||
|
||||
## Common failure modes
|
||||
|
||||
- **Bonjour doesn’t cross networks**: London/Vienna style setups require Tailnet (MagicDNS/IP) or SSH.
|
||||
- **Multicast blocked**: some Wi‑Fi networks (enterprise/hotels) disable mDNS; expect “no results”.
|
||||
- **Sleep / interface churn**: macOS may temporarily drop mDNS results when switching networks; retry.
|
||||
|
||||
## Disabling / configuration
|
||||
|
||||
- `CLAWDIS_DISABLE_BONJOUR=1` disables advertising.
|
||||
- `CLAWDIS_BRIDGE_ENABLED=0` disables the bridge listener (and therefore the bridge beacon).
|
||||
- `CLAWDIS_BRIDGE_HOST` / `CLAWDIS_BRIDGE_PORT` control bridge bind/port.
|
||||
- `CLAWDIS_SSH_PORT` overrides the SSH port advertised in `_clawdis-master._tcp`.
|
||||
- `CLAWDIS_TAILNET_DNS` publishes a `tailnetDns` hint (MagicDNS) in `_clawdis-master._tcp`.
|
||||
|
||||
## Related docs
|
||||
|
||||
- Discovery policy and transport selection: `docs/discovery.md`
|
||||
- Node pairing + approvals: `docs/gateway/pairing.md`
|
||||
@@ -42,6 +42,8 @@ Target direction:
|
||||
- The **gateway** advertises itself (and/or its bridge) via Bonjour.
|
||||
- Clients browse and show a “pick a master” list, then store the chosen endpoint.
|
||||
|
||||
Troubleshooting and beacon details: `docs/bonjour.md`.
|
||||
|
||||
#### Current implementation
|
||||
|
||||
- Service types:
|
||||
@@ -59,6 +61,8 @@ Disable/override:
|
||||
- `CLAWDIS_DISABLE_BONJOUR=1` disables advertising.
|
||||
- `CLAWDIS_BRIDGE_ENABLED=0` disables the bridge listener.
|
||||
- `CLAWDIS_BRIDGE_HOST` / `CLAWDIS_BRIDGE_PORT` control bind/port.
|
||||
- `CLAWDIS_SSH_PORT` overrides the SSH port advertised in the master beacon (defaults to 22).
|
||||
- `CLAWDIS_TAILNET_DNS` publishes a `tailnetDns` hint (MagicDNS) in the master beacon.
|
||||
|
||||
### 2) Tailnet (cross-network)
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ Target direction:
|
||||
- The bridge is transport only; it forwards/scopes requests and enforces ACLs, but pairing decisions are made by the gateway.
|
||||
|
||||
The macOS UI (Swift) can:
|
||||
- Subscribe to `node.pair.requested`, show an alert, and call `node.pair.approve` or `node.pair.reject`.
|
||||
- Subscribe to `node.pair.requested`, show an alert (including `remoteIp`), and call `node.pair.approve` or `node.pair.reject`.
|
||||
- Or ignore/dismiss (“Later”) and let CLI handle it.
|
||||
|
||||
## Implementation note
|
||||
|
||||
@@ -7,7 +7,7 @@ read_when:
|
||||
---
|
||||
# iOS Node (internal) — Voice Trigger + Screen/Canvas
|
||||
|
||||
Status: design plan (internal/TestFlight) · Date: 2025-12-12
|
||||
Status: prototype implemented (internal) · Date: 2025-12-13
|
||||
|
||||
## Goals
|
||||
- Build an **iOS app** that acts as a **remote node** for Clawdis:
|
||||
@@ -43,8 +43,8 @@ Why:
|
||||
|
||||
## Security plan (internal, but still robust)
|
||||
### Transport
|
||||
- Bridge listens on LAN and uses **TLS**.
|
||||
- Prefer **mutual authentication** (mTLS-like) or explicit public key pinning after pairing.
|
||||
- **Current (v0):** bridge is a LAN-facing **TCP** listener with token-based auth after pairing.
|
||||
- **Next:** wrap the bridge in **TLS** and prefer key-pinned or mTLS-like auth after pairing.
|
||||
|
||||
### Pairing
|
||||
- Bonjour discovery shows a candidate “Clawdis Bridge” on the LAN.
|
||||
@@ -53,7 +53,7 @@ Why:
|
||||
2) iOS connects to the bridge and requests pairing.
|
||||
3) The bridge forwards the pairing request to the **Gateway** as a *pending request*.
|
||||
4) Approval can happen via:
|
||||
- **macOS UI** (Swift app shows “Approve node”), or
|
||||
- **macOS UI** (Clawdis shows an alert with Approve/Reject/Later, including the node IP), or
|
||||
- **Terminal/CLI** (headless flows).
|
||||
5) Once approved, the bridge returns a token to iOS; iOS stores it in Keychain.
|
||||
- Subsequent connections:
|
||||
@@ -134,14 +134,13 @@ When iOS is backgrounded:
|
||||
|
||||
## iOS app architecture (SwiftUI)
|
||||
### App structure
|
||||
- Tab bar:
|
||||
- **Canvas/Screen** (WKWebView + overlay chrome)
|
||||
- **Voice** (status + last transcript + test)
|
||||
- **Settings** (node name, voice wake toggle, pairing state, debug)
|
||||
- Single fullscreen Canvas surface (WKWebView).
|
||||
- One settings entry point: a **gear button** that opens a settings sheet.
|
||||
- All navigation/mode selection is **agent-driven** (no local URL bar).
|
||||
|
||||
### Components
|
||||
- `BridgeDiscovery`: Bonjour browse + resolve (Network.framework `NWBrowser`)
|
||||
- `BridgeConnection`: TLS session + pairing handshake + reconnect
|
||||
- `BridgeConnection`: TCP session + pairing handshake + reconnect (TLS planned)
|
||||
- `NodeRuntime`:
|
||||
- Voice pipeline (wake-word + capture + forward)
|
||||
- Screen pipeline (WKWebView controller + snapshot + eval)
|
||||
|
||||
Reference in New Issue
Block a user