| … | … | @@ -6,18 +6,45 @@ The **full-node** that composes a Favela community's two substrates (see |
| 6 | 6 | 1. the community's **co-owned atproto DID** — an [hyper-proto-peer](../hyper-proto-peer)-style identity |
| 7 | 7 | whose admins co-sign its repo ([atproto-room](../atproto-room): `StateBaseV3` admins + `Lease` + |
| 8 | 8 | `Signer` / `LabelRoom` = the **multiswarm-PDS pattern**). Owns/signs the community's |
| 9 | | `app.bsky.feed.generator` + `app.bsky.labeler.service` records and its `com.atproto.label` labels; |
| 10 | | IS the feed-generator + labeler service DID. |
| 9 | `app.bsky.feed.generator` + `app.bsky.labeler.service` records and its `com.atproto.label` labels. |
| 10 | It is the **owner** of those records; the service each one points at is a `did:web`, not this DID |
| 11 | (see Notes). |
| 11 | 12 | 2. the keyless **CommunityRoom** roster ([favela-community](../favela-community)) — the MEMBER audience |
| 12 | 13 | [favela-web](../favela-web) reads. The room's **mods == the DID's admins** (one attested set). |
| 13 | 14 | |
| 14 | 15 | A community is thus a **co-owned did:plc**: to bsky it's one account; behind it, N admins co-author with no |
| 15 | 16 | single point of key custody — exactly how a multi-device hyper-proto-peer presents one DID. |
| 16 | 17 | |
| 18 | ## Which stack this belongs to |
| 19 | |
| 20 | The atproto / lens-export stack, alongside [favela-community](../favela-community) — not the |
| 21 | markdown-record stack, whose rooms live in `favela-peer` on autobee and share none of this. Live: |
| 22 | `serve.js` runs as a pgh daemon on tabertron and `loser-club` is in favela-web's shipped defaults. |
| 23 | |
| 24 | ## Serve |
| 25 | |
| 26 | ```sh |
| 27 | favela-node # pgh-installed launcher; entrypoint is serve.js |
| 28 | ``` |
| 29 | |
| 30 | A `pgh.type: daemon` app with `autostart`, a user systemd unit and a pidfile readiness check. |
| 31 | It durably serves each configured community's `CommunityRoom` — the keyless roster favela-web reads |
| 32 | for the feed skeleton — on **one shared swarm**, replicating every served room's autobase over each |
| 33 | connection. That is the keet model: one swarm, many topics muxed. It replaced a `nohup` |
| 34 | `serve-loser-club-room.mjs`. |
| 35 | |
| 36 | | var | default | | |
| 37 | |---|---|---| |
| 38 | | `FAVELA_NODE_CONFIG` | `~/.config/favela-node/config.json` | `{ communities: [{ roomKey, storage, namespace? }] }` | |
| 39 | |
| 40 | Adding a community is one config entry. A community that fails to open is logged and skipped rather |
| 41 | than taking the daemon down with it; no config, or an empty list, exits non-zero. |
| 42 | |
| 17 | 43 | ## API |
| 18 | 44 | |
| 19 | 45 | ```js |
| 20 | | import { createCommunity, attestAdmin, bootCommunityAdmin, |
| 46 | import { communityIdentity, createCommunity, attestAdmin, bootCommunityAdmin, |
| 47 | openCommunityRepo, recoverCommunityRepo, |
| 21 | 48 | publishFeedGenerator, publishLabelerService } from 'favela-node' |
| 22 | 49 | |
| 23 | 50 | // founder: mint the DID + boot its co-owned repo + create the linked keyless CommunityRoom |
| … | … | @@ -42,10 +69,26 @@ The community DID's genesis op declares `#bsky_fg` / `#atproto_labeler` services |
| 42 | 69 | always-on anchor. Never N=2. Other admin devices default to plain writers. |
| 43 | 70 | - Key derivation is kept **identical to hpp `bootSwarmDevice`** so a community repo is interoperable with |
| 44 | 71 | the hpp / membrane stack. |
| 72 | - `recoverCommunityRepo` boots a fresh co-owned repo under the **same** recovered DID and signing key |
| 73 | from saved secrets alone — no new PLC submit — and republishes the `feed.generator` and |
| 74 | `labeler.service` records. It is repo-only: an intact room is reopened separately and never |
| 75 | recreated, and a corrupted store is abandoned rather than reused. |
| 76 | - The `feed.generator` record's `did` is the **`did:web` service**, not the community DID. The |
| 77 | community DID is the record's owner; the service is what bsky resolves to fetch a skeleton. Getting |
| 78 | these the wrong way round is what 0.1.5 fixed. |
| 45 | 79 | |
| 46 | 80 | ## Test |
| 47 | 81 | |
| 48 | 82 | ```sh |
| 83 | npm test |
| 84 | ``` |
| 85 | |
| 86 | ``` |
| 49 | 87 | bare test/publish.test.mjs # record shapes + genesis service entries (single-node) |
| 50 | 88 | bare test/community.test.mjs # two admins co-sign the community repo over a testnet + keyless roster read |
| 89 | bare test/recover.test.mjs # recovery from saved secrets: same DID, fresh repo, records republished |
| 51 | 90 | ``` |
| 91 | |
| 92 | ## License |
| 93 | |
| 94 | Not yet declared — `package.json` carries no `license` field and there is no LICENSE file. |