CLI and dashboard
Triplex includes two operator tools: a published JSON-first CLI for repeatable commands and a browser dashboard for interactive exploration. Both are pre-1.0 and ship in the single @triplex-build/triplex package. The dashboard includes prebuilt static assets.
The experimental MCP server exposes the CLI's implementation as stdio tools for agent clients. It serves one database per process, advertises input schemas, requires an actor and command ID on writes, and returns the same data and runtime diagnostics as the CLI.
Build from the source checkout
After following Getting started, build the CLI and its workspace dependencies, then define a short shell function for the built binary:
pnpm turbo run build --filter=@triplex-build/triplex
triplex() { node --disable-warning=ExperimentalWarning packages/triplex/dist/bin.js "$@"; }The examples below use the repository command. Install @triplex-build/triplex from npm to use the same triplex binary in an application:
npm install @triplex-build/triplex effect @effect/sql-sqlite-node \
@effect/platform-node @effect/platform-node-shared
triplex --sqlite ./app.db describe
triplex --sqlite ./app.db statusdescribe returns the machine-readable command manifest in data.commands and the schema from live in data.schema. Use describe --ref preview to inspect another release ref. The schema includes release identity (id, label, rootContentId, ref), entity types with their attribute value types (including enum members), requiredness, cardinality, uniqueness, reference targets and sensitivity, every declared attribute including unused ones, constraint keys and kinds, and configuration/derivation kinds and keys.
schema.examples contains one { entityType, input } per type. Save its input as JSON for transaction apply --input <file>. Each example asserts required attributes and sets entityType on every fact, meta.actor, meta.commandId, and the release's meta.configSnapshot and meta.enforce. Replace synthetic placeholders such as "<text>", "<entity-id of Site>", "<actor>", and "<command-id for Worker>" before submitting. Reference targets must already exist and be valid at commit time; use fresh values for unique attributes. Numbers, booleans, dates, instants and enums start with 0, false, "2000-01-01", epoch 0, and the first enum member. Optional-only types get one attribute assertion so they exist as facts.
schema.rules repeats the essential write rules: one command per transaction, deterministic command IDs, pinning and enforcing the same release, one interval per entity, and retracting/re-asserting to end a value. When no release exists at the requested ref, data.schema is null and data.hint explains how to publish one with application-owned config.commit or move an existing release with config set-ref.
Successful CLI commands emit one { "ok": true, "command": "…", "data": … } JSON value to stdout. Decode/runtime failures emit an { "ok": false, "command": "query.run", "error": { "code": "datalog.unbound-variable", "message": "…", "path": "find", "fix": "Bind the variable in a positive pattern or rule application before using it.", "retryable": false } } envelope to stderr and exit nonzero. Branch on error.code, correct error.path using error.fix, and reload facts before retrying a diagnostic with retryable: true. See Error diagnostics. Add --debug to include the original cause string (which may contain input details). Add --pretty before the subcommand when reading output by hand.
Opening the SQLite convenience layer can apply pending Triplex migrations. Hosts that require migration separation should run migrations in their deployment process before granting CLI access.
Write and inspect a fact
transaction apply accepts the public TransactRequest wire shape. Mutation input must include an actor and a unique command ID:
triplex --sqlite ./app.db transaction apply --input - <<'JSON'
{
"operations": [
{
"op": "assert",
"entityId": "worker:maria",
"entityType": "Worker",
"attribute": ":worker/name",
"value": { "type": "string", "value": "Maria" }
}
],
"meta": {
"actor": "user:docs",
"commandId": "docs/create-maria/v1"
}
}
JSON
triplex --sqlite ./app.db entity types
triplex --sqlite ./app.db entity get worker:maria
triplex --sqlite ./app.db entity facts worker:maria
triplex --sqlite ./app.db journal receipt docs/create-maria/v1Do not retry that payload with a new command ID merely because the client lost the response. Look up the original receipt first. A duplicate committed command ID produces CommandAlreadyCommittedError; it does not silently execute or return success again.
Explain and preview
triplex --sqlite ./app.db why fact 01ARZ3NDEKTSV4RRFFQ69G5FAV
triplex --sqlite ./app.db why attribute worker:maria :worker/name
triplex --sqlite ./app.db why entity worker:maria --recorded-position 12 --valid-at 1788998400000
triplex --sqlite ./app.db why entity worker:maria --causation-depth 3
triplex --sqlite ./app.db why candidate --input candidate.json
triplex --sqlite ./app.db transaction preview --input command.json
triplex --sqlite ./app.db transaction apply --input command.jsonwhy runs the core explanation service. Stored subjects accept --valid-at, --recorded-at or --recorded-position, and --causation-depth (0–32). Recorded timestamp and position are mutually exclusive. An exact fact explanation includes its retracting transaction; an entity or attribute explanation returns visible facts at the selected cut.
Candidate input is { "subject": { "kind": "candidate", "definition": …, "candidateId": …, "revision": … }, "basis": { "validAt": …, "recordedPosition": … }, "causationDepth": 0 }. Supply the full immutable definition returned by Derivation.make, plus the candidate's ID and revision, and an explicit valid and recorded basis. Candidate flags can override input options. The MCP why tool uses this same input schema for all four subject kinds.
transaction preview accepts exactly the same input as apply, including required meta.actor and meta.commandId. It returns release, operations (ops and preconditions), changes, violations, and an empty derivations list. Preserve meta.configSnapshot and meta.enforce from describe: both preview and apply use the supplied enforcement, and a release pin alone does not enforce constraints. If the command ID is already committed, preview returns commandAlreadyCommitted with the receipt and empty operations and changes.
Preview does not write facts, receipts or journal positions. It is advisory: it does not check preconditions, run commit validators or reserve state against other writers. Commit checks the then-current state. Assertion IDs and commit metadata are assigned only on commit. Omitted validFrom uses the preview clock and is assigned again on commit; specify it to compare exact intervals. Sealed envelopes are redacted in both explanations and previews.
Run and page a query
Create query.json:
{
"find": ["?worker", "?name"],
"where": [["?worker", ":worker/name", "?name"]],
"orderBy": [{ "variable": "?name", "direction": "asc" }]
}Run or explain it:
triplex --sqlite ./app.db query run --input query.json --limit 50 --debug
triplex --sqlite ./app.db query explain --input query.jsonquery run returns one bounded page. If data.nextCursor is present, pass that exact opaque value back with --cursor; keep the query, page size, database, and temporal basis unchanged. The cursor pins the recorded snapshot so concurrent writes do not reshuffle an in-progress scan.
Use epoch milliseconds to inspect a historical basis:
triplex --sqlite ./app.db entity get worker:maria \
--recorded-at 1788998400000 --valid-at 1788998400000The same --recorded-at and --valid-at flags are available for fact and query reads. See Core concepts before interpreting a one-axis historical query.
Inspect configuration
Configuration authoring remains application-owned. Once an application has committed releases, operators can inspect and move their refs:
triplex --sqlite ./app.db config refs
triplex --sqlite ./app.db config releases
triplex --sqlite ./app.db config release --ref live
triplex --sqlite ./app.db config objects --kind entity-schema
triplex --sqlite ./app.db config object entity-schema Course
triplex --sqlite ./app.db config impact attribute :course/statusconfig releases returns the real snapshot IDs. To promote or roll back, copy the intended returned ID and pass it unchanged:
SNAPSHOT_ID='copy an actual snapshotId returned above'
triplex --sqlite ./app.db config set-ref live "$SNAPSHOT_ID"The assignment is an operator template, not a command to paste unchanged. Inspect the target release before moving the ref. Moving it does not change operational data; see Configuration releases and rollback.
Manage local databases
The CLI can manage isolated SQLite files in ./data (or --data-dir):
triplex db create app --description "Application database"
triplex db list
triplex --database-id app statusdb clear and db delete are destructive and require --yes. PostgreSQL can be selected with TRIPLEX_POSTGRES_URL; prefer the environment over a URL flag so credentials do not enter shell history. A host must resolve database IDs from authenticated server-owned state, not unchecked request input.
Explore in the dashboard
Start the browser-local demo:
pnpm dashboardOpen http://localhost:4173. The demo loads classroom fixtures into a fresh in-memory database. Edits are real Triplex writes but disappear when the app runtime restarts.
To inspect a persistent database, build and run the local HTTP host:
npx triplex dashboard --sqlite /absolute/path/to/triplex.dbThe dashboard includes a spreadsheet-like entity grid with managed/runtime type filters, typed form editing, raw JSON editing, and a semantic before/after diff before commit. It also includes entity timelines, raw Datalog and query plans, the transaction journal, derivation candidates, form previews, and full configuration release/ref/revision history. Its temporal control selects recorded and valid time independently; configuration and journal views remain current so the surrounding context stays visible. You can also try the same in-memory explorer in the website.
The server binds to 127.0.0.1 and has mutation capabilities. It is an operator tool, not an authenticated public server. Do not expose it as your application's authorization boundary.
Runnable demos
examples/demo— linked facts and Datalogexamples/compliance-host— configuration, journal catch-up, derivation reconciliation, overlays, and temporal wakeupsexamples/http-api— generated HTTP routes and OpenAPIexamples/config-explorer— configuration graphs, diffs, evaluation, and proof tamper detectionexamples/tenant-host-cloudflare— experimental multi-tenant reference host
For the exhaustive CLI command contract, see the package README. For common failures, continue to Troubleshooting and FAQ.