local-agent¶
A local-first org-navigator — a personal tool for capturing and querying your working relationships across a large company, with no data leaving the machine.
This is a distinct application. It does not depend on a cloud model to function. Glovebox is a future dependency for the optional L2 (aggregate-strategy) layer — it is not part of v0.
Architecture context:
glovebox/docs/architecture/local-first-egress.md(the L1/L2 split, the egress-firewall model, and why the local agent is the single interface and trust boundary).
Status: v0 built (slices 1–3)¶
The store, the query layer, and the terse CLI are done and tested. The next step is slice 4 — actually using it and finding the friction. No model, no cloud.
→ CLI cheatsheet for the full command reference.
Quickstart¶
# from this directory (uv manages the venv automatically)
uv run local-agent person "Me" --me # set the anchor first
uv run local-agent person "Alice Chen" --team SPEAR --role "Staff Eng"
uv run local-agent link Me Alice
uv run local-agent note "Needs feature X" --for Alice --owe --kind requirement
uv run local-agent who SPEAR # who I know on a team (1st/2nd degree)
uv run local-agent brief Alice # dossier before a 1:1
uv run local-agent owe # what I'm behind on
The store is a single SQLite file: --db PATH, else $LOCAL_AGENT_DB, else
~/.local/share/local-agent/store.db (auto-created). Back it up by copying that
file. Tip: use the ./la wrapper (or alias la='…/tool-chest/local-agent/la')
so a stale VIRTUAL_ENV from an old checkout does not warn; or pip install -e .
for a bare local-agent.
Chat capture (local model)¶
For lower-friction capture, talk to a local model instead of typing CLI
flags. The harness is Claude Code pointed at an Ollama
model via Ollama's Anthropic-compatible API on localhost:11434; a
skill teaches it to translate
plain sentences into la commands. Everything stays on the machine.
Set your model once in ../tool-chest.toml:
Then pull and (optionally) build a larger-context variant:
# one-time: install Ollama, pull a tool-use-capable model sized to your RAM
ollama pull mistral:7b # or whatever is set in tool-chest.toml
# (Claude Code needs >=64k context — optionally build a bigger-context variant)
ollama create local-agent-chat -f Modelfile # see Modelfile
# start chatting (interactive)
./chat # uses model from tool-chest.toml
LOCAL_AGENT_MODEL=local-agent-chat ./chat # one-off override
# then just talk:
# "I'm Nathan, set me as me"
# "met Priya, staff eng on SPEAR; I owe her the API doc Friday"
# "who do I know on SPEAR?"
Model sizing rule of thumb: ~4B for 8 GB RAM, ~9B for 16 GB, ~27B for 24 GB+.
Privacy contract¶
The local chat path is designed so no content leaves the machine:
- Local model only.
./chatand the audit refuse:cloudmodel tags (those route off-box). - All API traffic to localhost.
scripts/privacy_env.shreads model and URL from../tool-chest.toml, then pins every Claude Code model tier (main + housekeeping) to Ollama onlocalhost:11434, emptiesANTHROPIC_API_KEY, and disables telemetry — so background "housekeeping" calls cannot reach Anthropic. - Locked tool surface.
.claude/settings.jsonallows onlylashell commands and deniesWebFetch/WebSearch/curl/wget. - Caveat. Claude Code itself is closed-source; the boundary is "correctly-configured Claude Code on a local model", verified by the audit below.
Prove it: on-demand egress audit¶
Trust is testable, not assumed. Run the audit any time:
make audit # Layer A: config attestation (no sudo)
sudo -E make audit # + Layer B (deny-egress functional proof) + Layer C (packet evidence)
- Layer A — asserts the model is local and the env/permissions lock egress; hashes the controlling files.
- Layer B — blocks all non-loopback egress with the macOS
pffirewall, runs a real headless capture, and asserts the row still landed. If capture works with the network cut off, the content path needs only localhost. - Layer C — sniffs a normal run and asserts zero non-loopback packets.
Each run writes security-attestation.md (SPB v1
format) and appends to audit/egress-audit-log.md
(append-only, SPB-12). Layers that can't run (no sudo/ollama) report SKIPPED,
never PASS. See the workspace
Security & Privacy Baseline
(SPB-11/12/13).
What this is (and is deliberately not)¶
The honest framing, set by a pre-mortem: reliable-but-narrow beats impressive-but-fragile. The most valuable part of the whole system, for the actual goal (navigating a big org's opaque structure), is this minimal local store + a few good queries + frictionless capture. The clever parts (cloud strategy, an LLM router, adaptive orchestration) are later layers that must earn their place.
Local-model reliability is an experiment to run, not a promise to make. So v0 contains no model at all.
v0 scope (LLM-free, local-only)¶
- A canonical local store of people, relationships, and notes/requirements.
- Low-friction capture — terse one-line commands. This is the make-or-break feature; if logging "Bob needs X" mid-day feels like a chore, the system dies.
- Three deterministic queries, mapped to real needs:
- who do I know on
<team> - 1st / 2nd-degree connections to
<team or person> - open follow-ups / requirements (by person or team)
Explicitly NOT in v0¶
No LLM, no cloud, no Glovebox, no router, no calendar/email import, no vector search. Each is a later layer gated on v0 proving itself in daily use.
Success criterion (the bar)¶
Within the first week on the job, this is used to answer a real "who do I know on X?" about a real colleague, and capturing a note feels light. If that holds, later layers are justified. If not, the real constraint was capture, not intelligence — and we learned it in days.
Data architecture¶
Three data shapes, one embedded engine — not three databases.
| Shape | Where it lives | Notes |
|---|---|---|
| Tabular — people, attributes, notes | SQLite (the canonical file; system of record) | stdlib sqlite3, one file, no server |
| Graph — relationship edges | SQLite edge table → networkx in-memory for traversal | personal scale (hundreds–low-thousands of nodes); BFS/centrality are instant |
| Vector — embeddings for retrieval | reserved seam, not built in v0 | switches on when a local model arrives and needs RAG (likely sqlite-vec) |
The agent talks to a repository interface (get_people, neighbors(person,
depth), add_note, and a reserved semantic_search) — never to SQLite directly
— so the storage engine stays swappable and the vector index can slot in later
without touching agent logic.
Multi-tenancy / scale-up is out of scope unless the L2 experiments prove good enough results to warrant it.
Schema (canonical store)¶
people— id, name, aliases, team, org, role, contact, tags,is_me, timestampsedges— source, target, kind (reports_to|collaborates_with|knows), weight, provenance, timestampnotes— person_id (nullable for team-level), text, kind (requirement|followup|observation), status (open|done), due_date, provenance, timestampembeddings(reserved, unpopulated in v0) — entity_type, entity_id, vector, model
provenance is where future capture sources (manual, calendar, work email,
AnyType) record where a fact came from.
Build sequence¶
| Slice | What | Status |
|---|---|---|
| 1 | SQLite store + schema + repository interface | ✅ done |
| 2 | Query layer: neighbors / connections_on_team / intro_path / dossier / commitments (networkx over the store) |
✅ done |
| 3 | Low-friction capture + query CLI (local-agent) |
✅ done |
| 4 | Use it for real, iterate on friction | ← here (real use, week 1) |
Later (each gated on the prior earning its place): local model + context engineering + adaptive orchestration; the vector seam; the Glovebox-mediated L2 escalation; the optional per-turn router.
Engineering rhythm: cautious TDD, one slice at a time, verified gates, and the forward/backward (backpass) pass on each slice.