2026-10-05von RaySpec10 MinAnwendungs-Bundle

RaySpec 1.9.0: Die Anwendung ist eine Datei, die man weitergeben kann

RaySpec 1.9.0 packt eine gebaute Anwendung in ein einziges .ray-Bundle. Du kannst es lesen, ohne es auszuführen, es gegen einen geprüften Plan deployen, ein ganzes Deployment als eine verschlüsselte Datei umziehen und es in einer strengeren Betriebsart hosten. Hier steht, was das abdeckt und was nicht.

Bisher hast du eine RaySpec-Anwendung aus einem Quellverzeichnis deployt: eine Spec und daneben der Code und die statischen Dateien, auf die sie verweist. Das passt, solange dieselbe Person die Anwendung schreibt und betreibt. Sobald das zwei Personen sind, wird es unhandlich.

RaySpec 1.9.0 macht aus der gebauten Anwendung eine Datei. Ein .ray-Bundle, das sich prüfen lässt, ohne es auszuführen, das mit einem vorher geprüften Plan deployt wird, das verschlüsselt auf einen anderen Host umzieht und das in einer strengeren Betriebsart laufen kann. Die Release Notes fassen es in fünf Verben: packen, prüfen, deployen, umziehen, hosten. Dieser Beitrag geht sie in dieser Reihenfolge durch und endet mit dem, was das Release nicht tut.

Wenn du nur aktualisierst und nichts Neues setzt, läuft dein Deployment weiter wie bisher. Alles Folgende ist ein Befehl, den du bewusst ausführst, oder eine Einstellung, die du einschaltest.

Das Bundle#

rayspec pack macht aus einer bereits gebauten Anwendung eine Datei. Mit Team Notes, der Referenzanwendung aus dem Quickstart:

npx rayspec pack --spec team-notes/rayspec.yaml --output team-notes-1.0.0.ray

Darin liegen die Spec, der kompilierte Code und die statischen Dateien, die sie zur Laufzeit braucht, die Fremdpakete, die dieser Code importiert, ein Dependency-Lock, eine SBOM (CycloneDX 1.5) und die Lizenzhinweise. Nie darin: ein Secret-Wert, eine Datenbankzeile, ein Cloud-Konto. Secrets erreichen eine Anwendung beim Deploy als Bindings, und pack verweigert eine Datei, die einen PEM-Private-Key-Header enthält.

Drei Eigenschaften solltest du kennen, bevor du dich darauf verlässt:

Ein Bundle fasst höchstens 9.999 Dateien, Pfade bis 4.096 Bytes und 512 MiB. Bundles zielen auf linux/x64 und Node 22. Packing an application beschreibt, was hineinkommt, was nie hineinkommt und wie sich jede Ablehnung beheben lässt.

Lesen vor dem Ausführen#

Eine Datei, die dir jemand gibt, sollte lesbar sein, bevor irgendetwas darin läuft. Dafür gibt es zwei Befehle:

npx rayspec bundle inspect team-notes-1.0.0.ray
npx rayspec bundle verify team-notes-1.0.0.ray

bundle inspect ist passiv: Nichts im Archiv wird entpackt, importiert, ausgewertet oder ausgeführt, und nirgendwo wird etwas geschrieben. Es sagt, was das Bundle ist: die Anwendung und ihre Version, die Runtime, auf die es festgelegt ist, die Bindings, die es braucht, die ausgehenden Hosts, die es deklariert. bundle verify prüft alles, was inspect prüft, danach die Runtime, das Ziel, die Capabilities und die Spec, sucht nach Secrets und prüft die Signatur. Das Urteil lautet deployable oder not-deployable.

Lies deployable eng. Es heißt, dass jede Prüfung für diese Runtime bestanden ist. Für den Code im Bundle bürgt es nie.

Signieren#

Wer ein Bundle veröffentlicht, kann es mit dem eigenen Schlüssel signieren. Die Signatur ist Ed25519 über den SHA-256 des Bundles und liegt als kleine, separate Datei daneben; das Bundle selbst bleibt unverändert. Aus dem Packing-Guide, wo das Bundle notes-1.4.0.ray heißt:

openssl genpkey -algorithm ed25519 -out publisher.pem
chmod 600 publisher.pem
openssl pkey -in publisher.pem -pubout -out publisher.pub.pem
rayspec bundle sign notes-1.4.0.ray --key-file publisher.pem

Wer es deployt, prüft mit dem öffentlichen Schlüssel und lehnt ein unsigniertes Bundle ab:

rayspec bundle verify notes-1.4.0.ray --trusted-key publisher.pub.pem --require-signature

Eine gültige Signatur zeigt, dass das Archiv genau das ist, das der Inhaber des privaten Schlüssels signiert hat, und nicht mehr. Sie zeigt nicht, dass der Code sicher ist.

# note

bundle sign setzt deinen Schlüssel auf deine Anwendung. Es ist keine Signatur auf RaySpec selbst: Das Release-Manifest von 1.9.0 ist nicht signiert, und die npm-Pakete tragen keine Provenance-Attestierung. Mehr dazu unten bei den Grenzen.

Deployen gegen einen geprüften Plan#

Ein Bundle-Deploy besteht mit Absicht aus zwei Schritten:

npx rayspec deploy team-notes-1.0.0.ray --dry-run > plan.json
npx rayspec deploy team-notes-1.0.0.ray --plan-digest "$(node -p 'require("./plan.json").data.planDigest')"

Der Dry Run plant den Deploy gegen die laufende Datenbank und ändert nichts: Kein SQL ändert etwas, und nichts aus dem Bundle läuft. Der Plan nennt die Bindings, die das Bundle braucht, was mit dem Schema passiert, welche ausgehenden Hosts und Capabilities sich ändern und was ihn blockiert. Am Ende steht ein Digest. Der zweite Befehl wendet genau diesen Plan an, entpackt das Bundle in ein schreibgeschütztes Versionsverzeichnis und liefert die Anwendung aus, auf Loopback und Port 8080, solange du nichts anderes angibst.

Was rund um diese zwei Zeilen gilt:

Deploying a bundle on your own server führt ein Bundle von der Inspektion bis zum ausgelieferten, aktualisierten und wiederhergestellten Deployment.

Ein Deployment umziehen#

rayspec export schreibt den vollständigen Zustand eines Deployments als ein Migrations-Bundle: die deployte Anwendung, ihre Anwendungsdatenbank, ihre Workflow-Systemdatenbank und jeden gespeicherten Blob, mit age für genau einen Empfänger verschlüsselt. Das Schlüsselpaar entsteht auf der Maschine, die importiert, nicht auf der Quelle:

age-keygen -o migration-identity.txt   # keep this file private (mode 0600)
age-keygen -y migration-identity.txt   # prints the recipient: age1...

Nur der Empfänger geht an die Quelle. Einen Passphrase-Modus gibt es nicht.

rayspec export --deployment 3f9c0a1b2c3d4e5f \
  --recipient age1… \
  --output /srv/handover/app-migration.ray \
  --run-history included

Ein Export ist für Schreibzugriffe offline. Lesezugriffe werden weiter beantwortet, Änderungen und Uploads werden abgelehnt, und die Quelle bleibt nach einem erfolgreichen Export gesperrt. Auf der anderen Seite gilt: erst prüfen, dann wiederherstellen.

rayspec import /srv/handover/app-migration.ray \
  --target /srv/app/.rayspec-state \
  --identity-file migration-identity.txt \
  --dry-run

rayspec import prüft alles, bevor es irgendetwas wiederherstellt. Es stellt in ein neues, leeres Ziel wieder her, führt nie in eine Datenbank zusammen, die schon etwas enthält, vergleicht das Ergebnis mit dem Snapshot und lässt das Ziel gesperrt. Das Ziel liefert erst aus, wenn du es mit einem Cutover-Token freigibst, das genau einmal und nur innerhalb von 15 Minuten funktioniert.

Ein Umzug setzt Zugangsdaten zurück, und das ist so gewollt. Die neue Umgebung erzeugt ihre eigenen Boot-Secrets, also sind Sitzungen, API-Keys und Einladungen weg. Benutzer, Mitgliedschaften und Passwort-Hashes kommen mit: Mitglieder melden sich mit ihren bisherigen Passwörtern neu an. Ein Owner ohne Passwort bekommt mit rayspec tenant recover-owner einen einmaligen Weg zurück.

Behalte den Umfang im Blick. Ein Export ist kein Backup-Werkzeug, kein Replikationsstrom und kein Weg, zwei Deployments zusammenzuführen. Er zieht eine Organisation um, höchstens 500.000 Objekte und ein Bundle von höchstens 2 GiB, in ein leeres Ziel. Lass die Quelle für das Recovery-Fenster gesperrt, standardmäßig sieben Tage; ein Rückweg ist sie nicht mehr, sobald das Ziel einen Schreibzugriff angenommen hat. Die Guides dazu sind Exporting a deployment und Importing a deployment.

Eine strengere Betriebsart, wenn du sie einschaltest#

Der Standard hat sich nicht verschoben: Der Core ist für vertrauenswürdigen, selbstgehosteten Single-Node-Betrieb gebaut. 1.9.0 ergänzt eine gehärtete Betriebsart für eine Runtime, die Menschen erreichen können, denen du nicht voll vertraust. Sie besteht aus vier Schaltern. Jeder ist standardmäßig aus, und jeder lässt sich einzeln einschalten.

SchalterWas er tutWas er nicht tut
RAYSPEC_MIGRATION_DATABASE_URL, mit dem mitgelieferten Rollen-SetupRollentrennung und Row-Level Security. Nur ein Supervisor hält die Migrationsverbindung; ein Kindprozess liefert die Anwendung als Runtime-Rolle aus, die die Mandanten-Policies nicht umgehen kann.Er hindert Code im ausliefernden Prozess nicht daran, die Id eines anderen Mandanten selbst zu setzen.
RAYSPEC_SINGLE_TENANT=trueEine Organisation pro Runtime. Eine zweite wird auf jedem Weg abgelehnt.Er wählt nichts aus und versteckt nichts: Eine Datenbank, die schon mehr als eine Organisation enthält, verweigert den Boot.
RAYSPEC_HOSTING_POSTURE=managedEin Standardwert für jede Ausführungsgrenze, nur das Agent-Backend openai, deklarierte Rechte für jeden Handler, kein Export von Agent-Traces, solange du ihn nicht verlangst.Er lehnt die Backends anthropic, codex und pi ab, statt sie einzuhegen, und er erzwingt keine Egress-Regeln.
RAYSPEC_TRUSTED_PROXIESBenennt hinter einem Reverse Proxy die Proxy-Adressen, deren Forwarding-Header geglaubt werden.Er terminiert kein TLS und begrenzt keine Request-Bodys. Das macht der Proxy.

Der Managed-Posture-Receipt eines Releases nennt, welche Schutzmaßnahmen dessen Certification Lane getestet hat. Er ist ein Nachweis über Tests, kein Compliance-Zertifikat. Fang mit Hosting in the hardened posture und Database roles and row-level security an; das Threat Model sagt, was der Host um die Runtime herum selbst durchsetzen muss.

Drei Anwendungen und ein Quickstart#

Unter examples/ liegen drei Referenzanwendungen, jede von den Tests des Repositorys gepackt und aus ihrem Bundle deployt:

Der Quickstart führt in etwa zehn Befehlen von npm install zu einer deployten Anwendung, mit der veröffentlichten CLI und ohne RaySpec selbst zu bauen. Er braucht Node >=22.21.0 und PostgreSQL 16 und beginnt so:

npm install rayspec
npx rayspec --version

Was dieses Release nicht tut#

Klar gesagt, weil sich jeder dieser Punkte leicht stillschweigend annehmen lässt:

Das Threat Model führt jedes Restrisiko auf, das dieses Release akzeptiert.

Upgrade von 1.8#

Ein bestehendes Deployment braucht nichts Neues. Prüf zuerst Node:

node --version   # v22.21.0 or later on the 22 line

Sichere danach beide Datenbanken, installiere 1.9.0 und starte das Deployment so, wie du es bisher gestartet hast. Der erste Boot führt die Plattform-Migrationen 0012 bis 0017 aus; jede ist additiv. Nimm das Backup ernst: Eine 1.8.x-Runtime erkennt eine Datenbank nicht, die 1.9.0 migriert hat, der Rückweg wird also nicht unterstützt und muss bei diesem Backup beginnen. Wenn ein Skript Exit-Code 2 als einzigen Fehler behandelt, ändere es: Ein unerwarteter interner Fehler der CLI endet jetzt mit 7. Das Upgrade selbst wird vor jedem Release mit Daten geprüft, von 1.7.0 und von 1.8.0 aus.

So geht es weiter:

Die Anwendung ist jetzt eine Datei. Lies sie, bevor du sie ausführst.