Skip to content

feat(swarm): add swarm rendezvous messages for clients sharing records - #50

Draft
grumbach wants to merge 3 commits into
WithAutonomi:mainfrom
grumbach:feat/peer-swarm
Draft

grumbach wants to merge 3 commits into
WithAutonomi:mainfrom
grumbach:feat/peer-swarm

Conversation

@grumbach

@grumbach grumbach commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Wire types for clients that read the same file sharing its records over WebRTC, with nodes introducing them and staying the last seed (ADR-0026): the announce, answer and leave messages of a file's swarm room, their shared limits, and the SDP rule both a relaying node and an applying client use so a stranger cannot point readers' connectivity checks anywhere. A second commit makes ChunkMessage::decode refuse bytes after a message. The branch is rebased onto main as three commits, the swarm messages following its get-or-closer request (ant-node ADR-0020).

One change across six repositories. Merge order:

  1. ant-protocol feat(swarm): add swarm rendezvous messages for clients sharing records #50, saorsa-transport feat(webrtc): answer plain STUN Binding requests on the WebRTC Direct socket saorsa-transport#231 and saorsa-core feat(network): send a message on one named channel only saorsa-core#183, independent of each other
  2. ant-node feat(swarm): host a swarm rendezvous on every node ant-node#262
  3. ant-client feat(swarm): share records with other readers of a file, in every client ant-client#244
  4. Try https://github.com/WithAutonomi/try/pull/21, once a browser SDK release carries the swarm

This PR depends on: none.

Linear issue

Closes V2-1892

Risk tier

  • T0 — docs / tooling / CI / pure UX-output. Repo CI only.
  • T1 — client-only, no network-facing behavior change. CI + prod compat smoke.
  • T2 — node/client logic with behavioral surface, no protocol/format/economics change. Dev testnet + ADR.
  • T3 — protocol / storage format / payments / routing. T2 evidence + adversarial testing.

Proposed; a human confirms at review.

Compatibility

  • Wire: six ChunkMessageBody variants appended (tags 16 to 21, after main's get-or-closer pair); released nodes (v0.23.1) cannot decode them, log a warning and leave them unanswered (no panic or ERROR line in the rollout lab), and clients pass those nodes over. ChunkMessage::decode refuses bytes after a message, which no sender writes.
  • Storage: none
  • API: new swarm module, limits and constrain_swarm_sdp; ChunkMessageBody is #[non_exhaustive].

Semver impact

  • breaking
  • feature
  • fix

Test evidence

  • cargo test --lib: 130 passed at ffbed67, rebased onto main 5dc5537 (ADR-0020's get-or-closer request) as three commits with the same code as the reviewed 5c5a1f1. cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, cargo clippy -- -D warnings and RUSTDOCFLAGS="-D warnings" cargo doc --no-deps: clean (Rust 1.99).
  • New tests pin every new wire tag, the shared limits, decoding that refuses lists past their limit and bytes after a message, and constrain_swarm_sdp: only the standard form of a candidate or fingerprint line stays (other spellings, Unicode whitespace, stray carriage returns, out-of-range components and priorities go), a mostly dropped SDP holds only what it keeps, and the address classification, which is stricter than the IANA special-purpose registries: all of 192.0.0.0/24 and 2001::/23, and IPv6 outside 2000::/3, count as not public. The SDP tests fail with each fix reverted.
  • Adversarial: ant-core parses SDPs this filter let through with webrtc-rs's own SDP and ICE candidate parsers (fingerprints, candidate addresses, candidate priorities).
  • Consumers at their PRs' heads: ant-node d39c393f 1307 lib tests; ant-core 2f925d0 989 lib tests, e2e_swarm and 261 WASM tests, all passing.
  • CI at ffbed67: every build, test and lint job passes; Security Audit fails. It fails only on RUSTSEC-2026-0387 (crypto-bigint 0.5.5, through elliptic-curve 0.13), which cargo audit reports on this repository's main lockfile too, with the same dependents; its fixes wait on alloy and webrtc-rs releases. linear-link and pr-template, which check this description, pass.

New dependency

none

ADR

https://github.com/grumbach/ant-node/blob/feat/peer-swarm/docs/adr/ADR-0026-clients-share-records-nodes-introduce-them.md (new in the ant-node PR, status Proposed)

Mitigation / rollback

Revert: nothing sends the new messages until the node and client PRs land.

Clients reading the same file can share its records with each other over
WebRTC data channels, with nodes introducing them and staying the last
seed. This adds the wire types for that introduction.

- `swarm` module: `RoomKey`, `MemberId`, `OfferId`, `NodeId` and
  `Membership`, and the announce, answer and leave requests with their
  replies. An announce joins or refreshes a room, hands the node offers for
  other members and collects the signals waiting for this member,
  optionally waiting for one.
- Every request names the rendezvous node it is for, so a request one node
  saw cannot be replayed at another.
- A membership number in announce and leave lets a node keep each
  connection's newest membership of a room: a newer one replaces the old,
  an ended or older one is refused (`SwarmRefusal::Superseded`), and a
  leave ends only the membership it names. A node browsers cannot reach
  refuses with `SwarmRefusal::NotHosting`, which a client takes as "pass
  this node over now".
- Shared limits both sides enforce: offers per announce, SDP size, inbox
  size, reply size, wait time, ICE servers, offers counted as delivered,
  the request size a node refuses before decoding, and the length of a
  reply's error. The `validate` methods of the requests and of all three
  replies check them; the offers, inbox and STUN server lists are refused
  while decoding once past their limit, so nothing longer is ever built.
- `SWARM_SIGNAL_TTL_MS` (70 s): how long a node keeps a delivered offer for
  its answer and a signal for its member, which a client checks its own
  answering time against.
- `constrain_swarm_sdp`: the ICE candidates an SDP from another client may
  keep (UDP, an IP that is not unspecified, multicast or broadcast, or an
  mDNS name, at most 16), with a caller's rule on top. A node relaying an
  SDP and a client applying one both use it, so readers' connectivity
  checks cannot be pointed at a third party. Receivers read SDP leniently
  (webrtc-rs trims an attribute's value, Chrome takes a name followed by a
  space, Firefox names attributes in any case, webrtc-ice splits fields on
  any Unicode whitespace), so a line any of them may read as a candidate
  or a DTLS fingerprint stays only in its standard form (printable ASCII
  words separated by single spaces; a candidate's component 1 to 256 and
  its priority a 32-bit number, so no ICE agent's ranking of it
  overflows), and a line with a carriage return anywhere but before its
  line feed goes: what a receiver reads is exactly what was checked, and a
  link's binding covers every fingerprint a receiver uses. The result
  holds no more memory than its length needs. `is_global_ip` and
  `canonical_ip` help both apply their rules.
- Six `ChunkMessageBody` variants appended as discriminants 14 to 19, so
  existing messages keep their encoding. Tests pin every new tag.
`ChunkMessage::decode` took the message postcard decodes from the front of
the input and ignored whatever followed. A sender could therefore pad a
small message, such as a valid reply, out to the 5 MiB wire limit and still
have it taken, so a receiver's bound on a reply was really the wire limit.

Decoding now refuses any bytes after the message's end, as
`DeserializationFailed`. Every sender encodes a message exactly, so nothing
well formed changes; `test_decode_rejects_bytes_after_the_message` pins it.
The `RoomKey` doc said a public file's room is its address. A client
derives the room from the file's root DataMap, once a shrunk map is
resolved to its root, as ADR-0026 says: a public file published with a
shrunk map has a room other than its address, and a client that followed
the doc would wait in a room no other reader joins. The doc now says so.
@grumbach
grumbach marked this pull request as draft November 5, 2026 04:11
grumbach added a commit to grumbach/ant-node that referenced this pull request Nov 5, 2026
Patch ant-protocol, saorsa-core and saorsa-transport to their
feat/peer-swarm branches, which this change needs: the swarm messages
(WithAutonomi/ant-protocol#50), the send on one named channel
(WithAutonomi/saorsa-core#183) and the plain STUN answers
(WithAutonomi/saorsa-transport#231). Once those merge, these point at
their main branches, or at the released versions at the release train.
grumbach added a commit to grumbach/ant-client that referenced this pull request Nov 5, 2026
Patch ant-protocol, saorsa-core, saorsa-transport and ant-node to their
feat/peer-swarm branches, which this change needs: the swarm messages
(WithAutonomi/ant-protocol#50), the send on one named channel
(WithAutonomi/saorsa-core#183), the plain STUN answers
(WithAutonomi/saorsa-transport#231) and the rendezvous on every node
(WithAutonomi/ant-node#262). Once those merge, these point at their
main branches, or at the released versions at the release train.

Cargo.lock starts from main's lock and differs from it only where those
branches need it: the swarm's WebRTC and STUN crates, saorsa-transport's
main dropping its CLI (and its clap and reqwest dependencies), and
ant-node's main adding winapi-util on Windows. iana-time-zone keeps the
windows-core 0.62.2 main's lock has.
grumbach added a commit to grumbach/ant-node that referenced this pull request Nov 5, 2026
Patch ant-protocol, saorsa-core and saorsa-transport to their
feat/peer-swarm branches, which this change needs: the swarm messages
(WithAutonomi/ant-protocol#50), the send on one named channel
(WithAutonomi/saorsa-core#183) and the plain STUN answers
(WithAutonomi/saorsa-transport#231). Once those merge, these point at
their main branches, or at the released versions at the release train.
grumbach added a commit to grumbach/ant-client that referenced this pull request Nov 5, 2026
Patch ant-protocol, saorsa-core, saorsa-transport and ant-node to their
feat/peer-swarm branches, which this change needs: the swarm messages
(WithAutonomi/ant-protocol#50), the send on one named channel
(WithAutonomi/saorsa-core#183), the plain STUN answers
(WithAutonomi/saorsa-transport#231) and the rendezvous on every node
(WithAutonomi/ant-node#262). Once those merge, these point at their
main branches, or at the released versions at the release train.

Cargo.lock starts from main's lock and differs from it only where those
branches need it: the swarm's WebRTC and STUN crates, saorsa-transport's
main dropping its CLI (and its clap and reqwest dependencies), and
ant-node's main adding winapi-util on Windows. iana-time-zone keeps the
windows-core 0.62.2 main's lock has.
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