tomdeboneandClaude Opus 5 ac18ff7a16 test(appview): handle-sync-Tests messen wieder, was ihr Name sagt
Mit gesetztem DATABASE_URL_APPVIEW liefen diese Tests zum ersten Mal
überhaupt (ohne die Variable überspringen sie sich still) — und fielen
um. Zwei Ursachen:

1. Sechs Integrationstests hingen wie zuvor die Unit-Tests am globalen
   run_once()-Batch. select_candidates/resolve_batch sind dafür jetzt
   pub, damit auch die Integrationstests ihren eigenen DID durchreichen
   können statt zu hoffen, dass er es in den Batch schafft.
2. Die Dispatch-Tests für did:web und did:plc verdrahteten den fremden
   Stub als pds_resolver — also eine lokale PDS, die behauptet, eine
   fremde DID zu kennen. Die Moduldoku sagt ausdrücklich, dass die PDS
   vor der Methodenverzweigung befragt wird, damit ein did:key-Nutzer
   der eigenen PDS ohne Umweg über plc.directory auflöst. Die Fixtures
   haben also gegen die dokumentierte Regel getestet statt gegen die
   Verzweigung, um die es ihnen ging. Jetzt kennt die PDS-Stub die DID
   nicht, wie es der Realität entspricht.

Neu: pds_resolves_did_key_before_method_dispatch pinnt die PDS-zuerst-
Regel selbst — dasselbe DID-Verfahren, umgekehrtes Ergebnis, und der
Unterschied ist allein, ob die PDS den Nutzer hostet.

sync_skips_already_resolved prüft weiter über select_candidates: dass
ein DID mit Handle gar nicht erst bei einem Resolver landet, ist der
Punkt des Tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HC9HLrUU1LNwkzp8nkDLX
2026-09-09 23:03:12 +02:00

maarcadetweet

AT-Protocol-PDS in Rust + AppView + Tauri/Svelte-Desktop-Client. Posts sind auf 160 Zeichen limitiert (oldschool Twitter), erzwungen durch eigenes Lexicon app.twi.post.

Architektur

crates/
├── at-lexicon/    Lexicon-Schemas + 160-Char-Validierung
├── at-crypto/     k256, p256, CID, multibase, JWT, PLC-Ops, Repo-Signing
├── at-identity/   DID, PLC, Handle-Resolution
├── at-mst/        Merkle-Search-Tree
├── at-repo/       Repos, Commits, Blöcke, TID-Revs
├── at-blob/       S3-kompatibler Blob-Store (MinIO) + In-Memory-Store
├── at-firehose/   Jetstream-Consumer (WebSocket)
├── at-shared/     Config, Errors, DID, Cursor
├── pds-server/    axum HTTP PDS (bin)
└── appview/       Jetstream-Indexer + REST-API (bin)

crates/tauri-app/       Tauri 2 + Svelte 5 + Vite + TS Desktop-Client
  ├── src/              Svelte-Components (Terminal, StatusBar, NavRail, PostCard, ComposeBox,
  │                     LoginScreen, ProfileView, NotificationsView)
  ├── src/lib/styles/   tokens.css (1:1 vom maarcade-Design)
  └── src-tauri/        Rust-IPC-Layer

lexicons/app/twi/post.json     Custom Lexicon mit maxLength: 160
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)

Setup

# 1) Datenbanken + MinIO starten
docker compose up -d

# 2) Umgebungsvariablen — beide Binaries laden .env selbst (dotenvy).
#    PDS_JWT_SECRET muss Hex sein: openssl rand -hex 32
cp .env.example .env

# 3) Workspace kompilieren + Tests
cargo test --workspace
cargo check --workspace

# 4) Tauri-Frontend (Vite dev)
cd crates/tauri-app
npm install
npm run dev
# → http://127.0.0.1:1420

# 5) PDS / AppView (eigene Terminals)
cargo run -p pds-server
cargo run -p appview

Status

Phase Stand
0 Foundation, Workspace, Migrations, Lexicon, Crypto done
1 Identity (PLC-Ops vollständig signieren) done — did:plc: deterministisch aus signed op CID
2 MST + Repo (Spec-konforme CBOR-Encoding) done — encode_key = base64url(sha256(raw_key)) per atproto-Spec, split_around/wrap_with_split threaden den recursive right_sub korrekt als k_tree weiter. 27 MST + 13 Repo + 4 Commit Tests grün.
3 PDS-Server (com.atproto.* XRPC) done — createAccount/Session/Refresh, createRecord/deleteRecord, like/repost, follow
4 AppView-Foundation (Jetstream-Index) done — Jetstream-Indexer + identity-Event-Backfill + PLC-handle-sync-Worker
5 AppView-REST-API done — timeline, profile (by-did + by-handle), search, post-by-uri, thread, notifications, followers/following
6 Tauri-UI-Logik an Backend koppeln done — LoginScreen, NavRail, PostCard, ComposeBox, Profile/Compose/Search/Settings-Views
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)
8 Social-Graph + Benachrichtigungen done — notifications-Tabelle, Schreibpfad im Jetstream-Indexer (idempotent, keine Selbst-Notifications), `/api/notifications[/count

Tests

cargo test --workspace                     # Rust: PDS, AppView, at-* Crates
cd crates/tauri-app && npx vitest run      # Svelte/TS
cd crates/tauri-app/src-tauri && cargo test # Tauri-IPC (eigener Workspace!)

Stand zuletzt gegen den lokalen Dev-Stack (docker compose + laufender PDS + AppView): Rust-Workspace grün (u.a. 27 MST, 24 PDS-Integration, 49 AppView-Lib, 14 AppView-Integration), Frontend grün. Zwei Vorbehalte:

  • Die DB-gestützten Integrationstests sind fail-open — ohne erreichbare Postgres/PDS überspringen sie sich selbst und melden das nur auf stderr. Ein grüner Lauf ohne laufenden Stack sagt also weniger, als er aussieht.
  • Einige Tests hängen am Zustand der Dev-Datenbank; auf einer frischen DB können handle_sync-Tests abweichen. Wer sie ernst nimmt, prüft sie gegen eine definierte DB.

crates/tauri-app/src-tauri hat ein eigenes [workspace] und ist nicht Teil des Root-Workspace; cargo test --workspace von oben erfasst den IPC-Layer nicht.

Dokumentation

Datei Inhalt
docs/architecture.md Crate-Verantwortlichkeiten, Datenfluss PDS → Jetstream → AppView → Client, Tabellenübersicht
docs/deployment.md Betrieb: docker compose, Migrationen, alle Env-Variablen, Release-Build, systemd-Units, Reverse-Proxy, Health-Checks
docs/tauri-release.md Signing-Keys, Updater-Config, latest.json, Build pro Plattform, Artefaktpfade

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.
  • Die AppView-Leseschnittstelle hat keine Auth und CORS Any; bei /api/notifications sind das erstmals halbwegs private Daten.
  • 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 Updater-Plugin ist nicht installiert (siehe docs/tauri-release.md).
  • Reply-Notifications gehen verloren, wenn die Antwort vor ihrem Parent indiziert wird (kein Nachlauf) — bei Jetstream möglich.
  • at-blob spricht MinIO ohne Signature V4 — echtes AWS S3 funktioniert damit nicht.
  • Die PDS liefert kein .well-known/did.json; describeServer gibt die DID hart als did:web:pds.maarcadetweet.local zurück.

Design

Orange Akzent, IBM Plex Mono, schwarzer Hintergrund mit 3%-Grid, Terminal-Fenster-Component mit blinkendem Cursor. Tokens sind 1:1 von maarcade-shell/landing/assets/css/tokens.css abgeleitet, plus zwei neue Repos-Tokens (--cid-fg, --rev-fg).

S
Description
AT Protocol PDS + AppView + Tauri Desktop Client (160-char posts). Maarcade design.
Readme
1.9 MiB
Languages
Rust 80.6%
Svelte 11.6%
TypeScript 7.2%
PLpgSQL 0.3%
CSS 0.3%