@@ -11,21 +11,22 @@ can't read something, it leaves it alone on both devices.
1111
1212## Modules
1313
14- | Module | Job |
15- | ------------- | --------------------------------------------------------------------- |
14+ | Module | Job |
15+ | ------------- | ---------------------------------------------------------------------- |
1616| ` manifest.rs ` | what a folder holds, what couldn't be read, and what counts as content |
17- | ` plan.rs ` | what a round does, for both devices at once (pure) |
18- | ` merge.rs ` | three-way merge of a note |
19- | ` preview.rs ` | a plan as someone can read it before it runs, and a note's line diff |
20- | ` apply.rs ` | carrying out one device's half of a plan, safely |
21- | ` round.rs ` | one round, from both ends: the coordinator and the device answering |
22- | ` ids.rs ` | repairing two notes that carry one page id, the way the app does |
23- | ` session.rs ` | the transport: streams, message limits, silence |
24- | ` protocol.rs ` | wire messages |
25- | ` pairing.rs ` | pairing codes and claims |
26- | ` state.rs ` | identity, paired devices, baselines, lineage, ancestors, spool |
27- | ` mod.rs ` | endpoint, peer tasks, Tauri commands |
28- | ` e2e/ ` | real devices over loopback: scenarios, and seeded random work (fuzz) |
17+ | ` plan.rs ` | what a round does, for both devices at once (pure) |
18+ | ` merge.rs ` | three-way merge of a note |
19+ | ` preview.rs ` | a plan as someone can read it before it runs, and a note's line diff |
20+ | ` apply.rs ` | carrying out one device's half of a plan, safely |
21+ | ` round.rs ` | one round, from both ends: the coordinator and the device answering |
22+ | ` ids.rs ` | repairing two notes that carry one page id, the way the app does |
23+ | ` session.rs ` | the transport: streams, message limits, silence |
24+ | ` protocol.rs ` | wire messages |
25+ | ` pairing.rs ` | pairing codes and claims |
26+ | ` nearby.rs ` | finding devices on the same network over mDNS |
27+ | ` state.rs ` | identity, paired devices, baselines, lineage, ancestors, spool |
28+ | ` mod.rs ` | endpoint, peer tasks, Tauri commands |
29+ | ` e2e/ ` | real devices over loopback: scenarios, and seeded random work (fuzz) |
2930
3031` plan.rs ` has no disk or network access (three manifests and two lineages in, operations out), so
3132its rules are tested as cases, and ` e2e/ ` tests them again as whole rounds between real devices.
@@ -316,6 +317,27 @@ path.
316317 doesn't. Prompts are handled one at a time and recheck the secret, so a second device with the
317318 same code is refused as "used or replaced". If a freshly pasted code is refused, the claiming
318319 device removes the pairing.
320+ - ** Nearby, without a code:** works like Bluetooth, with two switches under Settings → Sync →
321+ "Pair with a nearby device":
322+ - ** Discoverable** (` sync_set_discoverable ` ) lets other devices on the network learn this one's
323+ name (` Request::Introduce ` ) and ask to pair (` Knock ` , a ` Claim ` with no secret). Off, both are
324+ refused (` not_discoverable ` ). It is never saved: leaving the Sync page turns it off, and so do
325+ a reload and a restart.
326+ - ** Find nearby devices** (` sync_search_nearby ` ) asks every device heard over mDNS who it is,
327+ every ` SEARCH_EVERY ` for ` SEARCH_WINDOW ` (or until Stop or leaving the page), and lists the
328+ discoverable ones. Searching doesn't make a device discoverable, nor the other way round.
329+
330+ Picking a device on the list sends the ` Knock ` , and the device picked asks ** "Pair with
331+ < ; name> ; ?"** exactly as for a code. That click is the whole gate, so:
332+ - Only a discoverable device takes a knock, and only from a device it hears over mDNS, so nobody
333+ can ask from across the internet, or from the network while nobody is pairing.
334+ - Both devices show the same six ** check digits** (` pairing::check_digits ` , from both endpoint
335+ ids) while the prompt is up. Names are whatever a device calls itself, so a stranger can take
336+ yours; matching digits mean each device is talking to the other. Allow only if they match.
337+ - One turned down can't knock again until ` COOLDOWN ` is over (` Throttle::block ` ), and unanswered
338+ knocks count toward ` MAX_ATTEMPTS ` , so a stranger can't keep a prompt on screen.
339+ - The pairing code isn't involved and isn't rotated.
340+
319341- ** Allowlist:** iroh authenticates the peer's key before any data is read, so a paired id is
320342 allowed and anything else isn't. An unpaired peer can only send a ` Claim ` (capped at
321343 ` MAX_CLAIM ` ).
@@ -327,6 +349,29 @@ path.
327349 synced folder. A malformed ` sync.json ` is reported, not regenerated, because regenerating would
328350 change the device's identity.
329351
352+ ## Devices on the same network
353+
354+ A device is dialed by its endpoint id. The addresses saved at pairing go stale as soon as either
355+ device changes network, and iroh's own lookup (n0's DNS and relay) needs the internet. So with sync on,
356+ each device also announces itself over mDNS (` nearby.rs ` ) as ` _set-sync._udp.local ` , under its own
357+ service name rather than iroh's shared one so Set only hears from Set, and iroh resolves a paired
358+ device's id from those announcements alongside DNS. Two devices on one Wi-Fi then sync directly, with
359+ no internet, wherever DHCP has moved them.
360+
361+ - ** Nothing more is trusted.** An announcement is an endpoint id and its addresses: a pairing code
362+ without the secret. Being nearby grants nothing; the allowlist and someone clicking Allow are
363+ still the only ways in (see Pairing and trust).
364+ - ** What it reveals.** Anyone on the network can see that a Set device is there and its endpoint id,
365+ which is stable. The device name isn't in the announcement; another Set device on the network can
366+ learn it only while this one is discoverable.
367+ - ** Optional.** Where the network allows no multicast, or macOS hasn't been allowed Local Network
368+ access (` NSLocalNetworkUsageDescription ` in ` Info.plist ` ; refused sends fail with "No route to
369+ host"), the lookup is logged as ` sync.mdns_unavailable ` or simply hears nothing, and sync goes
370+ through the relay as before.
371+ - ** Shown.** A paired device announcing itself is marked ` nearby ` in its status, and Settings → Sync
372+ says "on this network" beside it. Pairing with a device found this way is under Pairing and
373+ trust.
374+
330375## Transport
331376
332377- Incoming streams are each served on their own task (` session::serve_streams ` ). At most ` IN_FLIGHT `
0 commit comments