docs: Firehose, Follow-Lexicon und Phase 10

architecture.md zeigt den subscribeRepos-Pfad im Diagramm (er stand zuerst
irrtümlich am Pfeil zum externen Relay) und erklärt, warum es Push *und*
Firehose gibt: der eine ist schnell, der andere verlässlich. Dazu, wo die
Spec-Treue endet — die Frame-Hülle ist konform, die Blöcke darin nicht.

deployment.md bekommt einen Firehose-Abschnitt: Transaktionsgarantie, warum
die seq lückenfrei ist und was der globale Advisory-Lock an Durchsatz
kostet, Cursor-Semantik, WebSocket-Upgrade im Proxy, und die fehlende
Retention für firehose_events.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HC9HLrUU1LNwkzp8nkDLX
This commit is contained in:
tomdebone
2026-09-10 07:08:46 +02:00
co-authored by Claude Opus 5
parent 6fbea4fe6f
commit b7ce114677
3 changed files with 87 additions and 27 deletions
+10 -5
View File
@@ -25,6 +25,7 @@ crates/tauri-app/ Tauri 2 + Svelte 5 + Vite + TS Desktop-Client
└── src-tauri/ Rust-IPC-Layer
lexicons/app/twi/post.json Custom Lexicon mit maxLength: 160
lexicons/app/bsky/ like, repost, follow, actor.profile
migrations/pds/ PDS-DB-Schema (users, repos, blobs, sessions, plc_ops)
migrations/appview/ AppView-DB-Schema (posts, likes, follows, notifications, profiles, jetstream_cursor)
docs/ Deployment, Architektur, Tauri-Release (siehe unten)
@@ -69,6 +70,7 @@ cargo run -p appview
| 7 Polish (Tray, Notifications, Auto-Update) | ✅ done — Tray-Icon custom (`tauri::include_image!`), Notification-Click navigiert via `app://notification`-Event + `openThread`-Helper zu Thread-Detail, Auto-Update in Dev inert (Production-Weg: [`docs/tauri-release.md`](docs/tauri-release.md)) |
| 8 Social-Graph + Benachrichtigungen | ✅ done — `notifications`-Tabelle, Schreibpfad im Jetstream-Indexer (idempotent, keine Selbst-Notifications), `/api/notifications[/count|/seen]`, `/api/followers`, `/api/following`, eigene `/api/thread`-Route; im Client Notifications-View mit Unread-Badge und klickbare Follower-/Following-Listen im Profil |
| 9 Auth + Performance | ✅ done — AppView prüft Bearer-Tokens (ES256, Schlüssel aus dem neuen `/.well-known/did.json` der PDS, fail closed); Timeline und Notifications nur noch für die eigene DID; CORS-Allowlist statt `Any`; Indizes für Handle-Lookup und Cold-Start-Feed |
| 10 Lokaler Firehose | ✅ done — `com.atproto.sync.subscribeRepos` auf der PDS (Event in derselben Transaktion wie der Commit, `seq`-Cursor mit lückenfreiem Replay, WebSocket-Frames in atproto-Form); die AppView konsumiert ihn mit persistiertem Cursor. Ein verlorener Push ist damit nicht mehr endgültig. |
## Tests
@@ -103,11 +105,14 @@ Root-Workspace; `cargo test --workspace` von oben erfasst den IPC-Layer nicht.
## Bekannte Lücken
* Die eigene PDS speist **keinen** Firehose (`com.atproto.sync.subscribeRepos` fehlt) —
eigene Records erreichen die AppView nur über den Best-Effort-Push
`POST /internal/ingest-commit`.
* `aud` wird beim Token-Check nicht validiert (Signatur, Ablauf, `scope` und
`sub` schon).
* Der Firehose ist **lokal**: er verbindet die eigenen zwei Dienste. Ein fremder
Relay erfährt von dieser PDS weiterhin nichts.
* Die Frame-Hülle ist spec-konformes DAG-CBOR, die Blöcke darin nicht: CIDs
innerhalb von Commit-Blöcken sind Strings statt Tag-42-Links. Ein fremder
atproto-Consumer liest die Frames, scheitert aber an den Blockinhalten. Das
zu ändern hieße, jede CID im System zu ändern — inklusive der
`did:plc:`-Ableitung.
* `firehose_events` wird nie beschnitten.
* Notifications werden nie gelöscht: Unlike/Unfollow lässt die Zeile stehen, und der
Dedupe-Key macht sie „einmal pro (Empfänger, Autor, Art, Subject) für immer".
* Auto-Update ist nur dokumentiert, nicht verdrahtet: niemand ruft `check()` auf, das