Systemarchitektur

Tessera Systemkarte

Stand 2026-08-12 · Quelle: Claude-Artefakt „Tessera Systemkarte“ — hier als Seite im Template, damit die Karte im Repo lebt und per Patch weiterwächst.

Der komplette selbst-besessene Stack — Identität, Code, Builds, Pakete, Runtime und Betrieb — mit den Flüssen dazwischen. Unten die nummerierten Lücken als Diskussionsgrundlage.

Weg einer Änderung

Vom lokalen Commit bis zur laufenden App — ohne GitHub, ohne Coolify, ohne fremde Registry. Der goldene Schritt ist der einzige manuelle: das bewusste Produktions-Gate.

flowchart LR
  subgraph build [Build-Pfad]
    dev[Entwickler<br/>git push rad] -->|push| seed[Radicle-Seed<br/>seed.tessera.at]
    seed -->|rad sync| cib[CI-Broker<br/>baut + signiert]
    cib -->|SSHSIG| zot[zot-Registry<br/>oci.tessera.at]
    dev -.->|private Repos direkt, WireGuard 10.10.0.2| cib
  end
  subgraph deploy [Deploy-Pfad]
    mosaikrepo[mosaik-Repo<br/>deployment.yaml] -->|zieht| flux[Flux<br/>pull, Minutentakt]
    flux -->|reconcile| cluster[mosaik-Cluster<br/>App live]
  end
  zot ==>|Image-Tag von Hand nachziehen — das bewusste Prod-Gate| mosaikrepo
  zot -.->|Image-Pull| cluster

Kyverno prüft die Signatur — ein fehlender Tag blockt die ganze Kustomization. Seed, Watcher, Broker und zot laufen alle auf der CI-Box — eine einzige Box trägt den ganzen Weg vom Push bis zum signierten Image.

Seit 2026-08-12 baut der Broker auch Patches: gleiche Tore, aber ein Patch-Build published nichts — kein Tag, keine Signatur (Details: README.md → „Die Tore“).

Weg eines Logins — und der Daten

Jede App spricht Standard-OIDC gegen die Bridge; nur die Bridge kennt atproto. Die Passkey-Ceremony läuft direkt zwischen PDS und Browser — kein Passwort, keine E-Mail existiert im System.

flowchart LR
  user[Nutzer<br/>Passkey am Gerät] -->|Login| app[App<br/>SDK oidc-rp]
  app -->|OIDC| bridge[OIDC-Bridge<br/>id.tessera.at]
  bridge -->|atproto OAuth| pds[PDS, atproto-Fork<br/>pds.tessera.at]
  pds ==>|WebAuthn-Ceremony — direkt im Browser, passwort- und mailfrei| user
  pds <-.->|did:plc| plc[plc.directory<br/>extern · Föderation]
  pds -.->|Jetstream| index[tessera-index<br/>Jetstream → SQLite]
  index -->|read-only HTTP| apps[alle Apps<br/>Identity, Flairs, …]

tessera-index ersetzt N × listRecords-Polling gegen den PDS. Für Handles ist zusätzlich je ein DNS-TXT-Record _atproto.<kürzel>.tessera.at nötig (aktuell manuell in der Hetzner-Zone). Die EVM-Wallet lebt im PDS und ist per Sign-Popup-Protokoll tessera-sign/1 für Apps nutzbar.

Wo alles läuft

Zwei eigene Standorte tragen den Stack — selfbot ist seit der Migration komplett draußen.

HostRolleDienste
CI-Box (167.233.240.164)KritischRadicle-Seed (seed.tessera.at) · Explorer + Suche (code.tessera.at, Meilisearch) · radicle-watcher (Seeding ↔ PDS-Records) · CI-Broker (baut aus Radicle-Events) · zot-Registry (oci.tessera.at) · eigener CI-Radicle-Knoten (holt private Repos)
mosaik-Cluster (Talos · Flux · Kyverno · Envoy GW)Herzstücktessera-pds, -oidc, -web · tessera-index, -registry, -backup · cadence, ledger, skytess, start-complex · Tenants: salzgrotte, mbdrone, murmel, 2over1 · external-dns, cert-manager-hetzner, MetalLB, Gatus
hetzner-selfbot (ARM cax31, 46.225.72.168)kein TesseraUptime Kuma — Kandidat für den Totmann-Check (G1) · Media-Stack, LiteLLM, ntfy · Coolify läuft noch, hostet nur Nicht-Tessera
extern (bewusst minimal)Abhängigkeitplc.directory (DID-Föderation) · Hetzner DNS + Object Storage (VolSync) · Let's Encrypt (HTTP-01/DNS-01) · Offsite-Ziel von tessera-backup

Die Rolle der CI-Box ist wörtlich zu nehmen: sie ist der Single Point of Failure des Build-Pfads (G3).

Die sechs Schichten

Jede Schicht baut auf der darunter auf; die Identität trägt alles.

IIdentität — wer du bist

KomponenteDomainWoWas
atproto-Fork / PDSpds.tessera.atmosaikPasskey-only Accounts, E-Mail-frei, föderiert via plc.directory; EVM-Wallet eingebaut. Fork privat (bis zahlende Nutzer).
tessera-oidcid.tessera.atmosaikOIDC-Bridge: „Login mit Tessera“ für jede Standard-Auth-Lib. Admin-UI + Developer-Self-Service (/developers).
tessera-webtessera.atmosaikLanding, Design-System-Katalog, shadcn-Registry unter /r.

IICode-Hosting — was du erschaffst

KomponenteDomainWoWas
radicle-nodeseed.tessera.atCI-BoxEigener Seed, Policy default:block — ein Tessera-Konto darf hosten, ohne dass jemand freischaltet.
radicle-explorercode.tessera.atCI-BoxExplorer mit Tessera-Skin + Volltextsuche (Meilisearch, radicle-search aus dem Explorer-Workspace).
radicle-watcher—CI-BoxGleicht Seeding gegen PDS-Records ab; Besitz-Nachweis über Radicle-Delegates. Trägt zusätzlich das Gate (:8777, nur Stack-Netz, Shared Token): DID-gebundene Reads für Cadence — inkl. privater Repos, ohne deren Existenz zu leaken.

IIIBuild & Supply Chain — vom Tag zum Image

KomponenteDomainWoWas
CI-Broker (cib)—CI-BoxBaut aus Radicle-Events (main-Push, Tag, Patches — Patches ohne Publish), signiert Images; Tag-Builds sind immutable Releases.
zotoci.tessera.atCI-BoxOCI-Registry für alle signierten Images; Kyverno im Cluster verweigert Unsigniertes.
CI-Radicle-Knoten—CI-BoxHolt private Repos direkt vom Entwickler-Node (persistierter connect via WireGuard).

IVDistribution — Pakete & Bausteine

KomponenteDomainWoWas
tessera-registryregistry.tessera.atmosaiknpm-Registry, in der Pakete Handles gehören (@alice.tessera.at/thing) — ohne Antrag, ohne Freischaltung.
tessera-sdk@dev0zzz.tessera.at/sdkPaketSubpath-Exports: oidc-rp (Auth), identity (Profile, Wallet, Radicle, Zaps, Scores) — die eine Integrationsschicht.
shadcn-Registrytessera.at/rmosaik@tessera-Items inkl. auth-oidc als kanonischem Auth-Baustein; asChild-frei per AST-Wächtertest.
tessera-cli—lokalradicle init/create (Announce), pkg publish via SSHSIG — ohne Passkey-Ceremony publizierbar.

VRuntime — wo Apps leben

KomponenteDomainWoWas
mosaik*.tessera.atClusterTalos/K8s, Flux (GitOps aus dem mosaik-Repo), Kyverno, Envoy Gateway, external-dns, cert-manager-hetzner, MetalLB, Gatus. Wildcard-Record → neue App ohne DNS-Flip.
Eigene Appscadence · ledger · skytessmosaikPlus start-complex als lebendes Template mit /kosten (Offene Bücher).
Tenants4 migrierte Appsmosaiksalzgrotte, mbdrone, murmel, two-over-one — die ersten „fremden“ Nutzer der Plattform.

VIFabrik & Betrieb — wie Neues entsteht & Bestehendes überlebt

KomponenteDomainWoWas
start-simple / -complexstart-complex.tessera.atTemplateKanonische App-Vorlagen: Auth nur via SDK, Bun-Install/Node-Runtime, Pricing aus dem eigenen cgroup-Limit (P = ⌈C·φ⌉, Zykluskalender).
cadencecadence.tessera.atmosaikAnwendungsfabrik und Code-Werkstatt: eigene Forge-Ansicht (Tree, Blobs, Commits, Diffs, Patches inkl. Reviews, Issues) über das Watcher-Gate — DID-gebunden, private Repos sichtbar. Dazu Runs, Registry-, zot- und Cluster-Sicht. Später Schreibwege (Scaffold, Roll-Button). Zukunft von code.tessera.at: G12.
tessera-index—mosaikNetzwerkweiter Index aller at.tessera.*-Records (Jetstream → SQLite → read-only HTTP).
tessera-backup—mosaikNightly verschlüsselt offsite — deckt „Hetzner ist weg“, nicht nur „Volume kaputt“ (das macht VolSync stündlich).
tessera-agent—lokalClaude-Memory verschlüsselt als Records ins private PDS-Repo — Keimzelle der Agent-Identität (A1).

Lücken & Diskussionspunkte

Nummeriert, damit sie einzeln abarbeitbar sind. Reihenfolge = Priorisierung.

#SchwereLücke
G1KritischAlerting endet im Nichts. Prometheus feuert, aber der matrix-receiver ist disabled; der externe Totmann-Check (Uptime Kuma → pds / id / cadence) ist offen. Ein nächtlicher PDS-Ausfall bliebe bis zum Morgen unbemerkt — und der PDS ist der Single Point of Identity für alles.
G2KritischBus-Faktor 1 — auch beim Wissen. Migrations-Pipeline, Kyverno-Falle, private-Repo-Sync: das Betriebswissen liegt in Claude-Memory, nicht in Doku, die ein Dritter (Mensch oder KI) findet. Direkter Anknüpfungspunkt für die MCP/Docs-Idee (G11).
G3WichtigDie CI-Box trägt alles außer der Runtime. Seed, Explorer, Watcher, Broker und zot auf einer einzigen Box: fällt sie, sind Code-Hosting, Builds und Image-Pulls gleichzeitig weg (laufende Pods überleben, Neustarts nicht). Seit der selfbot-Migration ist die Konzentration höher als je zuvor — bewusst schlank, aber unbewertet, was ein Ausfall konkret kostet.
G4WichtigM3 Multi-Device & Recovery fehlt. Ein verlorenes Gerät = verlorenes Konto (kein zweiter Passkey, kein kalter Rotation-Key). Vor zahlenden Nutzern zwingend — wer zahlt, erwartet Wiederherstellbarkeit.
G5WichtigM4 Payment fehlt. Prepaid in USDC (Hyperliquid) ist Vision; ohne sie bleiben Handle-Abo, Anti-Spam-Gate und Offene-Bücher-Abrechnung Theorie. Die Pricing-Mechanik in den Templates ist fertig — es fehlt der Zahlweg.
G6ReibungFeedback-Schleife bis ~1 h. ARM-Build ~40 min plus bis zu 20 min Broker-Verzögerung zwischen Tag und Live. Optionen: Build-Cache, stärkere/x86-Build-Box, Broker-Polling verkürzen.
G7ReibungImage-Bump von Hand. Als Prod-Gate gewollt, aber unergonomisch (YAML editieren + pushen). Der geplante Roll-Button in Cadence (erzeugt einen Radicle-Patch) behält das Gate und entfernt die Reibung.
G8ReibungHandle-DNS manuell. Pro Handle ein TXT-Record von Hand in der Hetzner-Zone. Beim Signup automatisch anlegen (Hetzner-DNS-API) oder Wildcard + Traefik-Route — offene M4-Aufgabe.
G9Geplanttessera create <app> fehlt (CLI Phase 4). Die Fabrik hat Templates, SDK und Registries — aber noch keinen Scaffold-Befehl, der sie zusammensetzt.
G10KleinHygiene-Reste. Meilisearch-Master-Key nur in der .env auf der Box (nicht in KeePass); OIDC-Client von start-complex noch unverifiziert (⚠ im Consent).
G11IdeeAgenten-Fläche (MCP). mcp.tessera.at mit „Login mit Tessera“-OAuth: read-only Tools (Identity, Code-Suche, Build-/Deploy-Status, Pakete) plus kuratierte Docs als Kern — gemeinsame Logik mit Cadence im SDK, nicht doppelt gebaut. Löst G2 gleich mit, wenn die Doku das erste Tool ist.
G12GeplantCadence ersetzt code.tessera.at. Die Werkstatt liest übers Watcher-Gate bereits alles inkl. privater Repos — zwei Vorbedingungen fürs Abschalten des Explorers: (a) login-freie Sicht auf öffentliche Repos in Cadence (Gate ohne DID liefert nur public), damit das öffentliche Schaufenster bleibt; (b) Volltextsuche als Gate-Route mit RID-Filter je DID — nie darf ein privater Treffer in einer anonymen Suche auftauchen. Es fällt nur das Explorer-Frontend: radicle-httpd bleibt (Watcher liest Delegates daraus), radicle-search + Meilisearch bleiben als Backend. Ein Container weniger auf der CI-Box, GPL-Skin-Pflege entfällt.