technical breakdown

The same ideas, with their real names

Everything the rest of this site says in plain words, expanded with the terminology, the mechanisms, and the tradeoffs. The source of truth beneath even this page is the repo.

the machinery

What ships, precisely

ingest boundary

Every WebFetch, WebSearch, and MCP result is wrapped in an envelope marking it as untrusted data, never instructions — raising the cost of the indirect prompt-injection path (it is advisory context, not enforcement — and content fetched through plain curl in Bash is not covered). It also flags zero-width characters and bidirectional overrides. Strictly read-only: it never blocks a call, never mutates a result.

security guard

A PreToolUse hook that blocks irreversible catastrophe and emits the documented permissionDecision: "ask" before exfiltration-shaped commands. Distinguishes data from code in a heredoc — a Dockerfile mentioning rm -rf is text; the same text piped into bash is code. Fails closed on a broken policy for every mutating tool (Bash, Edit, Write — reads stay open); your policy at ~/.config/keel/policy.json can add rules or exempt shipped ones via bash.allow and paths.allow. Private key material is zero-access by default; the block message documents the demotion to ask-mode (paths.confirmRead), and editing the policy file itself prompts — the guard guards its own demotion.

commit hygiene

Blocks unwanted attribution trailers in commit messages (KEEL_BLOCKED_TRAILERS; off switch KEEL_TRAILERS_OFF=1). Fails open on any internal error, and only inspects actual git commit invocations — git log --grep still works.

activity log

Append-only monthly JSONL under the config dir, written from documented hook payloads (UserPromptSubmit, Stop) — never by parsing transcripts. Device-scoped filenames so multiple machines can never conflict. No model calls: hooks capture, skills interpret. KEEL_ACTIVITY_OFF=1 disables; KEEL_ACTIVITY_DIR relocates.

memory wiring

Third-party memory engines bolt on as bridge plugins declaring MCP servers via .mcp.json — a documented integration point. keel ships no vector store, no LLM call, no daemon. keel doctor verifies the wiring down to whether the engine's project mapping actually points at the configured store.

the cli

keel status · log · doctor · setup · settings · update · migrate · link — read-mostly, flags-first, idempotent writes only to KEEL_* keys it owns — plus, with your explicit consent, the permissions rules keel settings --yes appends — with a backup of the pre-run state before the first write. It lives on the Bash tool's PATH rather than your shell's; in practice Claude runs it for you. keel update verifies by commit SHA rather than trusting version strings.

the constitution

Three rules that can say no

01

Own your config, buy your capabilities

Sync, guardrails, and setup are configuration — personal, unshippable, correctly yours, a few hundred lines. Memory, recall, and reflection are capabilities — general, valuable to strangers, already built better by people who do it full time. Six separate times during design, “should I build this?” turned out to be “someone already did.” What survived as worth owning is small and unglamorous.

02

Documented surfaces only

Claude Code ships ~20 releases in a week. The only way a layer under it survives is to depend exclusively on surfaces Anthropic documents and treats as contract. The system keel replaced parsed session transcripts out of an undocumented format — it worked, and it was one format change from silent breakage. If it isn't in the docs, keel doesn't read, write, or parse it. The three exceptions are repairs, listed openly with blast radius and exit conditions: an exception stated is a debt; an exception denied is a lie.

03

Admission by felt need

Prose admits freely — teardown is rm. A test is admitted when it catches a failure class that has already occurred in this repo. Runtime — hooks, subcommands, anything a session executes — requires a need felt at least once, by a real user, on a real machine. “Another harness found it useful” is an argument from someone else's life. Anything that fails silent on removal must ship with the check that would notice.

The full text lives in the repo as two short ADRs and a glossary.

memory

Engines are swappable. Files are forever.

There's an arms race in agentic memory, and Claude Code hasn't picked a winner. keel's bet: own the interface and the substrate; let engines be swappable. The substrate is markdown you control. The interface is Claude Code's own documented hooks. An engine is a thing that turns experience into facts — replaceable, and never load-bearing for remembering.

Recall is local file reads. Retain is remote LLM work. Never invert it — invert it and you get a system that stops remembering when your homelab reboots. That's not a memory system, it's a dependency.

Knowledge classifies by who can falsify it: a fact grep can falsify is derivable — looked up live, never stored, so it cannot go stale. A fact only you can falsify is decided — the notes store holds it, as a pointer and a why. A fact a test suite can falsify is logic — it belongs in a package, because memory describing code is strictly dominated by code both projects import.

The full architecture →

deliberately not included

The absences are load-bearing

Every guard has an off-switch — KEEL_GUARD_OFF, KEEL_ACTIVITY_OFF, KEEL_TRAILERS_OFF, KEEL_INGEST_OFF — and every write has a backup and an undo. A guard that can break your workflow is a guard you'll eventually disable — so the ingest boundary never blocks, the log never calls a model, and the whole layer tears out in two commands.