Packen, signieren, ausliefern
Ein Plugin wird als ein Archiv ausgeliefert, das seine eigene Signatur trägt. Die Signatur sagt wer, niemals was.
Das Layout im Archiv
Abschnitt betitelt „Das Layout im Archiv“manifest.json <- genau eine, in der Wurzeldist/ <- nur wenn Sie ein Frontend deklarieren; der genannte Eintrag liegt hierruntime/ <- der Baum eines Sidecars, in beliebiger FormEin reines Backend-Paket liefert kein dist/, und das ist richtig. Wird ein solches
Paket mit layout_invalid abgelehnt, läuft die Gegenseite auf einem Host älter als
SDK 0.8.0.
Die Grenzen, beim Hochladen durchgesetzt
Abschnitt betitelt „Die Grenzen, beim Hochladen durchgesetzt“| Grenze | Wert |
|---|---|
| Paket gepackt | 50 MB |
| Paket entpackt, gesamt | 200 MB |
| Eine Datei | 50 MB |
| Einträge | 2000 |
| Pfadtiefe | 32 Segmente |
manifest.json |
256 KB |
Die Eintragszahl ist die, die einen Python-Baum beißt: viele kleine Dateien, nicht wenige große.
Zusätzlich abgelehnt wird ein Eintrag, der beim Entpacken aus dem Zielverzeichnis
herausführt, eine Verknüpfung ist oder nach Normalisierung mit einem anderen kollidiert
(unsafe_entry), sowie ein Archiv mit auffälligem Kompressionsverhältnis
(resource_limit).
Deterministisch packen
Abschnitt betitelt „Deterministisch packen“Ein zip-Aufruf reicht nicht. Übernehmen Sie plugins/tcp/scripts/pack.mjs und passen Sie
an, was es einsammelt. Was dieses Skript tut und ein zip nicht:
- Einträge nach ihren UTF-8-Bytes sortieren. Nicht
Array.sort()— das ordnet nach UTF-16-Codeeinheiten und ist ab U+FFFF anderer Meinung. Alles andere an Ihrem Paket kann richtig sein, und ein Dateiname mit einem Emoji liefert trotzdem einen anderen Digest als erwartet. - Rohe Bytes lesen, nie Zeichenketten. Ein Packer, der eine
.pydekodiert und neu kodiert, erzeugt ein Paket, das sich installieren lässt und dann beim Start stirbt. - Feste Zeitstempel. Gleiche Eingabe, gleiche Bytes — sonst wandert der Digest bei jedem Bau, und „erst prüfen, dann ausführen” bedeutet nichts mehr.
- Tests und
__pycache__weglassen. Eine.pycträgt einen absoluten Quellpfad und einen Zeitstempel; sie zu packen macht das Archiv maschinenabhängig.
node scripts/pack.mjs # Version aus package.jsonnode scripts/pack.mjs --version 0.2.0node scripts/pack.mjs --tag acme.tcp-v0.2.0 # der FreigabewegDie Auflösung der Version ist --tag vor --version vor package.json. Eine Version wird
nie ins Skript getippt.
Zeilenenden. Wenn Ihre Werkzeuge unter Windows CRLF schreiben, unterscheidet sich Ihr Paket Byte für Byte von einem, das unter Linux gebaut wurde. Beide funktionieren, aber sie sind nicht dasselbe Artefakt und ihre Digests unterscheiden sich. Halten Sie
.gitattributesauf LF und bauen Sie aus einem sauberen Checkout.
Signieren
Abschnitt betitelt „Signieren“Ein Paket ist Bytes plus eine Ed25519-Signatur über deren SHA-256-Digest.
node dist/cli/sign-package-cli.js --package build/acme.tcp-0.1.0.zip --key your-key.pemIm Arbeitsbereich während der Entwicklung:
tsx packages/sdk/src/cli/sign-package-cli.ts --package build/acme.tcp-0.1.0.zip --key your-key.pemFlags: --package und --key sind Pflicht, --out überschreibt den Ausgabepfad,
--detached wählt die alte Form.
Das Werkzeug liest nie einen Schlüssel aus einer Umgebungsvariablen und schreibt ihn nirgendwohin. Auf die Ausgabe gehen nur Pfade und der Digest-Fingerabdruck — ein öffentlicher Wert.
Zwei Formen, beide gültig
Abschnitt betitelt „Zwei Formen, beide gültig“- Eingebettet (Standard). Die Signatur wird als fester Eintrag
META-INF/signaturein das Archiv gelegt; heraus kommt eine Datei<id>-<version>.tspkg. Das ist die Form, die ein Betreiber hochlädt. - Getrennt (
--detached). Eine eigenständige<paket>.signeben dem unveränderten Archiv. Nicht veraltet: das Freigaberegal des Betreibers hält weiterhin Paare aus<id>-<version>.zipund<id>-<version>.zip.sig.
Der Server akzeptiert beide nach Inhalt, nie nach Dateiendung oder Aufrufflag.
Ein bereits eingebettetes Paket lässt sich nicht erneut signieren — signieren Sie das Archiv vor dem Einbetten.
Welcher Schlüssel — das ist wichtiger als der Befehl
Abschnitt betitelt „Welcher Schlüssel — das ist wichtiger als der Befehl“- Ein Paket, das mit dem Betreiberschlüssel signiert ist, läuft überall.
- Ein Paket mit Ihrem eigenen Schlüssel läuft nur dort, wo der Betreiber mandanteneigene Schlüssel zugelassen hat — und für ein Backend ist das noch einmal eine eigene Einstellung.
Trägt Ihr Paket Backend-Code und ist mit Ihrem eigenen Schlüssel signiert, wird es
heute mit signature_scope_denied abgelehnt, solange die Instanz nicht die abgeschottete
Backend-Laufzeit fährt. Ein reines Frontend-Paket mit Ihrem Schlüssel installiert sich
normal. Fragen Sie den Betreiber, bevor Sie darauf einen Plan bauen.
Ein Mandanten-Administrator hinterlegt eigene Schlüssel unter Einstellungen → Plugins →
Signaturschlüssel: dort wird der öffentliche Teil (public.pem) registriert. Der
private Teil bleibt bei Ihnen und wird nie hochgeladen.
Installieren
Abschnitt betitelt „Installieren“Zwei Wege hinein, und sie sind für verschiedene Leute.
Hochladen — die Plugin-Verwaltung hat einen Hochladedialog. Ziehen Sie die .tspkg
hinein. So testen Sie, und so installiert ein Kunde etwas, das Sie nur für ihn gebaut haben.
Katalog — das Freigaberegal des Betreibers. Eine Markierung im Produkt-Repository baut, signiert und veröffentlicht eine Freigabe, ein Skript legt sie aufs Regal, und sie erscheint im Katalog jedes Mandanten zum Installieren mit einem Klick. Der Katalog bietet ausschließlich Pakete an, die gegen den Herstellerschlüssel geprüft sind — nie ein mandantensigniertes.
Installieren ist nicht Einschalten. Ein Paket liegt installiert und untätig, bis jemand es für einen Mandanten einschaltet — und dann startet ein Backend.
Aktualisieren und entfernen
Abschnitt betitelt „Aktualisieren und entfernen“Aus dem Katalog — der normale Weg. Liegt im Katalog eine neuere Version eines
installierten Plugins, zeigt die Plugin-Liste unter Einstellungen → Plugins den Schritt als
Pfeil (0.1.0 → 0.2.0), und das Zeilenmenü bietet Auf 0.2.0 aktualisieren… an. Der Dialog
nennt den Schritt, fragt einmal nach und holt die neue Version vom Regal — keine Datei, keine
Signatur. Das Plugin behält seine Daten und seine Konfiguration. Gibt es nichts Neueres, steht
dort Aktuell; bei einem Plugin, das der Katalog nicht führt, Nicht im Katalog. Der Reiter
Katalog bietet dieselbe Aktualisierung in seiner eigenen Zeile an.
Ein Plugin nie zum Aktualisieren entfernen. Entfernen kann seine Daten löschen — dafür ist es da. Eine Aktualisierung braucht es nie.
Paket hochladen… im selben Zeilenmenü ist der zweite Weg, für Versionen, die der Katalog nicht führt — eigene Plugins oder ein für einen Kunden signiertes Paket. Er ist auch der Weg für eine Aktualisierung, die der Ein-Klick-Weg als mit Datenverlust ablehnt: die Katalog-Aktualisierung bestätigt nie stellvertretend eine Änderung, die Daten zerstört, und der Hochladeweg zeigt den Plan zur Bestätigung.
Eine Aktualisierung muss eine echt höhere Version tragen; Rückschritte und Rückwärtsmigrationen gibt es nicht. Dasselbe gilt, wenn die aktive Version gegen den aktuellen Vertrauensanker nicht mehr prüft: Patch-Version erhöhen, neu signieren, aktualisieren — die Version unterscheidet zwei Uploads, nicht die Signatur.
Vor dem Vollzug zeigt das System einen Plan und stuft jede Änderung ein: ohne Folgen, prüfpflichtig, mit Datenverlust, nicht zulässig. Was Zeilen kostet, wird beziffert und muss ausdrücklich bestätigt werden.
Beim Entfernen gibt es drei getrennte Absichten: das Plugin vollständig entfernen (Tabellen, Verzeichniseintrag, Dateien), nur die Daten dieses Mandanten löschen, oder nur den Verzeichniseintrag entfernen und die Tabellen stehen lassen.
Startet das Backend der neuen Version nicht, wird die Aktualisierung nicht angewandt und
die bisherige Version bleibt aktiv (backend_unstartable).
Wenn der Start scheitert
Abschnitt betitelt „Wenn der Start scheitert“Installieren, einschalten, dann in das Serverprotokoll schauen. Ein Sidecar, der hochkam, sagt es:
sidecar ready · runtimeVersion 3.12.10 · routes 5Wenn nicht:
| Was Sie sehen | Was es heißt |
|---|---|
entry_digest_mismatch |
Der Baum hat sich zwischen Packen und Starten geändert. Meist hat der Interpreter Bytecode hineingeschrieben — starten Sie Python mit -B. |
runtime_missing / runtime_too_old |
Der von Ihrem Manifest geforderte Interpreter fehlt oder ist älter. |
runtime_unsupported_language |
Ihr requires.language ist keines, das der Host starten kann. |
entry_outside_package |
Ihr entry-Pfad verlässt das Paket. Feuert auch für eine Verknüpfung, die nach außen zeigt. |
signature_scope_denied |
Die Signatur ist in Ordnung. Die Richtlinie des Betreibers erlaubt einem Schlüssel Ihrer Art nicht, dies auszuführen. |
permission_denied bei einem Schreiben |
Ein Recht fehlt — prüfen Sie das Tor der Fähigkeit, nicht nur capability:invoke. |
backend_entry_missing |
Die im Manifest genannte Einstiegsdatei liegt nicht im Archiv. |
start_timeout |
Ihr Backend hat sich nicht innerhalb von 10 s gemeldet. |
Zum Nachlesen im Repo
Abschnitt betitelt „Zum Nachlesen im Repo“plugins/tcp— Manifest mit Sidecar und Konnektor-Typ, Python-Backend, Pack-Skript, Tests, die das Manifest von der Platte lesen, undscripts/peer.pyals Gegenstelle am anderen Ende des Kabels.docs/design/PRODLOG-179-sidecar-kanal.md— die Frames auf den Leitungen, Byte für Byte.docs/design/PRODLOG-179-konnektor-plugin-vertrag.md— die fünf Routen und zwei Ereignisse eines Konnektors.