OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social commits / c006ee0

Resolve all 8 design gaps in ROOMS-ARE-THE-COMPUTER.md
Key findings: - dm.fromContact enables cold-contact DMs (no shared room needed) - Messages are text-only — use _hs JSON convention for structured data - MI computation trivially fast with inverted index (~5ms for 500 rooms) - Bridge bots are peers, not admins — admin endorses bridge config - Registry migration is a one-time script - DMs get separate "Messages" section in room list - Identity linking and moderation deferred to Phase 8 using _hs messages - Revised code audit: 2,020 lines removed, 540 added (73% reduction) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
c006ee0c01b67463a0c805c4bc115a5565fbec71
by plankatron <fleeky@gmail.com> · 2026-02-21T13:53:01.000Z

Changed files (1) +311 -34

docs/ROOMS-ARE-THE-COMPUTER.md +311 -34
……@@ -424,31 +424,35 @@ All four paths feed the same local graph. The group bar shows the result: a mix
424424
425425## What Changes in Code
426426
427See "Revised Code Estimates" in the Resolved Design Decisions section for the complete audit.
428
427429### keet-daemon
428430
429**Remove (~870 lines):**
431**Remove (~1,600 lines):**
430432```
431lib/room-discovery.js — Autobase registry class (~500 lines)
432index.js discovery plumbing — config, eager init, heartbeat (~170 lines)
433router.js discovery routes — register, unregister, list, etc. (~200 lines)
433lib/room-discovery.js — Autobase registry class (783 lines)
434lib/room-discovery-apply.js — Autobase state machine (396 lines)
435index.js discovery plumbing — config, eager init, heartbeat (170 lines)
436router.js discovery routes — 19 RPC endpoints (213 lines)
437test/discovery.js — test harness (40+ lines)
434438```
435439
436440**Add (~440 lines):**
437441```
438lib/registry-room.js (~80) — Post/read room-listing messages in registry rooms
439lib/dm-shares.js (~60) — Post/read room-share messages in DM rooms
440lib/local-graph.js (~200) — MI computation on local membership data
442lib/registry-room.js (~80) — Post/read _hs:room-listing messages in registry rooms
443lib/dm-shares.js (~60) — Post/read _hs:room-share messages in DM rooms
444lib/local-graph.js (~200) — MI computation (inverted member index, sparse)
441445lib/topic-announce.js (~60) — DHT announce/search for discoverable rooms
442446router.js new routes (~40) — registry.*, graph.*, topic.* routes
443447 (DM routes already exist: dm.request, dm.accept, etc.)
444448```
445449
446**Net: ~870 removed, ~440 added. Fewer lines AND simpler code.**
450**Net: ~1,600 removed, ~440 added. 73% reduction.**
447451
448The 440 new lines are trivial compared to the 870 they replace:
449- `registry-room.js` calls existing room/chat APIs
452The 440 new lines are trivial compared to the 1,600 they replace:
453- `registry-room.js` calls existing room/chat APIs to post `_hs` messages
450454- `dm-shares.js` calls existing DM + chat APIs (DM infrastructure already built)
451- `local-graph.js` is pure math on data already in memory
455- `local-graph.js` is pure math on data already in memory (~5ms for 500 rooms)
452456- `topic-announce.js` is a thin DHT wrapper
453457- No Autobase lifecycle management, no writer keys, no view corruption handling
454458
……@@ -457,19 +461,19 @@ The 440 new lines are trivial compared to the 870 they replace:
457461**Simplify:**
458462```
459463keet_discovery.gd (419→~200 lines)
460 — Remove: Autobase registry management, writer keys, admin roles
464 — Remove: Autobase registry management, writer keys, admin roles (33 methods → ~12)
461465 — Keep: list rooms, search, register/unregister (now calls room APIs)
462466
463467group_bar.gd (446 lines, same size, different logic)
464468 — Before: fetch registries from discovery config
465 — After: show computed clusters + joined registry rooms + followed profiles
469 — After: show computed clusters + joined registry rooms
466470```
467471
468472**Add:**
469473```
470474keet_dm.gd (~120 lines)
471475 — DM lifecycle: create, list, accept/reject requests
472 — Room sharing: post/edit/delete room-share messages in DMs
476 — Room sharing: post/edit/delete _hs:room-share messages in DMs
473477 — Read shared rooms from DM history
474478 — "Message" button on member popup
475479 — "Share Room" action with multi-contact picker
……@@ -484,7 +488,7 @@ keet_graph.gd (~150 lines)
484488keet_service.gd — add dm_mgr, graph_mgr (2 lines)
485489member_panel.gd — add "Message" button to member context (20 lines)
486490chat_panel.gd — detect bridge message prefix, show badge (30 lines)
487room_list_panel.gd — show DM rooms in a "Messages" section (40 lines)
491room_list_panel.gd — show DM rooms in "Messages" section (40 lines)
488492```
489493
490494### index.js (launcher)
……@@ -535,14 +539,8 @@ Kanban boards don't serve this context. If task tracking ever matters, it's a ro
535539A wiki page is a pinned, editable message — not a separate panel. You're discussing something, someone says "let's capture this," and the message becomes a wiki entry. Other people edit it in-place. The page list is a filtered view of chat history showing only wiki-type messages. No tab switching. The wiki lives in the conversation that created it.
536540
537541```javascript
538// A wiki entry is just a message with a type field:
539{
540 type: "wiki",
541 title: "How to set up Hyperswarm",
542 content: "## Step 1\n\nInstall the package...",
543 editedBy: "member:alice0",
544 editedAt: 1708402400000
545}
542// A wiki entry is just a message with the _hs convention:
543{"_hs":"wiki","title":"How to set up Hyperswarm","content":"## Step 1\n\nInstall the package...","editedBy":"member:alice0","editedAt":1708402400000}
546544```
547545
548546Renders inline as an expandable card. Click to read. Edit button for admins/mods. Version history = previous messages with the same title.
……@@ -573,7 +571,7 @@ Rooms with `discoverable: "true"` announce on topic-hashed DHT keys. Search by t
573571
574572### Step 6: Remove RoomDiscovery Autobase
575573
576Once registry rooms are stable and profile rooms handle user discovery, remove the hand-crafted Autobase code. ~870 lines deleted. The existing discovery registries migrate to registry rooms (one-time conversion).
574Once registry rooms are stable and DMs handle user discovery, remove the hand-crafted Autobase code. ~1,600 lines deleted (room-discovery.js, room-discovery-apply.js, router discovery routes, index.js plumbing). The existing discovery registries migrate to registry rooms (one-time conversion script — see Resolved Design Decisions §4).
577575
578576### Step 7: Add bridge awareness
579577
……@@ -597,25 +595,304 @@ Bridges set `bridgedTo` in room config when they connect. Chat panel detects bri
597595- **ROADMAP.md Phase 5** — Board panel doesn't serve this context. Drop it.
598596- **ROADMAP.md Phase 5** — Wiki panel as separate tab. Wrong UX. Wiki is inline in chat.
599597- **ROADMAP.md Phase 1** — Hub-daemon as separate package may be unnecessary. The routes are thin enough to add to keet-daemon's router directly. Decide during implementation.
600- **AUTO-DISCOVERY-SPEC.md** — Proposed ~1,400 LOC of changes. Actual changes are ~480 LOC of additions (after removing ~870 LOC of Autobase code). Net simpler.
598- **AUTO-DISCOVERY-SPEC.md** — Proposed ~1,400 LOC of changes. Actual changes are ~540 LOC of additions (after removing ~2,020 LOC of Autobase code including Godot client). 73% net reduction.
599
600## Resolved Design Decisions
601
602### 1. DM Initiation: Cold Contact IS Possible
603
604**Finding:** `dm.fromContact(memberId)` works WITHOUT a shared room. Only a memberId is required.
605
606Two DM initiation paths exist in keet-core:
607
608| Method | Requires Shared Room | Input |
609|--------|---------------------|-------|
610| `dm.request(roomId, memberId)` | Yes | roomId + memberId |
611| `dm.fromContact(memberId)` | **No** | memberId only |
612
613Both use blind pairing internally. Both produce a pending DM request that the target must accept.
614
615**Implication:** Any source of a memberId enables DM initiation — registry rooms, DHT discovery, QR codes, out-of-band sharing. Cold-contact discovery works. The `dm.fromContact` path is used when you learn someone's memberId from a registry room listing or topic DHT result without being in a room together.
616
617### 2. Structured Message Format (`_hs` Convention)
618
619**Finding:** keet-core messages are text-only. No metadata field, no content-type, no structured payload. The `send(roomId, text)` API accepts a string. The `editMessage(roomId, messageId, text)` API accepts a string. Files are a separate system.
620
621**Solution:** JSON-encoded text with a `_hs` type discriminator.
622
623```
624Detection: text.startsWith('{"_hs":') → structured hyper-social message
625Fallback: clients that don't understand _hs show raw JSON (functional, ugly)
626Rendering: clients that understand _hs render type-specific UI cards
627```
628
629**Message types:**
630
631```javascript
632// Room share (posted in DMs)
633{"_hs":"room-share","roomId":"abc123","title":"Rust P2P Dev","topics":["rust","p2p"],"inviteToken":"xyz789","sharedAt":1708402400000}
634
635// Room listing (posted in registry rooms)
636{"_hs":"room-listing","roomId":"abc123","title":"Rust P2P Dev","description":"Building distributed systems in Rust","topics":["rust","p2p"],"inviteToken":"xyz789","listedBy":"member:alice0"}
637
638// Room unlisting (posted in registry rooms)
639{"_hs":"room-unlisting","roomId":"abc123","unlistedBy":"member:alice0"}
640
641// Wiki entry (posted in any room, pinnable)
642{"_hs":"wiki","title":"How to set up Hyperswarm","content":"## Step 1\n\nInstall the package...","editedBy":"member:alice0","editedAt":1708402400000}
643```
644
645**Operations map to existing message APIs:**
646- Create → `send(roomId, JSON.stringify(payload))`
647- Update → `editMessage(roomId, messageId, JSON.stringify(updatedPayload))`
648- Remove → `deleteMessage(roomId, messageId)`
649- Read → load room messages, filter by `_hs` type
650
651**Why `_hs` prefix detection:**
652- O(1) check: `text.startsWith('{"_hs":')`
653- Extremely unlikely to collide with natural messages
654- Short (3 chars) to minimize overhead
655- Namespace is extensible (add new `_hs` values without protocol changes)
656
657### 3. MI Computation Cost: Not a Real Concern
658
659**Analysis:** The naive O(N² rooms) pairwise approach is unnecessary. Use an inverted index.
660
661**Sparse approach (inverted member index):**
662```
663Step 1: Build member → rooms map (one pass over all rooms)
664 alice0 → [room_A, room_C, room_D]
665 bob0 → [room_A, room_B]
666 carol0 → [room_A, room_B, room_C]
667
668Step 2: For each member, increment pair counters for all room combinations
669 alice0 is in {A,C,D} → pairs (A,C)+=1, (A,D)+=1, (C,D)+=1
670 bob0 is in {A,B} → pair (A,B)+=1
671 carol0 is in {A,B,C} → pairs (A,B)+=1, (A,C)+=1, (B,C)+=1
672
673Step 3: Compute MI for each pair with count > 0
674```
675
676**Complexity:** O(Σ C(r_i, 2)) where r_i = rooms per member i. For a typical user:
677- 30 rooms × 50 avg members = 1,500 total memberships
678- Average member in 3 rooms → C(3,2) = 3 pair increments per member
679- Total: 50 members × 3 = 150 pair operations
680- With MI computation: ~150 log2 operations
681- **Runtime: < 1ms**
682
683At scale (power user):
684- 500 rooms × 100 avg members = 50,000 memberships
685- Average member in 5 rooms → C(5,2) = 10 pair increments per member
686- Total: 10,000 unique members × 10 = 100,000 pair operations
687- **Runtime: ~5ms in JavaScript**
688
689**Decision:** Compute on a 60-second timer. No incremental updates needed. No sparse matrix needed. The inverted index makes it trivially fast for any realistic scale.
690
691### 4. Registry Room Migration
692
693**Procedure:** One-time script, run once per existing registry.
694
695```
696For each registry in discovery-keys.json:
697 1. Read all entries from the Autobase view (list all rooms)
698 2. Create a new keet-core room with config:
699 - type: "registry"
700 - title: registry.getMeta("name") or "Global"
701 - description: registry.getMeta("description")
702 3. For each registered room in the Autobase:
703 Post a room-listing message:
704 {"_hs":"room-listing","roomId":"...","title":"...","description":"...","topics":[...],"inviteToken":"...","listedBy":"member:migrator"}
705 4. Share the new room's invite with existing registry admins
706 5. Admins join the registry room → they now have admin role there
707 6. Old Autobase can be decommissioned after transition period
708```
709
710**Migration order:**
7111. Create registry rooms alongside existing Autobases (both run in parallel)
7122. godot-keet updated to read from registry rooms (new code path)
7133. Transition period: both sources available
7144. Remove Autobase code when registry rooms are stable
715
716**Existing registries:**
717- Global (`c5facb91...`) → "Global" registry room
718- testy (`adebddd0...`) → "testy" registry room
719- Any user-created registries → individual migration
720
721### 5. Bridge Bot Identity and Permissions
722
723**Bridge bots are regular room members.** They join rooms via invite, like any other member.
724
725**Permission model:**
726
727| Action | Required Role | Why |
728|--------|--------------|-----|
729| Post messages (relay chat) | Peer (default) | Any member can chat |
730| Set `bridgedTo` in room config | Admin | Only admins can write config |
731| Read messages (relay outbound) | Peer (default) | Any member can read |
732| Manage members | Not needed | Bridges don't moderate |
733
734**Problem:** Bridges need admin to set `bridgedTo` config, but admin is too much power.
735
736**Solution:** Bridge bots DON'T set room config. Instead:
737
7381. Bridge bot joins room as regular peer
7392. Bridge bot posts a structured announcement:
740 ```javascript
741 {"_hs":"bridge-announce","platform":"irc","network":"libera","channel":"#rust-p2p","botMember":"member:bridge-irc-bot0"}
742 ```
7433. Room admin sees the announcement, decides whether to endorse it
7444. Admin sets `bridgedTo` in room config (admin action, not bot action)
7455. Client shows bridge status based on room config (set by admin) NOT bot announcement
601746
602## Gaps Still Open
747**Why this is better:**
748- Bridge bot has minimal permissions (peer only)
749- Room owner explicitly approves bridges
750- A malicious bot can't claim to bridge to anywhere
751- Admin can remove bridge config without removing the bot
752- Multiple bridges per room, each admin-approved
603753
6041. **DM initiation requires shared room** — `dm.request` needs a `roomId` where both members are present. You can't DM someone you've never been in a room with. This is correct for privacy but means cold-contact discovery requires at least one shared room, registry room, or out-of-band exchange. `dm.fromContact` may work without a shared room (needs testing).
754**Bridge identity:** Bridge bots register as regular keet users with a naming convention: `bridge-irc-libera0`, `bridge-discord-myserver0`. The `bridge-` prefix signals bot status to the UI.
605755
6062. **Registry room migration** — Existing discovery registries (Global, testy) need to be migrated to registry rooms. One-time conversion: read all entries from the Autobase, create a room, post them as messages. The Autobase discovery key becomes the room's invite.
756### 6. DM Room Presentation
607757
6083. **Bridge bot identity** — When a bridge connects to a room and sets `bridgedTo` in config, it needs admin access. Bridge bots should be added as room members with moderator role (not admin — they shouldn't be able to delete the room).
758**DMs appear in a separate "Messages" section in the room list.**
759
760```
761GroupBar (left rail):
762 [Group icons]
763 [Home icon] ← shows all rooms + messages
764
765RoomList (when Home selected):
766 ┌──────────────────┐
767 │ Messages (3) │ ← DMs section (RoomType=2), count of unread
768 │ alice0 │
769 │ bob0 │
770 │ carol0 │
771 ├──────────────────┤
772 │ Rooms (12) │ ← Chat rooms section (RoomType=1)
773 │ Rust P2P Dev │
774 │ Hypercore Dev │
775 │ ... │
776 └──────────────────┘
777
778RoomList (when Group selected):
779 [filtered rooms from that group/cluster]
780 [no Messages section — DMs are personal, not per-group]
781```
782
783**Room-share rendering inside DMs:**
784
785When viewing a DM conversation, `_hs:room-share` messages render as joinable cards:
786
787```
788┌──────────────────────────────────┐
789│ alice0 shared a room: │
790│ ┌────────────────────────────┐ │
791│ │ 🏠 Rust P2P Dev │ │
792│ │ topics: rust, p2p │ │
793│ │ [Join] │ │
794│ └────────────────────────────┘ │
795│ Yesterday at 3:42 PM │
796└──────────────────────────────────┘
797```
609798
6104. **DM room presentation** — DMs need to appear distinctly from group rooms in the room list. Either a separate "Messages" section in the room list, or a filter/tab. keet-core marks them as `RoomType=2` so filtering is straightforward.
799If already joined, the card shows "Joined" instead of "Join". Click the card title to navigate to the room.
611800
6125. **MI computation cost** — For users in 100+ rooms with 1000+ total members, the pairwise MI computation is O(n² rooms × m members). Benchmark needed. May need sparse representation or incremental updates.
801**Filtering:** `RoomType=2` is already set by keet-core on DM rooms. The client filters: `rooms.filter(r => r.roomType === 2)` for Messages, `rooms.filter(r => r.roomType !== 2)` for Rooms.
613802
6146. **Cross-platform identity linking** — COMMUNITY-EMERGENCE.md discusses it, ROADMAP.md Phase 6 plans it, but no spec exists. Likely: identity links are room-share messages in DMs — "I am also alice on IRC Libera" — signed by both identities. The DM is the trust channel. Implementation TBD.
803### 7. Cross-Platform Identity Linking (Phase 8)
615804
6167. **Moderation service** — COMMUNITY-EMERGENCE.md describes moderation as a subscribable label service (Blacksky pattern). No implementation spec. Likely Phase 8. Could be: a room whose messages are moderation labels, subscribable by other rooms.
805**Identity links are claims posted in DMs, vouched by bridges.**
617806
6188. **Room-share message format** — Need a convention for structured messages. keet-core messages are text strings. Structured data must be JSON-encoded in the text field, with client-side parsing. Alternatively, messages may support metadata/attachments that could carry structured payloads. Needs investigation of what keet-core message fields support.
807```javascript
808// Alice posts in her DM with Bob:
809{"_hs":"identity-claim","platform":"irc","network":"libera","nick":"alice","claimedAt":1708402400000}
810
811// Bridge bot (in a shared room) can vouch:
812{"_hs":"identity-vouch","member":"member:alice0","platform":"irc","network":"libera","nick":"alice","vouchedBy":"member:bridge-irc-libera0","vouchedAt":1708402400000}
813```
814
815**Trust model:**
816- **Claim only (no vouch):** "Alice says she's alice on IRC." Trust based on DM relationship.
817- **Claim + bridge vouch:** "Alice says she's alice on IRC, AND the IRC bridge confirms it." Stronger.
818- **Vouch without claim:** Bridge bot automatically announces identity mappings for members it sees on both sides. No user action needed.
819
820**Storage:** Claims and vouches are just messages. The client collects them locally, builds an identity graph in memory. No new data structure. Same pattern as MI computation — local aggregation of distributed observations.
821
822**UI:** Member popup shows claimed identities with trust level:
823```
824alice0
825 └── alice @ IRC Libera (bridge confirmed ✓)
826 └── alice_dev @ Discord MyServer (self-claimed)
827```
828
829**This is Phase 8.** The `_hs` message convention makes it possible without protocol changes. Implementation is deferred until bridges are running (Phase 2-3).
830
831### 8. Moderation Service (Phase 8)
832
833**Moderation is a room whose messages are labels.** Same room primitive, different convention.
834
835```javascript
836// Moderation room config:
837{"type":"moderation","title":"P2P Safety Labels","description":"Community moderation labels"}
838
839// Label messages:
840{"_hs":"mod-label","target":"room:abc123","label":"spam","reason":"Crypto scam room","labeledBy":"member:moderator0","labeledAt":1708402400000}
841{"_hs":"mod-label","target":"member:scammer0","label":"harassment","reason":"Repeated abuse","labeledBy":"member:moderator0","labeledAt":1708402400000}
842
843// Unlabel:
844{"_hs":"mod-unlabel","target":"room:abc123","label":"spam","unlabeledBy":"member:moderator0"}
845```
846
847**Subscription model (Blacksky pattern):**
8481. Moderation teams create moderation rooms
8492. Users join moderation rooms to subscribe to their labels
8503. Client reads label messages, builds local label index
8514. Client applies labels: dim or hide rooms/members with negative labels
8525. Labels are advisory — the user chooses which moderation rooms to trust
8536. Multiple moderation services can overlap or conflict — user resolves
854
855**Why this works:**
856- Moderation team = room admins (existing role system)
857- Labels = messages (existing message system)
858- Subscription = room membership (existing join system)
859- No enforcement — the labeling room can't actually ban anyone from another room
860- Composable — subscribe to multiple moderation sources, each adds labels
861
862**This is Phase 8.** The `_hs` message convention makes it possible. Implementation is deferred until the core system (Steps 1-5) is stable.
863
864## Revised Code Estimates
865
866Based on the detailed audit of existing discovery code:
867
868### Remove (~2,020 lines)
869
870| File | Lines | What |
871|------|-------|------|
872| `room-discovery.js` | 783 | Autobase registry class |
873| `room-discovery-apply.js` | 396 | Autobase state machine |
874| `router.js` discovery routes | 213 | 19 RPC endpoints |
875| `index.js` discovery plumbing | 170 | Config, init, heartbeat |
876| `keet_discovery.gd` | 419 | Godot client (33 methods, 13 signals) |
877| Tests | 40+ | Discovery test harness |
878
879Also removes dependency on: `corestore`, `hyperswarm`, `autobase`, `hyperbee`, `protomux`, `compact-encoding` (for discovery only — these may still be used by keet-core itself).
880
881### Add (~540 lines)
882
883| File | Lines | What |
884|------|-------|------|
885| `registry-room.js` | 80 | Post/read `_hs:room-listing` messages |
886| `dm-shares.js` | 60 | Post/read `_hs:room-share` messages in DMs |
887| `local-graph.js` | 200 | MI computation (inverted index, sparse) |
888| `topic-announce.js` | 60 | DHT announce/search for discoverable rooms |
889| `router.js` new routes | 40 | `registry.*`, `graph.*`, `topic.*` |
890| `keet_dm.gd` | 120 | DM lifecycle + room sharing |
891| `keet_graph.gd` | 150 | Graph data → group bar |
892| `keet_discovery.gd` (rewrite) | 200 | Simplified: registry rooms + topic search |
893| UI modifications | 100 | Member "Message" button, DM section, bridge badges |
894
895**Net: ~2,020 removed, ~540 added. 73% reduction in discovery code.**
619896
620897## The Principle
621898