OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer commits / b0cf7a1

docs: price the rest of defect B — both halves, from the reference's constants
hyper-games #73 NEXT item 7 asked for the three-generation _serverRelays cycling and refresh-chain.js's reverse hash chain. Reading persistent.js and index.js prices both, and neither is a correctness defect. §9.2 left an explicit open question: does an empty `refresh` make a node treat a re-announce as a new record? It does not. onannounce validates the signature and calls records.add identically either way; `refresh` only registers a token so a LATER renewal can arrive as a tiny unsigned announce that _onrefresh expands from the cached record. Sending it empty costs a full signed announce per renewal instead of about forty bytes — bandwidth, not behaviour. ⛔ And the chain carries a failure mode the full announce does not: it is a fixed number of blocks built backwards from a seed, and a peer that exhausts it without falling back stops renewing SILENTLY. Taking on a silent-stall risk to save bandwidth is the wrong direction to trade in. Generation cycling costs at most one record TTL, and the reference states it: defaultMaxAge = 20 minutes (index.js:614). Our refresh cadences are 10 min ± 2 and 5 min, both inside it, so a holder we stop refreshing carries a REDUNDANT copy for up to twenty minutes — an extra copy, not a wrong answer. The case where it would be a wrong answer is the peer going away, and that is the liveness ping, which is built. Same discipline as A2 an hour earlier: the reading says what it would cost to build, and the cost says do not build it yet. Priced, not guessed, and not claimed to be invisible on the public DHT at scale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b0cf7a1385e29f8faf6589b06264d7e5055d3fc9
by plankatron <fleeky@gmail.com> · 2026-09-10T19:53:54.000Z

Changed files (1) +41 -4

docs/PARITY-hyperswarm.md +41 -4
……@@ -925,10 +925,47 @@ up to `SELF_ANNOUNCE_UPDATE_MS` instead of ~3 seconds.
925925- a ping round on a 3s schedule with tag→response accounting
926926- re-announce immediately when answered < min(held, 3)
927927
928⏳ Also still absent: the three-generation `_serverRelays` cycling and
929`refresh-chain.js`'s reverse hash chain (we always send `refresh` empty).
930Neither is required for the record to stay alive; both are needed for full
931parity.
928### 12.2 The rest of B, priced by reading (2026-09-10)
929
930The two remaining pieces were carried as open parity gaps. Reading
931`persistent.js` and `index.js` prices both, and neither is a correctness defect.
932
933**The refresh chain is an OPTIMISATION, and §9.2's open question is closed.**
934§9.2 asked whether an empty `refresh` makes a node treat a re-announce as a new
935record. It does not. `onannounce` (persistent.js:100-150) validates the
936signature and calls `this.records.add(k, publicKey, record)` on every full
937announce, identically, whether or not `refresh` is present. All `refresh` does
938is `this.refreshes.set(hash(refresh), {k, record, announceSelf})`, registering a
939token so a LATER renewal can arrive as a tiny announce with no peer and no
940signature at all — `_onrefresh` (`:72-97`) then re-adds the cached record and
941rotates the token.
942
943So sending it empty costs a full signed announce per renewal — peer record plus
944a 64-byte signature, and an Ed25519 verify at each of ~20 holders — where JS
945pays about forty bytes and a hash lookup. Real, and bandwidth rather than
946behaviour.
947
948⛔ It also carries a failure mode the thing it replaces does not: the chain is a
949fixed number of blocks built backwards from a seed, and a peer that exhausts it
950without falling back to a full announce stops renewing SILENTLY. That is a
951correctness risk taken on to save bandwidth, which is the wrong direction to
952trade in — so it is priced here and not built.
953
954**Generation cycling costs at most one record TTL.** JS explicitly unannounces
955at any node that took generation 1's announce but not generation 2's
956(announcer.js:192-196). We do not, so such a node keeps our record until it
957expires on its own. That bound is stated in the reference:
958`defaultMaxAge = 20 * 60 * 1000` (index.js:614) — **20 minutes**.
959
960Our cadences sit inside it: `TOPIC_REFRESH_INTERVAL_MS` 10 min ± 2 min jitter,
961`SELF_ANNOUNCE_UPDATE_MS` 5 min. So a record never lapses while we want it, and
962a holder we stop refreshing carries a redundant copy for at most 20 minutes.
963That is an extra copy, not a wrong answer — a lookup through it still returns a
964peer that is there. The case where it WOULD be a wrong answer is the peer going
965away, and that is the liveness ping (§12), which is built.
966
967⚠ Not claimed: that either would be invisible on the public DHT at scale. What
968is claimed is the price, from the reference's own constants.
932969
933970## 13. Slice 9 — defect D fixed: associate the topic before the early returns
934971