User guide
Db2 and Teradata
Use --dialect db2 or --dialect teradata when generating or importing DDL.
Live Db2 imports use a catalogued database alias and the db2 client. Live
Teradata imports use bteq with host, user, password, and database settings.
bluep keeps a database schema as a model in plain text files, generates DDL from it, reads existing
databases back into it, compares it with a live database, writes migrations, checks it against naming
standards and publishes documentation and diagrams. The model lives in a git repository like code.
Reviews
bluep review create records model-level changes between Git revisions. Comments can be anchored to model objects or changes, and bluep review check <revision> can enforce an approved review in CI.
Object anchors use entity:<entity>, attribute:<entity>.<attribute>, relationship:<relationship>,
table:<target>.<schema>.<table> and column:<target>.<schema>.<table>.<column>; change anchors use
change:<change-id>.
Install
Build from source with a stable Rust toolchain:
cargo build --release --workspace
The binary is target/release/bluep. It has no runtime dependencies of its own; psql, sqlite3, mysql
and git are used only by the commands that talk to a database or to a repository.
bluep --version
prints bluep 0.2.0.
A model directory
A model is a directory with a model.bluep file and, optionally, more *.bluep files (for example one per
subject area). Create one:
bluep init shop --name shop
This writes shop/model.bluep with a model shop { ... } declaration. The file format is described in
docs/format.md. A small complete model looks like this:
model mini {
description "Smallest model with exactly one lint finding."
}
naming {
entity {
case snake
}
}
entity OrderLine {
attribute id : bigint not null
attribute quantity : integer not null
key primary pk_order_line (id)
}
area "Core" {
entity OrderLine at 40 40
}
target pg : postgresql {
schema app {
table order_line {
entity OrderLine
column id bigint not null attribute id
column quantity integer not null attribute quantity
primary key pk_order_line (id)
}
}
}
The model has two layers. The logical layer (domain, enum, term, entity, relationship, area)
describes the business data. The physical layer (target) describes one concrete database per target and
maps every table and column back to its entity and attribute.
The everyday loop
Edit the text (or use the browser editor, see below).
bluep fmt <dir>rewrites every file in canonical form and prints the files it changed. Canonical form is deterministic, so two people editing the same model get small, mergeable diffs.bluep check <dir>parses and validates; exit code 1 and one line per issue when something is wrong, for examplerelationship 'order_customer': child attribute 'customer_id' does not exist in entity 'order'. Syntax errors are reported as<file>:<line>:<col>: <message>.bluep lint <dir>applies the naming standards and rules configured in the model. For the model above:model.bluep:11:8: warning [naming] entity 'OrderLine' violates case snakeWarnings do not change the exit code; errors make it 1.
--jsonprints the findings as JSON for CI and--level errorhides warnings. Rule severities are configured in the model:lint { missing-primary-key error unindexed-foreign-key warn }bluep generate ddl <dir> --target pgprints the complete, correctly orderedCREATEscript for the target;--out schema.sqlwrites it to a file.--dialect sqlitegenerates for a dialect the model has no target for, deriving tables from the entities.bluep generate docs <dir> --out sitewrites a static HTML data dictionary (index.html, one page per entity and subject area, a glossary) and SVG diagrams with an automatic layout. Opensite/index.htmlin a browser; nothing is fetched from the network.
Working against a database
Use bluep drift <dir> --target pg --database shop to report differences between a live database and the desired
target. Add --since-deploy to distinguish live changes from model changes made after the last deployment.
bluep diff <dir> --target pg --against-db --dialect postgresql --database shop --host 127.0.0.1 --port 5432 --user app reads the live catalog through psql and prints the structural differences between the model's
target and the database:
+ table public.payment
- column public.order.notes
~ column public.customer.email: type varchar(255) -> varchar(320)
? rename public.order_line.qty -> public.order_line.quantity (same type and nullability)
+ is an object to create, - one to drop, ~ an altered object and ? a rename candidate that is treated
as a drop plus an add unless --accept-renames is given. Exit code 1 means there are differences; --json
prints them as an array of change objects. --against-dir <dir2> compares two model directories instead.
bluep migrate takes the same arguments and prints the ALTER script that brings the database to the model.
Destructive changes (drops, type changes) are only emitted with --allow-destructive; otherwise the script
contains a -- skipped destructive: ... comment for each. On SQLite the tool rebuilds a table when ALTER
cannot express a change.
To start from an existing database or script:
bluep import db --dialect sqlite --database shop.db --out shop
bluep import ddl schema.sql --dialect postgresql --out shop
Add --report reverse-report.json to either form to save a structured fidelity report. Import always prints
notes and unrepresented objects to stderr even when no report path is supplied. See
reverse-fidelity.md for the stable JSON and text formats.
bluep import dbml schema.dbml --out shop
Each writes a model directory with the physical target and a derived logical layer (an entity per table, a
relationship per foreign key).
For Snowflake, BigQuery and Databricks pass connection details with --option (connection=
Importing from SQL Server and Oracle
bluep import db --dialect sqlserver --database shop --host 127.0.0.1 --port 1433 --user sa --password '...' --out ./shop
bluep import db --dialect oracle --database 127.0.0.1:1521/FREEPDB1 --user shop --password '...' --out ./shop
SQL Server runs sqlcmd; Oracle runs sqlplus (or SQLcl sql). A password can also be supplied through
BLUEP_DB_PASSWORD instead of --password. Oracle always requires --user and a password; without
--schema the connecting user's own schema is imported.
Browser editor
bluep serve shop
prints serving shop at http://127.0.0.1:7878/ and runs until interrupted. The page lists areas, entities and
relationships; you can edit descriptions, attributes, keys and relationships, create entities and areas, drag
boxes on a diagram and sync an entity to a target. Every change is validated and written back to the text
files in canonical form, so git diff shows exactly what changed. See docs/web-ui.md.
History
bluep history shop --from HEAD~1 --to HEAD
compares the model at two git revisions and reports model-level changes rather than text lines, for example
+ attribute payment.refund_at or ~ attribute customer.email: type text(255) -> text(320), followed by the
physical changes per target.
Interchange
bluep export json shop writes the model as canonical JSON (see docs/json-format.md); bluep import json
reads it back byte-identically.
Merging
Models merge at the model level: bluep merge <base_dir> <ours_dir> <theirs_dir> --out <dir> merges three
model directories, and bluep merge-file is a git merge driver for single .bluep files, so two branches that
edit different entities or attributes merge cleanly. Set it up once per repository:
git config merge.bluep.name "bluep model merge"
git config merge.bluep.driver "bluep merge-file %O %A %B"
and add *.bluep merge=bluep to .gitattributes. Conflicts are printed as CONFLICT <path>: ... lines and
resolved in favour of your side until you edit them. See docs/merge.md for the rules.
notation idef1x (in the model block or an area block) draws diagrams in IDEF1X instead of crow's foot,
and stereotype fact, stereotype dimension or stereotype bridge in an entity block marks its role in a
star schema: diagrams colour it, the data dictionary groups by it and the lint rule dimensional-star checks it.
Reports
Mappings and lineage
Mappings declare how local target tables or columns are sourced. Use bluep lineage <dir> column:target.schema.table.column --upstream to trace a local selector. See Mappings and lineage for the format, validation and external-source rules.
bluep report renders text from the model through a small template language. Seven templates are
built in; list them with:
bluep report --list
classification.html html matrix of classification levels with the entities and attributes at each level
columns.csv csv one row per physical column: target, schema, table, type, nullability, default, mapping, classifications
dictionary.md md data dictionary with one section per entity: attributes, primary and unique keys
entities.csv csv one row per entity: name, description, attribute count, stereotype, subject areas
glossary.md md glossary terms with definitions, aliases and the entities and attributes using them
lint.md md lint findings grouped by rule
relationships.md md relationships with columns, cardinalities and referential actions
Run a built-in template against a model directory:
bluep report devdata/models/commerce --template entities.csv
name,description,attributes,stereotype,areas
address,A postal address of a customer.,7,,Sales;
category,A product category; categories form a tree.,3,,Catalog;
...
Custom templates work the same way with --template-file <path> and --var k=v for extra variables.
See docs/reports.md for the full template language, functions and error messages.
Deployments
Deploy a reviewed model change with bluep deploy. The command imports the live database, creates a rollback script before changing it, reports conservative lock/rewrite risks, and records the successful deployment. Use --dry-run to inspect the exact SQL. A rollback restores schema definitions but cannot restore deleted data.
Exit codes
| code | meaning |
|---|---|
| 0 | success |
| 1 | differences found, lint errors, or model issues (check) |
| 2 | usage error |
| 3 | any other error: I/O, syntax, external command |
Bulk editing
The browser grid and deterministic CSV review workflow are described in Bulk editing. Use a plan before applying a complete CSV snapshot so inserts, updates, deletions, and rejected rows can be reviewed.
Hierarchies
Use many_to_many when two entities are connected through an associative table. Generation derives the table,
its combined primary key, foreign keys, supporting index, and any payload attributes. Reverse engineering remains
unchanged unless --infer-relationships or --many-to-many is supplied. Review evidence before accepting inferred
pull items with --accept-inferred-relationships; --yes alone never accepts them.
Use a hierarchy when one entity specialises another. Identity keeps shared-key subtype tables, rolldown writes leaf tables, and rollup writes one root table with nullable subtype fields.
Pulling changes from a database
Use bluep pull to compare a target with a database, DDL script, or another model.
Review the checkbox plan before applying it. Saved plans can be reviewed as JSON.
The command makes minimal text edits and can propagate physical changes to logical
entities and attributes.
Related
- Automation clients use the documented JSON-RPC API.
- Browser edits write files immediately; see editor productivity. Teams editing one Git-backed model should read the team server guide.
- For target-specific DDL profiles, ordered SQL hooks, and local statement templates, see Custom generation.