| … | … | @@ -1,31 +1,47 @@ |
| 1 | 1 | # favela-peer |
| 2 | 2 | |
| 3 | | The favela daemon. Feeds and documents of markdown records over Hypercore. |
| 3 | The favela daemon. Feeds, rooms and namespaces of markdown records over Hypercore. |
| 4 | 4 | |
| 5 | 5 | **A clean break from atproto and ActivityPub.** Bridges come after the MVP, and they are |
| 6 | 6 | *downstream* — they read this, this never reads them. |
| 7 | 7 | |
| 8 | Runs under Bare: `bare index.js`. pgh installs it and the launcher runs it; there is no `pear run` |
| 9 | path and never was. Storage is `~/.favela` and the control socket `~/.favela/favela.sock` |
| 10 | (`FAVELA_STORAGE` / `FAVELA_SOCK` override both). `bin/favela.js` is the CLI that speaks to it. |
| 11 | |
| 8 | 12 | ## The boundary |
| 9 | 13 | |
| 10 | 14 | ⛔ **This package must not import `atproto-*` or `ap-*`, and must not encode DAG-CBOR, build an MST, |
| 11 | 15 | mint a DID or sign a commit.** That is the acceptance criterion, not an aspiration |
| 12 | | (`hyper-social` #61 §10). A test asserts it over the resolved import closure. |
| 16 | (`hyper-social` #61 §10). `test/boundary.test.js` enforces it over `lib/` and the dependency list, |
| 17 | and it passes. |
| 13 | 18 | |
| 14 | | Legitimate dependencies: `favela-markdown` (the codec — a library with no storage and no network), |
| 15 | | Hypercore, and hyper-overlay. |
| 19 | Dependencies of substance: `favela-markdown` (the codec — a library with no storage and no |
| 20 | network), `favela-identity`, `hyper-identity2`, `hyper-overlay`, Hypercore/corestore/Hyperswarm, |
| 21 | `autobee`, `keet-identity-key`. |
| 16 | 22 | |
| 17 | | ## Two primitives |
| 23 | ## The primitives |
| 18 | 24 | |
| 19 | | - **feed** — one identity's log. Its writers are that identity's devices. You have exactly one. |
| 20 | | - **document** — a log with several deliberate writers and a key. Wiki page, DM, private group. |
| 25 | - **feed** (`lib/feed.js`, `lib/feed-base.js`) — one identity's log. Its roster is DEVICE-FLAT: |
| 26 | the writers are that identity's devices. You have exactly one. |
| 27 | - **room** (`lib/room.js`) — many MEMBERS, each with devices, and membership is the audience. |
| 28 | Two levels, so every authorisation question has to be asked at the right one — a role belongs to |
| 29 | a person, not to a laptop. The vocabulary is keet's (admin / moderator / peer / read-only); the |
| 30 | engine is **autobee**, not autobase, because autobase's `floor(N/2)+1` quorum freezes at N=2 and |
| 31 | a person's two devices make N=2 the ordinary posture. A DM is a room with one other member. |
| 32 | - **namespace** (`lib/namespace.js`) — human names that resolve to feeds. Not an authority, a |
| 33 | SUBSCRIPTION: two people on different namespaces resolve the same string to different feeds, and |
| 34 | that is correct. Resolution order is handle → self-label → key. |
| 21 | 35 | |
| 22 | | A *room* is not a primitive. It is the name someone gives a dense cluster of interactions once it is |
| 23 | | obvious there is a thing there — and naming it is a human act, never an automatic one. |
| 36 | ⚠ The earlier model here named a *document* as the second primitive and said a room was never one. |
| 37 | That was superseded: the room is the container, membership is the audience, and no separate |
| 38 | document type was ever built. |
| 24 | 39 | |
| 25 | 40 | ## The record |
| 26 | 41 | |
| 27 | 42 | A markdown file: JSON frontmatter plus a body. Mutable, edited in place. Bytes are content-addressed |
| 28 | | in hyper-overlay; the log holds a pointer. |
| 43 | in hyper-overlay; the log holds a pointer. Because it is mutable, **the rkey is the stable identity, |
| 44 | never the hash** — see `favela-markdown`. |
| 29 | 45 | |
| 30 | 46 | ## Interactions |
| 31 | 47 | |
| … | … | @@ -33,6 +49,23 @@ A like or a reply is a record in **your own** feed naming a target. Nobody becom |
| 33 | 49 | someone else's feed to interact with it, so a feed's writer set never grows beyond its owner's |
| 34 | 50 | devices — and counts are per-observer, never summed. |
| 35 | 51 | |
| 52 | The timeline is likewise LOCAL: a pure function of the feeds this node happens to hold. There is no |
| 53 | relay and no aggregator, so two peers legitimately compute different timelines from the same follow |
| 54 | list. A feed you follow but have not replicated contributes nothing — which is why `missing()` |
| 55 | exists: a gap you can name is a different thing from a gap that is silently empty. |
| 56 | |
| 36 | 57 | ## Status |
| 37 | 58 | |
| 38 | | Nothing works yet. See `hyper-social` #61 §10 for the build order and §11.2 for what would falsify it. |
| 59 | **Working.** 0.0.48, installed and running on tabertron under pgh alongside `favela-surface` (the |
| 60 | renderer) and `favela-web`. |
| 61 | |
| 62 | - `lib/` is 22 modules / ~6.3k lines; `lib/rpc.js` answers **92 methods** over the UDS. |
| 63 | - `npm test` — **342/342 tests, 813 asserts pass** (brittle-bare, ~35s, run 2026-09-09). |
| 64 | - Covered by tests: feeds, rooms, namespaces, names/handles, device pairing and attestation, |
| 65 | browser sessions, files and avatars, the mirror, the follow graph, search, notifications, |
| 66 | overlay seeding and replication. |
| 67 | |
| 68 | ⚠ **10 commits are unpushed.** The forge repo `favela-peer` is at 0.0.38 (`eb3a41d`); local master |
| 69 | is 0.0.48. `git push pear master`. |
| 70 | |
| 71 | See `hyper-social` #61 §10 for the build order and §11.2 for what would falsify it. |