OverviewFilesCommitsIssuesDocsReleasesPeers

favela-peer commits / a76fe11

README: rewrite for a reader — the last one was a status report about the repo
The version I wrote yesterday corrected the stale "Nothing works yet", but replaced it with metrics: module counts, line counts, "92 RPC methods", a test tally. None of that tells anyone whether this is for them or how to use it. Worse, it ended with "⚠ 10 commits are unpushed" and a git push command. That is transient project bookkeeping in a reader-facing document, and pushing them half an hour later made the README false. Repo state does not belong here at all. And it showed not one command, despite bin/favela.js carrying a good 25-command help text that answers "what can I actually do" better than any prose I could write. The first section is now seven of those commands. Restructured around what someone needs, in the order they need it: what it is, how to run it, how to drive it, the three ideas that make the model make sense, then the primitives, then the boundary, then status. The three ideas are the part worth having in prose, because each one surprises people and each one has a visible consequence. Following is a record in YOUR feed, so a feed's writer set never grows and counts are per-observer — your "12 likes" and mine are both honest and may differ. The timeline is local, so identical follow lists can legitimately show different timelines, which is why `favela missing` exists. And a record is mutable, so the name is the stable identity and the hash is not. Rooms keep the autobee explanation because the N=2 quorum freeze is the reason for the choice, and a person's two devices make N=2 ordinary rather than an edge case. Boundary section unchanged in substance. Status says it works and what the 342 tests cover, without the line counts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a76fe1102b32638282c75d6bd5b863140bec15db
by plankatron <fleeky@gmail.com> · 2026-09-09T17:57:09.000Z

Changed files (1) +75 -51

README.md +75 -51
……@@ -1,71 +1,95 @@
11# favela-peer
22
3The favela daemon. Feeds, rooms and namespaces of markdown records over Hypercore.
3**A social feed you actually hold.** Posts are markdown files in an append-only log that lives on
4your machine. There is no server, no account, and nobody to ask.
45
5**A clean break from atproto and ActivityPub.** Bridges come after the MVP, and they are
6*downstream* — they read this, this never reads them.
6This is the daemon. It keeps your data, talks to other peers, and answers questions over a unix
7socket. You read it through `favela-surface` in a browser, or the `favela` CLI below.
78
8Runs under Bare: `bare index.js`. pgh installs it and the launcher runs it; there is no `pear run`
9path 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.
9## Running
1110
12## The boundary
11```
12bare index.js # or the pgh-installed launcher, favela-peer
13```
1314
14⛔ **This package must not import `atproto-*` or `ap-*`, and must not encode DAG-CBOR, build an MST,
15mint a DID or sign a commit.** That is the acceptance criterion, not an aspiration
16(`hyper-social` #61 §10). `test/boundary.test.js` enforces it over `lib/` and the dependency list,
17and it passes.
15Storage is `~/.favela`, socket `~/.favela/favela.sock` — `FAVELA_STORAGE` and `FAVELA_SOCK` move
16them. Set `FAVELA_DEBUG=1` for JSON logs. Only one daemon may hold a socket; a second refuses to
17start rather than leaving the first unreachable.
1818
19Dependencies of substance: `favela-markdown` (the codec — a library with no storage and no
20network), `favela-identity`, `hyper-identity2`, `hyper-overlay`, Hypercore/corestore/Hyperswarm,
21`autobee`, `keet-identity-key`.
19## Using it
2220
23## The primitives
21`favela` is the CLI. Run it bare for the full list; these are the ones you need first:
2422
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.
23```
24favela whoami # this node's feed key
25favela link # attest this device from your identity vault
26favela claim alice # take a handle — you get e.g. alice0
27favela post "hello" # write a post
28favela follow <feedKey> # record a follow AND start holding the feed
29favela timeline # posts from every feed you hold
30favela invite # a one-time token to sign a browser in
31```
3532
36⚠ The earlier model here named a *document* as the second primitive and said a room was never one.
37That was superseded: the room is the container, membership is the audience, and no separate
38document type was ever built.
33It is a testing surface rather than the product surface — `favela-surface` is what a person uses —
34but everything the daemon can do is reachable from it, and it is the fastest way to see whether
35something works.
3936
40## The record
37## The ideas you need
4138
42A markdown file: JSON frontmatter plus a body. Mutable, edited in place. Bytes are content-addressed
43in hyper-overlay; the log holds a pointer. Because it is mutable, **the rkey is the stable identity,
44never the hash** — see `favela-markdown`.
39**Following is a record, not a subscription.** To follow someone you write a record in *your own*
40feed naming theirs. Nothing is written to theirs. They need not be online, need not consent, and
41need never know. Likes and replies work the same way. This is why a feed's writer set never grows
42past its owner's devices no matter how popular it is — and why counts are per-observer rather than
43summed. Your "12 likes" and mine are both honest and may differ.
4544
46## Interactions
45**The timeline is local, and it is a projection.** There is no relay and no aggregator. Your
46timeline is computed from the feeds this node happens to be holding, so two people with identical
47follow lists can legitimately see different timelines. A feed you follow but have not replicated
48contributes nothing — which is correct, and is why `favela missing` exists. A gap you can name is a
49different thing from a gap that is silently empty.
4750
48A like or a reply is a record in **your own** feed naming a target. Nobody becomes a writer in
49someone else's feed to interact with it, so a feed's writer set never grows beyond its owner's
50devices — and counts are per-observer, never summed.
51**A post is a markdown file** — JSON frontmatter, then a body. It is mutable and edited in place,
52so the content hash changes on every edit and **the name is the stable identity, never the hash**.
53The bytes live content-addressed in hyper-overlay; the log holds a pointer. See `favela-markdown`.
5154
52The timeline is likewise LOCAL: a pure function of the feeds this node happens to hold. There is no
53relay and no aggregator, so two peers legitimately compute different timelines from the same follow
54list. A feed you follow but have not replicated contributes nothing — which is why `missing()`
55exists: a gap you can name is a different thing from a gap that is silently empty.
55## The three primitives
5656
57## Status
57**Feed** — one identity's log. Its writers are that identity's devices, and its roster is flat:
58the only question is whether a writer is on it. You have exactly one.
59
60**Room** — many *members*, each with their own devices, and membership is the audience. Two levels
61rather than one, which matters more than it sounds: a role belongs to a person, not to a laptop.
62The vocabulary is keet's — admin, moderator, peer, read-only — because there was no reason to
63invent another. A DM is a room with one other member in it.
5864
59**Working.** 0.0.48, installed and running on tabertron under pgh alongside `favela-surface` (the
60renderer) and `favela-web`.
65The engine underneath is **autobee**, not autobase. Autobase needs `floor(N/2)+1` indexers, so at
66N=2 one member going offline freezes the view while it keeps growing, and it cannot heal by
67dropping to one. A person's two devices make N=2 the ordinary case, not an edge case. Weights
68retire that whole class of problem.
69
70**Namespace** — human names that resolve to feeds. It is a *subscription*, not an authority: two
71people subscribed to different namespaces resolve the same string to different feeds, and that is
72correct rather than a conflict. We give up global uniqueness and get, in exchange, a name that is
73always qualified by a namespace you can point at. Resolution runs handle → self-label → key, and
74the key is always shown.
75
76## The boundary
77
78⛔ **No `atproto-*` or `ap-*` imports. No DAG-CBOR, no MST, no DID, no signed commit.**
79
80This is a clean break from atproto and ActivityPub. Bridges come *after* the MVP and they are
81downstream — they read this, this never reads them. The moment the daemon imports the projection,
82the dependency has inverted and the recentring has failed.
83
84That is an acceptance criterion rather than an aspiration (`hyper-social` #61 §10), so
85`test/boundary.test.js` enforces it over every file in `lib/` and over the dependency list.
86
87## Status
6188
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.
89Working, and in daily use on tabertron alongside `favela-surface` and `favela-web`.
6790
68⚠ **10 commits are unpushed.** The forge repo `favela-peer` is at 0.0.38 (`eb3a41d`); local master
69is 0.0.48. `git push pear master`.
91`npm test` runs 342 tests over feeds, rooms, namespaces, handles, device pairing and attestation,
92browser sessions, files and avatars, the mirror, the follow graph, search, notifications and
93replication. All passing.
7094
71See `hyper-social` #61 §10 for the build order and §11.2 for what would falsify it.
95Build order and the two-peer acceptance tests are in `hyper-social` #61 §10 and §11.2.