Skip to content
hirefrankPublic

About

Confidant is relationship memory you own.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

438 Commits

Folders and files

Repository files navigation

Confidant

OpenSSF Scorecard CII Best Practices

Project page: hirefrank.com/confidant.

Confidant is relationship memory you own. It remembers the people in your life — the friend you meant to call, a sibling's birthday, the mentor you last saw in March — in one private place your AI agents can read and update. Each person is a Markdown file with notes, dates, and a log of every touch; confidant check turns that into who is overdue and which dates are coming up. Spec 0.4 keeps people first, adds built-in places and things to the personal schema, and adds corrections and record retirement.

Records are plain-text files in a git repo (the vault); the Rust CLI confidant reads, checks, and writes them. There is no server, and a ledger of dated facts (ledger/2026/10.cfd) keeps the record append-only and auditable. Mistakes are corrected by appending a visible compensating line, never by rewriting history.

Start with Install or the demo quickstart.

Starting over, or adopting Confidant in stages? See Starting fresh, and adopting in stages — personal relationships first (the active personal pack is built in: personal@0.4 on spec 0.4, personal@0.2 on older vaults), extra packs later, in the same vault.

Install

The preferred path: let your agent install it. Paste this into Muse, Grok Bot, Claude Code, Cursor, Codex, or any agent that can run commands; it installs the CLI, links the embedded skills, and asks where your vault is. This README is the canonical home for the prompt; docs/agent-setup.md covers headless hosts, environment variables, and updates.

Set up Confidant (relationship memory you own — a CLI plus embedded agent skills) on this machine:

1. Install the CLI:
   `curl --proto '=https' --tlsv1.2 -LsSf https://github.com/hirefrank/confidant/releases/latest/download/confidant-cli-installer.sh | sh`.
   Make sure `~/.cargo/bin` is on PATH
   (`export PATH="$HOME/.cargo/bin:$PATH"`) or the next step fails with
   `confidant: command not found`. Verify with `confidant --help`.
   If I want the TUI too, install it from the same GitHub Release as the
   CLI so their versions match. Alternatively, install both with
   `cargo install confidant-cli confidant-tui` (requires Rust 1.88+).
2. Install the skills (they ship inside the binary — no repo checkout):
   `confidant setup --agent auto`. This materializes the skills to a
   versioned share dir and links each skill into the detected agent's
   skills directory. (No vault configured yet? `confidant doctor` exits
   with `E_VAULT_NOT_FOUND`, but `confidant status` works without a vault
   and reports skill links honestly; `confidant agents` or
   `confidant which --skills-dir` also confirm the install.) (`confidant doctor` checks GitHub for the latest
   release unless `CONFIDANT_OFFLINE=1` is exported — add that to my
   shell rc too, this session and permanently, only if I want that
   request disabled.)
3. Check git before anything vault-shaped — a vault is a git repo, and
   every vault step below (`init`, clone, push/pull sync) needs it. Do
   this now, not at the first failure, when it is hardest to tell
   git-missing from git-unauthenticated from something else:
   - Run `git --version`. If git is missing, install it, then re-check
     before continuing: macOS `xcode-select --install` or
     `brew install git`; Linux via the distro package manager
     (`apt install git`, `dnf install git`, or `pacman -S git`); Windows
     runs under WSL2, so install git inside WSL2 with that distro's
     package manager (for example `apt install git` on Ubuntu/Debian,
     `dnf install git` on Fedora, `pacman -S git` on Arch) — a
     standalone git-scm/winget install is native Windows git, which
     Confidant does not support. If the version is below 2.34.0,
     upgrade (strongly recommended): macOS `brew upgrade git`, Linux
     `apt upgrade git`, `dnf upgrade git`, or `pacman -Syu git` (inside
     WSL2 for Windows). Below the floor is not a hard block on a fresh
     install: `confidant doctor` reports it as a warning, since older git
     is still usable for the commands Confidant runs. Upgrade when you
     can.
   - `init` commits, but it does not need a global git identity: it
     sets a neutral repo-local identity (`Confidant Operator
     <operator@confidant.local>`) in `<vault>/.git/config`, so the
     operator's global identity never lands in the vault's history.
     Do not set a global identity for this. If I want my own name in
     the vault's history, ask me for it (do not invent it) and set it
     per vault, inside the vault, with
     `git config --local user.name "Your Name"` and
     `git config --local user.email "you@example.com"` — that override
     survives re-runs of `init`.
   - Do not probe remote credentials here: at this point you do not
     yet know whether there is a remote at all, let alone its URL (step
     6 asks that). Version and identity above are the whole of step 3;
     the credential probe lives in step 6, at the point the URL is
     known, immediately before the first clone or first push that uses
     it. If this vault will be local-only, git is still required for
     `init` but remote auth is not — do not conflate the two: a missing
     credential is not a blocker for a local-only vault.
4. Ask me where my vault is. Export it for this session only:
   `export CONFIDANT_VAULT="<path>"`, and pass `--vault "<path>"`
   explicitly on the commands you run. Do **not** rely on
   `source ~/.bashrc` in a non-interactive or agent shell: an interactive
   guard such as `[[ $- != *i* ]] && return` skips the export, and a
   stale `CONFIDANT_VAULT` silently makes `check` and `doctor` inspect a
   different vault. Give me the line to add to my shell rc for interactive
   use as separate advice. If I have no vault yet, ask whether to create
   one at a path I choose or stop — do not invent a path.
5. Tell me plainly, before any vault step, that Confidant keeps **plaintext**
   records in a private git repo: every record body, history included, is
   readable by anyone who can read that repo, so it belongs on a machine with
   full-disk encryption (LUKS or FileVault) and a **private** remote. There is
   no mode to choose and no key to set up; `no-ai` on a record keeps its
   content out of what agents read, but it does not make the bytes secret.
   For a fresh vault, run `confidant init --create --no-input` against an
   absent or empty directory (add `--remote <url>` if I already have a
   remote; it sets origin and does not push): it scaffolds the directory,
   starts the git repo on branch `main` (and reports the branch), seeds
   `packs = []` (packs are optional), writes `.gitignore` and
   `confidant.toml`, creates the collection directories, and commits the
   skeleton. For an existing directory that is not yet initialized, make sure
   the working tree is clean (`git status`) and run `confidant init`.
6. Optional remote: **before** `init`, ask whether I already have a git
   remote URL for backup and putting this vault on another device. If I do,
   pass `--remote <url>` on `confidant init --create` (it sets `origin` and
   does not push). Before the first clone or first push that uses that
   URL — not before `init --remote` itself, which only sets `origin`
   and never contacts the host — probe it, non-interactively so a
   missing credential fails instead of hanging on a prompt in an agent
   shell: `GIT_TERMINAL_PROMPT=0 git ls-remote <url>` must succeed.
   That success is the gate; git installed and working does **not**
   mean a private clone works. If it fails, tell the two failures
   apart: an auth failure means fix credentials first and do not debug
   the vault step; a repository-not-found failure means the repo does
   not exist on the host yet — create it there first, then re-probe
   (credentials that are not broken do not need fixing).
   `gh auth status` is only a diagnostic, never a second gate: a
   private clone that already succeeds via an SSH key or a PAT in the
   credential helper passes the gate even when `gh` is not logged in,
   and on a non-GitHub host a `gh` login is irrelevant. `gh auth status`
   matters only when `ls-remote` also fails and the chosen route is
   GitHub-over-HTTPS via `gh`. Recommended default on a GitHub host:
   `gh auth login` (the device flow suits headless/agent machines; an
   interactive login registers `gh` as git's credential helper, so plain
   clone/pull/push work on the HTTPS route). After a non-interactive
   `gh auth login --with-token`, run `gh auth setup-git` separately —
   that login alone does not register the helper. If the operator chose
   SSH during login, the helper claim does not apply at all.
   Alternatives: an SSH key, where SSH to the host actually works, or a
   PAT stored via the git credential helper or the OS credential store —
   never pasted into a repo config, never committed, and never an account
   password (GitHub rejects passwords for git operations). A token
   that only exists somewhere else (for example one an agent can only
   use through an API) does not count: if it cannot be handed to git,
   the clone path is not working yet.
   If I decide after init, show me `git remote add origin
   <url>` (and probe before the first push, as above). In either case,
   set the upstream once with `git push -u origin
   main`, then use `confidant sync --push` for later pushes — never push
   without my explicit confirmation. On a **new device for an existing
   vault**, do not run `init`: confirm `git ls-remote <url>` succeeds first
   (the probe above), then clone the repo and start using it.
7. Verify: run `confidant check --vault "<path>" --json --no-input` and
   `confidant doctor --vault "<path>"`; report the error/warning counts,
   the branch `init` created, and any `git-remote` or `local-commits`
   warning.
8. Confirm the system-of-record step: `confidant setup` writes the
   Confidant rule into each detected agent's always-on instruction file
   (for example `~/.claude/CLAUDE.md`) and prints the exact text and paths.
   Re-print it with `confidant routing`. The rule covers read routing
   (`resolve`/`context`, `find`, QMD), writes through the CLI (dry-run →
   confirm → apply), the `no-ai` boundary, and failure honesty. Setup ends
   with a completion brief; `confidant status` reprints the readiness
   rollup and that brief. Also read the Privacy rules (trusted devices with
   full-disk encryption; a private remote; `.confidant/` is never
   committed; `no-ai` records stay out of agent output — see the README's
   Privacy section and `docs/privacy.md` in the GitHub repo).

Report back: CLI version (`confidant --version`), which skills you linked,
the vault branch, the `check` result, and any `confidant doctor` warnings.

No agent? The CLI has a one-liner that needs no Rust toolchain: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/hirefrank/confidant/releases/latest/download/confidant-cli-installer.sh | sh, then confidant setup --agent auto. With a Rust toolchain 1.88+ you can install both binaries instead: cargo install confidant-cli confidant-tui && confidant setup --agent auto (if rustc --version says "no default is configured", run rustup default stable). Prebuilt binaries are on GitHub Releases for macOS and Linux (x86_64), Windows under WSL2. To update, run confidant update (see Updating). Contributors: CONTRIBUTING.md.

Updating

confidant update compares this binary with the latest GitHub Release and then follows the install method it detects:

  • One-liner / release installer: it updates itself in place — the receipt the installer writes says where it lives, and the installer is checked against the release's published checksums before it runs — then prints the new version. Run confidant setup afterwards to re-link skills from the new binary. The one-liner, if you prefer to re-run it yourself, is curl --proto '=https' --tlsv1.2 -LsSf https://github.com/hirefrank/confidant/releases/latest/download/confidant-cli-installer.sh | sh.
  • Homebrew: it prints brew upgrade confidant-cli; Homebrew owns that copy and Confidant never replaces it.
  • cargo install: it offers to run cargo install confidant-cli in an interactive terminal (default No), or prints that command.
  • Unknown: it prints the generic reinstall guidance.

If the receipt names a different copy than the running binary (two installs on PATH), confidant update refuses with E_CONFLICT instead of replacing the wrong one. CONFIDANT_OFFLINE=1 skips the release check, and --json reports install_method, update_command, and self_update without changing the binary.

Remote (optional)

confidant init --create starts a local git repo on main. Pass --remote <url> to set origin (it still does not push). Local-only is a valid setup; a remote is what makes backup and putting the vault on another device possible. On a second device, clone the repo and use it — there is no enrollment step. If the vault already exists without a remote:

git remote add origin <url>
git push -u origin main

The URL is whatever your git host gives you — Confidant does not require GitHub. Writes commit locally and nothing pushes as a side effect; the only command that talks to a remote is confidant sync:

confidant sync             # fetch, report ahead/behind
confidant sync --push      # push after confirmation (--yes with --no-input)
confidant sync --pull      # fast-forward only, after confirmation
confidant sync --dry-run   # report the last-fetched state; no network

confidant doctor reports a git-remote check: local-only is fine, a private https:// or ssh:// remote is fine, and a cleartext or unverifiable transport is a warning. A second local-commits check reports ahead/behind from the last fetch. The manual rhythm, refusal codes, and the opt-in systemd/launchd timer are in docs/sync.md.

The remote must be private. A remoted vault exposes every record body, history included, to anyone with access to the repository, and Confidant cannot see a host's privacy setting. If that is not the tradeoff you want, remove the remote; anything already pushed stays readable in its history. Details: docs/agent-setup.md.

First five minutes: the people in your life

The demo vault is fake data: a friend, a sibling, a partner, a mentor, and one organization (Example Studio) that Dana Example works at. Copy it out so writes commit to a scratch repo:

git clone --depth 1 https://github.com/hirefrank/confidant
cp -r confidant/examples/demo-vault /tmp/confidant-demo
cd /tmp/confidant-demo && git init -q -b main && git add -A
git commit -qm "demo vault" && export CONFIDANT_VAULT="$PWD"

In an agent or other non-interactive session, pass --vault "$PWD" explicitly instead of relying on an exported variable — and never source ~/.bashrc to set it: an interactive guard skips the export, and a stale CONFIDANT_VAULT can make check inspect the wrong vault.

confidant record list prints the cleared roster and defaults to every identity kind — people, orgs, deals, plus any pack-declared type with roster = true. On this 0.2 demo that means people, orgs, and deals, so Example Studio appears without knowing to pass --kind org. Use --kind person for the people-only view, or --kind <type> for a declared pack type. Records marked no-ai: true are absent. confidant check lists what needs attention:

warning  W_CADENCE_OVERDUE  people/p-01M3TC5H00MPJG000000000101/profile.md  p-01M3TC5H00MPJG000000000101
    person p-01M3TC5H00MPJG000000000101 has no touchpoint on or after 2026-09-17 (last was 2026-09-10, cadence 21d)
    fix: Log a touchpoint with `confidant log touch p-01M3TC5H00MPJG000000000101 <kind>` or widen cadence
warning  W_DATE_UPCOMING  people/p-01M3TC5H00MPJG000000000102/profile.md  p-01M3TC5H00MPJG000000000102
    upcoming date for p-01M3TC5H00MPJG000000000102 on 2026-10-12 (in 4d)
    fix: Reach out to p-01M3TC5H00MPJG000000000102 or set `[checks] personal.date_upcoming = "off"`
0 error(s), 2 warning(s)

Those warnings are the feature, not breakage: Dana's 21-day cadence says "reach out"; Eli's birthday is four days after the pinned as_of (2026-10-08). Warnings never block a write; --fail-on warning makes them fail on purpose.

Add someone and log a touch

A person is a record with an ID in front matter; personal fields are optional:

cat > /tmp/robin.md <<'PERSON'
---
id: p-01M3TC5H00MPJG000000000105
type: person
name: Robin Example
relationship: friend
birthday: --11-04
cadence: 30d
---
Fake neighbour; we climb on Thursdays.
PERSON
confidant record add --file /tmp/robin.md
confidant check

Robin has no touchpoint, so check adds a W_CADENCE_OVERDUE with the "no touchpoint logged" wording. Log the contact and it clears:

confidant log touch "Robin Example" text --date 2026-10-08
confidant check

log touch resolves the display name and writes one committed ledger line. The demo is fake data. For your own vault, confidant init --create --no-input on an empty directory creates one on branch main. Records are plaintext in the repo, so keep it private and on a full-disk-encrypted machine (Privacy).

Link a person to an org

Edges are records under edges/e-…/edge.md with a, b, kind, and optional since / until. Core ships person↔person kinds (married, introduced, colleague) and directed person→org kinds (works_at, founder_of, investor_in, advisor_to). The demo's Dana Example already has a works_at edge to Example Studio:

confidant context o-01M3TC5H00MPJG000000000901

context accepts a person or org ID, or the ID of a pack-declared entity type. The bundle's entity is the target; person remains a deprecated alias for person targets. edges lists the relationships visible from that entity, with direction (out when the target is a, in when it is b, none for a symmetric kind) and the other endpoint. An org bundle also includes notes and interactions linked by org:; a pack-declared entity bundle includes notes and interactions linked by entity:.

An edge is agent-visible only when both endpoints are cleared; a no-ai: true endpoint removes the whole edge, with no count or hint. In spec 0.3 an edge can itself carry no-ai: true / false; a hidden edge is omitted from every agent read while both endpoints stay visible, and the operator path badges it. Orgs still do not take no-ai. Pack kinds can declare their own endpoint types with from / to in the pack manifest.

Places, things, and pack-declared types

Spec 0.4 is current. In a 0.4 vault the default personal@0.4 pack declares place and thing; a 0.3 vault can get the same shape from an external v3 pack. A v3 pack manifest declares a record type with [[record_types]]: its name, two-letter ID prefix, collection directory, main file, entity/person-like flags, no_ai policy, roster visibility, and required/optional fields. See spec/pack-manifest.md for the full table and the two-letter prefix rules. The built-in collections are places/pl-<ULID>/place.md and things/th-<ULID>/thing.md, alongside people/, orgs/, deals/, interactions/, notes/, edges/, and ledger/YYYY/MM.cfd.

The repo's checked-in testdata/vaults/personal-0.4 is the built-in default example, and testdata/vaults/place-0.3 is the external-pack version. In a 0.4 vault, add a place from a file and attach a note:

cat > /tmp/place.md <<'PLACE'
---
id: pl-01M3TC5H00MPJG000000000201
type: place
name: Example Studio
kind: work
address: 1 Example Way
---
Fake place.
PLACE

confidant record add --file /tmp/place.md
confidant record list --kind place
confidant context pl-01M3TC5H00MPJG000000000201

cat > /tmp/place-note.md <<'NOTE'
The back room was quiet; we stayed for two hours.
NOTE
confidant note add --entity pl-01M3TC5H00MPJG000000000201 --body-file /tmp/place-note.md
confidant context pl-01M3TC5H00MPJG000000000201   # bundle notes now include it

--kind <type> takes the declared type name (for example place), not a collection name. --kind all includes declared types with roster = true; --kind person stays core-person-only. context by name still resolves core people only; use the opaque ID for a pack type. record add, record edit, and note add --entity write the same check-gated commit as every other record.

The demo vault stays on spec 0.2 on purpose: it demonstrates the person/org model without requiring a pack. The 0.4 fixture demonstrates the built-in places/things model, and the 0.3 fixture demonstrates the external-pack model; neither vault is migrated by this release. Run their checked-in golden cases with:

confidant check --json --no-input --vault testdata/vaults/personal-0.4
confidant check --json --no-input --vault testdata/vaults/place-0.3

How your agents use it

confidant setup --agent auto links the five embedded skills — confidant (standing interaction rules), qmd-search (optional semantic search), pack-author (build a local pack), reconcile (a maintenance pass that verifies stale facts with you), and report-a-problem (file an issue without vault content) — and ends with an optional-layers checklist, so it offers QMD rather than installing it. Agents use the CLI — or confidant mcp, a thin stdio wrapper over the same commands — and follow one workflow: propose, confirm, apply. Setup also writes the system-of-record declaration into each detected agent's always-on instructions, so a session that never loads a skill still knows where relationship facts live and that they go through the CLI. Every command takes --json and --no-input; usage errors exit 2; branch on code, never message text.

confidant mcp exposes nine fixed tools (check, doctor, find, context, log_touch, import_ledger, note_add, schema, pack_skill); no tool can widen that list. A write runs check and commits once. --packs scopes reads (on mcp it is the instance ceiling) as a read-time policy filter, not access control (docs/privacy.md, docs/threat-model-agents.md).

no-ai keeps a record out of agent output; --for-operator on find, context, and record list is the operator-only escape hatch, and MCP never passes it. --person (on log touch, note add) takes a p-<ULID> or an exact display name. context takes a person or org ID, or an exact person display name; org names are not resolved, so use find for the o- ID. find stays fuzzy and no-ai: true people are ID-only.

Area Commands
Write record add, record edit, record resolve, record retire, record unretire, note add, note edit, log touch, retract, correct, import
Read find, context, resolve, alias resolve, record list, record show, note show, allowlist, schema, help
Check check (read-only; stable codes in spec/findings.md)
Packs pack init, pack add, pack list, pack verify, pack update, pack remove
Setup init, setup, update, doctor, status
Search find, plus the optional qmd-search skill
Sync sync (read-only report; --pull / --push explicit)
Agents mcp, agents, which, routing, tui

Corrections. confidant retract <id> --line <ref> --reason <token> appends a dated retract line that supersedes a prior ledger line; the original stays in the ledger and git history. confidant correct appends a replacement plus the retraction in one commit. Both are operator commands (never MCP tools) and require spec 0.4. confidant record retire <id> marks a record retired: true: it stays on disk but leaves agent reads, while record list --for-operator still shows it. Use find --json to copy a ledger hit's ref; bulk retract --verb/--match-date/--keep clears a duplicate batch in one commit. The operator context path still lists superseded lines, badged; the agent path stays effective-only. Model and limits: docs/privacy.md.

Contact values (emails, phones, handles, former names) are front-matter fields on the person, declared by the personal pack. alias resolve <kind> <value> matches them directly — trimmed, case-insensitive, whole-string — and canonicalizes through merges. On spec 0.4 it also reads the typed alias_history entries and prefers a current value over a historical one. It is operator-only and honours no-ai like other reads. Two different people sharing a value is E_AMBIGUOUS_PERSON, not a silent pick.

The TUI

confidant-tui is a terminal UI for the roster, a person's context, vault health, quick capture, and review of queued work. It consumes only the ADR-7 CLI JSON contract and keeps record content on the alternate screen only. Install both from the same release — cargo install confidant-cli confidant-tui — then run confidant tui --vault examples/demo-vault. confidant tui and confidant doctor warn when the CLI and TUI versions differ; upgrade both together.

Operator mode (!) is off by default; e edits through the operator-only path, t / n capture a touch or note, and 2 opens the review queue. Tabs and phase history: docs/tui.md.

Packs

A schema pack declares verbs, record types, fields, edge kinds, balance metrics, and check rules; its manifest (spec/pack-manifest.md) is the machine-readable contract.

The personal pack is built in and always on. Spec 0.4 selects personal@0.4; it supplies the relationships schema this README describes — birthdays (full dates or year-less --MM-DD), recurring dates, relationship, relationship_context, role_history, typed alias_history, profile_provenance, human_note, cadence with cadence_basis, cadence_note, and snooze_until, contact fields, relationship edges (person↔person; core adds the person→org kinds), the touch verb with optional duration: and mode: (1:1 or group), and two warning-level rules (W_CADENCE_OVERDUE, W_DATE_UPCOMING) that never block a write — plus built-in place and thing types with kind, address, tags, links, and the visited / lives_at / owns / located_at edges. cadence: never is the explicit "none, by design" state, distinct from an unset cadence. Contract: packs/personal/0.4.md. Vaults on spec 0.1–0.3 keep the frozen personal@0.2, which is person-only (packs/personal/0.2.md). A vault needs no other pack to be useful, and a personal@… entry in packs is a no-op; the vault spec selects the version. context exposes a place/thing's declared fields in entity.fields; --exclude-private strips address, tags, and links, and an uncleared place/thing returns no agent bundle at all.

Extra domains are optional packs in the same vault. init --create seeds packs = [], and check is clean without one.

Installing a pack. Where a pack comes from is an implementation detail: pack add takes a https:// URL or a local directory (--from-dir), and both behave the same. Without --yes it fetches the URL (or reads the local directory), validates, prints every claim and the full skill text, and writes nothing. With --yes it vendors the pack under packs/, pins its hash in confidant.lock, lists it in packs, runs check, and commits. Review that report as code first: a pack's SKILL.md becomes agent instructions.

A vault that lists a pack must vendor it. A missing copy or a drifted hash makes check fail with E_PACK_UNKNOWN and blocks writes; pack verify re-checks the vendored copy against the pin.

Writing a pack. An agent can build one for a local need with no git host and no core change — pack init scaffolds and validates it, and the embedded pack-author skill walks through the same steps:

confidant pack init ~/confidant-packs/weekly-checkin
confidant pack add --from-dir ~/confidant-packs/weekly-checkin            # review only
confidant pack add --from-dir ~/confidant-packs/weekly-checkin --yes      # install
confidant pack verify

Keep the source outside the vault: the copy under <vault>/packs/ is generated, never the editing surface. pack init scaffolds the manifest, skill, and versioned contract; fill in fields, verbs, finding codes, [checks] keys, and rules in the manifest. A rule names a core-implemented kind — the pack carries no code. After editing, bump [pack].version, rename the contract file, and review the change before applying it:

confidant pack update <name> --from-dir ~/confidant-packs/weekly-checkin
confidant pack update <name> --from-dir ~/confidant-packs/weekly-checkin --yes

The first command prints the diff and stops.

Migrating a pack with existing data: pack add --yes refuses when the vault already sets the pack's [checks] keys or vendors its copy but does not list it. Add "<name>@<version>" to packs in confidant.toml first (or remove the dependent [checks] keys), then re-run. If the vault already has the pack's data/checks, pack add --from-dir <dir> --allow-unresolved --yes installs over it: only the pre-check is skipped, the post-check and check still gate, and unrelated errors roll back. The flag needs --yes; without it, pack add stays the read-only review.

Removing a pack a vault still uses: pack remove <name> --yes refuses, because deleting the files and pin would turn its data into errors. pack remove <name> --allow-unresolved --yes instead drops the pack from packs and keeps its vendored files and lock pin. Its ledger lines, metrics, edge kinds, record types, and [checks] keys then warn W_PACK_ORPHAN rather than failing, so check exits 0 and later writes keep working while you migrate. The flag needs --yes and never prompts (--no-input/--json included). Migrate or delete the orphaned data, then run pack remove <name> --yes to delete the files and pin; re-adding the pack at the same version re-lists it and clears the warnings.

A local pack is hash-pinned but unsigned: pack verify detects drift, and nobody vets it for you. Review its SKILL.md as code, because it becomes agent instructions. It cannot widen the §12 allowlist or read no-ai content; the CLI enforces that, not the pack text.

Scoping. Packs share a vault, and --packs scopes a read to the packs you name. That is a read-time policy filter, not an access boundary — the vault is plaintext on disk (docs/privacy.md).

Search

confidant find scans the vault directly, exact and fail-closed: it returns content only from the cleared allowlist — parsed cleanly, no no-ai: true flag, every referenced ID cleared. An uncleared record is omitted, not redacted. Numbers: docs/search-benchmark.md.

QMD search is an optional on-device semantic layer (BM25 + vectors + reranking), shipped as an embedded skill, not a CLI dependency. The skill stages a copy of the cleared set under <vault>/.confidant/ (gitignored, never committed) and verifies every hit through confidant allowlist, so a query-path bug degrades to missing results; without QMD it falls back to confidant find and says so. BM25 is the default mode unless QMD preflight recommends otherwise; the LLM rerank stage is the expensive part, so a CPU-only host should use --no-rerank hybrid (or --mode bm25), and a stall at Reranking N chunks is reported with that fallback instead of being passed off as a successful search. confidant doctor warns when the index is behind vault HEAD or the last build skipped embeddings. Design: crates/confidant-cli/skills/qmd-search/docs/design.md.

Privacy

Records are plaintext on disk. A vault is Markdown and .cfd files in a git repo; record add, note add, and log touch --note write those bytes directly. There is no second secrecy layer and no key material, so a clone is the whole vault.

That makes the perimeter easy to state, and it is the whole privacy model:

  • The machine. Run Confidant on devices you control, with full-disk encryption on (LUKS, FileVault). Disk encryption is the operating-system setting that covers a stolen laptop; Confidant does not manage it.
  • The repo. Sync to a private remote. Anyone with access to the repo can read every record and every commit, history included, and anything pushed there stays in history (deleting a file is not erasing it).
  • no-ai. A record or a person can be marked no-ai, which keeps its content out of find, context, the allowlist, MCP, and the search corpus that agents read. no-ai: notes is the narrower person form: the person, birthday, and cadence stay visible while notes and interactions default to hidden. A person's context bundle still serves derived ledger metadata; a no-ai place or thing returns no agent bundle at all. The flag itself is visible in the record's front matter.

no-ai is an exposure control, not access control: the operator owns the files, and an agent with a shell on the same machine can read them. A stolen unlocked device, or malware on a trusted device, defeats every guarantee. See docs/privacy.md, docs/threat-model-agents.md, and docs/architecture.md §3–§4.

Content an agent reads is sent to a hosted model provider; this is explicit and accepted, and Confidant makes no claim about what the provider stores.

confidant doctor checks GitHub for the latest release and reports skills link health. The release check is one unauthenticated GET to api.github.com/repos/hirefrank/confidant/releases/latest; no vault data is included, and only the tag_name is read — the response body is discarded. confidant status makes the same release check as part of its readiness rollup. Set CONFIDANT_OFFLINE=1 to skip the request; the check then reports staleness check disabled.

Where to go deeper

Where we're going

The roadmap is the milestone board. No issue is open right now; the themes the recent releases close out:

  • Personal schema — personal@0.4 is the built-in pack for 0.4: places and things, cadence basis and mode notes, rich touches (duration:, mode:), and the person-story fields (relationship_context, role_history, alias_history, profile_provenance, human_note).
  • Corrections — retract, correct, and record retire/unretire fix a mistake without rewriting the ledger; superseded lines and retired records stay on disk and out of agent reads.
  • Agent surface — five embedded skills (confidant, qmd-search, pack-author, reconcile, report-a-problem), the fixed nine-tool confidant mcp server, and the optional QMD search layer with a CPU-only fallback.
  • Plaintext first-run — 0.8 shipped plaintext-only, and status plus the project page make the perimeter and the install state clear from the first session.

New work lands as an issue and a milestone; the board is where it shows up.

Security status

Confidant keeps records in plaintext by design (see Privacy), so there is no cipher for an outside auditor to review. What the project does maintain: the OSTIF / LeastAuthority security best-practices guide as its baseline, a private vulnerability reporting channel (SECURITY.md), CI running cargo deny, cargo audit and CodeQL on every change, -D warnings builds, no unsafe in confidant-core, fuzz targets for the parser, and attested release builds.

Two operational rules do the work that a secrecy layer would otherwise do: keep the vault on machines you control with full-disk encryption on, and push only to a private remote. check, the allowlist and no-ai are working controls over what agents read, not substitutes for those two rules.

License

Copyright 2026 Frank Harris. Licensed under the Apache License, Version 2.0 (license = "Apache-2.0" in Cargo.toml). See LICENSE for the full text. Some files are adapted from cr (MIT); see THIRD_PARTY_LICENSES and docs/borrowing.md.

About

Confidant is relationship memory you own.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages