_____ _ _ | __ \| |_ __ _| |__ | |__) | __| / _` | '_ \ | ___/| |_ | (_| | | | | |_| \__| \__,_|_| |_|
Datenbankmigrationen ohne Überraschungen.
Ptah vergleicht das gewünschte Schema mit einer laufenden Datenbank und erzeugt versionierte Migrationen oder wendet einen freigegebenen Plan an. Es migriert auch persistenten Inferenzzustand, etwa beim Neuberechnen der Embeddings einer pgvector-Tabelle. PostgreSQL, MySQL, MariaDB, SQLite und weitere. Open Source, MIT-Lizenz, keine Toolchain erforderlich.
$ curl -fsSL https://ptah.run/install.sh | sh
- Vollständig Open Source, MIT
- Keine Telemetrie
- Läuft in isolierten Netzen
- Warum das wichtig ist →
# Das Schema der Datenbank in eine Datei schreiben.$ ptah db read --db-url sqlite://app.db | tee schema.sqlCREATE TABLE "users" ( "id" INTEGER PRIMARY KEY, "email" TEXT NOT NULL); # Die Driftkontrolle vergleicht die Datei mit ihrer Quelldatenbank.$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbNo schema drift detected. # Eine Spalte zum gewünschten Schema hinzufügen.$ cat > schema.sql <<'SQL'CREATE TABLE "users" ( "id" INTEGER PRIMARY KEY, "email" TEXT NOT NULL, "created_at" TEXT);SQL # Dieselbe Prüfung hat jetzt eine Abweichung zu melden.$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: sqlite://app.db Findings:- columns_added: 1 (warning) # Die nötigen Änderungen ermitteln. Ein Dry Run führt nichts aus.$ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-runPlanned schema changes:ALTER TABLE "users" ADD COLUMN "created_at" TEXT; # Den geprüften Plan ausführen. --auto-approve eignet sich für eine entbehrliche Datei.$ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --auto-approvePlanned schema changes:ALTER TABLE "users" ADD COLUMN "created_at" TEXT;Auto-approval enabled; applying schema changes.Schema apply completed successfully. # Dieselbe Prüfung zum dritten Mal ausführen.$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbNo schema drift detected.
CREATE TABLE users ( id INTEGER PRIMARY KEY, email TEXT NOT NULL);
CREATE TABLE users ( id INTEGER PRIMARY KEY, email TEXT NOT NULL,+ created_at TEXT);
CREATE TABLE users ( id INTEGER PRIMARY KEY− email TEXT NOT NULL);
CREATE TABLE users ( id INTEGER PRIMARY KEY, email TEXT NOT NULL);+ +CREATE TABLE sessions (+ id INTEGER PRIMARY KEY,+ user_id INTEGER NOT NULL,+ token TEXT NOT NULL,+ FOREIGN KEY (user_id) REFERENCES users(id)+);
$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbNo schema drift detected.exit 0 $ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-runSchema is synced, no changes to be made.exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: sqlite://app.db Findings:- columns_added: 1 (warning)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-runPlanned schema changes:ALTER TABLE "users" ADD COLUMN "created_at" TEXT;exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbSchema drift detected (highest severity: destructive).Failure threshold: all. Failing: true.Database: sqlite://app.db Findings:- columns_removed: 1 (destructive)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-runPlanned schema changes:-- Disable foreign-key enforcement for the table rebuild belowPRAGMA foreign_keys = off;-- SQLite table rebuild for changes ALTER TABLE cannot express on usersCREATE TABLE "__ptah_rebuild_users" ( "id" INTEGER PRIMARY KEY);INSERT INTO "__ptah_rebuild_users" ("id") SELECT "id" FROM "users";DROP TABLE "users";ALTER TABLE "__ptah_rebuild_users" RENAME TO "users";-- Restore foreign-key enforcement after the table rebuildPRAGMA foreign_keys = on;exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: sqlite://app.db Findings:- constraints_added: 1 (warning)- tables_added: 1 (safe)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-runPlanned schema changes:CREATE TABLE "sessions" ( "id" INTEGER PRIMARY KEY, "user_id" INTEGER NOT NULL, "token" TEXT NOT NULL, CONSTRAINT "fk_sessions_user_id" FOREIGN KEY ("user_id") REFERENCES "users" ("id"));exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url postgres://app@db/appNo schema drift detected.exit 0 $ ptah schema apply --schema-file schema.sql \ --db-url postgres://app@db/app --dry-runSchema is synced, no changes to be made.exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url postgres://app@db/appSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: postgres://app@db/app Findings:- columns_added: 1 (warning)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url postgres://app@db/app --dry-runPlanned schema changes:-- Add/modify columns for table: users ---- ALTER statements: --ALTER TABLE "users" ADD COLUMN "created_at" TEXT;exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url postgres://app@db/appSchema drift detected (highest severity: destructive).Failure threshold: all. Failing: true.Database: postgres://app@db/app Findings:- columns_removed: 1 (destructive)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url postgres://app@db/app --dry-runPlanned schema changes:-- Remove columns from table: users ---- ALTER statements: --ALTER TABLE "users" DROP COLUMN "email" CASCADE;-- WARNING: Dropping column users.email with CASCADE - This will delete data and dependent objects! --;exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url postgres://app@db/appSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: postgres://app@db/app Findings:- constraints_added: 1 (warning)- tables_added: 1 (safe)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url postgres://app@db/app --dry-runPlanned schema changes:-- POSTGRES TABLE: sessions --CREATE TABLE "sessions" ( "id" INTEGER PRIMARY KEY NOT NULL, "user_id" INTEGER NOT NULL, "token" TEXT NOT NULL);-- ALTER statements: --ALTER TABLE "sessions" ADD CONSTRAINT "fk_sessions_user_id" FOREIGN KEY ("user_id") REFERENCES "users"("id");exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url mysql://app@db/appNo schema drift detected.exit 0 $ ptah schema apply --schema-file schema.sql \ --db-url mysql://app@db/app --dry-runSchema is synced, no changes to be made.exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url mysql://app@db/appSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: mysql://app@db/app Findings:- columns_added: 1 (warning)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url mysql://app@db/app --dry-runPlanned schema changes:-- Modify table: users ---- ALTER statements: --ALTER TABLE `users` ADD COLUMN `created_at` TEXT;exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url mysql://app@db/appSchema drift detected (highest severity: destructive).Failure threshold: all. Failing: true.Database: mysql://app@db/app Findings:- columns_removed: 1 (destructive)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url mysql://app@db/app --dry-runPlanned schema changes:-- Modify table: users ---- ALTER statements: --ALTER TABLE `users` DROP COLUMN `email`;-- WARNING: Dropping column users.email - This will delete data! --;exit 0
$ ptah schema drift --schema-file schema.sql \ --db-url mysql://app@db/appSchema drift detected (highest severity: warning).Failure threshold: all. Failing: true.Database: mysql://app@db/app Findings:- constraints_added: 1 (warning)- tables_added: 1 (safe)exit 1 $ ptah schema apply --schema-file schema.sql \ --db-url mysql://app@db/app --dry-runPlanned schema changes:-- MYSQL TABLE: sessions --CREATE TABLE `sessions` ( `id` INTEGER PRIMARY KEY, `user_id` INTEGER NOT NULL, `token` TEXT NOT NULL);-- ALTER statements: --ALTER TABLE `sessions` ADD CONSTRAINT `fk_sessions_user_id` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`);exit 0
Ein Vergleich. Alle Abläufe werten ihn aus.
Gleich, in welchem Format Sie das Schema schreiben: Ptah überführt es in ein einziges gewünschtes Schema und vergleicht es mit der Datenbank. Drift, Pläne, Lint und Anwenden sind vier Auswertungen dieses einen Diffs. Jede Prüfung endet mit dem Exit-Code 0, 1 oder 2. So hält CI eine fehlgeschlagene Verbindung nie für ein sauberes Schema.
schema.sqlschema.yamlschema.hclschema.dbmlmodels/*.goorm loader · stdout- gewünschtes Schemalaufende Datenbank
- Vergleich
schema driftNachweis 0 1 2schema plan · --dry-runPrüfgrundlagemigrations lintKontrollpunktschema apply · migrations upnach Freigabe → laufende Datenbank
Typische Aufgaben im Entwicklungsalltag.
Echte Befehle, echte Ausgaben. Die Exit-Codes sind einheitlich: 0 bei Erfolg, 1 bei negativem Prüfergebnis, 2 bei einem Aufruf- oder Verbindungsfehler.
Unsichere und destruktive Migrationen aus main heraushalten.
- uses: stokaro/ptah-action@v1 with: dir: ./internal/models db-url: ${{ secrets.PTAH_DATABASE_URL }} dialect: postgres migration-dir: ./migrations $ ptah migrations lint --dir ./migrations \ --dialect postgres migrations/1790114595_drop_bio.up.sql:1 [warning] BC104: dropping a column retires a name application versions already deployed against the old schema still select and insert, so each of them starts failing the moment this migration commits, whether or not the column held any rows; deploy readers that no longer use the column first, then drop it in a later release (dropped column breaks deployed code) migrations/1790114595_drop_bio.up.sql:1 [error] DS102: DROP COLUMN permanently deletes the column's data; deploy readers that no longer use the column first, then drop it in a later release (column dropped) 2 finding(s).
Ptah destructive-change verdictfailurePtah found destructive migration statements Safety verdict: destructive. Destructive changes: yes.
Die Action plant die Migration für die Zieldatenbank, veröffentlicht den Plan als Kommentar und legt einen Prüflauf an. Er schlägt fehl, wenn eine Anweisung Daten zerstören würde. ptah migrations lint endet mit 1 bei einem Befund und mit 2, wenn die Prüfung nicht laufen konnte. Eine fehlgeschlagene Verbindung sieht deshalb nie wie ein sauberes Verzeichnis aus.
Ungeprüfte Migrationen von der Datenbank fernhalten.
$ ptah migrations hash --dir ./migrations Wrote ./migrations/ptah.sum 4 migration file(s) hashed $ cat ./migrations/ptah.sum h1:wmq3WUjBVQT+Ll4SPPan7wJGghKMtqi2yDc2Tc4AKnM= 1790114505_create_users.down.sql h1:3hAVcH5B1mghhsNEDFiyIlGOdernvs+6dDRkaO79/F0= 1790114505_create_users.up.sql h1:WJmUNv9ET7Qaypt2hpFNHHrqSQ5XGu0Yi0gMV0gLEMo= 1790114510_add_created_at.down.sql h1:3E0vyUoTX+11C374nPdXruUtJycoXTRoGvl8XsHLLIk= 1790114510_add_created_at.up.sql h1:9wID/+fArtJnyBz/JvM/NDDd1Ovm5SvkaJYcygIXLc4=
$ ptah migrations up \ --db-url sqlite://app.db \ --migrations-dir ./migrations error: migration sum verification failed: migration directory does not match ptah.sum: changed: 1790114505_create_users.up.sql $ echo $? 2
ptah.sum speichert eine Prüfsumme pro Datei. up, validate und lint prüfen sie, bevor sie etwas tun, ebenso status mit --verify-sum. Was die CI geprüft hat, läuft auch in Produktion.
Das Embedding-Modell wechseln, ohne dass die Suche ausfällt.
run g1: cut_over, runningerror: not found: run g2run g1: cut_over, runningrun g2: boundary_captured, runningrun g1: cut_over, runningrun g2: backfilled, runningrun g1: cut_over, runningrun g2: caught_up, runningrun g1: cut_over, runningrun g2: indexed, runningrun g1: cut_over, runningrun g2: verified, runningrun g1: cut_over, runningrun g2: cut_over, running$ ptah inference plan --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' generation 4f8a8e77ec3904675efb9678ec1b3de138a135b21d267621d93decf47e581d85 source.table = docs (configured: the specification) model.identifier = ptah-docs-stub-v2 (configured: the specification) model.revision = 1 (configured: the specification) provider.credential = (configured: the specification, as a reference rather than a value) generation.reproducibility = full (inferred: the specification pins an immutable model revision) source.estimated_rows = 3 (measured) run.consistency_mode = outbox (configured: the migration specification) target.exists = false (measured) target.capability.vector_type = true (measured) target.capability.vector_index = true (measured) [prepare] create the vector column and metadata for generation 4f8a8e77ec3904675efb9678ec1b3de138a135b21d267621d93decf47e581d85 [capture] install the outbox mechanism and record its starting position [backfill] embed 3 in-scope source rows [catch-up] process every source change recorded after the snapshot began [index] build the vector index and wait for it to be valid [verify] run every verification layer against the exact target generation [cutover] point queries at generation 4f8a8e77ec3904675efb9678ec1b3de138a135b21d267621d93decf47e581d85, which nothing currently reads What is not established: - generation.reproducibility = full (inferred: the specification pins an immutable model revision) What leaves the database: - ptah-docs-stub-v2 at http://embeddings:8080/v1, declared local - the text of title, body - for 3 rows Consistency mode: outbox - the outbox event and the source change are one transaction, so a change that committed has an event
$ ptah inference prepare --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 prepared run g2 for generation 4f8a8e77ec39 - snapshot boundary: 788
$ ptah inference backfill --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 --batch-rows 10 backfill finished: 3 scanned, 3 embedded, 0 skipped
$ ptah inference catchup --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 --batch-rows 10 caught up to transaction 796: 0 changed rows, 0 tombstoned - the outbox keeps events this run has processed: floor 773 for public.docs is held by run g1 (generation b7cc9eb5c7d2ee13ad46ba3d5da8a479ec57b128d771e4151fab5bf6d1da0866)
$ ptah inference index --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 generation 4f8a8e77ec39 has a valid index
$ ptah inference verify --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 generation 4f8a8e77ec3904675efb9678ec1b3de138a135b21d267621d93decf47e581d85: 3 source rows, 3 target rows - every deterministic layer passed - not measured: the stored vectors were not read back, so their dimension was checked and their values were not; the target refuses a non-finite component on write, so nothing here could have carried one
$ ptah inference cutover --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 plan 949aa4c67b4b cutover refused: - this policy requires an approval and none was given error: cutover refused $ ptah inference cutover --spec /work/spec-g2.yaml --db-url 'postgres://ptah:ptah@postgres:5432/ptah?sslmode=disable' --run-id g2 --approve 949aa4c67b4b --approver 'site check' queries now read generation 4f8a8e77ec3904675efb9678ec1b3de138a135b21d267621d93decf47e581d85 (plan 949aa4c67b4b) - no stabilization window was asked for, so nothing is keeping the previous generation current and there is no rollback to it
Ptah baut die neue Generation neben der aktiven auf, ruft Ihren Embedding-Endpunkt auf und prüft das Ergebnis. Die Abfragen werden erst umgestellt, wenn eine Freigabe genau diesen Plan benennt. Ptah berechnet selbst keine Vektoren, und plan zeigt vor der Ausführung, was die Datenbank verlässt.
Verwendet Ihre vorhandenen Schemas.
Definieren Sie das gewünschte Schema und prüfen Sie die Änderungen, die dorthin führen. Vor der Planung wird jede Quelle in dasselbe interne Schema überführt: Dateien, Go-Annotationen, eine laufende Datenbank oder jedes Programm, das ein Schema ausgibt, auch ein ORM.
CREATE TABLE users ( id INTEGER PRIMARY KEY, email TEXT NOT NULL, created_at TEXT );
tables: users: columns: id: type: INTEGER primary: true email: type: TEXT not_null: true created_at: type: TEXT
table "users" { column "id" { type = integer null = true } column "email" { type = text null = false } column "created_at" { type = text null = true } primary_key { columns = [column.id] } }
Table users { id integer [pk] email text [not null] created_at text }
package models //ptah:schema:table name="users" type User struct { //ptah:schema:field name="id" type="INTEGER" primary="true" ID int64 //ptah:schema:field name="email" type="TEXT" not_null="true" Email string //ptah:schema:field name="created_at" type="TEXT" CreatedAt *string }
$ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-run Planned schema changes: ALTER TABLE "users" ADD COLUMN "created_at" TEXT;
$ ptah schema apply --schema-file schema.yaml \ --db-url sqlite://app.db --dry-run Planned schema changes: ALTER TABLE "users" ADD COLUMN "created_at" TEXT;
$ ptah schema apply --schema-file schema.hcl \ --db-url sqlite://app.db --dry-run Planned schema changes: ALTER TABLE "users" ADD COLUMN "created_at" TEXT;
$ ptah schema apply --schema-file schema.dbml \ --db-url sqlite://app.db --dry-run Planned schema changes: ALTER TABLE "users" ADD COLUMN "created_at" TEXT;
$ ptah schema apply --root-dir ./models \ --db-url sqlite://app.db --dry-run Planned schema changes: ALTER TABLE "users" ADD COLUMN "created_at" TEXT;
Jedes Programm, das das vollständige Schema als SQL, HCL oder YAML auf stdout ausgibt, ist eine Quelle. Ptah startet es ohne Shell, begrenzt Laufzeit und Ausgabe und lehnt eine leere Ausgabe ab.
- GORM
- SQLAlchemy
- Django
- TypeORM
- Sequelize
- Hibernate
- Ihr Skript
ein im Repository eingechecktes Rezept mit fester Version · die übrigen geben SQL-DDL aus und können dieselbe Schnittstelle nutzen
$ ptah schema drift \ --schema-cmd ./scripts/export-schema \ --db-url sqlite://app.db No schema drift detected.
Lookup-Zeilen, direkt neben den Tabellen deklariert.
Ptah vergleicht die deklarierten Zeilen mit der laufenden Tabelle und schreibt eine umkehrbare Datenmigration. Driftkontrollen und direkte Pläne lesen dieselben Zeilen. Eine Datenmigration, die Zeilen ändert oder löscht, erfordert --allow-destructive.
//ptah:schema:data · countries.yaml- Vergleich per Schlüssel mit der laufenden Tabelle
INSERT · UPDATE · DELETE.up.sql / .down.sql · ptah.sum
Rollen, Berechtigungen und Richtlinien auf Zeilenebene, zusammen mit den Tabellen deklariert.
ptah schema security liest die laufende Datenbank und meldet eine Tabelle, auf die eine Rolle Rechte hat, obwohl für sie keine Row-Level Security aktiv ist. ptah viz --security markiert denselben Befund im deklarierten Schema. Mit --fail-on any schlägt die Prüfung eines Pull Requests daran fehl.
GRANT … TO roleptah schema security- PRV01 · RLS fehlt bei einer Tabelle mit GRANT
- eine Richtlinie deklarieren
Ein Schema, exportiert für jedes Werkzeug, das es braucht.
Sieben Ziele. Aus Go-Annotationen lassen sich alle sieben erzeugen, aus einer Schemadatei alle außer HCL. Verträge für Maschinen, Dokumente für Menschen und zwei weitere Schemaformate. Kein Export verbindet sich mit einer Datenbank, und jedes Ergebnis ist ein Entwurf, den Sie prüfen.
$ ptah schema export --to openapi-v3 --root-dir ./models
$ ptah schema export --to graphql --root-dir ./models \ --graphql-operations list,by-id
$ ptah schema export --to protobuf --root-dir ./models \ --proto-package shop.v1 --out shop.proto
$ ptah schema export --to markdown --root-dir ./models
$ ptah schema export --to html --root-dir ./models --out schema.html
$ ptah schema export --to dbml --root-dir ./models
$ ptah schema export --to hcl --root-dir ./models --out schema.hcl
openapi: 3.0.3 info: title: Ptah Exported Schema version: 1.0.0 servers: - url: / paths: {} components: schemas: products: type: object required: - id - name - price - status properties: id: type: integer format: int32 name: type: string maxLength: 255 price: type: number status: type: string enum: - active - inactive
# Code generated by ptah; DO NOT EDIT. # Operation shapes below were generated on request. They are type declarations # only: Ptah generates no resolvers, no data access, no authorization or tenant # isolation, and no filtering, ordering, pagination, or transaction behavior. enum ProductStatus { active inactive } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } type Query { products(first: Int, after: String): ProductConnection product(id: ID!): Product } type Product { id: ID! name: String! price: Float! status: ProductStatus! } type ProductEdge { node: Product! cursor: String! } type ProductConnection { edges: [ProductEdge!]! pageInfo: PageInfo! }
Exported Protobuf schema to /tmp/app/shop.proto Exported 1 message(s), 4 field(s), 1 enum(s) bootstrapped new compatibility history 2 export warning(s) reported $ cat shop.proto edition = "2023"; package shop.v1; message Product { int32 id = 1; string name = 2; string price = 3; ProductStatus status = 4; } enum ProductStatus { PRODUCT_STATUS_UNSPECIFIED = 0; PRODUCT_STATUS_ACTIVE = 1; PRODUCT_STATUS_INACTIVE = 2; } // Code generated by ptah schema export --to protobuf; DO NOT EDIT. // ptah:protobuf-export-version=2 // ptah:content-sha256=4b1c2203313b7090dd40822f26eda0ee8d0b1df0ffb61c3691aa34d06de3c81b
# Schema reference - [products](#products) ## products | Column | Type | Null | Default | Key | Comment | | --- | --- | --- | --- | --- | --- | | id | SERIAL | no | — | PK | — | | name | VARCHAR(255) | no | — | — | — | | price | DECIMAL(10,2) | no | — | — | — | | status | product_status | no | — | — | — | ## Enums - `product_status` — active, inactive
Exported schema documentation to /tmp/app/schema.html
Found 1 table(s), 4 field(s), 1 enum(s)Enum "product_status" { "active" "inactive" } Table "products" { "id" SERIAL [pk, not null] "name" VARCHAR(255) [not null] "price" DECIMAL(10,2) [not null] "status" product_status [not null] }
Exported HCL schema to /tmp/app/schema.hcl Found 1 table(s), 4 field(s), 1 enum(s) $ cat schema.hcl // Code generated by ptah; DO NOT EDIT. enum "product_status" { values = ["active", "inactive"] } table "products" { column "id" { type = SERIAL } column "name" { type = VARCHAR(255) } column "price" { type = DECIMAL(10,2) } column "status" { type = enum.product_status } primary_key { columns = [column.id] } }
api_name, api_type und api_expose gestalten den veröffentlichten Vertrag, ohne die Migrationen zu beeinflussen. Ein DECIMAL kann als String ausgeliefert werden, und eine Spalte lässt sich aus dem Vertrag herausnehmen. Protobuf-Feldnummern bleiben in der Datei erhalten, die Sie einchecken.
Zwei Wege, ein Modell mit dem Projekt zu verbinden. Dieselben Werkzeuge. Nie die Datenbank.
Legen Sie fest, wer die Modellverbindung verwaltet. Alles Weitere ist dasselbe Ptah: elf Werkzeuge mit einem Arbeitsbereich, jede Schreiboperation mit Vorschau und Abgleich gegen einen Digest, eine Audit-Zeile für jede Entscheidung.
- Lesen → Vorschau → Freigabe → AnwendenDie Freigabe gilt für genau einen Patch, benannt nach seinem Digest. Haben sich die Dateien nach der Vorschau geändert, wird das Anwenden abgelehnt.
- Allow once · for this session · NoSie antworten im Terminal oder im Client. Eine Richtlinie im Repository kann Befugnisse nur einschränken.
- Schreibt nie in die DatenbankKein Werkzeug führt SQL aus, ändert eine Datenbank oder wendet eine Migration an, und keine Einstellung fügt ein solches Werkzeug hinzu.
ptah migrations upbleibt ein Schritt, den ein Mensch ausführt. - Audit und Byte-ZählungMit einem Arbeitsbereich wird jede Berechtigungsentscheidung in
.ptah/agent-audit.jsonlprotokolliert. In ptah assist endet jede Antwort mit der Angabe, wie viele Bytes Projektinhalt beim Anbieter angekommen sind.
↑ up • ↓ down • / filter • enter submit
> now write the migration for that column into the migrations directory in Ptah's native format, and call apply_patch for it. be brief Held — not written. Two blockers, both mine, not yours: • Format unknown: no tool result in this session has shown me Ptah's native migration file format, and I won't invent one. • No preview to apply: apply_patch needs a preview token/digest from a fresh preview_patch against the current migrations artifact (last listed: 0 files, digest sha256:70f6b4… ). I have none, and I can't claim a write or a verification pass without them. If the migration tool surface is enabled here, I'll: preview a migration adding tasks.active (matching the schema change already applied), show you the diff, then apply it with its token. -- AcmeQuant/Qwen3.8-27B via router, 1 turn(s), 0 tool call(s), answer -- No Ptah tool answered, so nothing above was checked against this project.
Vollständig Open Source. Keine Telemetrie. Läuft auch dort, wo kein Netzwerk hinkommt.
Für Daten, die ihren Standort nicht verlassen dürfen: reguliert, souverän oder als Verschlusssache eingestuft. Jede Komponente lässt sich selbst betreiben, und keine nimmt von sich aus Kontakt nach außen auf.
- MITVollständig Open Source
Die CLI,
ptah-compat,ptah-ls, der Kubernetes-Operator, die GitHub Action und diese Website. Vier Repositories, eine Lizenz, und keine Sicherheitsprüfung ist einer kostenpflichtigen Version vorbehalten. - PrivatKeine Telemetrie
Ptah verbindet sich nur mit Adressen, die Sie angeben: Ihrer Datenbank, dem Embedding-Endpunkt, dem Modellanbieter für ptah assist und der Registry, in die Sie Artefakte hochladen oder aus der Sie sie laden. Keine Nutzungsmeldungen, keine Update-Prüfung, kein Konto.
- Bei IhnenStandardmäßig souverän
Schemas und Migrationsverzeichnisse als OCI-Artefakte in Ihrer eigenen Registry: ECR, GAR, Harbor, GHCR, Docker Hub oder eine einfache
registry:2. Der Operator läuft in Ihrem Cluster. Zugangsdaten sind Verweise, nie Werte. Eine Spezifikation ist deshalb eine Datei, die Sie einchecken können. - Air-GapKein Netzwerk erforderlich
Die Release-Archive enthalten ptah, ptah-compat und ptah-ls, gebaut ohne CGO. Ein Archiv lässt sich ohne Netzwerk gegen
checksums.txtprüfen. Für Inferenz akzeptiert--releaseein Verzeichnis im oci-layout-Format, das Sie auf einem Datenträger übertragen haben.
Bevor ein Inferenzlauf einen Anbieter erreicht, gibt plan genau aus, was nach außen geht: welches Modell, welcher Endpunkt mit seiner deklarierten Klasse, welche Spalten, wie viele Zeilen. Das ist dieselbe Liste, die eine Datenschutzprüfung verlangt.
What leaves the database: - ptah-docs-stub-v2 at http://embeddings:8080/v1, declared local - the text of title, body - for 3 rows Consistency mode: outbox - the outbox event and the source change are one transaction, so a change that committed has an event
Eine Datenbank mit einem Schema abgleichen, das als OCI-Artefakt veröffentlicht ist.
Der Ptah-Operator legt den Tag auf einen Digest fest und verwendet nur diesen Digest. Er prüft das Artefakt, beobachtet die Datenbank und veröffentlicht einen Plan. Standardmäßig wendet er den Plan erst nach einer Freigabe an, die ihn benennt. Danach beobachtet er die Datenbank erneut, statt dem Exit-Code des Jobs zu vertrauen. Destruktive Pläne sind standardmäßig deaktiviert. Auch wenn sie aktiviert sind, bleibt die Freigabe an die exakten Bytes des Plans gebunden.
- Tag → Digest auflösen
- Artefakt prüfen
- Datenbank beobachten
- Plan veröffentlichen
- freigeben
- anwenden
- erneut beobachten
- 05
- destruktive Pläne sind standardmäßig deaktiviert; ihre Aktivierung bindet die Freigabe an die exakten Bytes des Plans
- 07
- die Schleife vertraut dem beobachteten Zustand der Datenbank, nicht dem Exit-Code des Apply-Jobs
Wissen, was auf Ihrer Datenbank funktioniert.
Zehn Datenbankmotoren. Wählen Sie Ihren und sehen Sie, was Ptah dort verwaltet, welche Einschränkung Sie kennen sollten und welche Release-Linien getestet sind.
Verwaltet Tabellen, Views, Funktionen, Erweiterungen, eigene Typen, Rollen und Row-Level-Security.
14 – 18Ptah-zertifiziert13nach Supportende getestetSchemaänderungen, einschließlich automatischer Tabellen-Rebuilds für Änderungen, die SQLite nicht als ALTER TABLE ausdrücken kann.
3Ptah-zertifiziertPlanung nach den DDL-Regeln von MySQL: Indizes, Constraints, Fremdschlüssel und das SQL, das MySQL tatsächlich akzeptiert.
8.4 · 9.7 · 26.7Ptah-zertifiziertMariaDB-spezifisches DDL, Indizes und Constraints, geplant nach den Regeln des eigenen Dialekts.
10.11 · 11.4 · 11.8 · 12.3Ptah-zertifiziertSchemaänderungen und TTL-Richtlinien auf Zeilenebene, geplant über den PostgreSQL-Pfad mit den Regeln von CockroachDB.
25.4 · 26.2 · 26.3Ptah-zertifiziertSchemaänderungen, Rollen und Row-Level-Security über den PostgreSQL-Pfad.
2024.2 · 2025.2 · 2026.1Ptah-zertifiziertT-SQL-Tabellen, gefilterte Indizes und das Lesen des Katalogs einer laufenden Datenbank.
2019 · 2022 · 2025Ptah-zertifiziertSchema lesen, planen und versionierte Migrationen.
21 · 23Ptah-zertifiziertMergeTree-Tabellen mit Engine-Einstellungen und Sortierschlüsseln, Materialized Views und TTL-Richtlinien.
26.3 · 26.7 · 26.8 · 26.9Ptah-zertifiziert24.10nach Supportende getestetSchema-Workflows über die PostgreSQL-Schnittstelle, getestet gegen den Spanner-Emulator.
Fähigkeiten Ihres Servers prüfen.
Zeigt die erkannte Version, ihre Support-Stufe und das Fähigkeitsprofil, mit dem Ptah plant.
$ ptah db capabilities --db-url "$DATABASE_URL"
Kompatibilitätsprüfungen, belegt durch die CI.
Automatisierte Prüfungen laufen bei jedem Push auf master und jede Nacht: 28 Ptah-zertifizierte Release-Linien, also die Linien, die Ptah in der CI testet und für die es innerhalb ihrer geprüften Fähigkeiten einsteht. Das eingebettete SQLite und der Spanner-Emulator sind als solche deklariert, nicht als laufende Server.
Nachweise zur Unterstützung ansehen →ptah auf der Kommandozeile. ptah-ls im Editor für Go-Annotationen. Die GitHub Action in der CI, der Kubernetes-Operator im Cluster, Go-Pakete unter ptah.run in Ihrem eigenen Code.
ptah-compat akzeptiert Befehle in Atlas-Form und führt dahinter das Verhalten von Ptah aus. Unabhängige Clean-Room-Implementierung, ohne Verbindung zu Ariga. Atlas-Kompatibilität →