Föderation mit Bluesky (Relay)
Gilt für prod. Der Anschluss ist dort vorgesehen, der Zeitpunkt offen (ROADMAP Stufe R). Der Test-PDS ist absichtlich nicht am Relay, höchstens temporär (
PDS_CRAWLERSnicht gesetzt,ATVOTO_RELAY_FEDERATION=false), und darf zurückgesetzt werden. Was über Bluesky läuft (Crosspost, Poller, Feed): INTEROPERABILITY.md. Erfahrungen aus poltr (Throttling vonpds.poltr.info, Umzug aufpds2.poltr.info) sind hier eingeflossen.
Damit Konten von atvoto auf bsky.app sichtbar sind, abonniert der Relay
(bsky.network) den Firehose unseres PDS (com.atproto.sync.subscribeRepos) und
reicht #commit, #identity und #account an die Bluesky-AppView weiter.
PDS (pds.atvoto.ch) ──WebSocket──► Relay (bsky.network) ──► Bluesky-AppView (bsky.app)Entdeckt wird der PDS über PDS_CRAWLERS=https://bsky.network (Meldung beim Start)
oder com.atproto.sync.requestCrawl. Der Relay löst die Server-DID über
plc.directory auf und verbindet sich.
Goldene Regeln (prod)
Abschnitt betitelt „Goldene Regeln (prod)“pds-datanie löschen, den PDS nie zurücksetzen.- Server-DID und Rotation-Key nie rotieren. Der Rotation-Key gehört zusätzlich in den Passwort-Manager.
- Kein Restore ohne die Reset-Prozedur (unten): Ein Restore ist für den Relay ein Erststart.
- Den endgültigen Hostnamen nie im Test verwenden. Throttling hängt am Hostnamen und ist praktisch nicht rückgängig zu machen.
- Vor 100 Konten Bluesky um ein höheres Limit bitten (unten). Community-Konten zählen mit.
Einrichtung
Abschnitt betitelt „Einrichtung“- Server-DID als
did:plc. Der Bluesky-PDS liefert kein/.well-known/did.json,did:webfunktioniert für die Föderation deshalb nicht (in test stehtdid:web:pds.test.atvoto.ch, dort egal). Die DID wird aus dem Rotation-Key abgeleitet und beiplc.directoryregistriert: Genesis-Operation mitrotationKeysundverificationMethods.atproto=did:keydes Keys,alsoKnownAs: ["at://pds.atvoto.ch"], Dienstatproto_pds→https://pds.atvoto.ch, signiert über DAG-CBOR.scripts/testenv_pds_reset.py(Modus 1) erzeugt Key und DID und registriert sie; trotz Name ist dieser Teil allgemein, vor prod durchsehen. Weicht die vonplc.directoryberechnete DID ab, gilt die aus deren Antwort. - PDS konfigurieren:
PDS_SERVICE_DID(so heisst die Variable),PDS_CRAWLERS=https://bsky.network,PDS_HOSTNAME=pds.atvoto.ch. Prüfen:curl https://pds.atvoto.ch/xrpc/com.atproto.server.describeServer | jq .did. - Ingress für WebSockets: Der Relay hält eine dauerhafte Verbindung. Zu kurze
Read-/Idle-Timeouts am Ingress (Traefik) trennen sie
(
ERR_STREAM_PREMATURE_CLOSEim PDS-Log). In prod prüfen und ggf. Timeouts für den PDS-Host hochsetzen (poltr/nginx brauchte 3600 s). - Sichere Boot-Reihenfolge einhalten (unten), dann
requestCrawl:Terminal-Fenster curl -X POST https://bsky.network/xrpc/com.atproto.sync.requestCrawl \-H 'Content-Type: application/json' -d '{"hostname": "pds.atvoto.ch"}' - Registrar umstellen:
ATVOTO_RELAY_FEDERATION=true(siehe „Neue Konten“). - Prüfen: PDS-Log zeigt den WebSocket-Upgrade des Relays (101);
getRepoStatuseines Kontos istactive; das Profil erscheint auf bsky.app.
Neue Konten: Reihenfolge der Events
Abschnitt betitelt „Neue Konten: Reihenfolge der Events“Der PDS sendet beim Anlegen ein #identity-Event und den Commit mit dem Profil.
Verarbeitet die Bluesky-AppView das #identity vor dem Commit, legt sie einen
kaputten Stub an (createdAt: 0001-01-01…), der sich von unserer Seite nicht mehr
reparieren lässt (Handle-Toggle, Profil-Update, Posts, requestCrawl helfen nicht).
atvoto umgeht das im Registrar (relay_sync in
services/writer/src/registrar/pds_admin.py, nur mit
ATVOTO_RELAY_FEDERATION=true), bei createAccount und createDossier:
1. Konto auf dem PDS anlegen2. warten, bis die DID auf plc.directory auflösbar ist3. Profil schreiben (app.bsky.actor.profile/self)4. requestCrawl an den Relay5. warten, bis der Relay den Commit hat (getLatestCommit = 200)6. Handle kurz umschalten → neues #identity-Event, jetzt mit Repo-DatenAlle Schritte nach 3 sind nicht fatal: Das Konto entsteht auch, wenn der Relay nicht antwortet.
Grenzen des Relays
Abschnitt betitelt „Grenzen des Relays“Nicht vollständig dokumentiert; Quelle ist der Relay-Code
(indigo cmd/relay).
Konten pro PDS: 100. Ab Konto 101 setzt der Relay neue Konten auf
host-throttled und verwirft deren Events stillschweigend. requestCrawl
gelingt weiter, der PDS merkt nichts. Höheres Limit nur über das Bluesky-Team
(PDS-Admins-Discord): Projekt, erwartete Kontenzahl, Moderation, Missbrauchsplan,
Betreiber angeben. Wird das Limit erhöht, werden gedrosselte Konten automatisch
wieder aktiv. → Ab etwa 80 Konten anfragen; Kontenzahl überwachen
(goat pds account list --pds-host … | wc -l).
Event-Raten pro PDS: etwa 50/s, 1500/h, 10 000/Tag, Konto-Erstellung 5/s (skaliert mit dem Konto-Limit). Bei Überschreitung wartet der Relay; Events werden verzögert, nicht verworfen. Die Zähler leben nur im Speicher des Relays.
Status: Host active/idle/offline/throttled/banned; Konto
active/throttled/host-throttled. Von aussen erscheint host-throttled als
throttled, die beiden sind nicht unterscheidbar.
Praktisch heisst das: ATProto ist offen, die Sichtbarkeit in Bluesky hängt aber
am Relay von Bluesky. Föderation mit bsky.network ist faktisch
bewilligungspflichtig.
Relay-Probleme bei Erststart, Reset und Restore
Abschnitt betitelt „Relay-Probleme bei Erststart, Reset und Restore“Alle drei treten nur bei (Neu-)Initialisierung oder Restore auf, nicht im laufenden Betrieb.
Throttling beim Erststart
Abschnitt betitelt „Throttling beim Erststart“Sendet der PDS #identity, bevor das Repo beim Relay ingestiert werden kann, stuft
der Relay es als verdächtig ein: RepoInactive: throttled, dauerhaft. Nur ein
Neuaufbau des Kontos hilft; trifft es den Hostnamen, nur ein neuer Hostname.
Sichere Boot-Reihenfolge:
1. PDS starten2. warten, bis Firehose und Relay-Handshake stehen3. Repo anlegen, ersten Commit schreiben4. prüfen: Relay getLatestCommit OK5. erst dann #identity sendenSequenz-Lücke nach einem Reset
Abschnitt betitelt „Sequenz-Lücke nach einem Reset“Nach einem Reset beginnt seq wieder bei 1, der Relay verbindet sich aber mit
seinem letzten Cursor (z.B. 2200) und ignoriert alles darunter. Symptom: Der PDS
läuft, im Netz kommt nichts an. Lösung: PDS stoppen, in sequencer.sqlite
(repo_seq) die Sequenz über den alten Wert setzen (Schema vorher prüfen;
sqlite_sequence allein setzt der PDS beim Start zurück), PDS starten, echte
Aktivität erzeugen (z.B. Profil-Update), requestCrawl. Lücken in seq sind
erlaubt, die Folge muss nur steigen.
Restore
Abschnitt betitelt „Restore“Technisch ein Erststart, also dasselbe Throttling-Risiko. Identity-Events kommen
zuletzt: PDS ohne Identity-Sync starten, Repos einspielen, Relay-Verbindung
stabilisieren, warten bis getLatestCommit dem aktuellen Stand entspricht, erst
dann Identity-Events zulassen. Notfall, wenn es trotzdem passiert: Konto löschen,
PLC-Eintrag rotieren, Repo neu importieren, sauber neu ankündigen.
Diagnose und Hilfe
Abschnitt betitelt „Diagnose und Hilfe“# Konto beim Relaycurl "https://bsky.network/xrpc/com.atproto.sync.getRepoStatus?did=<did>" # active / throttled / RepoNotFoundcurl "https://bsky.network/xrpc/com.atproto.sync.getLatestCommit?did=<did>" # Relay hat den Commitcurl "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=<did>"
# PDScurl https://pds.atvoto.ch/xrpc/com.atproto.server.describeServer | jq .didcurl https://plc.directory/<server-did>| Symptom | Ursache, Massnahme |
|---|---|
RepoNotFound für alle Konten |
Server-DID falsch oder nicht bei plc.directory; Relay hat nie gecrawlt (requestCrawl); WebSocket-Timeouts. Neue PDS brauchen 30–120 min bis zum Abo |
throttled für einzelne Konten |
Konto-Limit erreicht oder Spam-Verdacht → Bluesky-Team |
throttled für alle, auch Konto 1 |
Hostname markiert → Bluesky-Team oder neuer Hostname (alle Konten gehen verloren) |
ERR_STREAM_PREMATURE_CLOSE |
Ingress-Timeout für WebSockets |
| Events kommen nicht an nach Reset | Sequenz-Lücke (oben) |
Was nur Bluesky kann: Konto-Limit erhöhen (/admin/pds/changeLimits), Host
oder Domain entsperren, erzwungener Crawl, Takedowns. Kontakt über den
PDS-Admins-Discord, ohne SLA.
Variablen
Abschnitt betitelt „Variablen“| Variable | Wo | Zweck |
|---|---|---|
PDS_SERVICE_DID |
PDS | did:plc des Servers |
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX |
pds-secrets |
signiert PLC-Operationen |
PDS_CRAWLERS |
PDS | Relay, der beim Start benachrichtigt wird (nur prod) |
PDS_HOSTNAME |
PDS, Registrar | öffentlicher Hostname |
ATVOTO_RELAY_FEDERATION |
Registrar | relay_sync bei neuen Konten (nur prod true) |
BSKY_RELAY_URL |
Registrar, Skript | Standard https://bsky.network |