Home · Quick start · Editor · charter.yaml · Sources · Agent · Guides · Evals · Audit · Policies · CLI · MCP · Workspace · Desktop · About · FAQ
Every command takes an optional workspace directory (default: the current one).
Run datacharter <command> --help for the exact flags.
Setup
init [directory] [--demo] [--force] [--template NAME] [--list-templates]
Scaffold a workspace: charter.yaml, queries/, .env.example, .gitignore.
--demo includes a generated demo dataset; --force overwrites an existing
charter.yaml. --template NAME starts from a gallery starter with governance
already wired — postgres (PII + a row filter), warehouse (Snowflake with
aggregate-only + k-anonymity policies), files (local CSV/Parquet), or secure
(the whole stack: firewall, canaries, policies, quarantine). --list-templates
prints them. Fill in the ${ENV} credentials and run datacharter serve.
demo [directory]
A zero-config, narrated walkthrough of the governance — no server, no account.
Scaffolds a throwaway demo workspace (or uses one you point it at) and shows what
an AI agent actually sees through the governed tools: PII masked (with the raw
value shown alongside so the difference is visible), writes refused, the
contract in control. Ends by pointing you at serve / mcp to keep exploring.
Try it in one command: uvx datacharter demo.
connect [directory] [--client NAME] [--serve-url URL]
Print the ready-to-paste MCP-server config for popular clients — the file it goes
in, and a one-click install deeplink where the client supports one — so you
don’t hand-edit JSON. Clients: claude-desktop, claude-code, cursor, vscode
(uses the servers key, not mcpServers), cline, windsurf, lmstudio (or
all, the default). Uses an absolute binary path so a GUI-launched client’s minimal
PATH still finds it. With --serve-url, emits HTTP config for a running
datacharter serve instead of the local stdio server.
import dbt <manifest.json> [-o path] [--force]
Generate a charter.yaml from a dbt project’s target/manifest.json. The
warehouse type comes from the dbt adapter; models + sources become tables grouped
by database/schema; columns flagged PII in dbt (meta: {pii: true} or a
pii/sensitive/phi tag, BigQuery policy tags) become masked columns; and
model/source descriptions become per-table agent context. Connection host and
credentials aren’t in the manifest, so they’re written as ${ENV} placeholders to
fill in. Turns a whole dbt project into a governed contract in one command.
import odcs <contract.yaml|.json> [-o path] [--force]
Generate a charter.yaml from an Open Data Contract Standard
(ODCS) DataContract. The source type + connection come from servers; each
schema object becomes a table; a property classified PII/sensitive/…
(or tagged pii) becomes a masked column; descriptions become agent context.
DataCharter reads the open standard, so an existing data contract adopts governance
in one step.
export odcs [directory] [-o path]
Publish your charter.yaml as an ODCS DataContract — each source a server,
each table a schema object, each declared-PII column classified PII. Prints to
stdout or writes a file. Round-trips with import odcs, so DataCharter plugs into
the data-contract ecosystem in both directions.
serve [directory]
Start the local web app (API + UI) on http://127.0.0.1:8321.
| Flag | Effect |
|---|---|
--host |
Bind address (default 127.0.0.1, localhost only). |
--port |
Port (default 8321). |
--local |
Use a local Ollama model for the agent. |
--model |
Model name to use with --local. |
--no-spill |
Fail queries instead of spilling to disk (regulated environments). |
--offline |
No-egress mode: disable the LLM agent and write a no-egress attestation. |
In the UI, flip Agent view on any result to preview it with PII columns masked — exactly what the agent and the MCP server see.
Resource limits (any engine command). DATACHARTER_DUCKDB_MEMORY_LIMIT
(e.g. 2GB) caps DuckDB’s memory so a heavy query spills or errors within budget
instead of OOM-killing a container; unset, it derives ~80% of a detected container
memory limit, else leaves DuckDB’s own default. DATACHARTER_DUCKDB_THREADS pins
the thread count.
secrets set|list|rm <name>
Manage ${NAME} secrets in the OS keyring. set prompts without echo (or pass
--value); list shows names only; rm removes one.
Explore and govern
mcp [directory] [--serve-url URL]
Run a Model Context Protocol server over stdio, exposing the four
governed query tools to any MCP client — read-only, PII-masked. With
--serve-url, the server proxies tool calls to an already-running
datacharter serve instead of opening its own engine (this is how the in-app
Claude Code integration bridges to the governed toolbox); without it, it opens
the workspace directly.
diff <left> <right> [directory] [--key cols]
Diff two relations across sources: rows only in each side plus the common count.
With --key, rows are matched by key and changed rows are counted separately.
explain <sql> [directory]
Show a query’s plan and ~N rows estimates without running it — a pre-flight
cost check.
query <sql> [directory] [--format table|csv|json]
Run a read-only SQL query across your sources and print the result — federated
joins, the same read-only guard as the app. --format selects table (default),
CSV, or JSON output.
Relation names: sources are queried as source.table (e.g.
store.customers); tables listed under a source’s tables: also get flat
source__table views. Run datacharter query "SHOW ALL TABLES" to list every
queryable relation.
sample <relation> [directory] [--rows N]
Print a PII-masked CSV sample of a relation (contract PII columns come back as
•••), safe to paste into a ticket. Default 10 rows.
scan [directory] [--write]
Suggest PII columns for charter.yaml by column name and by sampled values.
--write merges the suggestions into charter.yaml (round-trip; credential
references are left untouched).
drift [directory] [--update]
Report schema drift against the baseline saved in .datacharter/schema.json
(written automatically on first run): changed columns, and declared tables or
PII columns that no longer exist in the live sources (a missing PII column is a
masking gap). Exits non-zero on drift — usable as a CI gate. --update accepts
the current schema as the new baseline.
metric <name> [directory] [--by cols] [--grain g]
Run a contract-defined metric — a named aggregation
declared under metrics: — resolved to one governed query. --by overrides the
grouping dimensions; --grain day|week|month|quarter|year groups by a
date_trunc of the metric’s time_column.
access diff [directory] [--against git:REF | --old PATH] [--new PATH] [--json | --md] [--fail-on widened]
An Access Plan — terraform plan for AI data access. Diffs the effective
agent-visible surface between two charter versions and classifies every change
as WIDENED (agent can now see more), NARROWED (more protection), or
neutral: a table granted, a PII column unmasked, min group size lowered, a
no joins to dropped, a row filter removed — all called out in plain English
before it takes effect. It reads the declared governance only (no source
connection), so it runs offline and needs no credentials in CI.
By default the old side is git:HEAD (the committed charter); pass --against
git:main~1 for any ref, or --old PATH to compare two files directly. --json
and --md emit machine- and PR-comment-friendly reports. --fail-on widened
exits 2 if any change widens the surface — drop it into the GitHub Action to
block a PR that quietly widens what an agent can see:
# .github/workflows/access.yml — block PRs that widen agent data access
- run: pipx run datacharter access diff --fail-on widened
Because the whole governance surface is a file in git, agent data access can be
code-reviewed like any other change — something a runtime-state governance server
structurally can’t offer. For wiring test, drift, and this check into Dagster,
Airflow, or CI as run-blocking gates, see
Governance gates for data pipelines.
firewall <sql> [directory] [--status]
The Data Firewall — the governed choke point every agent routes through. Turn
it on in charter.yaml with firewall: block (deny) or firewall: log (record
only), and the Reasoning Governor runs on every query the agent makes
through this workspace: a query the governor denies is refused at the tool
boundary with its reason, so the agent gets actionable feedback instead of raw
rows. This command evaluates a query through the configured mode — BLOCKED,
FLAGGED (log mode), or PASSED — and --status shows the current mode. The same
enforcement runs live in serve and mcp. Default off; opt in per workspace.
govern <sql> [directory] [--purpose TEXT] [--json]
The Reasoning Governor. Turn a query’s intent, declared --purpose, and the
PII it touches into one graduated decision: allow · add-noise · mask-more ·
step-up · deny — each with a reason and a concrete next step. Static RBAC can say
“may this principal read this column?”; it can’t say “yes, but this query — a
whole-row serialization for an export purpose — is denied.” The governor sits on
the query shape (via the intent risk scorer) and picks the
least-restrictive action that still holds the line: an aggregate over PII →
add-noise (run dp); to_json(row) → mask-more; naming PII → step-up; an
export purpose over PII or a honeytoken reference → deny. Exit code encodes the
decision (0 allow, 1 deny, 2 in between) for scripting; --json for the full
record.
risk <sql> [directory] [--json] [--fail-on low|medium|high]
Query-intent risk scoring. Grade how risky a query’s shape is before it runs
— so a governed surface can graduate its response by intent, which static
table/column RBAC can’t express. A transparent, capped heuristic reads the SQL text
and the contract’s PII list (no data): SELECT *, naming PII columns, whole-row
serialization (to_json/string_agg — a masking-evasion shape), unbounded reads,
set-operation differencing, multi-join re-identification, and honeytoken
references each carry a named weight. Prints a 0–100 score and band
(low/medium/high) with the reasons, or --json. --fail-on medium|high exits 2
at that band — a step-up/deny gate.
subject-access <value> [directory] [--column email] [-o file]
Subject-access receipt (DSAR). Produce a signed record of exactly what an AI
agent can see about one person — GDPR Art. 15 / EU AI Act transparency, answered
from the data plane. It looks the subject up by key column (--column, default
email) across every governed relation that carries it, and seals the result with
the workspace provenance key. What the receipt shows is what the
agent sees: PII columns come back masked (•••). Verify it offline with
datacharter provenance verify. -o writes the receipt to a file.
synth <relation> [directory] [--rows N] [--format csv|json] [-o file] [--seed S]
Governed synthetic data. Generate realistic rows that match a relation’s schema
but hold no real data — PII columns come out as clearly-synthetic stand-ins
(user1234@example.com, +1-555-01xx), never a real value. Because the generator
reads the same charter.yaml that guards production, your dev/test/CI fixtures
inherit the same PII policy. --rows sets the count (default 100), --seed makes
output reproducible, -o writes a file, --format selects CSV (default) or JSON.
dp <sql> [directory] [--epsilon E] [--bound B] [--budget C] [--status] [--reset]
Differential-privacy query mode. Add calibrated Laplace noise to an aggregate
answer and spend from a per-workspace ε budget — so an agent can’t chain
“safe” aggregates to difference-out one individual. Supports COUNT (sensitivity
1) and SUM (pass --bound B, the per-row value bound, as its sensitivity); each
result row is assumed to cover distinct individuals (bounded contribution).
--epsilon sets the per-query privacy loss (default 1.0); --budget the workspace
cap (default 5.0, sequential composition — sticky: set once, honored on later
runs). When a query would exceed the budget it is refused. --status shows
spent/remaining; --reset clears it.
Because DP done wrong is false security, the mechanism refuses anything it can’t
noise correctly: SUM without --bound (would silently under-noise), a query
mixing COUNT and SUM (they need different sensitivities — run them separately),
AVG/MIN/MAX (unbounded sensitivity — derive them from noised COUNT and SUM),
and any row-level query (checked against comment/string-stripped SQL, so an
aggregate keyword hidden in a comment can’t sneak raw rows through). Numeric
GROUP BY keys pass through un-noised, and if a result still carries a PII column
the query is refused rather than emitted.
asof <ref> [directory] [--query SQL | --relation R] [--rows N] [--json]
Governance time-travel. Reconstruct the agent-visible surface as it existed at
a git ref — “what would the agent have seen under last March’s rules?”. With no
--query/--relation it prints the surface as of that charter version (tables,
masked columns, row filters, surface_hash). With --query or --relation it
runs the query against current data but masks it by that ref’s PII rules, so
you can replay a question under an older policy. Versions the governance, not
just the data — the same masked column can come back raw at an earlier ref and
••• today, proving exactly when a rule took effect.
monitor [directory] [--json] [--no-gauntlet]
Continuous compliance. Run every governance gate in one pass — test,
drift, access diff --fail-on widened (vs git:HEAD), and the redteam
gauntlet — and report a single status. Each gate is the real command’s code, so a
green monitor is evidence about what actually runs. Exits non-zero if any gate
reports a violation, so a scheduler (cron, a CI schedule) turns point-in-time
evidence into a repeatable, alertable signal. --json emits the per-gate report
for alerting; --no-gauntlet runs the fast gates only.
test [directory] [--select name]
Run the data assertions declared under tests: and
exit non-zero if any fail — for CI. --select runs one test by name.
lineage [directory] [--relation R] [--json]
Show cross-source lineage aggregated from your local query history: which
relations get read together, and which output columns derive from which inputs.
--relation filters to one relation; --json emits the graph for tooling.
History is recorded as you run queries in the app.
openlineage [directory] [--url URL] [--namespace NS] [--job NAME] [-o file]
Emit the governed catalog as an OpenLineage COMPLETE RunEvent — one event whose
inputs are every governed relation, each with a schema facet (columns + types) and
a custom governance facet recording which columns are PII, which are masked on the
agent surface, and the read-only guarantee. --url posts to any OpenLineage
receiver (Marquez, DataHub, OpenMetadata) at <url>/api/v1/lineage; -o writes
the event JSON to a file; with neither it prints the event to stdout. Reads live
schemas, so sources must be reachable. Built on the stdlib — no OpenLineage client
dependency. Verified end-to-end against Marquez.
provenance keygen|pubkey|seal|verify
Signed, independently-verifiable answer-provenance receipts — the AI answer you
can take to a regulator or an auditor. keygen creates the workspace’s Ed25519
signing key; pubkey prints the public key to publish. seal <sql> runs the query
through the governed surface and emits a signed receipt sealing the query, the
relations read, the masked columns, the row count, a result hash, the governance
surface_hash, and the audit-chain head. verify <receipt> checks it offline —
recompute the hash, verify the signature, and (--pubkey) pin the key; `–flight