One CLI, driven by the instance's own catalog.
ingactl does not hard-code the API surface. It syncs a command catalog from whatever instance it points at, so a route the server serves appears in the CLI with no rebuild — with its contract, its required permission, and where the project id travels. The same properties that make it convenient for people make it operable by agents.
A real session
An unedited session against a dev instance: sync, artifact emission, search, contract, dry-run — the CLI redacts the credential itself.
Install
The CLI ships in the repository as a single binary crate.
cargo build --release -p ingactl # → target/release/ingactl
cp target/release/ingactl ~/.local/bin/Connect
Profiles live in ~/.config/ingadb/config.toml. A credential is scoped to one workspace; if you belong to several, make one profile per workspace and switch with--profile.
# Token — long-lived, scoped, revocable, audited as you
ingactl auth login --url http://localhost:9876 --token cut_...
# Email + password — a session token that expires
ingactl auth login --url http://localhost:9876 --email [email protected]
# password: INGADB_PASSWORD, else a hidden prompt
# --workspace <name|id> picks the workspace (default: your first)
ingactl auth status # who you are, your workspace, your permissionsIn CI, set INGADB_URL and INGADB_TOKEN — environment variables override any profile.
The loop
Every call follows the same four steps: discover a route, confirm its exact contract, preview the resolved request, then execute.
ingactl api search causal # 1. find candidate routes
ingactl api show data/causal-path # 2. contract + permission (✓/✗ for you)
# + where the project id travels
ingactl api call data/causal-path \
--project 'packaging-line' \
--param node_id=E_BEARING \
--dry-run # 3. preview the resolved request
ingactl api call data/causal-path \
--project 'packaging-line' \
--param node_id=E_BEARING -o json # 4. execute- Body starts from the catalog example;
--set a.b=valuemerges JSON,--set-strforces a string,--file body.jsonreplaces it whole. --param k=vadds query parameters.--project <name|id>resolves the name and places the id where the route actually expects it — you never guess?id=versus?project_id=.- Destructive routes confirm first (
--yesto skip). A missing permission warns but never blocks — the server is the judge. - Output:
-o table|json|raw; table on a TTY, json when piped. A non-zero exit code means the{"error": ...}envelope fired.
Query the causal store
These read routes answer causal questions directly from the store — computed deterministically and stamped with the revision they came from.
| Route | Answers |
|---|---|
data/tree/search | Full-text search over tree events and gates by label or id. |
data/tree/summary | All nodes, structure, top gate, and whether fresh analysis exists — in one call. |
data/causal-path | The causal path from any node up to the top gate. |
data/importance | Fussell-Vesely importance, contribution, and containing cut sets for one event. |
data/cutset | Labels, probabilities, and parent gates for the events of one cut set. |
data/event-evidence | The incidents linked to one tree event. |
data/analysis | The full computed view — with staleness and the exact changes_since deltas. |
pipeline/what-if | The model under overridden inputs — computed without a write. |
projects/overview | A project profile: stats, analysis status, and top contributors. |
For agents
ingactl skill emits an agent operating guide generated from the synced catalog: a step-0 environment self-check, the discover → show → dry-run → execute loop, worked examples built from routes the instance actually serves, and an error-kind → remedy table. Because the guide is generated from data, it never drifts from the deployment.
ingactl skill # the guide, to stdout
ingactl api schema # catalog format version — check before parsing
ingactl completions zsh # shell completion (bash|zsh|fish|powershell|elvish)--format and --out write the same guide where agents already look:
mkdir -p .claude/skills/ingadb
ingactl skill --format claude-skill --out .claude/skills/ingadb/
# → wrote .claude/skills/ingadb/SKILL.md (YAML frontmatter included)
ingactl skill --format agents-md --out .
# → wrote ./AGENTS.md
ingactl padmin skill --format claude-skill --out . # platform tier, same flagsThree formats: plain (default, stdout), claude-skill (a SKILL.md with name/description frontmatter for Claude Code skill directories), and agents-md (an AGENTS.md for Codex-class agents). Passing a directory to --out applies the default file name (SKILL.md, AGENTS.md, or ingactl-skill.md); writes are atomic and the command prints the path it wrote. Regenerate after ingactl sync — the examples and route groups follow the catalog.
The rails that make unattended use safe:
- Permission preflight.
api showmarks whether the caller holds each route's permission; the server remains the final judge. - Dry-run everything.
--dry-runprints the fully resolved request — method, URL, body, redacted auth — without sending it. - Example-body guard. A mutating call with no
--set/--filewould send the catalog's example body verbatim; interactive runs confirm, scripts fail loudly unless--yes. - Destructive routes confirm. Only routes marked destructive in the catalog prompt; reads never do.
- Typed failures. Errors arrive as a JSON envelope and a non-zero exit code — an agent branches on data, not on prose.
Composite workflows
Two commands wrap the common multi-step jobs and watch them to completion.
ingactl analyze --project 'packaging-line' # analysis job → watch
ingactl jobs list --project 'packaging-line'
ingactl jobs watch <job-id>How it stays honest
The catalog ships inside the server binary and is served versioned at/api/schema/catalog; per-route permissions come from the server's own/api/schema/rbac. ingactl sync refreshes both, soapi show reports the instance's rules rather than a copy that drifted. The catalog version changes whenever the surface does, and the CLI re-syncs on a cache miss.
Platform administration — ingactl padmin
The platform tier — workspaces, users, memberships, platform tokens — is a separate command namespace with its own profiles and credentials. The separation is syntactic on purpose: no command string is valid in both tiers, workspace commands refuse platform credentials (and vice versa), and every padmin command states who it is acting as on stderr before doing anything.
# Day 0: a fresh instance to a working workspace login, one command
ingactl padmin init --url https://ingadb.example.com \
--workspace acme --user-email [email protected]
# Connect later: session (7-day expiry) or a scoped cpt_ token
ingactl padmin login --url <url> --email [email protected]
ingactl padmin status
# Provision
ingactl padmin workspace create 'Acme'
ingactl padmin user create [email protected] # temp password, shown once
ingactl padmin membership assign --user-id <id> --workspace-id <id> --role Analyst
ingactl padmin token mint ci --scope read --scope workspaces --expires-days 90- Same discovery loop.
ingactl padmin api search|show|callandingactl padmin skillwork over the platform catalog, with preflight adapted to its rules: sessions pass everything, tokens are checked against each route's scope domain. - Typed-name confirmation. Deleting a workspace asks for its name typed back;
--yesfor automation. - Secrets print once. Temp passwords and minted
cpt_tokens appear a single time and are never written to any cache. - No credential administers credentials. Token management requires a platform-admin session — a token can never mint or revoke tokens.
Everything the CLI does is plain HTTP. The same catalog and RBAC endpoints are there for any client — see the API reference.