OverviewFilesCommitsIssuesDocsReleasesPeers

favela-node commits / 2ce110e

favela-node 0.1.0: co-owned community DID + keyless CommunityRoom linkage (phase 1)
The full-node that composes a community's two substrates (favela-web/spec/lens-export.md §3): - The CO-OWNED atproto DID — an hyper-proto-peer-style identity (atproto-room StateBaseV3 admins + RepoBase) whose admins co-sign the community repo. Key derivation kept IDENTICAL to hpp bootSwarmDevice (NS() + roomKey/roomSeed/encryptionKey) so a community repo is interoperable with the hpp/membrane stack. The multiswarm-PDS pattern. - The keyless CommunityRoom roster (favela-community), linked via meta:did = the community DID. The room's MODS == the DID's admins. API: communityIdentity / attestAdmin (root attests a co-admin, hands a keyless bundle) / openCommunityRepo / createCommunity (founder: mint DID + boot repo + create linked room) / bootCommunityAdmin (a co-admin boots the same DID's repo). PROVEN on a local testnet (test/community.test.mjs): community mints a did:plc; two attested admins co-sign an app.bsky.feed.generator record into ONE shared co-owned repo (convergence verified); a keyless reader reads the community DID + member roster holding no key. Next (spec §8): PLC service entries (#bsky_fg/#atproto_labeler/#atproto_label), publish the records via the co-signed repo, favela-web serving repoint, subscribeLabels WS, membrane proxy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2ce110ef0fe2f9d566234889bf4f90f557577b78
by plankatron <fleeky@gmail.com> · 2026-07-15T14:10:15.000Z

Changed files (7) +1397 -0

.gitignore +2 -0
……@@ -0,0 +1,2 @@
1node_modules/
2data/
index.js +9 -0
……@@ -0,0 +1,9 @@
1// favela-node — the co-owned community full-node.
2//
3// A community is TWO linked substrates (favela-web/spec/lens-export.md §3):
4// 1. its CO-OWNED atproto DID — an hyper-proto-peer-style identity whose admins co-sign its repo
5// (StateBaseV3 admins + Lease + Signer; the multiswarm-PDS pattern). Owns/signs the community's
6// app.bsky.feed.generator + app.bsky.labeler.service records and its com.atproto.label labels.
7// 2. the keyless CommunityRoom roster (favela-community) — the MEMBER audience favela-web reads.
8// The room's MODS == the DID's admins (one attested group). This module composes them.
9export { communityIdentity, attestAdmin, openCommunityRepo, createCommunity, bootCommunityAdmin } from './lib/community.js'
lib/bare-globals.js +50 -0
……@@ -0,0 +1,50 @@
1// Minimal web-global polyfills for Bare. Bare omits TextEncoder/TextDecoder (which
2// several npm deps — e.g. @noble/curves — use at MODULE LOAD) and a global `process`
3// (our lib reads process.env / process.exit). Buffer IS a Bare global, so we back the
4// polyfills with it. Import this FIRST in any Bare entrypoint, before deps that touch
5// these globals. No-op under Node (globals already present).
6
7import bareProcess from 'bare-process'
8if (typeof globalThis.process === 'undefined') globalThis.process = bareProcess
9
10if (typeof globalThis.TextEncoder === 'undefined') {
11 globalThis.TextEncoder = class TextEncoder {
12 get encoding () { return 'utf-8' }
13 encode (input = '') { return new Uint8Array(Buffer.from(String(input), 'utf8')) }
14 encodeInto (src, dest) {
15 const bytes = this.encode(src)
16 const n = Math.min(bytes.length, dest.length)
17 dest.set(bytes.subarray(0, n))
18 return { read: src.length, written: n }
19 }
20 }
21}
22
23// AbortController/AbortSignal — Bare omits them; used by our firehose subscribe
24// cancellation AND by streamx (bare-ws sockets). Minimal, no EventTarget dependency;
25// covers the subset consumers use (aborted + add/removeEventListener('abort')).
26if (typeof globalThis.AbortController === 'undefined') {
27 class AbortSignal {
28 constructor () { this.aborted = false; this.reason = undefined; this._listeners = [] }
29 addEventListener (type, cb) { if (type === 'abort') this._listeners.push(cb) }
30 removeEventListener (type, cb) { if (type === 'abort') this._listeners = this._listeners.filter(l => l !== cb) }
31 _fire () { this.aborted = true; const ls = this._listeners; this._listeners = []; for (const cb of ls) { try { cb() } catch {} } }
32 }
33 class AbortController {
34 constructor () { this.signal = new AbortSignal() }
35 abort (reason) { if (!this.signal.aborted) { this.signal.reason = reason; this.signal._fire() } }
36 }
37 globalThis.AbortSignal = AbortSignal
38 globalThis.AbortController = AbortController
39}
40
41if (typeof globalThis.TextDecoder === 'undefined') {
42 globalThis.TextDecoder = class TextDecoder {
43 get encoding () { return 'utf-8' }
44 decode (input) {
45 if (input == null) return ''
46 const u8 = input instanceof Uint8Array ? input : new Uint8Array(input.buffer || input, input.byteOffset || 0, input.byteLength)
47 return Buffer.from(u8).toString('utf8')
48 }
49 }
50}
lib/community.js +101 -0
……@@ -0,0 +1,101 @@
1// community.js — mint + boot a community's CO-OWNED atproto DID and LINK it to a keyless CommunityRoom.
2//
3// The community DID is an hyper-proto-peer-style identity: an encrypted, attestation-gated StateBaseV3
4// whose admins (devices attested to the community ROOT) co-own the repo; the lease-holder signs each
5// commit with the SHARED #atproto key (RFC6979-deterministic → byte-identical → failover-safe). This is
6// the SAME substrate hpp's bootSwarmDevice uses — key derivation is kept IDENTICAL (the NS() namespace +
7// roomKey/roomSeed/encryptionKey), so a community repo is interoperable with the hpp/membrane stack.
8//
9// Phase 1 (spec/lens-export.md §8) uses StateBaseV3 + RepoBase directly (co-owned repo + linkage);
10// membrane serving (bootSwarmDevice's control/membrane swarms) is a later phase.
11import crypto from 'hypercore-crypto'
12import b4a from 'b4a'
13import { join } from 'bare-path'
14import { createSwarmIdentity } from 'atproto-identity'
15import { StateBaseV3 } from 'atproto-room/lib/state-base.js'
16import { RepoBase } from 'atproto-room/lib/repo-base.js'
17import { makeAttest, makeVerifyAttest, attestDeviceChain, deviceKeetKeyPair, rootIdentityPublicKey } from 'atproto-room/lib/membership.js'
18import { CommunityRoom } from 'favela-community'
19
20// MUST match hyper-proto-peer/lib/device-node.js NS() — the community repo shares hpp's key derivation.
21const NS = (s) => b4a.from('atproto-base/v1/' + s)
22
23function roomSecrets (did, signingKey) {
24 const sharedKey = typeof signingKey === 'string' ? b4a.from(signingKey, 'hex') : signingKey
25 return {
26 roomKey: crypto.hash([NS('room-key'), b4a.from(did)]),
27 roomSeed: crypto.hash([NS('room-seed'), b4a.from(sharedKey)]),
28 encryptionKey: crypto.hash([NS('enc'), b4a.from(sharedKey)]),
29 sharedKey
30 }
31}
32
33const hex = (k) => (typeof k === 'string' ? k : b4a.toString(k, 'hex'))
34
35// The community's atproto identity from its ROOT mnemonic (the founder holds it; the recovery key stays
36// root-side — devices never receive it). Returns { did, signedOp, signingKey, recoveryKey, didKey }.
37export function communityIdentity (rootMnemonic, { handle, pdsUrl } = {}) {
38 return createSwarmIdentity(rootMnemonic, { handle, pdsUrl })
39}
40
41// ROOT-side: attest an admin device into the community (a keet root→device chain). The founder (root
42// holder) runs this to admit a co-admin, then hands the admin its bundle { did, signingKey, rootPub,
43// deviceProof } — NEVER the root mnemonic (admins can't self-recover / re-root; same as hpp provisionDevice).
44export async function attestAdmin ({ rootMnemonic, deviceMnemonic, devicePublicKey } = {}) {
45 if (!rootMnemonic || (!deviceMnemonic && !devicePublicKey)) throw new Error('attestAdmin: rootMnemonic + (deviceMnemonic | devicePublicKey) required')
46 const devPub = devicePublicKey ? (typeof devicePublicKey === 'string' ? b4a.from(devicePublicKey, 'hex') : devicePublicKey)
47 : (await deviceKeetKeyPair(deviceMnemonic)).publicKey
48 const deviceProof = await attestDeviceChain({ rootMnemonic, devicePublicKey: devPub })
49 return { devicePublicKey: hex(devPub), deviceProof: b4a.toString(b4a.from(deviceProof), 'hex') }
50}
51
52// Open ONE admin's view of the co-owned community repo (StateBaseV3 + RepoBase). All admins share
53// { did, signingKey, rootPub, members }; each holds its OWN deviceMnemonic + attestation proof.
54export async function openCommunityRepo ({ storage, did, signingKey, rootPub, deviceMnemonic, deviceProof, members, swarmOpts = null, network = true } = {}) {
55 if (!storage || !did || !signingKey || !rootPub || !deviceMnemonic || !deviceProof || !members) throw new Error('openCommunityRepo: storage, did, signingKey, rootPub, deviceMnemonic, deviceProof, members required')
56 const deviceKp = await deviceKeetKeyPair(deviceMnemonic)
57 const proof = typeof deviceProof === 'string' ? b4a.from(deviceProof, 'hex') : deviceProof
58 const rootPubBuf = typeof rootPub === 'string' ? b4a.from(rootPub, 'hex') : rootPub
59 const { roomKey, roomSeed, encryptionKey } = roomSecrets(did, signingKey)
60 const sb = new StateBaseV3(join(storage, 'statebase'), roomKey, {
61 roomSeed,
62 encryptionKey,
63 network,
64 swarmOpts,
65 selfKey: deviceKp.publicKey,
66 attest: makeAttest(deviceKp, proof),
67 verifyAttest: makeVerifyAttest(rootPubBuf),
68 getMembers: async () => members
69 })
70 await sb.ready()
71 const repo = new RepoBase(sb, { did })
72 return { sb, repo, self: hex(deviceKp.publicKey) }
73}
74
75// FOUNDER: create a community end to end — mint the DID, boot the co-owned repo as the first admin,
76// project the admin roster, and create the LINKED keyless CommunityRoom (meta:did = the community DID).
77// adminDeviceKeys: pre-known co-admin device pubkeys (hex/Buffer) to seed into the roster now (attest +
78// hand them their bundles separately via attestAdmin). Returns everything a caller needs to add records +
79// admins. roomStore/repoStorage are separate (the room is plaintext/keyless; the repo is encrypted).
80export async function createCommunity ({ rootMnemonic, founderDeviceMnemonic, adminDeviceKeys = [], name, roomStore, repoStorage, handle, pdsUrl = 'https://pds.7rnx.net', swarmOpts = null, network = true } = {}) {
81 if (!rootMnemonic || !founderDeviceMnemonic || !roomStore || !repoStorage) throw new Error('createCommunity: rootMnemonic, founderDeviceMnemonic, roomStore, repoStorage required')
82 const identity = communityIdentity(rootMnemonic, { handle, pdsUrl })
83 const signingKey = b4a.toString(identity.signingKey.privateKey, 'hex')
84 const rootPub = b4a.toString(await rootIdentityPublicKey(rootMnemonic), 'hex')
85 const founderKp = await deviceKeetKeyPair(founderDeviceMnemonic)
86 const founderProof = b4a.toString(b4a.from(await attestDeviceChain({ rootMnemonic, devicePublicKey: founderKp.publicKey })), 'hex')
87 const members = [
88 { key: hex(founderKp.publicKey), role: 1 },
89 ...adminDeviceKeys.map((k) => ({ key: hex(k), role: 1 }))
90 ]
91 const { sb, repo, self } = await openCommunityRepo({ storage: repoStorage, did: identity.did, signingKey, rootPub, deviceMnemonic: founderDeviceMnemonic, deviceProof: founderProof, members, swarmOpts, network })
92 await sb.projectMembers(members)
93 const room = await CommunityRoom.create(roomStore, { name, did: identity.did })
94 return { did: identity.did, identity, signingKey, rootPub, members, sb, repo, room, self }
95}
96
97// A CO-ADMIN boots its view of an existing community from the bundle the founder handed it. Returns the
98// { sb, repo } — poll sb.effectiveWritable() to know when the founder's roster/attestation has admitted it.
99export async function bootCommunityAdmin ({ storage, did, signingKey, rootPub, deviceMnemonic, deviceProof, members, swarmOpts = null, network = true } = {}) {
100 return openCommunityRepo({ storage, did, signingKey, rootPub, deviceMnemonic, deviceProof, members, swarmOpts, network })
101}
package-lock.json +1116 -0
……@@ -0,0 +1,1116 @@
1{
2 "name": "favela-node",
3 "version": "0.1.0",
4 "lockfileVersion": 3,
5 "requires": true,
6 "packages": {
7 "": {
8 "name": "favela-node",
9 "version": "0.1.0",
10 "dependencies": {
11 "atproto-identity": "file:../atproto-identity",
12 "atproto-room": "file:../atproto-room",
13 "b4a": "^1.8.1",
14 "bare-path": "^3.0.0",
15 "bare-process": "^4.0.0",
16 "corestore": "^7.4.0",
17 "favela-community": "file:../favela-community",
18 "hypercore-crypto": "^3.4.0",
19 "hyperswarm": "^4.11.0"
20 },
21 "devDependencies": {
22 "@hyperswarm/testnet": "^3.1.4"
23 }
24 },
25 "../atproto-identity": {
26 "version": "0.0.1",
27 "hasInstallScript": true,
28 "dependencies": {
29 "@ipld/dag-cbor": "^9.2.1",
30 "@noble/curves": "^1.6.0",
31 "@noble/hashes": "^1.5.0",
32 "@scure/bip32": "^1.5.0",
33 "@scure/bip39": "^1.4.0",
34 "hypercore-crypto": "^3.0.0",
35 "keet-identity-key": "^3.2.0",
36 "multiformats": "^13.3.0"
37 },
38 "devDependencies": {
39 "brittle": "^4.0.2"
40 }
41 },
42 "../atproto-room": {
43 "version": "0.2.0",
44 "hasInstallScript": true,
45 "dependencies": {
46 "@ipld/dag-cbor": "^9.2.1",
47 "atproto-identity": "file:../atproto-identity",
48 "atproto-repo": "file:../atproto-repo",
49 "atproto-store": "file:../atproto-store",
50 "autobase": "7.28.1",
51 "b4a": "^1.6.0",
52 "bare-fs": "^4.7.1",
53 "bare-path": "^3.0.1",
54 "bare-process": "^4.2.1",
55 "compact-encoding": "^2.16.0",
56 "corestore": "^7.10.0",
57 "hypercore": "^11.33.1",
58 "hypercore-crypto": "^3.0.0",
59 "hyperdb": "^6.7.0",
60 "hyperdispatch": "^1.6.0",
61 "hyperschema": "^1.21.0",
62 "hyperswarm": "^4.17.0",
63 "keet-identity-key": "^3.2.0",
64 "multiformats": "^13.4.2",
65 "protomux": "^3.11.0",
66 "protomux-rpc": "^1.10.0"
67 },
68 "devDependencies": {
69 "@hyperswarm/testnet": "^3.1.4",
70 "@noble/curves": "^1.6.0",
71 "brittle": "^4.0.2"
72 }
73 },
74 "../favela-community": {
75 "version": "0.1.3",
76 "dependencies": {
77 "autobase": "^7.28.0",
78 "b4a": "^1.8.1",
79 "bare-fs": "^4.0.0",
80 "bare-path": "^3.0.0",
81 "bare-process": "^4.0.0",
82 "corestore": "^7.4.0",
83 "hyper-rooms": "file:../hyper-rooms",
84 "hyperswarm": "^4.11.0"
85 },
86 "bin": {
87 "favela-community": "bin/favela-community.js"
88 }
89 },
90 "node_modules/@hyperswarm/secret-stream": {
91 "version": "6.9.1",
92 "resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz",
93 "integrity": "sha512-xb0S5y3YJwBakD77JOGBHlBxdp63mHClZoXBYoLv+9wH8e054ESKlmQptWqjJK5dv5VMUIVYOJB4MaOpB0JdGw==",
94 "license": "Apache-2.0",
95 "dependencies": {
96 "b4a": "^1.1.0",
97 "hypercore-crypto": "^3.3.1",
98 "noise-curve-ed": "^2.0.1",
99 "noise-handshake": "^4.0.0",
100 "sodium-secretstream": "^1.1.0",
101 "sodium-universal": "^5.0.0",
102 "streamx": "^2.14.0",
103 "timeout-refresh": "^2.0.0",
104 "unslab": "^1.3.0"
105 }
106 },
107 "node_modules/@hyperswarm/testnet": {
108 "version": "3.1.4",
109 "resolved": "https://registry.npmjs.org/@hyperswarm/testnet/-/testnet-3.1.4.tgz",
110 "integrity": "sha512-Jw8JIZ9kdZ4R9jwJHsqctj5YeRqecw3aeCg2NS12HBXCqMioQIYzVqHs/8BWdOQCf34+BEXkwMQjba9CzaLwkg==",
111 "dev": true,
112 "license": "Apache-2.0",
113 "dependencies": {
114 "hyperdht": "^6.6.0"
115 },
116 "bin": {
117 "hyperswarm-testnet": "bin.js"
118 }
119 },
120 "node_modules/adaptive-timeout": {
121 "version": "1.0.1",
122 "resolved": "https://registry.npmjs.org/adaptive-timeout/-/adaptive-timeout-1.0.1.tgz",
123 "integrity": "sha512-+seGiBtHqbnyZ0Quq/ZGxxwP66153WBABawa07/QeyuPFzbduPBjiGQAyCiriiLDzt3dkbMBskSlfqtRwblK8Q==",
124 "license": "Apache-2.0",
125 "dependencies": {
126 "xache": "^1.2.1"
127 }
128 },
129 "node_modules/atproto-identity": {
130 "resolved": "../atproto-identity",
131 "link": true
132 },
133 "node_modules/atproto-room": {
134 "resolved": "../atproto-room",
135 "link": true
136 },
137 "node_modules/b4a": {
138 "version": "1.8.1",
139 "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
140 "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
141 "license": "Apache-2.0",
142 "peerDependencies": {
143 "react-native-b4a": "*"
144 },
145 "peerDependenciesMeta": {
146 "react-native-b4a": {
147 "optional": true
148 }
149 }
150 },
151 "node_modules/bare-abort": {
152 "version": "2.0.13",
153 "resolved": "https://registry.npmjs.org/bare-abort/-/bare-abort-2.0.13.tgz",
154 "integrity": "sha512-zdc8l88eB11Jsz5rDd6sCAgv2kUFXgdrZWoMlgU6JMkfAi1/uuGFC3IEHswKbIRQTk5H3T5CMuechsXYxiaHlQ==",
155 "license": "Apache-2.0"
156 },
157 "node_modules/bare-addon-resolve": {
158 "version": "1.10.0",
159 "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz",
160 "integrity": "sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==",
161 "license": "Apache-2.0",
162 "dependencies": {
163 "bare-module-resolve": "^1.10.0",
164 "bare-semver": "^1.0.0"
165 },
166 "peerDependencies": {
167 "bare-url": "*"
168 },
169 "peerDependenciesMeta": {
170 "bare-url": {
171 "optional": true
172 }
173 }
174 },
175 "node_modules/bare-ansi-escapes": {
176 "version": "2.2.3",
177 "resolved": "https://registry.npmjs.org/bare-ansi-escapes/-/bare-ansi-escapes-2.2.3.tgz",
178 "integrity": "sha512-02ES4/E2RbrtZSnHJ9LntBhYkLA6lPpSEeP8iqS3MccBIVhVBlEmruF1I7HZqx5Q8aiTeYfQVeqmrU9YO2yYoQ==",
179 "license": "Apache-2.0",
180 "dependencies": {
181 "bare-stream": "^2.6.5"
182 },
183 "peerDependencies": {
184 "bare-buffer": "*"
185 },
186 "peerDependenciesMeta": {
187 "bare-buffer": {
188 "optional": true
189 }
190 }
191 },
192 "node_modules/bare-assert": {
193 "version": "1.2.0",
194 "resolved": "https://registry.npmjs.org/bare-assert/-/bare-assert-1.2.0.tgz",
195 "integrity": "sha512-c6uvgvTJBspTDxtVnPgrBKmLgcpW3Fp72NVKDLg6oT4QjQbhGtvrkHMhGYMK1sh4vjBHOBmuUalyt9hSzV37fQ==",
196 "license": "Apache-2.0",
197 "dependencies": {
198 "bare-inspect": "^3.1.2"
199 }
200 },
201 "node_modules/bare-env": {
202 "version": "3.0.1",
203 "resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.1.tgz",
204 "integrity": "sha512-BdoLvnzaWR0YyyJreEx4zG2Od0AC/s9JSZ+EXyMZKunpn8zEgR/dGv37hdFUaY1bjDkcRKvcKkFyJIwlJ0CaYA==",
205 "license": "Apache-2.0",
206 "dependencies": {
207 "bare-os": "^3.0.1"
208 }
209 },
210 "node_modules/bare-events": {
211 "version": "2.9.1",
212 "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
213 "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
214 "license": "Apache-2.0",
215 "peerDependencies": {
216 "bare-abort-controller": "*"
217 },
218 "peerDependenciesMeta": {
219 "bare-abort-controller": {
220 "optional": true
221 }
222 }
223 },
224 "node_modules/bare-fs": {
225 "version": "4.7.4",
226 "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz",
227 "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==",
228 "license": "Apache-2.0",
229 "dependencies": {
230 "bare-events": "^2.5.4",
231 "bare-path": "^3.0.0",
232 "bare-stream": "^2.6.4",
233 "bare-url": "^2.2.2",
234 "fast-fifo": "^1.3.2"
235 },
236 "engines": {
237 "bare": ">=1.16.0"
238 },
239 "peerDependencies": {
240 "bare-buffer": "*"
241 },
242 "peerDependenciesMeta": {
243 "bare-buffer": {
244 "optional": true
245 }
246 }
247 },
248 "node_modules/bare-hrtime": {
249 "version": "2.1.1",
250 "resolved": "https://registry.npmjs.org/bare-hrtime/-/bare-hrtime-2.1.1.tgz",
251 "integrity": "sha512-VMb3tHo05gsnbu3OXTmkDiwTjMlOsbQmKoysKqKEyR09m77TuDrYFbj3Q5GGk10dAKsUHrnXmwCaeJqzVpB5ZA==",
252 "license": "Apache-2.0"
253 },
254 "node_modules/bare-inspect": {
255 "version": "3.1.4",
256 "resolved": "https://registry.npmjs.org/bare-inspect/-/bare-inspect-3.1.4.tgz",
257 "integrity": "sha512-jfW5KRA84o3REpI6Vr4nbvMn+hqVAw8GU1mMdRwUsY5yJovQamxYeKGVKGqdzs+8ZbG4jRzGUXP/3Ji/DnqfPg==",
258 "license": "Apache-2.0",
259 "dependencies": {
260 "bare-ansi-escapes": "^2.1.0",
261 "bare-type": "^1.0.0"
262 },
263 "engines": {
264 "bare": ">=1.18.0"
265 }
266 },
267 "node_modules/bare-module-resolve": {
268 "version": "1.12.3",
269 "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.3.tgz",
270 "integrity": "sha512-ZIfm+Pd7MXsdqojk87zpuTcWo8iw+W+g5ATy63wKq3LCvu8DxpliQIVethgl44jDRL9PvBT1moj9JdYmRVve/A==",
271 "license": "Apache-2.0",
272 "dependencies": {
273 "bare-semver": "^1.0.0"
274 },
275 "peerDependencies": {
276 "bare-url": "*"
277 },
278 "peerDependenciesMeta": {
279 "bare-url": {
280 "optional": true
281 }
282 }
283 },
284 "node_modules/bare-os": {
285 "version": "3.9.3",
286 "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.3.tgz",
287 "integrity": "sha512-fF4Q7QsyKVF5Rj0qvI8BgUNjqzC2JvQlpTaPLjVJVxYVUX5Zr9un+y3w1HmA4nNKdFmRBT8z/WmrjvXzXVerKQ==",
288 "license": "Apache-2.0",
289 "engines": {
290 "bare": ">=1.14.0"
291 }
292 },
293 "node_modules/bare-path": {
294 "version": "3.1.1",
295 "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz",
296 "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==",
297 "license": "Apache-2.0"
298 },
299 "node_modules/bare-pipe": {
300 "version": "4.2.2",
301 "resolved": "https://registry.npmjs.org/bare-pipe/-/bare-pipe-4.2.2.tgz",
302 "integrity": "sha512-Wkf6//CmKS8inHrkgi3X/xNMeaGfs2I0ayLM0B8cbPB4DxYHcoes6Ng+muCBfXNIJ9qmEjwDDJjAUz7nRxxjgQ==",
303 "license": "Apache-2.0",
304 "dependencies": {
305 "bare-events": "^2.0.0",
306 "bare-stream": "^2.0.0"
307 },
308 "engines": {
309 "bare": ">=1.16.0"
310 }
311 },
312 "node_modules/bare-posix": {
313 "version": "1.0.1",
314 "resolved": "https://registry.npmjs.org/bare-posix/-/bare-posix-1.0.1.tgz",
315 "integrity": "sha512-b4quA8dyRVvX0aFIAIiev5zFATHwIUGr42Gmt+CqiBNg/bdsNkrj4bbkeotMOMiljJS4DCME5kywJO/TFwKo1A==",
316 "license": "Apache-2.0"
317 },
318 "node_modules/bare-process": {
319 "version": "4.5.1",
320 "resolved": "https://registry.npmjs.org/bare-process/-/bare-process-4.5.1.tgz",
321 "integrity": "sha512-CaAvy1trputD49mtwfJ6G75vydhnirLrW/F3Sznp4H556e1uZn8YMo9ELicBTrGYy7RBNUgPl9bpB/ERzRCiDw==",
322 "license": "Apache-2.0",
323 "dependencies": {
324 "bare-abort": "^2.0.13",
325 "bare-env": "^3.0.0",
326 "bare-events": "^2.3.1",
327 "bare-hrtime": "^2.0.0",
328 "bare-os": "^3.7.1",
329 "bare-posix": "^1.0.1",
330 "bare-signals": "^5.0.0",
331 "bare-stdio": "^1.0.1"
332 }
333 },
334 "node_modules/bare-semver": {
335 "version": "1.1.0",
336 "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.1.0.tgz",
337 "integrity": "sha512-1Hw5qJ7hXdVt3uPUqjeFTuxyvBUJauvz5A1I2jk8gzjZMHp04n//6nV9MDbG9CMw78JHY2lGV0w6s//LrASm2w==",
338 "license": "Apache-2.0"
339 },
340 "node_modules/bare-signals": {
341 "version": "5.0.0",
342 "resolved": "https://registry.npmjs.org/bare-signals/-/bare-signals-5.0.0.tgz",
343 "integrity": "sha512-8Gn8bBFUh2AUCJ9wWWFjSGWIo1HIUSIQnXRJGSm/f7GCDMqsuJhRmR1dT+HtDaDBkeu2l8Koxt2JLurfJ5yHVw==",
344 "license": "Apache-2.0",
345 "dependencies": {
346 "bare-events": "^2.5.3"
347 },
348 "engines": {
349 "bare": ">=1.7.0"
350 }
351 },
352 "node_modules/bare-stdio": {
353 "version": "1.0.3",
354 "resolved": "https://registry.npmjs.org/bare-stdio/-/bare-stdio-1.0.3.tgz",
355 "integrity": "sha512-8BRMx7RWMWCbBmKhyHgBco62J+Pgss7+EPI21L3R9+w0UpwDYWUUcbE0YTOesGyt7M6N7VtoshYbzwrP0pI52Q==",
356 "license": "Apache-2.0",
357 "dependencies": {
358 "bare-fs": "^4.5.2",
359 "bare-pipe": "^4.1.5",
360 "bare-tty": "^5.0.3"
361 }
362 },
363 "node_modules/bare-stream": {
364 "version": "2.13.3",
365 "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz",
366 "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==",
367 "license": "Apache-2.0",
368 "dependencies": {
369 "b4a": "^1.8.1",
370 "streamx": "^2.25.0",
371 "teex": "^1.0.1"
372 },
373 "peerDependencies": {
374 "bare-abort-controller": "*",
375 "bare-buffer": "*",
376 "bare-events": "*"
377 },
378 "peerDependenciesMeta": {
379 "bare-abort-controller": {
380 "optional": true
381 },
382 "bare-buffer": {
383 "optional": true
384 },
385 "bare-events": {
386 "optional": true
387 }
388 }
389 },
390 "node_modules/bare-tty": {
391 "version": "5.1.2",
392 "resolved": "https://registry.npmjs.org/bare-tty/-/bare-tty-5.1.2.tgz",
393 "integrity": "sha512-wDHPU/yVQtrDCUrKVdIVOLmfIw7dFPETz30UayuFlROvgz8Juv2SmISSMhqxYQVO+fVnG/1ZoLcbGh/35eAjDA==",
394 "license": "Apache-2.0",
395 "dependencies": {
396 "bare-events": "^2.2.0",
397 "bare-signals": "^5.0.0",
398 "bare-stream": "^2.0.0"
399 },
400 "engines": {
401 "bare": ">=1.16.0"
402 }
403 },
404 "node_modules/bare-type": {
405 "version": "1.1.0",
406 "resolved": "https://registry.npmjs.org/bare-type/-/bare-type-1.1.0.tgz",
407 "integrity": "sha512-LdtnnEEYldOc87Dr4GpsKnStStZk3zfgoEMXy8yvEZkXrcCv9RtYDrUYWFsBQHtaB0s1EUWmcvS6XmEZYIj3Bw==",
408 "license": "Apache-2.0",
409 "engines": {
410 "bare": ">=1.2.0"
411 }
412 },
413 "node_modules/bare-url": {
414 "version": "2.4.5",
415 "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz",
416 "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==",
417 "license": "Apache-2.0",
418 "dependencies": {
419 "bare-path": "^3.0.0"
420 }
421 },
422 "node_modules/big-sparse-array": {
423 "version": "1.0.3",
424 "resolved": "https://registry.npmjs.org/big-sparse-array/-/big-sparse-array-1.0.3.tgz",
425 "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==",
426 "license": "MIT"
427 },
428 "node_modules/bits-to-bytes": {
429 "version": "1.3.0",
430 "resolved": "https://registry.npmjs.org/bits-to-bytes/-/bits-to-bytes-1.3.0.tgz",
431 "integrity": "sha512-OJoHTpFXS9bXHBCekGTByf3MqM8CGblBDIduKQeeVVeiU9dDWywSSirXIBYGgg3d1zbVuvnMa1vD4r6PA0kOKg==",
432 "license": "ISC",
433 "dependencies": {
434 "b4a": "^1.5.0"
435 }
436 },
437 "node_modules/blind-relay": {
438 "version": "1.6.1",
439 "resolved": "https://registry.npmjs.org/blind-relay/-/blind-relay-1.6.1.tgz",
440 "integrity": "sha512-38YmKCl/m9NcTkwueBbcGIt9Zx3IT/Q9Nsluu6C5Gur6PqfE0zWPhPwCzia1hri/g0ICYxrqkgLtEaoWD5WeSQ==",
441 "license": "Apache-2.0",
442 "dependencies": {
443 "b4a": "^1.6.4",
444 "bare-events": "^2.2.0",
445 "bits-to-bytes": "^1.3.0",
446 "compact-encoding": "^3.0.0",
447 "compact-encoding-bitfield": "^1.0.0",
448 "protomux": "^3.5.1",
449 "sodium-universal": "^5.0.0",
450 "streamx": "^2.15.1"
451 }
452 },
453 "node_modules/bogon": {
454 "version": "1.3.0",
455 "resolved": "https://registry.npmjs.org/bogon/-/bogon-1.3.0.tgz",
456 "integrity": "sha512-04tu0G/v0f2HPuQlSfhO635WwHDBCaITJ490rhxH9ttUlgPqOirZVKV02YB6NvPf5VkmbqJCm3bnZwrPk/eDSg==",
457 "license": "MIT",
458 "dependencies": {
459 "compact-encoding": "^3.0.0",
460 "compact-encoding-net": "^1.2.0"
461 }
462 },
463 "node_modules/compact-encoding": {
464 "version": "3.3.0",
465 "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-3.3.0.tgz",
466 "integrity": "sha512-e64XyzlBvTRJ3iScuU/U2w25Dglkm7fhrRbrGaHIwuTlNnzjRKMaQBCVl7mJRvZg7wI5a9wX1IVTXCuaAANNkw==",
467 "license": "Apache-2.0",
468 "dependencies": {
469 "b4a": "^1.3.0"
470 }
471 },
472 "node_modules/compact-encoding-bitfield": {
473 "version": "1.1.0",
474 "resolved": "https://registry.npmjs.org/compact-encoding-bitfield/-/compact-encoding-bitfield-1.1.0.tgz",
475 "integrity": "sha512-F6wliSKHi50BsBStmnsRJAzJgYSIYzdfSe3M0oHj4uQ1RcctJK/NQVD7wF51bj/DBMne2i5gUxrGUFkNZQns5w==",
476 "license": "Apache-2.0",
477 "dependencies": {
478 "compact-encoding": "^3.0.0"
479 }
480 },
481 "node_modules/compact-encoding-net": {
482 "version": "1.3.0",
483 "resolved": "https://registry.npmjs.org/compact-encoding-net/-/compact-encoding-net-1.3.0.tgz",
484 "integrity": "sha512-HIYjL3aMiSENApR691aMLngXt6rkTHb9HUkEukcx3YwIZpoMUVXHXyjBzoo9kVwC7KakooBA1RRWb46Cu6qtAQ==",
485 "license": "Apache-2.0",
486 "dependencies": {
487 "compact-encoding": "^3.0.0"
488 }
489 },
490 "node_modules/corestore": {
491 "version": "7.11.1",
492 "resolved": "https://registry.npmjs.org/corestore/-/corestore-7.11.1.tgz",
493 "integrity": "sha512-X6UsGFyHeAcqffTqgoDV7WDZjhfvolWabwzws6GbdqeXQY0CTM6gNpNMGEi6RGvAdZDPnFaboF5jFBy1lw1rDA==",
494 "license": "MIT",
495 "dependencies": {
496 "b4a": "^1.6.7",
497 "bare-events": "^2.8.3",
498 "hypercore": "^11.32.0",
499 "hypercore-crypto": "^3.4.2",
500 "hypercore-errors": "^1.4.0",
501 "hypercore-id-encoding": "^1.3.0",
502 "ready-resource": "^1.1.1",
503 "sodium-universal": "^5.0.1",
504 "streamx": "^2.26.0",
505 "which-runtime": "^1.2.1"
506 }
507 },
508 "node_modules/device-file": {
509 "version": "2.3.1",
510 "resolved": "https://registry.npmjs.org/device-file/-/device-file-2.3.1.tgz",
511 "integrity": "sha512-bmON44lwxJPle9N2OcH4tqM44pMGZKT8G6OkzXkz0urvqQ9LKkoQdTS6w0ztYfmLdUE27R4UUmx0+y2gEz4Jug==",
512 "license": "Apache-2.0",
513 "dependencies": {
514 "b4a": "^1.6.7",
515 "bare-fs": "^4.0.1",
516 "bare-path": "^3.0.0",
517 "fd-lock": "^2.1.0",
518 "fs-native-extensions": "^1.4.0",
519 "ready-resource": "^1.2.0"
520 }
521 },
522 "node_modules/dht-rpc": {
523 "version": "6.27.0",
524 "resolved": "https://registry.npmjs.org/dht-rpc/-/dht-rpc-6.27.0.tgz",
525 "integrity": "sha512-NsfgRlFDQnkA2+H+hJXMPLmBOn/TSIIc0cRMV8q42M4xc1uAXWtpvIIQsONF5tYcYC6px0bslrUGideN1h4S4A==",
526 "license": "MIT",
527 "dependencies": {
528 "adaptive-timeout": "^1.0.1",
529 "b4a": "^1.6.1",
530 "bare-events": "^2.2.0",
531 "compact-encoding": "^3.0.0",
532 "compact-encoding-net": "^1.2.0",
533 "fast-fifo": "^1.1.0",
534 "kademlia-routing-table": "^1.0.1",
535 "nat-sampler": "^1.0.1",
536 "sodium-universal": "^5.0.0",
537 "streamx": "^2.13.2",
538 "time-ordered-set": "^2.0.0",
539 "udx-native": "^1.5.3"
540 }
541 },
542 "node_modules/events-universal": {
543 "version": "1.0.1",
544 "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
545 "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
546 "license": "Apache-2.0",
547 "dependencies": {
548 "bare-events": "^2.7.0"
549 }
550 },
551 "node_modules/fast-fifo": {
552 "version": "1.3.2",
553 "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
554 "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
555 "license": "MIT"
556 },
557 "node_modules/favela-community": {
558 "resolved": "../favela-community",
559 "link": true
560 },
561 "node_modules/fd-lock": {
562 "version": "2.2.0",
563 "resolved": "https://registry.npmjs.org/fd-lock/-/fd-lock-2.2.0.tgz",
564 "integrity": "sha512-Il4jWBhjjgvUg+z8d0bC7ncXqB42caqKTUpnZ9jpcqiPmw8bkIixt7Kic1czbZPMxAQD/9kEqfZ5Dq77nSll0w==",
565 "license": "Apache-2.0",
566 "dependencies": {
567 "bare-fs": "^4.5.0",
568 "fs-native-extensions": "^1.4.4",
569 "ready-resource": "^1.2.0",
570 "resource-on-exit": "^1.0.0"
571 }
572 },
573 "node_modules/flat-tree": {
574 "version": "1.13.0",
575 "resolved": "https://registry.npmjs.org/flat-tree/-/flat-tree-1.13.0.tgz",
576 "integrity": "sha512-fT3HIuCPwHhFgJ20QYzDHgUG0zMmFg5cHvFiFo5h+QMSJ28TihsEVY0f8HGliuO+pOzmvjMx1odToeaEWkTnyQ==",
577 "license": "MIT"
578 },
579 "node_modules/fs-native-extensions": {
580 "version": "1.5.0",
581 "resolved": "https://registry.npmjs.org/fs-native-extensions/-/fs-native-extensions-1.5.0.tgz",
582 "integrity": "sha512-nuZLFm9mGCxvyi7Llww/J4OyifKCS21nEUTAmnlTZp3FObPOvA32aCedCmt4Z+8yk+caqfClNaSCfn/P7T7FLQ==",
583 "license": "Apache-2.0",
584 "dependencies": {
585 "require-addon": "^1.1.0",
586 "which-runtime": "^1.2.0"
587 }
588 },
589 "node_modules/generate-object-property": {
590 "version": "2.0.0",
591 "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-2.0.0.tgz",
592 "integrity": "sha512-KwuURPyqn2Mz8DdV29pJwQu0Y7tcsbkULr82eeOcY/ZllFK6I9Wm8dsRByIu7CKWlFi9BdW1b3mcXMp/kQBQsw==",
593 "license": "MIT",
594 "dependencies": {
595 "is-property": "^1.0.0"
596 }
597 },
598 "node_modules/generate-string": {
599 "version": "1.0.1",
600 "resolved": "https://registry.npmjs.org/generate-string/-/generate-string-1.0.1.tgz",
601 "integrity": "sha512-IfTY0dKZM43ACyGvXkbG7De7WY7MxTS5VO6Juhe8oJKpCmrYYXoqp/cJMskkpi0k9H8wuXq0H+eI898/BCqvXg==",
602 "license": "MIT"
603 },
604 "node_modules/hypercore": {
605 "version": "11.34.0",
606 "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-11.34.0.tgz",
607 "integrity": "sha512-wXln1nAf4pAOAxxxyWMvN/c2PrOxZSbJnMTeAPKVA4fhhQx/b6DrqfdAfS2vkvcieqRD50EJRwi5179rINheTQ==",
608 "license": "MIT",
609 "dependencies": {
610 "@hyperswarm/secret-stream": "^6.0.0",
611 "b4a": "^1.1.0",
612 "bare-events": "^2.2.0",
613 "big-sparse-array": "^1.0.3",
614 "compact-encoding": "^3.0.0",
615 "fast-fifo": "^1.3.0",
616 "flat-tree": "^1.9.0",
617 "hypercore-crypto": "^3.2.1",
618 "hypercore-errors": "^1.5.0",
619 "hypercore-id-encoding": "^1.2.0",
620 "hypercore-storage": "^3.2.0",
621 "is-options": "^1.0.1",
622 "nanoassert": "^2.0.0",
623 "protomux": "^3.5.0",
624 "quickbit-universal": "^2.2.0",
625 "random-array-iterator": "^1.0.0",
626 "safety-catch": "^1.0.1",
627 "sodium-universal": "^5.0.1",
628 "streamx": "^2.12.4",
629 "unslab": "^1.3.0",
630 "z32": "^1.0.0"
631 }
632 },
633 "node_modules/hypercore-crypto": {
634 "version": "3.7.0",
635 "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.7.0.tgz",
636 "integrity": "sha512-SWxobptQf2V/+ZLCCDRTXqFzGfTPIkNIuDyLKXpEMfcpJ1/ec94N9aWgylHcWOHmRt14ng/KR7z0BugebWHK9Q==",
637 "license": "MIT",
638 "dependencies": {
639 "b4a": "^1.6.6",
640 "compact-encoding": "^3.0.0",
641 "sodium-universal": "^5.0.0"
642 }
643 },
644 "node_modules/hypercore-errors": {
645 "version": "1.5.0",
646 "resolved": "https://registry.npmjs.org/hypercore-errors/-/hypercore-errors-1.5.0.tgz",
647 "integrity": "sha512-5KQ/SuDxsvet+7qWA35Ay6zdD9WyAHQoyWHGcPUTbmJBd300gvNIJoi3oma7kp4TTCSzii6qYumNZe/s0j/saQ==",
648 "license": "Apache-2.0",
649 "dependencies": {
650 "hypercore-id-encoding": "^1.3.0"
651 }
652 },
653 "node_modules/hypercore-id-encoding": {
654 "version": "1.3.0",
655 "resolved": "https://registry.npmjs.org/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz",
656 "integrity": "sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ==",
657 "license": "Apache-2.0",
658 "dependencies": {
659 "b4a": "^1.5.3",
660 "z32": "^1.0.0"
661 }
662 },
663 "node_modules/hypercore-storage": {
664 "version": "3.2.0",
665 "resolved": "https://registry.npmjs.org/hypercore-storage/-/hypercore-storage-3.2.0.tgz",
666 "integrity": "sha512-i5O5ZMkdZaNAWGuNRXUZjuzv7B41OIhDHUwLCZPjucgVmywY0ZE5PDafu6e5oPuhtghjl6S8q6Jn+POVKN2BvQ==",
667 "license": "Apache-2.0",
668 "dependencies": {
669 "b4a": "^1.6.7",
670 "bare-path": "^3.0.0",
671 "compact-encoding": "^3.1.0",
672 "device-file": "^2.1.2",
673 "flat-tree": "^1.12.1",
674 "hypercore-crypto": "^3.4.2",
675 "hyperschema": "^1.21.0",
676 "index-encoder": "^3.3.2",
677 "resolve-reject-promise": "^1.0.0",
678 "rocksdb-native": "^3.11.0",
679 "scope-lock": "^1.2.4",
680 "streamx": "^2.21.1",
681 "xache": "^1.2.1"
682 }
683 },
684 "node_modules/hyperdht": {
685 "version": "6.33.0",
686 "resolved": "https://registry.npmjs.org/hyperdht/-/hyperdht-6.33.0.tgz",
687 "integrity": "sha512-veSvVKptjPTu2di3q5IWI62ZDFzEmTj1QSTAkiXW/cg0+lgMUlwfVWB4dX+WI14xNb54vogVIjA/Ph3KiVuRJQ==",
688 "license": "MIT",
689 "dependencies": {
690 "@hyperswarm/secret-stream": "^6.6.2",
691 "b4a": "^1.3.1",
692 "bare-events": "^2.2.0",
693 "blind-relay": "^1.3.0",
694 "bogon": "^1.0.0",
695 "compact-encoding": "^3.0.0",
696 "dht-rpc": "^6.15.1",
697 "hypercore-crypto": "^3.3.0",
698 "hypercore-id-encoding": "^1.2.0",
699 "hyperdht-address": "^1.0.1",
700 "noise-curve-ed": "^2.0.0",
701 "noise-handshake": "^4.0.0",
702 "record-cache": "^1.1.1",
703 "safety-catch": "^1.0.1",
704 "signal-promise": "^1.0.3",
705 "sodium-universal": "^5.0.1",
706 "streamx": "^2.16.1",
707 "unslab": "^1.3.0",
708 "xache": "^1.1.0"
709 },
710 "bin": {
711 "hyperdht": "bin.js"
712 }
713 },
714 "node_modules/hyperdht-address": {
715 "version": "1.1.1",
716 "resolved": "https://registry.npmjs.org/hyperdht-address/-/hyperdht-address-1.1.1.tgz",
717 "integrity": "sha512-Mu/+7SW2cwvHxMXswa5mOrhrS5BJyntViAuB25k8d8wNtA8eAPs4LaIq6TwN1SS/KQY02h1r+gM8cQeN/k360w==",
718 "license": "Apache-2.0",
719 "dependencies": {
720 "compact-encoding": "^3.0.0",
721 "hyperschema": "^1.20.1"
722 }
723 },
724 "node_modules/hyperschema": {
725 "version": "1.21.0",
726 "resolved": "https://registry.npmjs.org/hyperschema/-/hyperschema-1.21.0.tgz",
727 "integrity": "sha512-lEnIbLTUQf7w6FU6X+R3yUJhBwCzF4ewRnAaYOrPdcVWe1rbOf94PzMiXb5pBKxroCXzlrPLxVujxAsTrrHc8g==",
728 "license": "Apache-2.0",
729 "dependencies": {
730 "bare-fs": "^4.0.1",
731 "compact-encoding": "^3.0.0",
732 "generate-object-property": "^2.0.0",
733 "generate-string": "^1.0.1"
734 }
735 },
736 "node_modules/hyperswarm": {
737 "version": "4.17.0",
738 "resolved": "https://registry.npmjs.org/hyperswarm/-/hyperswarm-4.17.0.tgz",
739 "integrity": "sha512-oe86sK961Ueg7rvDN/veFwG8xH+Iv6vObPhGDkPJcDVxk/NduW41ZhAcVDnHzRbm7S0eLU7WaDUvehOYoKSpRQ==",
740 "license": "MIT",
741 "dependencies": {
742 "b4a": "^1.3.1",
743 "bare-events": "^2.2.0",
744 "hyperdht": "^6.21.0",
745 "safety-catch": "^1.0.2",
746 "shuffled-priority-queue": "^2.1.0",
747 "streamx": "^2.22.1",
748 "unslab": "^1.3.0"
749 }
750 },
751 "node_modules/index-encoder": {
752 "version": "3.5.0",
753 "resolved": "https://registry.npmjs.org/index-encoder/-/index-encoder-3.5.0.tgz",
754 "integrity": "sha512-idZ1cxtZz2dRV6rUiaP9Xo99UjXbSzjcMacoQmxUMu/A7fEQcNPngvwDJYeWelQUS5XFlY71/or70lKn6XnwbQ==",
755 "license": "Apache-2.0",
756 "dependencies": {
757 "b4a": "^1.6.4"
758 }
759 },
760 "node_modules/is-options": {
761 "version": "1.0.2",
762 "resolved": "https://registry.npmjs.org/is-options/-/is-options-1.0.2.tgz",
763 "integrity": "sha512-u+Ai74c8Q74aS8BuHwPdI1jptGOT1FQXgCq8/zv0xRuE+wRgSMEJLj8lVO8Zp9BeGb29BXY6AsNPinfqjkr7Fg==",
764 "license": "MIT",
765 "dependencies": {
766 "b4a": "^1.1.1"
767 }
768 },
769 "node_modules/is-property": {
770 "version": "1.0.2",
771 "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
772 "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
773 "license": "MIT"
774 },
775 "node_modules/kademlia-routing-table": {
776 "version": "1.0.6",
777 "resolved": "https://registry.npmjs.org/kademlia-routing-table/-/kademlia-routing-table-1.0.6.tgz",
778 "integrity": "sha512-Ve6jwIlUCYvUzBnXnzVRHDZCFgXURW9gmF3r7n05kZs/2rNbLHXwGdcq0qIaSwdmJCvtosgR4JensnVU65hzNQ==",
779 "license": "MIT",
780 "dependencies": {
781 "bare-events": "^2.2.0"
782 }
783 },
784 "node_modules/nanoassert": {
785 "version": "2.0.0",
786 "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz",
787 "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==",
788 "license": "ISC"
789 },
790 "node_modules/nat-sampler": {
791 "version": "1.0.1",
792 "resolved": "https://registry.npmjs.org/nat-sampler/-/nat-sampler-1.0.1.tgz",
793 "integrity": "sha512-yQvyNN7xbqR8crTKk3U8gRgpcV1Az+vfCEijiHu9oHHsnIl8n3x+yXNHl42M6L3czGynAVoOT9TqBfS87gDdcw==",
794 "license": "MIT"
795 },
796 "node_modules/noise-curve-ed": {
797 "version": "2.1.0",
798 "resolved": "https://registry.npmjs.org/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz",
799 "integrity": "sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg==",
800 "license": "ISC",
801 "dependencies": {
802 "b4a": "^1.1.0",
803 "nanoassert": "^2.0.0",
804 "sodium-universal": "^5.0.0"
805 }
806 },
807 "node_modules/noise-handshake": {
808 "version": "4.2.0",
809 "resolved": "https://registry.npmjs.org/noise-handshake/-/noise-handshake-4.2.0.tgz",
810 "integrity": "sha512-9O/VTNX/E2/AToyMTTDU0J/4WhaXMTdqc2DHs9vf+snoZ0cenSBq0dNYTVV1snYYEkmo6QeRrYMxtqtoYnY+LA==",
811 "license": "Apache-2.0",
812 "dependencies": {
813 "b4a": "^1.1.0",
814 "nanoassert": "^2.0.0",
815 "sodium-universal": "^5.0.0"
816 }
817 },
818 "node_modules/protomux": {
819 "version": "3.11.0",
820 "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.11.0.tgz",
821 "integrity": "sha512-Ze43XaNHbL6zQ/zJwVYvue7Aj8pDB1HNnISsrFhAur3291Y+Iu2fMhOpySD73J/32BoBIUYrhl07lJsBR/fUVA==",
822 "license": "MIT",
823 "dependencies": {
824 "b4a": "^1.3.1",
825 "compact-encoding": "^3.0.0",
826 "queue-tick": "^1.0.0",
827 "safety-catch": "^1.0.1",
828 "unslab": "^1.3.0"
829 }
830 },
831 "node_modules/queue-tick": {
832 "version": "1.0.1",
833 "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
834 "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
835 "license": "MIT"
836 },
837 "node_modules/quickbit-native": {
838 "version": "2.4.8",
839 "resolved": "https://registry.npmjs.org/quickbit-native/-/quickbit-native-2.4.8.tgz",
840 "integrity": "sha512-FcCcqI+nIAWGknqhtrYT5TSD7t/N+Xd8ctM+2PrIIBuwOi5hx0SxAvuPtzLIEMfT/2h9+fhBakUe2uALOHX6yw==",
841 "license": "Apache-2.0",
842 "optional": true,
843 "dependencies": {
844 "require-addon": "^1.1.0"
845 }
846 },
847 "node_modules/quickbit-universal": {
848 "version": "2.2.0",
849 "resolved": "https://registry.npmjs.org/quickbit-universal/-/quickbit-universal-2.2.0.tgz",
850 "integrity": "sha512-w02i1R8n7+6pEKTud8DfF8zbFY9o7RtPlUc3jWbtCkDKvhbx/AvV7oNnz4/TcmsPGpSJS+fq5Ud6RH6+YPvSGg==",
851 "license": "ISC",
852 "dependencies": {
853 "b4a": "^1.6.0",
854 "simdle-universal": "^1.1.0"
855 },
856 "optionalDependencies": {
857 "quickbit-native": "^2.2.0"
858 }
859 },
860 "node_modules/random-array-iterator": {
861 "version": "1.0.0",
862 "resolved": "https://registry.npmjs.org/random-array-iterator/-/random-array-iterator-1.0.0.tgz",
863 "integrity": "sha512-u7xCM93XqKEvPTP6xZp2ehttcAemKnh73oKNf1FvzuVCfpt6dILDt1Kxl1LeBjm2iNIeR49VGFhy4Iz3yOun+Q==",
864 "license": "MIT"
865 },
866 "node_modules/ready-resource": {
867 "version": "1.2.0",
868 "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.2.0.tgz",
869 "integrity": "sha512-nfcco/8iAFV0M+2PYnmIc+/xY0iRb35d42HFHQ7AfjulbGEAFa+XWpByfwSyeVeiBoMLLFVMv1HixxNCqzSQ1g==",
870 "license": "MIT",
871 "dependencies": {
872 "bare-events": "^2.2.0"
873 }
874 },
875 "node_modules/record-cache": {
876 "version": "1.2.0",
877 "resolved": "https://registry.npmjs.org/record-cache/-/record-cache-1.2.0.tgz",
878 "integrity": "sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw==",
879 "license": "MIT",
880 "dependencies": {
881 "b4a": "^1.3.1"
882 }
883 },
884 "node_modules/refcounter": {
885 "version": "1.0.0",
886 "resolved": "https://registry.npmjs.org/refcounter/-/refcounter-1.0.0.tgz",
887 "integrity": "sha512-1WosVzUy0kPUaPMEtlNDwm99UsteALIhXXR8rerELoa63WkYIXAl0hxgwPFrIYBRWZPGUyekQ04FRtPJ7dHk9w==",
888 "license": "Apache-2.0"
889 },
890 "node_modules/require-addon": {
891 "version": "1.2.0",
892 "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz",
893 "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==",
894 "license": "Apache-2.0",
895 "dependencies": {
896 "bare-addon-resolve": "^1.3.0"
897 },
898 "engines": {
899 "bare": ">=1.10.0"
900 }
901 },
902 "node_modules/resolve-reject-promise": {
903 "version": "1.1.0",
904 "resolved": "https://registry.npmjs.org/resolve-reject-promise/-/resolve-reject-promise-1.1.0.tgz",
905 "integrity": "sha512-LWsTOA91AqzBTjSGgX79Tc130pwcBK6xjpJEO+qRT5IKZ6bGnHKcc8QL3upUBcWuU8OTIDzKK2VNSwmmlqvAVg==",
906 "license": "MIT"
907 },
908 "node_modules/resource-on-exit": {
909 "version": "1.0.0",
910 "resolved": "https://registry.npmjs.org/resource-on-exit/-/resource-on-exit-1.0.0.tgz",
911 "integrity": "sha512-ViJwJAknCkLRJRPR+9SISQQ7R5eRgtdIHLJsM2hHx1MweAJbJxJ5XnMjjq0Lc7ZGv44ufzAqds1nKxiVkdy4ag==",
912 "license": "Apache-2.0"
913 },
914 "node_modules/rocksdb-native": {
915 "version": "3.17.2",
916 "resolved": "https://registry.npmjs.org/rocksdb-native/-/rocksdb-native-3.17.2.tgz",
917 "integrity": "sha512-6kijLkb7NrUFCyWQsIxKIU/iQASxdqLDWrteAeXVEq1MJWrJUcXP6MDPRh8Ux7im2sBereDiI/D4vxYV2ia90A==",
918 "license": "Apache-2.0",
919 "dependencies": {
920 "compact-encoding": "^3.0.0",
921 "ready-resource": "^1.0.0",
922 "refcounter": "^1.0.0",
923 "require-addon": "^1.0.2",
924 "resolve-reject-promise": "^1.1.0",
925 "signal-promise": "^1.0.3",
926 "streamx": "^2.16.1"
927 },
928 "engines": {
929 "bare": ">=1.16.0"
930 }
931 },
932 "node_modules/safety-catch": {
933 "version": "1.0.3",
934 "resolved": "https://registry.npmjs.org/safety-catch/-/safety-catch-1.0.3.tgz",
935 "integrity": "sha512-Zq+J1TefpoEq/HTUabo0YXX5MNvttjWYODGohgPBO2jfko8Wqx3JYMgE823szDFVamdH5PlpByvfiWScTdSYDA==",
936 "license": "MIT"
937 },
938 "node_modules/scope-lock": {
939 "version": "1.2.4",
940 "resolved": "https://registry.npmjs.org/scope-lock/-/scope-lock-1.2.4.tgz",
941 "integrity": "sha512-BpSd8VCuCxW9ZitcdIC/vjs3gMaP9bRBL5nkHcyfX2VrS52n13/rHuBA2xJ/S/4DPuRdAO/Bk8pWd8eD/gHCIA==",
942 "license": "Apache-2.0"
943 },
944 "node_modules/shuffled-priority-queue": {
945 "version": "2.1.0",
946 "resolved": "https://registry.npmjs.org/shuffled-priority-queue/-/shuffled-priority-queue-2.1.0.tgz",
947 "integrity": "sha512-xhdh7fHyMsr0m/w2kDfRJuBFRS96b9l8ZPNWGaQ+PMvnUnZ/Eh+gJJ9NsHBd7P9k0399WYlCLzsy18EaMfyadA==",
948 "license": "MIT",
949 "dependencies": {
950 "unordered-set": "^2.0.1"
951 }
952 },
953 "node_modules/signal-promise": {
954 "version": "1.0.3",
955 "resolved": "https://registry.npmjs.org/signal-promise/-/signal-promise-1.0.3.tgz",
956 "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==",
957 "license": "MIT"
958 },
959 "node_modules/simdle-native": {
960 "version": "1.3.9",
961 "resolved": "https://registry.npmjs.org/simdle-native/-/simdle-native-1.3.9.tgz",
962 "integrity": "sha512-Isc8sP4OiiIU0mpslD4GHEnR0VQWvR/54WN7YtwEDkNdTJVWtpmvsSvsgRlw5BNGxdYXlVRegdnrSu10H/PhvA==",
963 "license": "Apache-2.0",
964 "optional": true,
965 "dependencies": {
966 "b4a": "^1.6.0",
967 "require-addon": "^1.1.0"
968 }
969 },
970 "node_modules/simdle-universal": {
971 "version": "1.1.2",
972 "resolved": "https://registry.npmjs.org/simdle-universal/-/simdle-universal-1.1.2.tgz",
973 "integrity": "sha512-3n3w1bs+uwgHKQjt6arez83EywNlhZzYvNOhvAASTl/8KqNIcqr6aHyGt3JRlfuUC7iB0tomJRPlJ2cRGIpBzA==",
974 "license": "ISC",
975 "dependencies": {
976 "b4a": "^1.6.0"
977 },
978 "optionalDependencies": {
979 "simdle-native": "^1.1.1"
980 }
981 },
982 "node_modules/sodium-native": {
983 "version": "5.1.0",
984 "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz",
985 "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==",
986 "license": "MIT",
987 "dependencies": {
988 "bare-assert": "^1.2.0",
989 "require-addon": "^1.1.0",
990 "which-runtime": "^1.2.1"
991 },
992 "engines": {
993 "bare": ">=1.16.0"
994 }
995 },
996 "node_modules/sodium-secretstream": {
997 "version": "1.2.0",
998 "resolved": "https://registry.npmjs.org/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz",
999 "integrity": "sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q==",
1000 "license": "MIT",
1001 "dependencies": {
1002 "b4a": "^1.1.1",
1003 "sodium-universal": "^5.0.0"
1004 }
1005 },
1006 "node_modules/sodium-universal": {
1007 "version": "5.0.1",
1008 "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz",
1009 "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==",
1010 "license": "MIT",
1011 "dependencies": {
1012 "sodium-native": "^5.0.1"
1013 },
1014 "peerDependencies": {
1015 "sodium-javascript": "~0.8.0"
1016 },
1017 "peerDependenciesMeta": {
1018 "sodium-javascript": {
1019 "optional": true
1020 }
1021 }
1022 },
1023 "node_modules/streamx": {
1024 "version": "2.28.0",
1025 "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
1026 "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==",
1027 "license": "MIT",
1028 "dependencies": {
1029 "events-universal": "^1.0.0",
1030 "fast-fifo": "^1.3.2",
1031 "text-decoder": "^1.1.0"
1032 }
1033 },
1034 "node_modules/teex": {
1035 "version": "1.0.1",
1036 "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
1037 "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
1038 "license": "MIT",
1039 "dependencies": {
1040 "streamx": "^2.12.5"
1041 }
1042 },
1043 "node_modules/text-decoder": {
1044 "version": "1.2.7",
1045 "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
1046 "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
1047 "license": "Apache-2.0",
1048 "dependencies": {
1049 "b4a": "^1.6.4"
1050 }
1051 },
1052 "node_modules/time-ordered-set": {
1053 "version": "2.0.1",
1054 "resolved": "https://registry.npmjs.org/time-ordered-set/-/time-ordered-set-2.0.1.tgz",
1055 "integrity": "sha512-VJEKmgSN2UiOLB8BpN8Sh2b9LGMHTP5OPrQRpnKjvOheOyzk0mufbjzjKTIG2gO4A+Y+vDJ+0TcLbpUmMLsg8A==",
1056 "license": "MIT"
1057 },
1058 "node_modules/timeout-refresh": {
1059 "version": "2.0.1",
1060 "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-2.0.1.tgz",
1061 "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==",
1062 "license": "MIT"
1063 },
1064 "node_modules/udx-native": {
1065 "version": "1.20.7",
1066 "resolved": "https://registry.npmjs.org/udx-native/-/udx-native-1.20.7.tgz",
1067 "integrity": "sha512-FNG0QpnSt0us2xbLP5mmG5Q0UKdLYjKHuh2eR9VrJSFmoZMzeszYLLhK12+iq5x3eq/rgoGG/lLwPn7IDAr6pg==",
1068 "license": "Apache-2.0",
1069 "dependencies": {
1070 "b4a": "^1.5.0",
1071 "bare-events": "^2.2.0",
1072 "require-addon": "^1.1.0",
1073 "streamx": "^2.22.0"
1074 },
1075 "engines": {
1076 "bare": ">=1.17.4"
1077 }
1078 },
1079 "node_modules/unordered-set": {
1080 "version": "2.0.1",
1081 "resolved": "https://registry.npmjs.org/unordered-set/-/unordered-set-2.0.1.tgz",
1082 "integrity": "sha512-eUmNTPzdx+q/WvOHW0bgGYLWvWHNT3PTKEQLg0MAQhc0AHASHVHoP/9YytYd4RBVariqno/mEUhVZN98CmD7bg==",
1083 "license": "MIT"
1084 },
1085 "node_modules/unslab": {
1086 "version": "1.3.0",
1087 "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz",
1088 "integrity": "sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ==",
1089 "license": "Apache-2.0",
1090 "dependencies": {
1091 "b4a": "^1.6.6"
1092 }
1093 },
1094 "node_modules/which-runtime": {
1095 "version": "1.4.0",
1096 "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.4.0.tgz",
1097 "integrity": "sha512-0ugbP4CJW4e2D20jvEcC4973dCgIaHI4Rw1PT+26U9zEve7FyYdWAIwUnoeOYvoCfn+wXHoHTKb1KhkYlb60Pw==",
1098 "license": "Apache-2.0"
1099 },
1100 "node_modules/xache": {
1101 "version": "1.3.0",
1102 "resolved": "https://registry.npmjs.org/xache/-/xache-1.3.0.tgz",
1103 "integrity": "sha512-ekAknjLTiyXJcuezqwa/cOMfFenVVY2toL4J7D3AfTn7UEDYb8JSgRnv3CK8khF7u38XZ4NjS1MXEw4RwqJ4oA==",
1104 "license": "MIT"
1105 },
1106 "node_modules/z32": {
1107 "version": "1.1.0",
1108 "resolved": "https://registry.npmjs.org/z32/-/z32-1.1.0.tgz",
1109 "integrity": "sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ==",
1110 "license": "MIT",
1111 "dependencies": {
1112 "b4a": "^1.5.3"
1113 }
1114 }
1115 }
1116}
package.json +24 -0
……@@ -0,0 +1,24 @@
1{
2 "name": "favela-node",
3 "version": "0.1.0",
4 "description": "favela-node — the full-node that composes a community's CO-OWNED atproto DID (hyper-proto-peer / atproto-room: StateBase admins + Lease + Signer/LabelRoom = the multiswarm-PDS pattern) with the keyless CommunityRoom roster (favela-community) and, later, the favela-web lens-export gateway. See favela-web/spec/lens-export.md.",
5 "type": "module",
6 "main": "index.js",
7 "scripts": {
8 "test": "bare test/community.test.mjs"
9 },
10 "dependencies": {
11 "atproto-identity": "file:../atproto-identity",
12 "atproto-room": "file:../atproto-room",
13 "favela-community": "file:../favela-community",
14 "corestore": "^7.4.0",
15 "hyperswarm": "^4.11.0",
16 "hypercore-crypto": "^3.4.0",
17 "bare-path": "^3.0.0",
18 "bare-process": "^4.0.0",
19 "b4a": "^1.8.1"
20 },
21 "devDependencies": {
22 "@hyperswarm/testnet": "^3.1.4"
23 }
24}
test/community.test.mjs +95 -0
……@@ -0,0 +1,95 @@
1// favela-node — the co-owned community, end to end over a local testnet:
2// - FOUNDER mints the community DID + boots its co-owned repo + creates the linked keyless CommunityRoom.
3// - a CO-ADMIN (attested to the community root) boots the SAME DID's repo and CO-SIGNS a feed.generator
4// record; it converges to the founder (one shared, co-owned repo — the multiswarm-PDS pattern).
5// - a KEYLESS reader reads the CommunityRoom's community DID + roster holding no key.
6// This proves spec/lens-export.md §3 (two substrates) + §8 phase 1 (DID + linkage). Run: bare test/community.test.mjs
7import '../lib/bare-globals.js' // FIRST — shim TextEncoder/process for @noble/* at module load (Bare)
8import Corestore from 'corestore'
9import b4a from 'b4a'
10import createTestnet from '@hyperswarm/testnet'
11import { deviceKeetKeyPair } from 'atproto-room/lib/membership.js'
12import { RepoBase } from 'atproto-room/lib/repo-base.js'
13import { CommunityRoom } from 'favela-community'
14import { createCommunity, attestAdmin, bootCommunityAdmin } from '../index.js'
15
16const assert = (c, m) => { if (!c) { console.error('FAIL:', m); process.exit(1) } console.log(' ok -', m) }
17const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
18async function until (fn, ms = 30000) { const end = Date.now() + ms; while (Date.now() < end) { try { if (await fn()) return true } catch {} await sleep(500) } return false }
19const stamp = Date.now()
20const tmp = (s) => `data/fn-${s}-${stamp}`
21
22// community root + two admin devices attested to it (the founder holds the root; admins get bundles)
23const ROOT = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
24const FOUNDER_DEV = 'legal winner thank year wave sausage worth useful legal winner thank yellow'
25const ADMIN2_DEV = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
26const GEN = 'app.bsky.feed.generator'
27
28const testnet = await createTestnet(3)
29const swarmOpts = { bootstrap: testnet.bootstrap }
30
31// admin2's device pubkey (known up front so the founder seeds it into the roster at create)
32const admin2Kp = await deviceKeetKeyPair(ADMIN2_DEV)
33
34// ── FOUNDER: mint the community DID + co-owned repo + linked keyless CommunityRoom ──
35const roomStore = new Corestore(tmp('room'))
36const c = await createCommunity({
37 rootMnemonic: ROOT,
38 founderDeviceMnemonic: FOUNDER_DEV,
39 adminDeviceKeys: [admin2Kp.publicKey], // admit admin2 into the roster now
40 name: 'Cat Lovers',
41 roomStore,
42 repoStorage: tmp('founder-repo'),
43 handle: 'catlovers.test',
44 swarmOpts
45})
46assert(/^did:plc:/.test(c.did), 'community minted an atproto did:plc')
47assert((await c.room.communityDid()) === c.did, 'the linked CommunityRoom records the community DID (meta:did)')
48assert(c.members.length === 2, 'roster seeded with founder + admin2 (two equal admins)')
49
50// founder adds a member to the roster (the room's write layer — mods manage membership)
51await c.room.addMember('did:plc:member2222222222222222222')
52
53// ── CO-ADMIN: founder attests admin2, admin2 boots the SAME DID's repo ──
54const bundle = await attestAdmin({ rootMnemonic: ROOT, deviceMnemonic: ADMIN2_DEV })
55assert(bundle.deviceProof && bundle.devicePublicKey === b4a.toString(admin2Kp.publicKey, 'hex'), 'founder attested admin2 (root→device proof)')
56const admin2 = await bootCommunityAdmin({
57 storage: tmp('admin2-repo'),
58 did: c.did,
59 signingKey: c.signingKey,
60 rootPub: c.rootPub,
61 deviceMnemonic: ADMIN2_DEV,
62 deviceProof: bundle.deviceProof,
63 members: c.members,
64 swarmOpts
65})
66
67// admin2 CO-SIGNS a feed.generator record into the community repo (poll until attestation admits it)
68const genRecord = { $type: GEN, did: c.did, displayName: 'Cat Lovers', createdAt: '2026-07-15T00:00:00.000Z' }
69let admitted = false
70for (let i = 0; i < 40 && !admitted; i++) {
71 try { await admin2.repo.putRecord(GEN, 'catlovers', genRecord) } catch {}
72 await admin2.sb.set('__ping__', { i }).catch(() => {})
73 await sleep(1500)
74 admitted = await admin2.sb.effectiveWritable().catch(() => false)
75}
76assert(admitted, 'CO-ADMIN admin2 was admitted as a writer via attestation (co-owned repo)')
77
78// convergence: the FOUNDER sees admin2's feed.generator record — one shared, co-owned community repo
79const founderRepo = new RepoBase(c.sb, { did: c.did })
80const seen = await until(async () => { await c.sb.base.update().catch(() => {}); const r = await founderRepo.getRecord(GEN, 'catlovers'); return r && r.did === c.did ? r : null })
81assert(seen, 'FOUNDER sees admin2\'s co-signed app.bsky.feed.generator record (multiswarm-PDS: co-owned repo)')
82
83// ── KEYLESS reader: the CommunityRoom DID + roster, holding no key (favela-web's path) ──
84const readerStore = new Corestore(tmp('reader'))
85const reader = await CommunityRoom.openReadonly(readerStore, c.room.key)
86const rs1 = reader.base.replicate(true); const rs2 = c.room.base.replicate(false)
87rs1.on('error', () => {}); rs2.on('error', () => {}); rs1.pipe(rs2).pipe(rs1)
88assert(await until(async () => (await reader.communityDid()) === c.did && (await reader.members()).includes('did:plc:member2222222222222222222')), 'KEYLESS reader reads the community DID + member roster (no key held)')
89assert(!reader.writable, 'the keyless reader holds no key')
90
91console.log('\nFAVELA-NODE OK: community = co-owned DID (two admins co-sign the repo) + linked keyless CommunityRoom roster')
92try { rs1.destroy(); rs2.destroy() } catch {}
93await c.sb.close(); await admin2.sb.close(); await c.room.close(); await reader.close()
94try { await roomStore.close(); await readerStore.close() } catch {}
95await testnet.destroy()