Zum Inhalt springen
Architektur

Architektur

Für alle, die mitbauen: wie die Teile zusammenpassen. Jede Entscheidung hat ein ADR in specs/adr/, mit Index in dessen README.md; der Plan ist specs/plan.md.

Grundsätze

  • Der Server hält die Wahrheit. Clients halten einen Lese-Cache und einen Ausgang von Feldänderungen, gestempelt mit einer hybriden logischen Uhr (HLC) und ihrem Gerät. Der Server entscheidet Last Writer Wins je Feld. Es gibt kein zweites Op-Log.
  • Ein Konto, eine Datenbank. Jedes Konto ist eine eigene SQLite-Datei; server.db hält Konten und Sitzungen.
  • Verfügbarkeit wird berechnet, nie gespeichert, an genau einer Stelle: samu-core. Server und Handy rufen denselben Code auf.
  • Zeit: Ein ganzer Tag ist ein lokales Datum ohne Zone; eine Uhrzeit ist ein Zeitpunkt plus Zone. Wiederholung ist eine RRULE plus einer von drei Modi.
  • Die API ist der Kern. Jede Oberfläche ist ein Client; jede Route hat ein Kommando in der CLI.

Crates

crates/
  samu-api/      Wire-Typen (nur serde), eigens versioniert und veröffentlicht
  samu-core/     Modellregeln, Verfügbarkeit, Wiederholung, Schnellerfassung,
                 Suche, Forecast, Erinnerungen, Bericht, Gewohnheiten; kein I/O, kein async
  samu-store/    SQLite (rusqlite), nummerierte Migrationen
  samu-server/   axum: API, Server-Sent Events, Hooks, Kalender, die Web-Oberfläche
                 eingebettet; Binary `samu-server` (serve, replica, profile, front)
  samu-client/   HTTP-Client (rustls) auf samu-api; geteilt von CLI und App
  samu-cli/      Binary `samu`
  samu-replica/  Spiegel, Ausgang und Sync eines Clients, ohne uniffi;
                 geteilt von App und lokaler Replik
  samu-mobile/   Rust-Seite der Android-App (uniffi-Bindings)

Abhängigkeiten zeigen in eine Richtung: samu-api ← samu-core ← samu-store ← samu-server; samu-api ← samu-client ← samu-cli, samu-mobile; samu-core, samu-client ← samu-replica ← samu-mobile, samu-server. Nichts hängt von samu-server ab; samu-api hängt von nichts im Workspace ab.

Weitere Teile

Verzeichnis
web/die Web-Oberfläche: ES-Module, kein Framework, kein Build-Schritt; eingebettet ins Server-Binary (crates/samu-server/src/web.rs listet die Dateien)
android/Kotlin und Compose über samu-mobile
emacs/samu.el, über die CLI
dms/das Desktop-Widget, über die CLI
tui/, pult/fzf-Menüs und ihr Helfer, über die CLI
deploy/Dockerfile, Compose-Datei, systemd-Units
specs/Plan, ADRs, Recherche
testdata/nur synthetische Daten

Datenfluss

 Web ────┐
 CLI ────┼── HTTPS ──► samu-server ──► accounts/<name>.db
 Emacs ──┤  (über CLI)     │
 Widget ─┘                 └─ GET /events (SSE) ──► jeder Client
 Android-App: lokaler Spiegel + Ausgang ──► POST /changes, GET /changes
 Replik auf einem Rechner: dieselbe API auf 127.0.0.1, Spiegel + Ausgang

Bauen und prüfen

Stabiles Rust (Edition 2024), siehe rust-toolchain.toml.

make check        # fmt, clippy -D warnings, Tests, Paket samu-api, samu.el
make install      # samu nach ~/.local/bin
cargo build -p samu-server
target/debug/samu-server --data /tmp/samu serve --listen 127.0.0.1:8492

Regeln, die das Projekt einhält:

  • Jede neue Funktion bekommt Tests, jeder Bugfix einen Regressionstest. Tests nutzen nur synthetische Daten und rufen keine Dienste von außen auf.
  • Jede API-Route bekommt im selben Commit ein CLI-Kommando. Ausgabe der CLI: Daten auf stdout, Meldungen auf stderr, --format plain|json|jsonl|tsv, festgelegte Exit-Codes.
  • Jede Änderung an samu-api kommt mit einer JSON-Fixture unter crates/samu-api/tests/fixtures/.
  • Schema-Änderungen nur als nummerierte Migrationen in samu-store.
  • Kein unsafe außerhalb von FFI, kein unwrap() außerhalb von Tests.

Schwesterprojekt

samudo hat ein Schwesterprojekt, catastr, einen Speicher für Dokumente und Wissen. Aufgaben verlinken dessen Objekte per URI (catastr:<id>), und catastr liest samudo als Quelle über dieselbe API und das Crate samu-api. Keins fasst die Datenbank des anderen an.