Get started

From empty laptop to a scoped, leak-proof query in five minutes

This is the one path we recommend you take first. Five commands, every decision already made for you, copy-paste top to bottom. By the end you'll have booted Verity, ingested a real directory, run a permission-filtered query, watched a second agent get zero results on the same data, and wired a live agent over MCP. No menus, no branching. When you want the depth behind a step, follow the link at the end of it.

prerequisites A recent Rust toolchain (cargo), Docker running, and jq (for the step-4 checks). That's it. dev pulls a Postgres image, builds the server, and writes its config to ~/.verity/config.toml; you don't install a database or edit a config by hand. After step 1, put the CLI on your PATH so the verity-cli commands below run. dev prints the exact line (cargo install --path crates/verity-cli).

1 · Boot the whole local plane

One command stands up everything Verity needs and leaves you with a working, org-wide scope handle already saved. It is idempotent; re-run it anytime.

sh
cargo run --release -p verity-cli -- dev

what you'll see

console
verity dev — local memory plane, five minutes

  ✓ postgres       docker compose up -d (paradedb pg17)
  ✓ server         http://127.0.0.1:7717  (/healthz ok)
  ✓ tenant         "dev" → c1e6…
  ✓ scope handle   saved to ~/.verity/config.toml (org-wide, principals [1])
  ✓ config         ~/.verity/config.toml

  Verity is up.
    server         http://127.0.0.1:7717
    tenant (dev)   c1e6…
    scope handle   saved to ~/.verity/config.toml (org-wide, principals [1])

You just booted a Postgres, the Rust server, a dev tenant, and an org-wide scope handle (principals [1]), the one credential Verity issues. Every command below reuses the handle from that config; you never paste it.

What each piece is, and how to run against an existing server instead: Ingestion → verity-cli.

2 · Ingest something real

Point add at a directory. It recurses over text-like files (.txt/.md/.json/.csv/.html), chunks and embeds each, and uploads them under a scope whose principals are exactly your --visibility tokens. --visibility has no default: Verity never guesses who may see a memory, so you always name it. Here, 1 is the org-wide token from step 1.

sh
verity-cli add ./docs --visibility 1

what you'll see

console
  ingesting ./docs (recursive over .txt/.md/.json/.csv/.html, ≤200 files)

  1/9  ingestion.html   → 34 chunk(s)
  2/9  permissions.html → 28 chunk(s)
  …
   214 chunk(s) indexed from 9 file(s) — try  verity-cli query "<something in it>"

You just filled the memory with real, permission-tagged content, and saw the exact chunk count go in. Every one of those chunks carries admin-assigned provenance: the visibility was an explicit decision (--visibility 1), not a mirrored source ACL.

Files, URLs, stdin, entity tags, and narrower visibility: Ingestion → add. Connecting a live SaaS source instead of files: Connecting your systems.

3 · Query it back: scoped, with provenance

Ask a natural-language question. query uses the handle dev saved, runs scoped hybrid recall, and prints each hit with its score, kind, and ACL-provenance tag.

sh
verity-cli query "what does an entity tag do?"

what you'll see

console
  1.  0.782  [content] [admin-assigned]  Memory is tagged with the entities it concerns (account:acme…
        entities account:acme  ·  provenance cli:add
  2.  0.741  [content] [admin-assigned]  a chunk touching two entities is retrievable only in a scope that…
        provenance cli:add

  every hit passed the scope's visibility pre-filter in-index — nothing above is post-filtered.

You just got a scoped hit back with its provenance visible on the line. admin-assigned tells you exactly how much to trust the filter. There is no post-hoc filtering step to forget: the scope was compiled into the index query itself.

Custom k, raw JSON, and the four provenance tags: Ingestion → query and ACL provenance.

4 · The “aha”: watch the wall hold

Now the thing Verity actually exists to do. Tag a memory to one account, then mint a second scope handle (same org, different entity scope) and prove it can't see it. This is the eve-bot guarantee in miniature, done prescriptively.

First, write a memory tagged to account:acme under your org handle, then mint an Acme scope and a Globex scope from the same principals; entity scope is the only difference:

bash
# read the values dev saved (server, tenant, and the org-wide handle)
export V=http://127.0.0.1:7717
export TENANT=$(grep tenant_id ~/.verity/config.toml | cut -d'"' -f2)
export H=$(grep scope_handle ~/.verity/config.toml | cut -d'"' -f2)

# a memory only an Acme-scoped agent should ever see
curl -s -X POST "$V/v1/episodes" -H 'content-type: application/json' -d "{
  \"scope_handle\":\"$H\",
  \"observation\":\"Acme's renewal quote is \$84,000 net-30.\",
  \"entities\":[\"account:acme\"]}"

# two handles, SAME principals [1], different entity scope — that is the whole trick
mint() { curl -s -X POST "$V/v1/scopes" -H 'content-type: application/json' \
  -d "{\"tenant_id\":\"$TENANT\",\"principals\":[1],\"entity_scope\":$1,\"actor_azp\":\"$2\"}" | jq -r .scope_handle; }
ACME=$(mint   '["account:acme"]'   agent:sales-bot)
GLOBEX=$(mint '["account:globex"]' agent:eve-bot)

Now run the same recall under each handle:

bash
# the Acme-scoped agent finds it
curl -s -X POST "$V/v1/recall" -H 'content-type: application/json' \
  -d "{\"scope_handle\":\"$ACME\",\"text\":\"acme renewal quote\",\"k\":5}" | jq length
# → 1

# the Globex-scoped agent — same org token, different entity scope — sees nothing
curl -s -X POST "$V/v1/recall" -H 'content-type: application/json' \
  -d "{\"scope_handle\":\"$GLOBEX\",\"text\":\"acme renewal quote\",\"k\":5}" | jq length
# → 0

You just proved the core property yourself: the second agent holds a legitimate org credential (it is not an outsider), yet it got 0 results, not because a model chose to withhold, but because the entity-scope filter excluded every Acme chunk inside the index query. A prompt-injected “ignore your scope” can't widen it, because the filter was never in the prompt to override.

note The grep lines above pull $TENANT and $H straight out of the config dev wrote: the same values printed in the step 1 summary. The full multi-agent version (adding restricted pricing, a support purpose-clamp, and org-wide analytics) is the Permissions walkthrough.

5 · Connect a real agent over MCP

Finally, hand this memory to an agent. mcp install prints the exact claude mcp add line for your config. Identity (tenant, principals, actor) is baked into the environment, never a tool argument the agent can talk its way around.

sh
verity-cli mcp install

what you'll see

console
  Wire Claude Code to Verity:

      claude mcp add verity \
        -e VERITY_URL=http://127.0.0.1:7717 \
        -e VERITY_TENANT_ID=c1e6… \
        -e VERITY_PRINCIPALS=1 \
        -e VERITY_ACTOR_SUB=user:me \
        -e VERITY_ACTOR_AZP=agent:claude-code \
        -- /path/to/verity/target/release/verity-mcp

  tools: memory_open_scope, memory_recall, memory_get, memory_remember,
  memory_record_action, memory_activity, memory_brief, the ingest trio,
  memory_forget, memory_whoami.

  re-run with --run to execute this command now.

You just got the exact command to give any MCP-capable agent (Claude Code, Cursor, …) permission-aware memory. Run that claude mcp add line (or verity-cli mcp install --run to have it executed for you) and the agent opens a scope with memory_open_scope, then recalls, remembers, and records actions through the same in-index enforcement you just tested.

All 14 MCP tools and when to reach for each: API reference → MCP tools.

Where to next

You have a working, scoped, agent-connected memory. Pick the tier that matches what you're actually doing. Each goes as deep as you need and no deeper.

in a hurry? The Cookbook is the same commands as copy-paste recipes: “scope an agent to one account”, “ingest a HubSpot account”, “connect an agent over MCP”. Each answers one goal with minimal prose.