| … | … | @@ -0,0 +1,105 @@ |
| 1 | # The MVP — what favela is when nothing is installed |
| 2 | |
| 3 | **Status:** proposed. Awaiting approval. |
| 4 | **Date:** 2026-09-05 |
| 5 | **Reads with:** `SOCIAL-MODEL.md` (the model), `NAMING.md`, `EDGE-GRAPH.md`, `FAVELA-PEER.md`, |
| 6 | `COMMUNITY-MODERATION.md` (moderation, already researched — used as-is). |
| 7 | |
| 8 | **The test this list is built to pass:** *the core must be a complete product with **zero extensions |
| 9 | loaded**.* If it needs a bridge to be useful, it is not the centre. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## 1. Core — agnostic of atproto and ActivityPub |
| 14 | |
| 15 | | | what | |
| 16 | |---|---| |
| 17 | | **Identity** | one hyperident seed → N accounts (SLIP-48 `accountIndex`) → N attested devices per account. Each device has its own keypair and an `attestDevice` chain; **the seed never moves** | |
| 18 | | **Rooms** | a multi-writer autobase. **Membership is the audience** — there is no visibility field to honour or forget | |
| 19 | | **Records** | a markdown file: JSON frontmatter + body. Mutable; edited in place | |
| 20 | | **Media** | content-addressed bytes in hyper-overlay, referenced from the markdown. The **pointer** carries the level list — no manifest file, no duplication | |
| 21 | | **Interactions** | edges — like, reply, repost, follow, block, flag — written **in the room their subject lives in**, so audience is structural | |
| 22 | | **Graph view** | see what was actually exchanged, per `EDGE-GRAPH.md`, with each edge's delivery state | |
| 23 | | **Moderation** | room roles + `flag` signals + delisting. `COMMUNITY-MODERATION.md` already answers this; **use it, do not redesign it** | |
| 24 | | **Naming** | hashes in bytes, names on screen, namespaces you subscribe to | |
| 25 | | **Surface** | one SSR renderer; thin native shells display it (`favela-android` unchanged) | |
| 26 | |
| 27 | **Components** |
| 28 | |
| 29 | ``` |
| 30 | favela-markdown the codec — a library. No storage, no network, no daemon |
| 31 | favela-peer the daemon — rooms, records, identity, admission |
| 32 | favela-surface the renderer it serves |
| 33 | favela-android the existing shell — unchanged, it displays HTML |
| 34 | ``` |
| 35 | |
| 36 | ⛔ **All four are agnostic of atproto and ActivityPub.** That is the acceptance criterion, not an |
| 37 | aspiration: if any of them imports an `atproto-*` or `ap-*` package, the recentring has failed. |
| 38 | |
| 39 | ## 2. Extensions — installed and toggled by the identity manager |
| 40 | |
| 41 | | | is | |
| 42 | |---|---| |
| 43 | | **`bridge:bsky`** | the **current p2p atproto stack**, unchanged — hpp, atproto-store, atproto-room's signer, atproto-repo, the keyless membrane | |
| 44 | | **`bridge:fediverse`** | **ap-bridge**, which is nearly independent already — own RSA key, own delivery, own credentials | |
| 45 | |
| 46 | ⚠ **`bridge:fediverse` is NOT currently separate — it is downstream of `bridge:bsky`.** Measured: |
| 47 | `ap-bridge/lib/records.js` imports `DidPeer` and calls |
| 48 | `peer.listRecords('app.bsky.feed.post')`. The fediverse therefore receives a **projection of a |
| 49 | projection** — already stripped and already truncated at bsky's 300-grapheme ceiling, on a network |
| 50 | that has no such limit. |
| 51 | |
| 52 | ⇒ **Separating it is one read**, and it makes the output strictly better rather than merely |
| 53 | better-factored: AS2's `source: { content, mediaType }` can carry the **markdown verbatim** (it is |
| 54 | currently hardcoded `text/plain`, `design.md` §7). |
| 55 | |
| 56 | ## 3. ⚠ Cut or deferred — where the previous work was leaking in |
| 57 | |
| 58 | | carried over | why it leaks | disposition | |
| 59 | |---|---|---| |
| 60 | | **`collection` in the address** | atproto's `at://did/collection/rkey`. A room already says where a record is; type is a property, not a location | **cut** → `favela://<room>/<position>`, type in frontmatter | |
| 61 | | **facets in `app.bsky.richtext.facet` shape** | `strip()` emits bsky's `$type` strings — atproto's data format, in the core library's output | **cut** → native `{start, end, kind, target}`; bridges translate | |
| 62 | | **`net.favela.post` NSIDs** | reverse-DNS is atproto's convention | **cut** | |
| 63 | | `reach` as a record field | names networks inside the record; with installable extensions that is config | **defer** | |
| 64 | | `maxReach()` | built 2026-09-05; bridge-shaped and **inert with no bridges installed** | keep the code, **do not wire** | |
| 65 | | `project` / `fromAS2` / `fromBsky` / loss reports | extension-only | **defer** | |
| 66 | | DID, PLC, rotation keys, signing, stored projections, the double-write | every one is `bridge:bsky`'s concern | **defer entirely** | |
| 67 | |
| 68 | **This is roughly half of what was specified on 2026-09-05.** The specs keep it; it stops being core. |
| 69 | |
| 70 | ## 4. Designed to extend — and what would instead be a rewrite |
| 71 | |
| 72 | The MVP is small on purpose, so the things we will want next must be **additions, not migrations**. |
| 73 | |
| 74 | **Free to add later, by construction:** |
| 75 | |
| 76 | | | why it is free | |
| 77 | |---|---| |
| 78 | | **new verbs** | an unknown verb is native-only and cannot fork a shared derivation (`interaction.md` §5.2) | |
| 79 | | **new frontmatter fields** | `parse` preserves unknown keys and `serialize` orders them after the pinned ones, so an extension **never reorders — and so never changes the bytes of — a record that predates it** (tested) | |
| 80 | | **new namespaces** | subscribe to one more; nothing central changes (`NAMING.md`) | |
| 81 | | **new bridges** | a grant plus a co-process. Adding one adds **zero** stored files | |
| 82 | | **more media levels** | the pyramid may be partial; a level is added to a pointer | |
| 83 | | **new room kinds** | `maxReach` gains a row; deny-by-default means an unknown kind is already safe | |
| 84 | |
| 85 | ⛔ **Would be a rewrite — so decide before the MVP ships:** |
| 86 | |
| 87 | 1. **The address form.** `favela://<room>/<position>` vs keeping a type segment. Records are |
| 88 | immutable in their addressing once they exist (§8 forbids rewriting). |
| 89 | 2. **Interactions: frontmatter-only records, or pointer-valued log entries?** Changes what is on |
| 90 | disk for every edge ever written. |
| 91 | 3. **The facet shape.** If it ships bsky-shaped it leaks forever, because every consumer will have |
| 92 | been written against it. |
| 93 | |
| 94 | ## 5. Not in the MVP, deliberately |
| 95 | |
| 96 | Discovery UI, notifications UI, live updates, feeds and lists, DMs, per-network posting controls, |
| 97 | offline recovery keys. Each is real product work (hyper-social #45, #28) and none of it is blocked |
| 98 | by anything above — which is the point of keeping the core small. |
| 99 | |
| 100 | ## 6. What this does not claim |
| 101 | |
| 102 | - It does not delete the atproto stack. It stops **loading** it. ~17,900 lines stay where they are. |
| 103 | - It does not make the core faster or leaner as a goal — leanness is a consequence, and by itself a |
| 104 | bad reason (hyper-social #28: plumbing must not outrank the product). |
| 105 | - It does not settle §4's three rewrite-class questions. Those need a decision, not a plan. |