OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-bridge commits / 2beff27

0.9.54: membrane metrics() — per-DID peer state, relay seq, guard
Per fronted DID: served handle and head rev, cached and live co-owner fronts, last firehose append (atproto-did-peer 0.2.13 diagnose.lastAppendAt), rival lines refused, denied keys. Plus the relay's emitted seq and seqBase (what bsky's cursor is compared against) and the guard's last pass and heals. Local state only, no network call. Test first (red: m.metrics is not a function); Bare 18/18. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2beff2749b58feb9918d5de8559f079f14552195
by plankatron <fleeky@gmail.com> · 2026-10-13T22:37:49.000Z

Changed files (3) +32 -1

package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "atproto-bridge",
3 "version": "0.9.53",
3 "version": "0.9.54",
44 "description": "Stateless keyless atproto MEMBRANE \u2014 fronts self-custody hyper-proto-peers to bluesky over HTTP/WS, holds no keys and never signs. Bare-runnable. Deploy root / flat closure over the identity+repo+store+pds packages.",
55 "type": "module",
66 "main": "index.js",
pds/lib/membrane.js +21 -0
……@@ -273,6 +273,27 @@ export async function startMembrane (cfg, { log = defaultLog, bootstrap = null }
273273 status: () => ({ ok: true, membrane: true, port, fronted: peers.list() }),
274274 // The relay guard's last pass and heals, for `metrics`. Read-only.
275275 guard: () => ({ ...guard }),
276 // What the gateway's panel and CLI show for this role, from local state only — no network call.
277 // Per fronted DID: the served co-owner's handle and head rev, how many co-owner fronts are cached and
278 // how many are connected now, when its firehose last appended, rival lines refused and keys denied.
279 metrics: () => {
280 const d = peers.diagnose()
281 const dids = d.fronted.map((did) => {
282 const fronts = d.fronts.filter((f) => f.did === did)
283 const served = fronts.find((f) => f.served) || fronts[0] || null
284 return {
285 did,
286 handle: served ? served.handle : null,
287 rev: served ? served.rev : null,
288 fronts: fronts.length,
289 live: fronts.filter((f) => f.live).length,
290 lastAppendAt: (d.lastAppendAt && d.lastAppendAt[did]) || null,
291 forkRejected: fronts.reduce((n, f) => n + (f.forkRejected || 0), 0),
292 denied: (d.denied[did] || []).length
293 }
294 })
295 return { dids, relay: { emitted: relay.length, seqBase: relay.seqBase }, guard: { ...guard } }
296 },
276297 // The stop sequence, unchanged in order from pds-serve.js (control is the caller's, so it is not
277298 // here), plus the seq-guard timer.
278299 steps: [
pds/test/bare/start-membrane.test.js +10 -0
……@@ -83,3 +83,13 @@ test('the control verbs are exposed as plain handlers, for whichever socket serv
8383 }
8484 t.alike((await m.handlers['list-fronted']({})).fronted, [], 'answers')
8585})
86
87test('★ metrics: per-DID peer state, the relay seq and the guard, with no network call', async (t) => {
88 const m = await startMembrane(cfgFor(tmp()), { bootstrap: [], log: () => {} })
89 t.teardown(() => stop(m))
90 const x = m.metrics()
91 t.alike(x.dids, [], 'nothing fronted, nothing listed')
92 t.alike(x.relay, { emitted: 0, seqBase: 0 }, 'the relay seq bsky\'s cursor is compared against')
93 t.is(x.guard.configured, false, 'no crawlRelay configured: the guard is off, and says so')
94 t.is(x.guard.heals, 0)
95})