Datenbank-DevOps mit Open Source

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

v0.13.0 · pre-GA · MITWindows: irm https://ptah.run/install.ps1 | iex

Änderung
sh · ptah-quick-start 49 / 49
# 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.
schema.sql entspricht der Datenbank+1 Zeile−1 Zeile+7 Zeilen
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)+);
sh
--db-url
$ 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
Sie beschreiben das gewünschte Schema. Ptah zeigt die nötigen Änderungen vor der Ausführung. Anschließend prüft die Driftkontrolle, ob die Datenbank dem Schema entspricht. Weitere aufgezeichnete Abläufe →
So funktioniert es

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.

QUELLEN · FREI KOMBINIERBARschema.sqlschema.yamlschema.hclschema.dbmlmodels/*.goorm loader · stdoutgewünschtes SchemaLAUFENDE DATENBANKalle Datenbanken →Vergleichein DiffERGEBNISSEschema driftNachweis012schema plan · --dry-runPrüfgrundlagemigrations lintKontrollpunktschema apply · migrations upnach Freigabe
  1. schema.sqlschema.yamlschema.hclschema.dbmlmodels/*.goorm loader · stdout
  2. gewünschtes Schemalaufende Datenbank
  3. Vergleich
  4. schema driftNachweis 0 1 2
  5. schema plan · --dry-runPrüfgrundlage
  6. migrations lintKontrollpunkt
  7. schema 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.

$ in der CI.github/workflows/ptah.yml
- 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).
Prüfungen · Pull Requeststokaro/ptah-action@v1
Ptah destructive-change verdictfailure
Ptah found destructive migration statements
Safety verdict: destructive.
Destructive changes: yes.
Kommentar von ptah-action
## Ptah migration plan

-- Remove columns from table: users --
-- ALTER statements: --
ALTER TABLE "users" DROP COLUMN "bio" CASCADE;

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.

shabsichern
$ 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=
shspäter, nachdem eine angewendete Datei geändert wurde
$ 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.

g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellerror: not found: run g2
g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellrun g2: boundary_captured, running
g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellrun g2: backfilled, running
g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellrun g2: caught_up, running
g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellrun g2: indexed, running
g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellrun g2: verified, running
g1 · ptah-docs-stub · von den Abfragen gelesenrun g1: cut_over, running
g2 · ptah-docs-stub-v2 · das neue Modellrun 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.

Schema als Code

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.

schema.sqlschema.yamlschema.hclschema.dbmlmodels/user.go
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
}
derselbe Plan, unabhängig von der Quelleexit 0exit 0exit 0exit 0exit 0
$ 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;
Oder das ORM als Schema verwenden

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.
Referenzdaten als Code

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.

  1. //ptah:schema:data · countries.yaml
  2. Vergleich per Schlüssel mit der laufenden Tabelle
  3. INSERT · UPDATE · DELETE
  4. .up.sql / .down.sql · ptah.sum
Datenbankzugriff als Code

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.

  1. GRANT … TO role
  2. ptah schema security
  3. PRV01 · RLS fehlt bei einer Tabelle mit GRANT
  4. eine Richtlinie deklarieren
Schemaexport

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.

Agentische Entwicklung

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.

ptah assistPtah verwaltet die Modellverbindung über ein Profil, das Sie konfigurieren: die OpenAI-kompatible oder die Anthropic-API.
SieTerminal
ptah assistein Modell auf Ihrem Rechner, ein Gateway oder ein Anbieter
Ihr ProjektSchema · Migrationen · eine Entwicklungsdatenbank
ptah mcpDer Client verwaltet das Modell. Claude, Cursor, VS Code, Zed oder jeder MCP-Client, der einen lokalen Server über stdio startet.
Sieim Client
Ihr MCP-ClientClaude · Cursor · VS Code · Zed
Ihr ProjektSchema · Migrationen · eine Entwicklungsdatenbank
  • 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 up bleibt ein Schritt, den ein Mensch ausführt.
  • Audit und Byte-ZählungMit einem Arbeitsbereich wird jede Berechtigungsentscheidung in .ptah/agent-audit.jsonl protokolliert. In ptah assist endet jede Antwort mit der Angabe, wie viele Bytes Projektinhalt beim Anbieter angekommen sind.
ptah assist · der Moment der Freigabelive aufgezeichnet
┃ Ptah is asking for artifact.write:schema.
┃
┃ apply a 1-file patch to the schema artifact
┃   artifact: schema
┃   directory: /home/developer/projects/ptah-assist-lab/workspace/schema
┃   base digest: sha256:c2faa21a30324714af7e7de289200af7f5a84908223ba1cba094b4a9
┃   result digest: sha256:fcb3f414e6936591b9510583b77fdead27f6419602161183a89a00
┃   update: schema.yaml
┃
┃ This approval covers exactly sha256:6e2a256e07315164caa524dd82d73a922ede320cd1
┃ > Allow once
┃   Allow for this session
┃   No

↑ up • ↓ down • / filter • enter submit
ptah assist · auch das Modell kann ablehnenlive aufgezeichnet
> 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.txt prüfen. Für Inferenz akzeptiert --release ein Verzeichnis im oci-layout-Format, das Sie auf einem Datenträger übertragen haben.

Ein Nachweis, kein Versprechen

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.

$ ptah inference planvor der Ausführung ausgegeben
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
GitOps für Datenbanken

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.

PtahSchema · Abgleichsschleifeoperator.ptah.run
ABGLEICHperiodisch · bei Änderungnach dem Anwenden01Tag → Digest auflösen02Artefakt prüfen03Datenbank beobachten04Plan veröffentlichen05freigeben06anwenden07erneut beobachtenSTART
  1. Tag → Digest auflösen
  2. Artefakt prüfen
  3. Datenbank beobachten
  4. Plan veröffentlichen
  5. freigeben
  6. anwenden
  7. 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
Datenbanken

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.

PostgreSQLHauptziel

Verwaltet Tabellen, Views, Funktionen, Erweiterungen, eigene Typen, Rollen und Row-Level-Security.

Das breiteste Objektmodell. Auch Enum-, Domain-, Composite- und Range-Typen, Trigger, Sequenzen und Materialized Views werden hier verwaltet.
Unterstützung für PostgreSQL ansehen →
Getestete Release-Linien
14 – 18Ptah-zertifiziert
13nach Supportende getestet
In der CI gegen einen laufenden Server geprüft.
SQLiteEingebettet

Schemaänderungen, einschließlich automatischer Tabellen-Rebuilds für Änderungen, die SQLite nicht als ALTER TABLE ausdrücken kann.

Rebuilds werden geplant, nicht versteckt. Ein Rebuild kopiert die Tabelle, und der Plan kennzeichnet ihn als SQLite-Tabellen-Rebuild.
Unterstützung für SQLite ansehen →
Getestete Release-Linien
3Ptah-zertifiziert
Eingebettete Bibliothek, im Prozess getestet, ohne Server.
MySQLUnterstützt

Planung nach den DDL-Regeln von MySQL: Indizes, Constraints, Fremdschlüssel und das SQL, das MySQL tatsächlich akzeptiert.

Nicht austauschbar mit MariaDB. Eigener Dialekt und eigene Fähigkeiten, auch wenn die Planer-Familie gemeinsam ist.
Unterstützung für MySQL ansehen →
Getestete Release-Linien
8.4 · 9.7 · 26.7Ptah-zertifiziert
In der CI gegen einen laufenden Server geprüft.
MariaDBUnterstützt

MariaDB-spezifisches DDL, Indizes und Constraints, geplant nach den Regeln des eigenen Dialekts.

Eigene Fähigkeiten. Ein MariaDB-Server wird an seiner eigenen Versionskennung erkannt, auch hinter mysql://, und mit dem MariaDB-Preset geplant, nie mit dem von MySQL.
Unterstützung für MariaDB ansehen →
Getestete Release-Linien
10.11 · 11.4 · 11.8 · 12.3Ptah-zertifiziert
In der CI gegen einen laufenden Server geprüft.
CockroachDBPostgreSQL-Wire-Protokoll

Schemaänderungen und TTL-Richtlinien auf Zeilenebene, geplant über den PostgreSQL-Pfad mit den Regeln von CockroachDB.

Nicht PostgreSQL. Operationen, die CockroachDB fehlen, werden abgelehnt oder umgeschrieben, bevor SQL entsteht, und nie unverändert ausgeliefert.
Unterstützung für CockroachDB ansehen →
Getestete Release-Linien
25.4 · 26.2 · 26.3Ptah-zertifiziert
In der CI gegen einen laufenden Server geprüft.
YugabyteDBPostgreSQL-Wire-Protokoll

Schemaänderungen, Rollen und Row-Level-Security über den PostgreSQL-Pfad.

Indexoperationen unterscheiden sich. Das nebenläufige Anlegen von Indizes wird unterstützt; DROP INDEX CONCURRENTLY schließt das YugabyteDB-Preset aus.
Unterstützung für YugabyteDB ansehen →
Getestete Release-Linien
2024.2 · 2025.2 · 2026.1Ptah-zertifiziert
In der CI gegen einen laufenden Server geprüft.
SQL ServerUnterstützte Teilmenge

T-SQL-Tabellen, gefilterte Indizes und das Lesen des Katalogs einer laufenden Datenbank.

Eine konservative Teilmenge von T-SQL. Änderungen außerhalb der Teilmenge brauchen eine manuelle Migration.
Unterstützung für SQL Server ansehen →
Getestete Release-Linien
2019 · 2022 · 2025Ptah-zertifiziert
In der CI gegen einen laufenden Server geprüft.
OracleUnterstützt

Schema lesen, planen und versionierte Migrationen.

Die deklarative Konvergenz hat bekannte Grenzen. Der Vergleich einer deklarierten Datei mit dem Katalog einer laufenden Oracle-Datenbank konvergiert noch nicht, und Migrationsläufe werden nicht durch einen Advisory Lock serialisiert.
Unterstützung für Oracle ansehen →
Getestete Release-Linien
21 · 23Ptah-zertifiziert
In der CI gegen einen laufenden Server geprüft.
ClickHouseAnalytisch

MergeTree-Tabellen mit Engine-Einstellungen und Sortierschlüsseln, Materialized Views und TTL-Richtlinien.

Durch Fähigkeiten begrenzt. Keine Fremdschlüssel und keine Transaktionen zu modellieren; der Plan arbeitet mit dem, was die Engine bietet.
Unterstützung für ClickHouse ansehen →
Getestete Release-Linien
26.3 · 26.7 · 26.8 · 26.9Ptah-zertifiziert
24.10nach Supportende getestet
In der CI gegen einen laufenden Server geprüft.
SpannerPostgreSQL-Schnittstelle

Schema-Workflows über die PostgreSQL-Schnittstelle, getestet gegen den Spanner-Emulator.

Grenze der Nachweise. Gegen den Emulator getestet; die Kompatibilität mit dem verwalteten Dienst ist nicht zertifiziert.
Unterstützung für Spanner ansehen →
Grenze der Nachweise
Emulatorin der CI getestet
verwaltetnicht zertifiziert
Der Emulator beweist nichts über den verwalteten Dienst.

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"
So lesen Sie die Ausgabe →

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 in Ihrem Workflow

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.

Sie kommen von Atlas?

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 →