7f834f9: „Wire Radicle CI once the lab repo is published (patches already wired)." Die Seed-Node läuft in-cluster, der Patch-Workflow steht — der CI-Broker ist das letzte fehlende Stück. Dieser Beitrag beschreibt, wie er andocken soll.In Von SourceHut zu Radicle ist der Lab-Code in eine Peer-to-Peer-Forge umgezogen: keine zentrale Instanz mehr, Patches und Issues leben als signierte Collaborative Objects (COBs), Flux deployt vom kanonischen main. Ein Faden blieb dort offen — und der ist genau der hier: Radicle ist eine Forge, kein CI-System. Der Code liegt P2P, der Build aber noch nicht.
Der offene Faden
Heute läuft die CI als externes .build.yml, ausgelöst, nachdem ff0x nach main merged — nicht aus dem Cluster heraus. Der Signing-Flow ist dabei durchaus solide:
buildx ──► skopeo copy :build-amd64 / :build-arm64 (unsignierte Staging-Tags)
──► crane index append ──► :SHA und :latest
──► cosign sign --recursive (auf :SHA + :latest)
──► cosign verify (gleicher Index-Digest, fail-closed)
──► oci.this-is-fine.io (Zot)
Zwei Dinge stören daran. Erstens: Feedback gibt es erst nach dem Merge — ein kaputter Build fällt auf, wenn die Änderung schon kanonisch ist. Zweitens: Es ist die letzte harte Abhängigkeit von einer externen, zentralen Forge — ausgerechnet beim Schritt, der die Container-Images der eigenen Lieferkette baut und signiert.
Wie Radicle CI funktioniert
Der Trick von Radicle CI ist nicht „noch ein Runner", sondern wo der Trigger herkommt. Eine Radicle-Node führt einen Event-Stream: Jede neue oder aktualisierte Patch-Ref, jedes COB-Update wird als Ereignis sichtbar. Ein CI-Broker abonniert diesen Stream, übersetzt ein Patch-Event in einen Pipeline-Lauf auf einem Backend und schreibt das Resultat als Check/Kommentar zurück auf den Patch — also vor dem Merge.
Das ist dieselbe Idee wie ein GitHub-Webhook auf einen Pull Request — nur dass der „Webhook" hier ein dezentraler, signierter Event-Stream ist, den jede replizierende Node mitliest.
Die Verdrahtung im Lab
Das Schöne: Die Eventquelle steht schon. Die Seed-Node läuft in-cluster, hält die kanonischen Refs und spricht das Radicle-Protokoll auf :8776:
# radicle/seed-node/app/deployment.yaml (gekürzt)
containers:
- name: seed-node
image: "oci.${EXTERNAL_DOMAIN}/radicle/node:latest"
env:
- { name: MODE, value: seed-node }
ports:
- { containerPort: 8776 } # Radicle-Node-Protokoll → Event-Quelle
volumeMounts:
- { name: storage, mountPath: "/node/radicle" }
Was dazukommt, ist der Broker als eigenes Deployment, das an dieser Node lauscht und einen Runner startet. Der Runner führt denselben Flow wie heute aus — buildx → skopeo → crane index → cosign sign/verify → Push nach Zot — nur jetzt in-cluster und an den Patch gebunden statt an den Merge. Der Lauf rendert sich zurück in den Patch:
Patch geöffnet ──► Broker startet Run ──► ✓/✗ als Check am Patch
(Build, Test, cosign-verify)
Damit verschiebt sich das Feedback vom „nach dem Merge" zum „am Vorschlag" — und der Build verlässt den Cluster nicht mehr.
Das eigentliche Problem: Vertrauen
Hier liegt der interessante, P2P-spezifische Teil. In einer klassischen Forge gibt es einen Owner, der entscheidet, wessen Code die CI ausführen darf. In Radicle kann ein Patch von jeder NID kommen — die Forge hat keinen zentralen Türsteher. Und CI ist nichts anderes als willkürliche Code-Ausführung mit Registry- und Cluster-nahen Secrets.
Im Lab ist das nicht theoretisch: Der ZeroClaw-Bot hat eine eigene NID und schlägt selbst Patches vor; der Hub seedet das Repo mit scope: all, damit auch die Nicht-Delegate-Namespaces (z. B. claw) repliziert werden. Der Broker muss also explizit entscheiden:
- Wessen Patches laufen automatisch? Naheliegend: nur Patches von Delegates (den vertrauten Identitäten des Repos) lösen sofort einen Lauf aus.
- Was passiert mit dem Rest? Patches fremder NIDs entweder gar nicht bauen, erst nach manueller Freigabe, oder in einer Sandbox ohne Zugriff auf Signing-Keys und Registry-Push.
Das ist die Frage, die GitHub einem abnimmt und die eine P2P-Forge zurückgibt. Sie muss vor dem Scharfschalten beantwortet sein — sonst ist der CI-Broker ein offenes Tor zu COSIGN_KEY und Registry-Credentials.
Wo die Lieferkette zusammenläuft
Zieht die CI in den Cluster, schließt sich der Kreis mit dem bestehenden Supply-Chain-Setup. Der Runner baut und signiert mit cosign, pusht in die eigene Zot-Registry, und Kyverno verifiziert die Signatur beim Admission. Vom Patch bis zum laufenden Pod liegt dann jeder Schritt — Forge, Build, Signatur, Registry, Policy — auf eigener Infrastruktur:
Patch → CI-Broker → build + cosign sign → Zot → Kyverno verify → Pod
(Radicle) (in-cluster) (eigener Key) (eigene Reg) (Admission)
Was noch fehlt
„Patches already wired" heißt: Die Pipeline-Definition liegt bereits im Repo, das Repo ist inzwischen public, die Seed-Node läuft. Was bleibt, ist den Broker zu deployen, ihn an den Event-Stream der Node zu hängen und das Trust-Gating zu setzen. Der Lohn ist der Wegfall der letzten externen Abhängigkeit im Dev-Loop: vom git push rad HEAD:refs/patches bis zum signierten Image in der eigenen Registry, ohne dass eine einzige zentrale Forge dazwischensteht.
