OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-did-peer commits / 350f01a

router.onLabels: tell a consumer when a front gains a label core
A label core opening is not a firehose event, so the gateway (which serves labels) had no way to learn of one. The common case is the labeler upgrading while the gateway runs: the router learns of the core from an announce on an already-open front. onLabels(fn) → unsubscribe; called once per front, with the front. Unreleased, so still 0.2.15. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
350f01a794af0686e1ef7a75e1a0d99a784ffaa1
by plankatron <fleeky@gmail.com> · 2026-10-14T02:11:22.000Z

Changed files (2) +20 -0

lib/did-peer.js +17 -0
……@@ -80,6 +80,7 @@ export class DidPeer {
8080 // consumer's job (the gateway lens), exactly as it verifies commits.
8181 this._labelKey = null
8282 this.labels = null
83 this._onLabels = null // set by DidPeerRouter: (peer) => void once a label core is open
8384 this._channels = new Set()
8485 this.head = null
8586 this._opening = null
……@@ -158,6 +159,7 @@ export class DidPeer {
158159 core.on('append', () => this._announceAll())
159160 this.labels = core
160161 this._announceAll()
162 if (this._onLabels) { try { this._onLabels(this) } catch {} }
161163 return true
162164 } finally { this._labelOpening = false }
163165 }
……@@ -641,6 +643,20 @@ export class DidPeerRouter {
641643
642644 _isCared (did) { return this._fronted.has(did) || this._peers.has(did) || this._opts.has(did) }
643645 _joinDid (did) { if (this._join) { try { this._join(did) } catch {} } }
646 // A label core opening is not a firehose event, so a consumer serving labels (the gateway) has no other
647 // way to learn that a front gained one — the common case is the labeler upgrading while we run, which
648 // the router learns of from an announce on an already-open front. → unsubscribe().
649 onLabels (fn) {
650 if (!this._labelListeners) this._labelListeners = new Set()
651 this._labelListeners.add(fn)
652 return () => this._labelListeners.delete(fn)
653 }
654
655 _tellLabels (did, peer) {
656 if (!this._labelListeners) return
657 for (const fn of this._labelListeners) { try { fn(did, peer) } catch {} }
658 }
659
644660 _wake (did) { const ws = this._waiters.get(did); if (ws) { this._waiters.delete(did); for (const fn of ws) fn() } }
645661
646662 _setHandle (handle, did) {
……@@ -797,6 +813,7 @@ export class DidPeerRouter {
797813 if (entry.coords.handle) peer.handle = entry.coords.handle
798814 // EVERY peer announcing this (did, firehoseKey) is a candidate; _call tries them until one answers.
799815 if (this._useRpc) for (const r of entry.conns.values()) if (r) peer.addRpc(r)
816 peer._onLabels = (p) => this._tellLabels(did, p)
800817 await peer.provide({ repoKey: entry.coords.repoKey, firehoseKey: entry.coords.firehoseKey, labelKey: entry.coords.labelKey || null })
801818 // Re-sync: more peers may have announced this same (did, firehoseKey) during the open above.
802819 if (this._useRpc) for (const r of entry.conns.values()) if (r) peer.addRpc(r)
test/labels.test.mjs +3 -0
……@@ -54,12 +54,15 @@ const rStore = openCorestore(dir('labels-reader-'))
5454const router = new DidPeerRouter(rStore)
5555const rSwarm = new Hyperswarm({ bootstrap: testnet.bootstrap })
5656rSwarm.on('connection', (c) => router.onconnection(c))
57const told = []
58router.onLabels((did, f) => told.push({ did, f }))
5759router.add(id.did, { verifyKey: id.publicKey, noRepo: true })
5860await rSwarm.join(discoveryTopic(id.did), { server: true, client: true }).flushed()
5961const front = await router.front(id.did, { timeout: 20000 })
6062assert(!!front, 'the reader fronts the DID')
6163assert(await waitFor(() => front.labels && front.labels.contiguousLength === 2), 'the front holds both labels (fully downloaded)')
6264assert(b4a.equals(front.labels.key, labels.key), 'the announced core, by key')
65assert(told.length === 1 && told[0].did === id.did && told[0].f === front, 'router.onLabels told the consumer once, with the front — a label core is not a firehose event, so a consumer has no other way to learn of it')
6366await labels.append(b4a.from('label-2'))
6467assert(await waitFor(() => front.labels.contiguousLength === 3), 'and follows a new label')
6568