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 genau um den geht es 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:
1buildx ──> skopeo copy :build-amd64 / :build-arm64 (unsignierte Staging-Tags)
2 ──> crane index append ──> :SHA und :latest
3 ──> cosign sign --recursive (auf :SHA + :latest)
4 ──> cosign verify (gleicher Index-Digest, fail-closed)
5 ──> 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:
1# radicle/seed-node/app/deployment.yaml (gekürzt)
2containers:
3 - name: seed-node
4 image: "oci.${EXTERNAL_DOMAIN}/radicle/node:latest"
5 env:
6 - { name: MODE, value: seed-node }
7 ports:
8 - { containerPort: 8776 } # Radicle-Node-Protokoll → Event-Quelle
9 volumeMounts:
10 - { 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:
1Patch geöffnet ──> Broker startet Run ──> ✓/✗ als Check am Patch
2 (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 die Ausführung beliebigen Codes 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 bei der Admission. Vom Patch bis zum laufenden Pod liegt dann jeder Schritt — Forge, Build, Signatur, Registry, Policy — auf eigener Infrastruktur:
1Patch -> CI-Broker -> build + cosign sign -> Zot -> Kyverno verify -> Pod
2(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: den Broker deployen, ihn an den Event-Stream der Node hängen und das Trust-Gating scharf schalten. 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.
