Architecture
Firekeep is two halves. A server you run once, and a client kit installed on each developer machine that wires your agent runtimes into it.
Server services
| Service | Port | Purpose |
|---|---|---|
| Cortex | 8100 API, 8080 MCP | Long-term memory — semantic and graph recall |
| Bridge | 8070 | Session context that survives compression and crashes |
| Sentinel | 8060 | Environment observer — containers, git, file activity |
| Relay | 8050 | Agent-to-agent coordination, leases, tasks, messages |
| Dashboard | 8040 | Web UI over all of the above |
Backed by three datastores in the same stack—Neo4j (knowledge graph), Qdrant (vector embeddings) and Redis (cache, queues, streams)— plus Ollama for local model inference.
Client kit
Installs to ~/.firekeep and renders configuration for whichever agent runtimes you
use — Claude Code, Codex, Kiro and OpenCode are configured together. It provides the MCP
gateway your agent talks to, plus lifecycle automation where each runtime's hook surface
allows it. The day-to-day section below names the current differences explicitly.
Symdex code intelligence runs client-side. It has to be local to the code it indexes, so it ships as a stdio MCP server on your machine rather than in the server stack. The Decision Board is client-side too: it opens an ephemeral loopback browser surface and returns submitted answers to the waiting agent.
Capability map
Firekeep joins durable memory, working context, code intelligence and agent coordination. The boundaries below are part of the product: it records what agents explicitly send and automates only the lifecycle events each client exposes.
| Capability | Customer outcome | Current boundary |
|---|---|---|
| Cortex memory | Store decisions, incidents and proven fixes, then retrieve project-scoped evidence through semantic and relationship recall within a context budget. | Memory is explicitly written and is not a truth-verification system; retrieved sources remain inspectable. |
| Bridge sessions | Keep an explicit shadow of plans, decisions, progress and file notes so work can survive compression, interruption or a crash. | Direct session tools work everywhere; automatic capture and completion depend on the client's lifecycle hooks. |
| Relay | Coordinate agent identities with tasks, channels, direct messages, presence and fenced file or task leases. | Presence is best-effort and leases still require clients to cooperate with the protocol. |
| Corpus & Knowledge | Make an ingested document searchable immediately, then draft detected procedures into skills for human review. | Drafting requires a configured generation model. Without one, the source remains searchable but no procedure drafts are produced. |
| Decision Board | Collect answers to several agent-supplied questions in one browser board, alongside retrieved evidence and optional visual comparisons. | Evidence and suggestions are best-effort; headless clients receive a text fallback. Answers return to the agent but do not execute actions or automatically become memory. |
| Replay | Inspect Firekeep-mediated lifecycle, tool, memory and policy events, open the exact or nearest saved context and narrow likely contributors to a failure. | Replay is an inspectable Firekeep trace, not a screen, keystroke or complete client recorder; contributor ranking is not causal proof. |
| Sentinel | Follow commits, selected file activity, custom operational events and optional container state. | Watch paths and repositories must be selected; Docker observation is deliberately off until an operator enables access. |
| Symdex | Index local symbols and relationships so agents can request focused caller, impact and architecture context instead of opening every file. | The index stays on the workstation; code context requested by an agent enters that agent's model context. |
| Vault, policy & dashboard | Keep credentials out of memory, mediate supported edits and inspect memories, sessions, skills, events, coordination and operations in one UI. | Vault values are encrypted at rest and returned in plaintext to authorized readers. Policy enforcement varies by client, as documented below. |
| FirekeepScope | Run shared, server-backed clarification screens that can block for an answer or stay open while an agent continues. | Headless or MCP-only agents call the scope_* tools; answers are human actions through the companion or dashboard. |
| Evals & patterns | Review trace-derived session health, recall use and prediction calibration, and surface recurring tool sequences, hotspots or failure modes. | Metrics are operational proxies, not answer-accuracy scores. Pattern observations are not proof; automated validation and experiments are off by default. |
Symdex local code intelligence
Symdex is installed and registered automatically with Firekeep. It parses your working tree locally with tree-sitter and gives supported agents focused codebase context through MCP.
The built-in parsers cover Python, JavaScript, TypeScript, Go, Rust, Java, PHP, C, C#, Ruby, Kotlin and Swift. Depending on the language's syntax, the index captures functions, classes, methods, types, constants, routes and their relationships. Thirty tools are available by default for symbol lookup, search, callers, architecture, impact and related context; eight heavier analytics tools are optional.
Symdex can serve symbol-level context when that is sufficient and include broader file context when an edit needs it. Firekeep's code-and-memory tools can also cross-reference the current code with decisions, fixes and change history recalled from earlier sessions.
The index stays on the workstation and is not uploaded to the Firekeep server. Tool output requested by an agent becomes part of that agent's working context, so it may be sent to whichever model provider you configured for that client.
Targeted symbol benchmark
The repeatable benchmark compares the stable response from a verified Symdex symbol lookup with reading that symbol's entire containing file. It uses one pinned public repository per supported language and 30 deterministic, file-balanced functions, methods, classes or types from each repository. Test, fixture, example and benchmark paths are excluded. No generation model or subjective judge is involved.
| Language | Reference project | Sampled files | Mean estimated-token reduction | Exact Git-source checks |
|---|---|---|---|---|
| Python | Click | 17 | 82.01% lower | 30/30 |
| JavaScript | Express | 6 | 83.13% lower | 30/30 |
| TypeScript | p-queue | 5 | 60.39% lower | 30/30 |
| Go | Cobra | 18 | 80.47% lower | 30/30 |
| Rust | clap | 30 | 69.64% lower | 30/30 |
| Java | Gson | 30 | 46.40% lower | 30/30 |
| PHP | Guzzle | 30 | 21.74% lower | 30/30 |
| C | jq | 29 | 67.35% lower | 30/30 |
| C# | Humanizer | 30 | 0.55% lower | 30/30 |
| Ruby | Rack | 30 | 32.95% lower | 30/30 |
| Kotlin | Moshi | 30 | 57.12% lower | 30/30 |
| Swift | Swift Argument Parser | 30 | 56.27% lower | 30/30 |
| Balanced result | 12 projects | 285 | 54.83% lower | 360/360 |
The primary result is the mean of all 360 per-lookup reductions, with equal samples per
language and file-balanced selection within each project. Token counts use the
cl100k_base encoding; o200k_base produces a similar 54.54% result.
Every indexed file and returned source was also checked byte-for-byte against its pinned Git
blob. That exact-source check shows the requested code was preserved; it is not an
answer-accuracy score. Results varied substantially: the median reduction was 78.42%, while
58 of 360 lookups used more tokens because the serialized tool response can exceed an already
small source file. The benchmark measures direct known-symbol retrieval versus a whole-file
baseline—not symbol discovery, ordinary range reads, complete agent tasks or whole-session use.
Mixed-task Click benchmark
A separate archived benchmark asked 20 comprehension, navigation and modification questions about Click, with three generated answers per question and an LLM judge. Across the 20 unique context comparisons, Symdex consumed 23.31% fewer total context tokens. Its judged-accuracy point estimate was 4.42 versus 4.45 for raw-file context.
| Workload | Unique context comparisons | Total context-token result | Judged accuracy: Symdex / raw |
|---|---|---|---|
| All questions | 20 | 23.31% fewer | 4.42 / 4.45 |
| Comprehension | 7 | 47.67% fewer | 4.62 / 4.67 |
| Navigation | 7 | 39.75% fewer | 4.43 / 4.43 |
| Modification | 6 | 25.30% more | 4.17 / 4.22 |
This older result is an in-sample observation, not proof of equal answer quality or a guarantee for other repositories. The accuracy difference was small, but the sample was not designed to establish statistical non-inferiority. Modification questions consumed more context because the tested builder combined raw files with structural analysis.
Working-state restore benchmark
When an agent's context window is compacted, it restores its working state from the session server. That restore returns the whole session document — plan, decisions, file knowledge, progress and scratchpad — and still does by default. An agent may instead hand back an opaque cursor and receive only what changed since it last looked. This benchmark measures the difference between those two responses. No generation model or subjective judge is involved; token counts use the cl100k_base encoding.
| Scope | Full restore | Delta restore | Token reduction |
|---|---|---|---|
| 26 real sessions, aggregate | 16,725 | 10,075 | 39.8% lower |
| The 7 sessions above 1,000 tokens | not recorded separately | not recorded separately | 50.7% lower |
| Weakest single session | 991 | 983 | 0.8% lower |
| Strongest single session | 470 | 153 | 67.4% lower |
This is a per-restore reduction, not a per-request one. A delta is only returned when an agent asks to restore its working state, which happens after a context compaction rather than on every turn. Read that before comparing this figure with any per-request cost.
The spread is wide and the mean hides it: per-session reductions ranged from 0.8% to 67.4%. The weak cases are structural rather than noise. Scratchpad entries are stored without per-entry timestamps, so a delta has nothing to filter on and always returns them in full; the same applies to proactively recalled memories, which are replaced wholesale rather than appended. Scratchpad alone accounted for 14.1% of all measured session tokens. Of the reduction that does occur, 80% comes from filtering decision, progress and file entries and only 20% from omitting an unchanged plan.
These 26 sessions came from a single operator on a single machine, so the result is an in-sample observation rather than a prediction for another team's usage pattern. A separate check against a live deployment on 2026-08-01 reduced one session's restore from 12,122 to 4,977 characters — a character-level measurement of one session, reported here because it is a production observation rather than a simulation, not because one session establishes a rate.
The delta is opt-in. An agent that never sends a cursor receives byte-identical output to before, and six independent conditions — an absent, unparsable, wrong-session, stale, unreadable or unanchored cursor — each return the complete document rather than a partial one. Where the mechanism can be wrong, it returns too much rather than too little. A restore that omits content states what it withheld and how to retrieve it, so an agent reading a delta cannot mistake an omission for an absence.
Requirements
Server
- Linux host with Docker and Docker Compose v2
- Git
- 16 GB RAM recommended for the default stack
- No open ports required — a default install binds to
127.0.0.1
8 GB is the practical floor, and only with a small embedding model
(EMBEDDING_MODEL=granite-embedding:30m, EMBEDDING_DIM=384).
Below that, containers are OOM-killed while HTTP health checks still pass — a
failure mode that looks like anything except running out of memory.
Client
- macOS, Linux or Windows
- Nothing preinstalled — the bootstrap fetches its own pinned toolchain
- Installing from a source checkout instead needs a system
python3 >= 3.10
Solo and Team
Firekeep has one client, one server and one capability set. The commercial boundary is people—not agents, devices or individual features.
| Solo — free | Team — coming soon | |
|---|---|---|
| Members | One natural person | Multiple people under a seat entitlement |
| Agents and devices | Unlimited personal agents, terminals and devices | Unlimited per member |
| Capabilities | Complete current product | The same product, shared by more people |
| License document | None required | Signed, workspace-bound entitlement |
firekeep personal temporarily makes Firekeep dormant for private work. It
is a session state, not a pricing plan. Solo is the free one-member product edition.
Server installation
The public path provisions the same self-hosted server for Solo and Team. It does not ask you to choose a plan and does not require a registry account or license token.
-
Install the Firekeep client kit on the Linux Docker host
$ curl -fsSL https://kapella-hub.github.io/firekeep-dist/latest/install.sh | sh
The bootstrap brings its own pinned Python toolchain and verifies downloads before use.
-
Provision the latest public server release
$ firekeep init
It downloads and checksum-verifies a source-free deployment bundle, then pulls the public Firekeep images.
-
Add your workstation
On a remote server, first install the client kit on your workstation, then run
firekeep connect user@server --remote-dir ~/.firekeep/server. The explicit directory matches the public install default. The command uses your existing SSH access to issue a single-use code, enroll this first device and open a reusable local tunnel. Visithttp://127.0.0.1:8040/, enter the admin key printed byinit, and use Dashboard → Devices → Add device for later workstations. Dashboard-issued join codes are single-use and valid for 24 hours.
Use firekeep init --version vX.Y.Z to install a specific published server
release. firekeep init --office enables the bundled TLS reverse-proxy
deployment. Neither flag changes the Solo/Team edition.
Only the four Firekeep service images are published. Neo4j, Redis, Qdrant and Ollama are pulled by your own Docker daemon from their upstream registries — Firekeep never redistributes them.
As a developer — build from source
From a checkout of the repository you have been granted access to:
A source checkout builds the Firekeep services locally. Add --pull only when
deliberately testing the published-image path.
What the installer does
- Checks for Docker and Compose v2, installing Docker if absent
- Prompts for host address and Neo4j password, writing a validated
.envat mode600 - Generates a vault encryption key
- Mints API keys and prints an admin key exactly once
- Pulls public images or builds source images, then starts the complete stack
It is printed once and never stored. On a long-running server it simply does not exist any more, and that key is what administers devices and workspace membership. If you lose it, key minting must be done on the server itself, where a shell already implies total control.
Your first authenticated call
A fresh install is closed by default in two independent ways.
| Default | Value | Effect |
|---|---|---|
AUTH_ENABLED | true | Protected API routes require a key |
BIND_ADDR | 127.0.0.1 | Ports answer only on the host itself |
Both are deliberate. A stock install used to publish six ports on every interface and treat every caller as an anonymous admin — enough to read the vault and mint keys.
If you are following an older walkthrough, both are the security defaults doing their
job. /health and /version stay keyless precisely so you can
distinguish a service that is down from one that is merely gated.
Server configuration
Everything lives in .env beside the compose file. The settings
that actually change behaviour:
| Variable | Default | What it controls |
|---|---|---|
BIND_ADDR | 127.0.0.1 | Which interface ports publish on. Widening this exposes the stack. |
AUTH_ENABLED | true | Per-key auth and scopes on protected API surfaces. |
IMAGE_TAG | dev | Published version to pull. dev is never published. |
EMBEDDING_MODEL | — | Embedding model. Changing it requires re-embedding. |
EMBEDDING_DIM | — | Vector width. Changing it requires a collection rebuild. |
VAULT_KEY | generated | Encrypts stored secrets at rest. |
NEO4J_PASSWORD | prompted | Graph credential. Embedded in the data volume. |
EMBEDDING_MODEL and EMBEDDING_DIM are load-bearing. Changing the
model without re-embedding leaves old memories unsearchable; changing the dimension
without rebuilding the collection breaks recall outright. They are independent of the
generation model — conflating the two forces an avoidable rebuild.
Client installation
One command on a bare machine. No system Python or Node required.
What the bootstrap does
- Resolves the version and fetches that version's checksum manifest once.
- Verifies before use. Both a mirrored toolchain and the client wheel are checksum-verified against that manifest before either is executed. The wheel is fetched to a local file and installed by path — never by URL, which does no hash checking, and never by name.
- Installs the kit into
~/.firekeepwith its own standalone Python. - Renders every shipped adapter for Claude Code, Codex, Kiro and OpenCode.
Recommended device enrollment
On an existing server, open Dashboard → Devices → Add device and run the
generated command. It supplies a single-use join code, so there are no profile, host,
API-key, plan or runtime prompts. The client creates its credential locally, redeems the
code once, writes the single server connection and runs firekeep doctor.
Device credentials default to 90 days; doctor warns during the final 14 days,
and a new join code rotates the device onto a fresh credential.
Copy the bare code from the dashboard and run firekeep join fk_join_….
From a source checkout
./install installs the kit and renders adapters. firekeep install run
from that environment re-renders adapters only — it skips the package install,
because the code it would install is the code already running.
Use firekeep install --runtime <name> only to repair or re-render one
adapter. A normal install prepares all four supported clients together.
Connecting a device
A dashboard join code is the normal attachment path. The code identifies the server and enrollment ticket; the client chooses its own credential and stores one connection.
The client generates the secret and sends only its SHA-256 hash while redeeming the single-use code. Later authenticated calls necessarily send the plaintext API key, so remote connections still require an SSH tunnel, private network or HTTPS.
SSH shortcut for operators
An operator who already has shell access can use firekeep connect. It issues a
single-use code over SSH and then calls the same join path locally:
A stock server answers only on 127.0.0.1. The SSH shortcut establishes a
tunnel and reuses it on later runs. Tailscale, WireGuard or your own HTTPS reverse proxy
are also valid; Firekeep does not require a network vendor.
Restart your agent session afterwards so it picks up the newly registered MCP servers.
CLI reference
| Command | What it does |
|---|---|
firekeep init | Provision or update a local/self-hosted Firekeep server |
firekeep join <code> | Enroll this machine through a single-use join code |
firekeep install | Install from a source checkout, or re-render adapters in an installed kit |
firekeep connect <target> | Operator SSH shortcut that creates a join code, tunnel and verified connection |
firekeep login <url> | Reserved for hosted OAuth sign-in when a server supports it |
firekeep doctor | Preflight: health, versions, key ACLs, CA expiry, permissions |
firekeep personal [on|off|status] | Toggle personal mode — dormant, nothing recorded |
firekeep update | Update the kit. --check reports only; --to pins or rolls back |
firekeep night-shift | Distil queued sessions into memory and draft skills via a local model |
firekeep version | Print the client version |
Start with doctor
When anything is wrong, this is the first command to run — it checks the whole chain rather than one link.
Day to day
The core MCP capabilities work through every shipped adapter. Lifecycle automation varies because each coding client exposes different hook surfaces.
Automation by client
| Client | Automatic surface | Current boundary |
|---|---|---|
| Claude Code | The richest hook coverage: start briefing and presence, prompt polling, pre-compaction capture, pre/post edit mediation, final snapshots and true session-end deregistration. | Its pre-edit hook can hard-block a denied edit; service failures still degrade according to the documented hook policy. |
| Kiro | Agent-spawn and prompt hooks provide briefing, presence, inbox polling and snapshots; pre/post tool hooks record mediated actions. | There is no true session-end or PreCompact event. On validated Kiro 2.12.1, the pre-edit hook fires but is advisory rather than a hard block. |
| OpenCode | A local plugin bridges the lifecycle and edit events OpenCode exposes; supported edits can be hard-blocked and session deletion drives final capture. | Briefing and inbox output appear in the console, not model context. Its lifecycle surface is narrower than Claude's and has no pre-compaction event. |
| Codex | MCP servers and repository guidance expose the full direct-call tool surface. | Codex has no Firekeep hooks: recall, session updates, inbox polling and policy calls are manual. Presence, heartbeat and snapshots require starting the optional sidecar yourself. |
These differences affect convenience, not access to the underlying MCP capabilities. Every supported client can call the same server tools when its agent chooses to do so.
What your agent calls directly
Firekeep exposes its server capabilities through one local gateway, with Symdex and Decision Board running client-side. The tools that matter in ordinary work include:
| Area | Tools | Use |
|---|---|---|
| Memory | memory_recall / memory_learn | Find prior evidence, then record a fix, decision or non-obvious outcome. |
| Skills | skill_recall / skill_create | Find or author a reusable playbook; document-derived drafts stay out of recall until reviewed. |
| Knowledge | corpus_ingest / knowledge_ingest / knowledge_ingest_url | Make pasted content or a bounded public documentation crawl searchable; the Knowledge front door also queues reviewable procedure drafts. |
| Sessions | ctx_start_session / ctx_update / ctx_get_shadow / ctx_resume_session | Persist explicit working state across compression, interruption and crashes. |
| Relay leases | relay_lease / relay_heartbeat / relay_release | Coordinate ownership of a file or task with a renewable fenced lease. |
| Relay work | relay_task_post / relay_task_list / relay_task_update | Create, assign and advance work that another agent identity can pick up. |
| Relay messages | relay_post / relay_read / relay_send_dm / relay_get_dm | Post to a catch-up channel or exchange pollable direct messages; relay_who_is_online reports best-effort presence. |
| Decision Board | decision_board / decision_board_check | Open a multi-question clarification board and wait for the human's submitted answers. |
| FirekeepScope | scope_start / scope_ask / scope_post / scope_check | Run gating or asynchronous clarification screens through Relay and the dashboard. |
| Replay | replay_timeline / replay_inspect / replay_context_at / replay_narrow | Move from a recorded timeline to event details, nearby context and likely contributors to a failure. |
| Sentinel | sentinel_get_events / sentinel_get_health / sentinel_push_event | Read configured environment activity, check collector health or add a custom operational event. |
| Vault | vault_list / vault_retrieve | Discover metadata and explicitly retrieve an authorized secret without putting it in memory. |
| Vault administration | vault_store / vault_delete | Create or irreversibly remove entries; these operations require an admin-scoped credential. |
| Evals | eval_session / eval_summary | Inspect trace-derived operational metrics for one completed session or recent sessions. |
Recall before starting and learn after finishing. Relevant recorded decisions and fixes can save rediscovery, and the value compounds as later agents retrieve evidence from earlier work in the same project.
The dashboard
Served on port 8040. Because the stack binds to loopback, reach it through the
tunnel connect established — at http://127.0.0.1:8040/, not the
server's own address.
Personal mode
An escape hatch that makes Firekeep dormant for work you do not want it to record.
Hooks, the presence sidecar and Decision Board re-read the state and stop communicating immediately. An MCP shim that is already running evaluates hard bypass only when it starts, so restart the active agent after changing Personal mode. A restarted shim exposes zero Firekeep tools while the mode is on; restart again after turning it off to restore them.
Claude Code and OpenCode wire a true session-end or session-deletion event to clear the
marker. Kiro has no true session-end event, and Codex has no hooks, so turn it off with
firekeep personal off or rely on the 12-hour TTL backstop. firekeep doctor
reports a warning whenever it is active.
For a whole session that is personal from the start, set FIREKEEP_BYPASS=1
before launching your agent.
Operations
Updating
The update rebuilds the kit's environment, and a running session holds MCP servers open from it. The installer names the holding processes and asks you to close them rather than failing with an unexplained access error. On macOS and Linux the running session keeps working and the new version applies to the next one.
What version am I running?
Every service answers /version without a key and without touching its
dependencies, so it responds even when the things behind it are down.
Health
Compose health checks are TCP-only. A container can report healthy while the service
inside it cannot reach Redis or Qdrant — which is exactly what an out-of-memory kill
looks like from the outside. Check /health bodies, not just container state.
Backups
Use the shipped deploy/backup.sh so Neo4j, Qdrant and Redis are quiesced before
their files are copied. It archives the four base volumes together: Neo4j, Qdrant, Redis
and Ollama. A partial or hot filesystem copy can look valid while containing inconsistent
graph, vector or auth state.
Separately protect the original .env as a secret. It contains the
VAULT_KEY required to decrypt stored secrets, the Neo4j password, internal
credentials and stable workspace/member IDs. An office-mode deployment must also back
up its caddy_data volume, which contains the internal CA private key. Restore
those identities with the data; generating replacements can make the restored vault
unreadable, invalidate clients or change workspace ownership.
Troubleshooting
Everything returns 401
Expected on a fresh install — AUTH_ENABLED=true is the default. Confirm the
service is actually up with the keyless /health, then check that your client
connection has an API key. firekeep doctor reports a missing or rejected key
directly.
Connection refused from another machine
Also expected. BIND_ADDR=127.0.0.1 means the ports answer only on the host.
Use the connection encoded by the dashboard join code, establish an SSH/private-network
route, or run the operator shortcut
firekeep connect user@host --remote-dir ~/.firekeep/server. Pointing a
client at an unbound public address simply times out.
The dashboard says a service is unreachable
Check the service directly on the host with curl against
/health. If that answers and the dashboard disagrees, the proxy in front of the
dashboard is misrouting rather than the service being down.
Containers restart or get killed
Almost always memory. Check docker stats against the requirements above. The
symptom is misleading: health checks keep passing while the process inside is killed and
restarted.
The agent does not see the tools
MCP servers are registered at install, and a running agent session does not pick up new
ones. Restart the session. If they are still missing, firekeep install --runtime
<name> re-renders that runtime's configuration.
Still stuck
Run firekeep doctor and include its output when you
get in touch. It reports
the whole chain — connection, auth, versions, permissions — rather than one link of it.