_____ _ _ | __ \| |_ __ _| |__ | |__) | __| / _` | '_ \ | ___/| |_ | (_| | | | | |_| \__| \__,_|_| |_|
Des migrations de bases de données sans surprises.
Ptah compare le schéma souhaité à une base existante, puis génère des migrations versionnées ou applique un plan approuvé. Il migre aussi l’état d’inférence persistant, par exemple pour recalculer les embeddings d’une table pgvector. PostgreSQL, MySQL, MariaDB, SQLite et d’autres moteurs. Open source, licence MIT, sans chaîne de compilation à installer.
$ curl -fsSL https://ptah.run/install.sh | sh
- Entièrement open source, MIT
- Aucune télémétrie
- Fonctionne en réseau isolé
- Pourquoi c’est important →
# Écrire le schéma de la base dans un fichier.$ ptah db read --db-url sqlite://app.db | tee schema.sqlCREATE TABLE "users" ( "id" INTEGER PRIMARY KEY, "email" TEXT NOT NULL); # Le contrôle de dérive compare ce fichier à la base dont il provient.$ ptah schema drift --schema-file schema.sql \ --db-url sqlite://app.dbNo schema drift detected. # Ajouter une colonne au schéma souhaité.$ cat > schema.sql <<'SQL'CREATE TABLE "users" ( "id" INTEGER PRIMARY KEY, "email" TEXT NOT NULL, "created_at" TEXT);SQL # Le même contrôle a maintenant une différence à signaler.$ 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) # Déterminer les changements nécessaires. Une simulation n’exécute rien.$ ptah schema apply --schema-file schema.sql \ --db-url sqlite://app.db --dry-runPlanned schema changes:ALTER TABLE "users" ADD COLUMN "created_at" TEXT; # Exécuter le plan revu. --auto-approve convient à un fichier jetable.$ 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. # Exécuter le même contrôle une troisième fois.$ 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
Une seule comparaison. Chaque flux de travail s’appuie sur elle.
Quel que soit le format de votre schéma, Ptah le convertit en un seul schéma souhaité et le compare à la base. Dérive, plans, analyse statique et application sont quatre lectures de ce même diff. Chaque contrôle se termine avec le code 0, 1 ou 2 : la CI ne prend donc jamais une connexion en échec pour un schéma conforme.
schema.sqlschema.yamlschema.hclschema.dbmlmodels/*.goorm loader · stdout- schéma souhaitébase existante
- comparer
schema driftla preuve 0 1 2schema plan · --dry-runl’objet de la revuemigrations lintle garde-fouschema apply · migrations upaprès approbation → base existante
Des tâches courantes au fil de la semaine.
De vraies commandes et leurs sorties réelles. Les codes de retour sont constants : 0 en cas de succès, 1 pour un contrôle négatif, 2 pour une erreur d’utilisation ou de connexion.
Tenir les migrations risquées et destructives hors de main.
- 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.
L’Action planifie la migration sur la base cible, publie le plan en commentaire et crée un contrôle qui échoue si une instruction devait détruire des données. ptah migrations lint se termine avec le code 1 quand il trouve un problème et 2 quand il n’a pas pu s’exécuter : une connexion en échec ne passe donc jamais pour un répertoire sans problème.
Tenir les migrations non relues à l’écart de la base.
$ 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 conserve une somme de contrôle par fichier. up, validate et lint la vérifient avant toute action, et status le fait avec --verify-sum. Ce que la CI a relu est exactement ce que la production exécute.
Changer de modèle d’embeddings sans interrompre la recherche.
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 construit la nouvelle génération à côté de celle que lisent les requêtes, appelle votre service d’embeddings, vérifie le résultat et fait basculer les requêtes quand une approbation désigne le plan. Il ne calcule jamais de vecteur lui-même, et plan indique ce qui sort de la base avant toute exécution.
Travaille avec vos schémas existants.
Décrivez le schéma souhaité, puis relisez les changements nécessaires pour y parvenir. Chaque source est convertie dans le même schéma interne avant la planification : fichiers, annotations Go, base existante ou tout programme qui affiche un schéma, ORM compris.
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;
Tout programme qui écrit le schéma complet en SQL, HCL ou YAML sur stdout est une source. Ptah le lance sans shell, en borne l’exécution et refuse une sortie vide.
- GORM
- SQLAlchemy
- Django
- TypeORM
- Sequelize
- Hibernate
- votre script
une recette enregistrée dans le dépôt, versions figées · les autres affichent du DDL SQL et peuvent suivre le même contrat
$ ptah schema drift \ --schema-cmd ./scripts/export-schema \ --db-url sqlite://app.db No schema drift detected.
Des lignes de référence, déclarées à côté des tables.
Ptah compare les lignes déclarées à la table existante et écrit une migration de données réversible. Le contrôle de dérive et les plans directs lisent les mêmes lignes. Une migration de données qui modifie ou supprime des lignes exige --allow-destructive.
//ptah:schema:data · countries.yaml- comparaison par clé avec la table existante
INSERT · UPDATE · DELETE.up.sql / .down.sql · ptah.sum
Rôles, privilèges et politiques au niveau des lignes, déclarés avec les tables.
ptah schema security lit la base existante et signale une table sans sécurité au niveau des lignes sur laquelle un rôle a reçu des privilèges. ptah viz --security marque le même constat sur le schéma déclaré. Avec --fail-on any, ce constat bloque une pull request.
GRANT … TO roleptah schema security- PRV01 · pas de RLS sur une table avec privilèges
- déclarer une politique
Un seul schéma, exporté vers tout ce qui en a besoin.
Sept cibles. Les annotations Go les atteignent toutes ; un fichier de schéma, toutes sauf HCL. Des contrats pour les machines, des documents pour les humains, deux autres formats de schéma. Aucun export ne se connecte à une base de données, et chaque export est une proposition à relire.
$ 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 et api_expose façonnent le contrat publié sans toucher au moteur de migration : un DECIMAL peut être publié sous forme de chaîne, et une colonne peut être exclue du contrat. Les numéros de champs Protobuf sont conservés dans le fichier que vous versionnez.
Deux façons de faire travailler un modèle sur le projet. Un seul jeu d’outils. Jamais la base de données.
Choisissez qui gère la connexion au modèle. Tout le reste est le même Ptah : onze outils avec un espace de travail, chaque écriture prévisualisée et vérifiée par rapport à un digest, une ligne d’audit pour chaque décision.
- lire → prévisualiser → approuver → appliquerL’approbation couvre un seul patch, désigné par son digest. Si les fichiers ont changé après la prévisualisation, l’application est refusée.
- Allow once · for this session · NoVous répondez dans le terminal ou dans le client. La politique du dépôt ne peut que restreindre les autorisations.
- N’écrit jamais dans la base de donnéesAucun outil n’exécute de SQL, ne modifie une base ou n’applique une migration, et aucun réglage ne peut en ajouter un.
ptah migrations upreste une étape exécutée par une personne. - Audit et nombre d’octetsAvec un espace de travail, chaque décision d’autorisation est écrite dans
.ptah/agent-audit.jsonl. Dans ptah assist, chaque réponse se termine par le nombre d’octets du projet transmis au fournisseur.
↑ 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.
Entièrement open source. Aucune télémétrie. Fonctionne là où le réseau ne va pas.
Conçu pour les données qui doivent rester où elles sont : réglementées, souveraines, classifiées. Chaque composant peut être auto-hébergé, et aucun ne transmet de données à l’extérieur.
- MITEntièrement open source
La CLI,
ptah-compat,ptah-ls, l’opérateur Kubernetes, la GitHub Action et ce site. Quatre dépôts, une seule licence, et aucun contrôle de sécurité n’est réservé à une offre payante. - PrivéAucune télémétrie
Ptah se connecte uniquement aux adresses que vous lui donnez : votre base de données, le service d’embeddings, le fournisseur de modèle de ptah assist et le registre où vous publiez ou d’où vous récupérez les artefacts. Aucune statistique d’usage, aucune vérification de mise à jour, aucun compte.
- À vousSouverain par défaut
Schémas et répertoires de migrations sous forme d’artefacts OCI dans votre propre registre : ECR, GAR, Harbor, GHCR, Docker Hub ou un simple
registry:2. L’opérateur s’exécute dans votre cluster. Les identifiants sont des références, jamais des valeurs : une spec est donc un fichier que vous pouvez versionner. - IsoléAucun réseau requis
Les archives des versions contiennent ptah, ptah-compat et ptah-ls, compilés sans CGO. Vérifiez une archive avec
checksums.txtsans accès au réseau. Pour l’inférence,--releaseaccepte un répertoire oci-layout que vous avez transféré sur disque.
Avant qu’une exécution d’inférence ne contacte un fournisseur, plan affiche exactement ce qui sort : quel modèle, quel point de terminaison et sa classe déclarée, quelles colonnes, combien de lignes. C’est la liste même que demande une revue de protection des données.
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
Réconcilier une base de données avec un schéma publié comme artefact OCI.
L’opérateur Ptah fige le tag sur un digest et n’utilise que ce digest. Il vérifie l’artefact, observe la base de données et publie un plan. Par défaut, il n’applique le plan qu’après une approbation qui le désigne, puis observe à nouveau la base au lieu de se fier au code de retour du job. Les plans destructifs sont désactivés par défaut ; les activer lie toujours l’approbation aux octets exacts du plan.
- résoudre le tag → digest
- vérifier l’artefact
- observer la base
- publier le plan
- approuver
- appliquer
- observer à nouveau
- 05
- les plans destructifs sont désactivés par défaut ; les activer lie l’approbation aux octets exacts du plan
- 07
- la boucle se fie à ce qu’elle observe dans la base, pas au code de retour du job d’application
Sachez ce qui fonctionne sur votre base de données.
Dix moteurs. Choisissez le vôtre pour voir ce que Ptah y gère, la limite à connaître et les branches de version testées.
Gère les tables, vues, fonctions, extensions, types personnalisés, rôles et la sécurité au niveau des lignes.
14 – 18certifiée par Ptah13testée après la fin du supportModifications de schéma, y compris la reconstruction automatique des tables pour les changements que SQLite ne sait pas exprimer en ALTER TABLE.
3certifiée par PtahUne planification qui respecte les règles DDL de MySQL : index, contraintes, clés étrangères et le SQL que MySQL accepte réellement.
8.4 · 9.7 · 26.7certifiée par PtahDDL, index et contraintes propres à MariaDB, planifiés selon les règles de son dialecte.
10.11 · 11.4 · 11.8 · 12.3certifiée par PtahModifications de schéma et politiques TTL au niveau des lignes, planifiées par la voie PostgreSQL avec les règles de CockroachDB.
25.4 · 26.2 · 26.3certifiée par PtahModifications de schéma, rôles et sécurité au niveau des lignes par la voie PostgreSQL.
2024.2 · 2025.2 · 2026.1certifiée par PtahTables T-SQL, index filtrés et lecture d’un catalogue existant.
2019 · 2022 · 2025certifiée par PtahLecture du schéma, planification et migrations versionnées.
21 · 23certifiée par PtahTables MergeTree avec paramètres de moteur et clés de tri, vues matérialisées et politiques TTL.
26.3 · 26.7 · 26.8 · 26.9certifiée par Ptah24.10testée après la fin du supportWorkflows de schéma par l’interface PostgreSQL, testés sur l’émulateur Spanner.
Vérifiez les capacités de votre serveur.
Affiche la version détectée, son niveau de prise en charge et le profil de capacités avec lequel Ptah planifie.
$ ptah db capabilities --db-url "$DATABASE_URL"
Des vérifications de compatibilité appuyées par la CI.
Des sondes automatiques s’exécutent à chaque push sur master et chaque nuit : 28 branches de version certifiées par Ptah, celles que Ptah teste dans la CI et sur lesquelles il s’engage dans les limites des capacités sondées. SQLite embarqué et l’émulateur Spanner sont déclarés comme tels, pas comme des serveurs réels.
Voir les preuves de prise en charge →ptah en ligne de commande. ptah-ls dans l’éditeur pour les annotations Go. La GitHub Action dans la CI, l’opérateur Kubernetes dans le cluster, des paquets Go sur ptah.run dans votre propre code.
ptah-compat accepte des commandes au format Atlas et exécute derrière elles le comportement de Ptah. Implémentation indépendante en salle blanche, sans affiliation à Ariga. Compatibilité Atlas →