Repository navigation
Conversation
Clients reading the same file share its records over WebRTC, BitTorrent
style. Every node now introduces them and stays the last seed: a record no
client has comes from the nodes as before. There is no new binary and no
new port. Design: ADR-0026.
The rendezvous (`src/swarm.rs`):
- Rooms are keyed by a file's root DataMap address. Readers announce at the
2 closest nodes to it that host rooms, on the connections they already
have (QUIC for native clients, WebRTC Direct for browsers).
- The node names each member from its connection, hands each offer to a
different member, and passes an answer back only for an offer it
delivered to the answerer, once, before it expires (the protocol's 70 s
signal lifetime).
- An offer or answer is passed on with only the candidates another reader
may try: no public address but its sender's own host, as the node reaches
it on that request, no local address or mDNS name unless the node reaches
the sender at a local address itself, and at most 16 (ant-protocol's
`constrain_swarm_sdp`), so a stranger cannot point readers' connectivity
checks at a third party or at their own home networks. A stored SDP is
charged the memory it holds.
- A native membership continued over a connection from another address
counts against that address from then on, if it has room.
- Native announces may wait up to 20 s for a signal. Browser announces never
wait, so they hold no browser request slot.
- A node hosts rooms only once browsers can reach it: until its browser
listener has an endpoint and a STUN server to name, it refuses announces
as `NotHosting`, so native readers pass it over at once and meet browsers
at the closest nodes both reach.
- Native requests are signed and checked for freshness and replay. A
request naming another node is refused before it touches that history.
- Membership numbers let the node keep each connection's newest
membership. Ended memberships are remembered for as long as a request sent
before they ended can be taken, so a late request cannot end, take over
or revive one.
- Native replies go only on the connection the request came on, through
saorsa-core's `send_message_on_channel`.
- Hard limits with no configuration:
- rooms, members per room and in all, and rooms per connection;
- members, histories, ended memberships and stored SDP bytes per host
(an IPv4 address or an IPv6 /64);
- unanswered offers per member and waiting announces.
- The WebRTC Direct socket answers plain STUN Binding requests for hosts
that announced in the last 60 s, rate limited in saorsa-transport.
Routing: `NativeSwarmRouter` in `src/node.rs` (shared with the devnet)
handles native swarm requests on their own task budget. `src/web_rtc.rs`
handles browser ones before the storage check, so nodes that store no
chunks host rooms too, and advertises `swarm_rendezvous`. Browser swarm
requests run on a request budget of their own (64 a second, 8 per source):
one within it gives the reads' budget back the token it took on arrival,
one arriving when the reads' budget is spent is taken on the swarm's, and
one past the swarm's budget is refused without closing the channel the
reads share, so a room's announces never spend the request budget reads
need, nor lose the channel to it.
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.
cargo tree percent-encodes the slash in a git branch's name and cargo metadata does not, so the notices generator matched no package on such a branch (this PR's dependency branches are feat/peer-swarm): it now compares both decoded. saorsa-transport's main pulls granit-parser 2.0.0, which ships its licence texts as 1.3.0 did; its entry names the new version.
Rust 1.99's clippy (assert_is_empty, which CI's stable toolchain runs) asks for assert_eq when a test checks that a value is empty, so a failure shows the value.
This was referenced Oct 30, 2026
Draft
The pending-offer test never let B take its offers, so B's 32-signal inbox stopped delivery whatever A's own cap on unanswered offers did. B now collects every offer without answering, and A's 33rd offer is still refused while B's inbox is empty. The expiry test swept past the 70 s signal deadline, which is also past the 60 s a silent member lasts, so the members left and took their offers with them whether or not signals expired. The members are now kept fresh across that deadline, the offer and its SDP must still be there just before it and gone just after it, and silent members expiring is a test of its own. Each test fails with the limit or expiry it covers removed.
The earlier test answered an offer that did not exist, so it passed with the recipient check removed. Now A's live offer goes to B, a third member of the same room answers it with its real id and is refused, the offer stays pending, and B's answer still reaches A. It fails if any member of the room may answer.
The client now counts each of its own fetches from the nodes, not only reads of a file's data records, when it limits what it sends other readers: opening a file's map, a raw read or an existence check needs its CPU and line first too.
Browsers' swarm requests run on a budget of 64 a second per node and 8 per source, but native clients' only had a cap on tasks running at once. A native client can make identities for free, so one host could announce with wait_ms 0 and fresh memberships as fast as the slots turned over, and every replacement does its work under the rendezvous lock browsers use too. Native swarm requests now take a place from a budget of their own with the same figures, 64 a second in all and 8 from one host (an IPv4 address or an IPv6 /64), before they are decoded or given a task; past it they are dropped and counted (native_over_budget), and the client reads from nodes alone. The node's budget is taken first, so once it is spent a flood costs one check a request. At most 4096 hosts' budgets are remembered, those that have refilled forgotten first, which always leaves room for a new host since the node's own budget keeps all but a few full. Tests: a host's identities and an IPv6 /64 share one budget, many hosts together get the node's and no more, the hosts remembered stay bounded and a forgotten one starts afresh, and a flood through the router from one host is held to its budget while another host gets through. They fail with the router's check or the per-host budget removed. ADR-0026 says so.
…or read The client now tries a waiting room join again when the file is joined again or a read of one of its records starts, not only when a record of it comes in, so a file whose reads the nodes fail still joins once a room has gone unread.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every node hosts swarm rooms: it introduces clients reading the same file to each other, carries their WebRTC offers and answers, answers their STUN on its WebRTC Direct socket, and stays the last seed for records. No new binary or port. Design, bounds and rollout: ADR-0026 (new, Proposed). The commits after the feature point the three dependencies at their matching branches, let the notices generator match git dependencies on a branch whose name has a slash, and meet Rust 1.99's clippy.
One change across six repositories, as draft PRs. Merge order:
This PR depends on: protocol, transport and core, as git branch dependencies on their
feat/peer-swarmbranches. Once those merge, the dependency commit moves to theirmainbranches (or the released versions at the release train).Linear issue
No linked Linear issue was identified for this work; one can be added by a maintainer.
Risk tier
Proposed; a human confirms at review.
Compatibility
swarm_rendezvousto browsers and answers plain STUN for hosts that announced in the last 60 s; clients without the swarm see no change.swarmmodule the node and its devnet use.Semver impact
Test evidence
cargo test --lib: 1232 passed (on the git dependencies of this PR).cargo fmt --check,cargo clippy --all-targets --all-features -D warnings,cargo clippy -D warningsandcargo doc -D warnings: clean.cargo clippy --no-default-features -D warningsreports main's five existingunused_selfinreplication/audit_metrics.rs, nothing new.src/swarm.rs,src/web_rtc.rs): requests naming another node, introductions, answers nobody asked for, long-poll wake-up and shutdown, caps and leaving, expiry, the STUN allowlist, the unanswered-offer cap, browsers never waiting, replayed or stale native requests, histories and members capped per host, ended memberships refused however late, a relayed SDP keeping no public address but its sender's, a stored SDP charged the memory it holds, and the browser request budgets (swarm requests on a budget of their own, refunded to the reads, taken on the swarm's when the reads' is spent, all-or-nothing across buckets).ant file downloadCLI joins the same rooms as the pages (25 s beside four pages, which took 144 MB from each other). No dev testnet run yet.xhigh, per repository and as one system with the five sibling PRs, until no finding remained.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: clients pass over nodes that host no rooms and read from nodes alone, as today.