OverviewFilesCommitsIssuesDocsReleasesPeers

ap-bridge commits / 3c40af8

0.10.21: heldFeed + observationsForFeed for other roles (bsky ingest, I2b)
A held favela feed by key (read-only) and the observation-feed sink for the identity that feed belongs to (null unless registered here), so the gateway's bsky ingest writes into the same feed the inbox does. 388/388. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3c40af807d03bc8bcccd1389d267ea844d0d0603
by plankatron <fleeky@gmail.com> · 2026-10-14T23:37:29.000Z

Changed files (3) +22 -4

lib/bridge.js +19 -1
……@@ -172,6 +172,9 @@ export async function startApBridge (config, { log = defaultLog, bootstrap = nul
172172 }
173173 // The router's observation-feed sink (router.js observe/unobserve). Keyed by the REGISTRATION generation, so a
174174 // released and re-claimed name writes to a new feed, never its predecessor's.
175 // favela feed key → its registration, for OTHER roles in this process (the gateway's bsky ingest, #82 I2b): what
176 // reaches a registered identity from another network goes into the same observation feed. Filled at admission.
177 const feedRegs = new Map()
175178 if (obsFeeds && registrations) {
176179 const genOf = async (user) => { const reg = await registrations.get(user.username); return reg ? reg.generation : null }
177180 ctx.observe = {
……@@ -402,7 +405,10 @@ export async function startApBridge (config, { log = defaultLog, bootstrap = nul
402405 // Their observation feed exists from admission on, so a node can hold it before the first observation.
403406 if (obsFeeds && registrations) {
404407 registrations.get(username)
405 .then((reg) => (reg ? obsFeeds.open(username, reg.generation) : null))
408 .then((reg) => {
409 if (reg && feedKey) feedRegs.set(String(feedKey).toLowerCase(), { username, generation: reg.generation })
410 return reg ? obsFeeds.open(username, reg.generation) : null
411 })
406412 .then((e) => { if (e) log({ msg: 'observation feed', username, key: e.key }) })
407413 .catch((err) => log({ msg: 'observation feed open failed', username, error: err.message }))
408414 }
……@@ -725,6 +731,18 @@ export async function startApBridge (config, { log = defaultLog, bootstrap = nul
725731 port,
726732 adminPort,
727733 bridgeKey: open.reg ? open.reg.key : null,
734 // For other roles in this process (the gateway's bsky ingest, hyper-social #82 I2b): a held favela feed by key
735 // (read-only), and the observation-feed sink for the identity that feed belongs to — null unless it is
736 // registered here. Narrow on purpose: no registry, no keys, no write path into anyone's own feed.
737 heldFeed: (feedKey) => (holder ? holder.get(String(feedKey || '').toLowerCase()) : null),
738 observationsForFeed: (feedKey) => {
739 const reg = obsFeeds ? feedRegs.get(String(feedKey || '').toLowerCase()) : null
740 if (!reg) return null
741 return {
742 note: (obs) => obsFeeds.note(reg.username, reg.generation, obs),
743 remove: (activity) => obsFeeds.remove(reg.username, reg.generation, activity)
744 }
745 },
728746 ...callbacks,
729747 metrics,
730748 handlers: bridgeHandlers({ name: 'ap-bridge', version, ...callbacks }),
package-lock.json +2 -2
……@@ -1,12 +1,12 @@
11{
22 "name": "ap-bridge",
3 "version": "0.10.20",
3 "version": "0.10.21",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "ap-bridge",
9 "version": "0.10.20",
9 "version": "0.10.21",
1010 "license": "MIT",
1111 "dependencies": {
1212 "atproto-did-peer": "file:../atproto-did-peer",
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "ap-bridge",
3 "version": "0.10.20",
3 "version": "0.10.21",
44 "description": "ActivityPub membrane \u2014 a second projection of the self-custody atproto peer",
55 "type": "module",
66 "main": "index.js",