Zum Inhalt springen

Teilnehmer mit fremdem PDS (OAuth)

Status: Konzept, nicht umgesetzt (2026-10-26). Das ist Stufe 1 in ROADMAP.md. Die Berechtigung ist dort geregelt: Rechte folgen der Mitgliedschaft bei einem Client und dessen Teilnahmeregeln.

Heute kann nur mitmachen, wer über den Registrar (createAccount) ein Konto auf unserem PDS bekommt. Ziel: zusätzlich Personen zulassen, die schon ein ATProto-Konto anderswo haben (z.B. bsky.social), ohne den bestehenden Weg (Magic Link, passwortlos) aufzugeben.


Eigener PDS Fremder PDS
Konto vom Registrar angelegt besteht bereits
Login des Users beim Client, z.B. Magic Link (Methode wählt der Client) ATProto-OAuth: Login auf dem fremden PDS, mit dessen Methode
Was der Client hält App-Passwort (von createAccount) OAuth-Tokens (widerrufbar, mit Scopes, DPoP-gebunden)
Schreiben ins User-Repo Client mit App-Passwort-Session Client mit OAuth-Token
Lesen mit Viewer atproto-proxy → Service-Auth identisch
Magic Link (eigener PDS) OAuth (fremder PDS)
──────────────────────── ───────────────────
User ─E-Mail-Link─► Client User ─Handle eingeben─► Client
│ App-Passwort │ Weiterleitung zum fremden PDS,
│ (vom Registrar) │ User loggt sich DORT ein
▼ ▼
unser PDS fremder PDS ──► Token mit Scopes
│ │
└──── atproto-proxy ──┴──► AppView (unverändert)

Warum nicht alles über OAuth? Auch unser PDS hat einen OAuth-Server, dessen Login-Seite kennt aber nur Passwort + E-Mail-Code, keinen Magic Link. Für eigene Konten bleibt deshalb der bisherige Weg.

Warum nicht App-Passwörter fremder Konten? Ein Client soll kein Passwort eines fremden Dienstes speichern; OAuth ist dafür der ATProto-Standard.

AppView: keine Änderung nötig. Service-Auth (RUNBOOK §14) funktioniert mit jedem PDS. Das Token wird gegen das DID-Dokument des Users geprüft, egal wo es liegt. Hinweis: das DID-Dokument bei Signaturfehler neu laden (Schlüsselwechsel, PDS-Umzug), nicht zu lange cachen.


  1. Firehose fremder PDS. Der Indexer hört nur auf unseren PDS (FIREHOSE_URL, Default wss://${PDS_HOSTNAME}, services/indexer/src/service.js). Records in fremden Repos kommen nie an. Lösung: zusätzlich Relay bzw. Jetstream abonnieren (bsky.network), gefiltert auf ch.atvoto.* und/oder auf DIDs aus registry.participants. Cursor-Verwaltung pro Quelle.
  2. Externe Teilnehmer registrieren. registry.participants wird heute nur von createAccount befüllt. Neu: Registrar-Methode z.B. registerParticipant(did), mit der ein Client eine externe DID seinem User zuordnet (die Zuständigkeit des Clients bleibt, Widerruf über revokeParticipant). Die Spalten origin und assurance kommen schon in Stufe 0 (ROADMAP.md).
  3. Berechtigungsregeln (entschieden 2026-10-26). Jeder Client legt in seinen Teilnahmeregeln fest, ob und wofür er externe Konten zulässt (origin), und meldet ihre Prüfstufe (assurance). Der Store setzt das durch. Siehe ROADMAP.md.
  4. Profile. Anzeigename/Avatar externer User kommen nicht über unseren PDS → Profile aus dem Firehose bzw. per getProfile beim fremden PDS (bekannte Lücke).
  5. Moderation. In fremden Repos können wir nichts löschen. Das Muster Original → kanonische Kopie (Writer) deckt das ab: gezählt wird nur die Kopie im Community-Konto. Kommentare und Bewertungen werden direkt aus User-Repos projiziert, hier also Ausblenden im Read-Model statt Löschen.
  1. OAuth-Client: Client-Metadaten unter einer HTTPS-URL, JWKS für einen vertraulichen Client, PAR, PKCE, DPoP. Z.B. mit @atproto/oauth-client-node.

  2. Scopes, etwa:

    • repo:ch.atvoto.dossier.argument, repo:ch.atvoto.dossier.comment, repo:ch.atvoto.content.rating, repo:ch.atvoto.peerreview.request, repo:ch.atvoto.peerreview.response
    • rpc:<methode>?aud=did:web:api.atvoto.ch%23atvoto_appview

    Option: ein Permission-Set als Lexicon veröffentlichen (z.B. ch.atvoto.authParticipant), damit alle Clients dasselbe Paket anfragen und die Login-Seite es verständlich anzeigt. Vorher prüfen, wie weit der PDS das unterstützt.

  3. Eine Session-Schicht für beide Wege: beide Logins ergeben eine DID; danach unterscheidet sich nur, womit der Client den PDS aufruft (App-Passwort-Session oder OAuth-Token). Die Aufrufe an den Store sind identisch.


  1. Stufe 0 aus ROADMAP.md (Mitgliedschaft, Teilnahmeregeln) muss stehen.
  2. Store: Punkt 1 (Firehose), unabhängig vorbereitbar; dann 2, 4, 5.
  3. Client: 6–8.

Prüfen, dass es mit fremden PDS grundsätzlich geht (Service-Auth): scripts/service_auth_check.sh https://bsky.social <handle> → HTTP 200.

Siehe auch: RUNBOOK §14 (Service-Auth), ARCHITECTURE_SPLIT §5.1 (Registrar), NEW_ATVOTO_CLIENT.md.