Repository navigation
Configuration
Most people change nothing here. The settings that exist are a few environment variables for the server, and chores.json for background upkeep. The memory budgets are fixed by design.
What reads a variable is the server process, so where to set it depends on what started the server:
-
systemd service (Linux). When
kg setupwrites~/.config/systemd/user/kg-memory.service, it copiesPATH,CODEX_HOMEand everyKG_*variable from its own environment into the unit. Later changes to your shell profile don't reach it, and rerunning setup leaves a working unit as it is. To change a variable, edit the unit'sEnvironment=lines, then runsystemctl --user daemon-reload && kg restart. -
Otherwise. A server started by
kg start,kg mcpor a session hook inherits that process's environment. Set the variables in your shell profile, thenkg restart.
For example, without the service:
export KG_LOG_LEVEL=DEBUG # in ~/.bashrc or ~/.zshrc
kg restart| Variable | Default | Description |
|---|---|---|
KG_STORAGE_ROOT |
~/.knowledge-graph |
Root directory for all graph data. One server per storage root: a second one on the same root refuses to start |
KG_HTTP_PORT |
8765 |
Server port. The server, kg, the hooks and kg mcp must all see the same value (see below) |
KG_HTTP_HOST |
127.0.0.1 |
Bind address. Keep it local; there is no authentication (SECURITY.md) |
KG_SAVE_INTERVAL |
30 |
Seconds between background maintenance ticks |
KG_AUTOCOMMIT_INTERVAL |
900 |
Seconds between git auto-commits of the storage root; 0 disables. Acts only when the storage root is a git repository |
KG_ORPHAN_GRACE_DAYS |
365 |
Days an orphaned node may go unrecalled before it is permanently deleted |
KG_CHORES |
unset |
1 switches background upkeep on, 0 forces it off, overriding "enabled" in chores.json
|
KG_BUDGET_NOTICES |
unset (on) |
0 turns budget notices off (same as "budget_notices": false in chores.json) |
KG_LOG_LEVEL |
INFO |
Server log level (DEBUG, INFO, WARNING, ERROR) |
CODEX_HOME |
~/.codex |
Where Codex keeps its session logs, which the server reads for Codex quota. A custom value must be in the server's environment too |
EDITOR_PORT |
8766 |
Visual editor port, read by kg editor
|
Content changes and promotions are saved immediately. The background tick (KG_SAVE_INTERVAL) persists read timestamps and session state, retries failed saves, and runs compaction, refill and orphan pruning.
A shorter orphan grace keeps the storage smaller: KG_ORPHAN_GRACE_DAYS=90 deletes orphaned nodes after three months instead of a year. Orphaned nodes no longer appear in kg_read, but kg_search still finds them, and reading a connected node can bring them back until the grace period ends.
Change KG_HTTP_PORT only if another program holds 8765. Every client must then see the new value: the server (in the unit, if you use the service), your shell profile (for kg, the hooks and the kg mcp your harnesses launch), and any app that starts without your shell's environment, such as Claude Desktop. A server on another port keeps its PID file and logs in ~/.local/state/knowledge-graph/port-<N>/. kg editor points the editor at whatever port kg sees.
Don't edit the plugin's bundled .mcp.json. It only launches kg mcp, and every plugin update overwrites it.
The graph's budget is measured in exact rendered characters, not estimated tokens, and is not configurable: 22,000 per level for compaction, 50,000 for the combined full-graph read. The budget sets how much memory a session gets; how it reaches each client is handled separately:
- Preload: at most 9,500 UTF-16 units in Claude Code, 9,000 bytes in Codex and 10,000 bytes in Antigravity, each in the unit that client counts.
-
Long reads arrive in parts. A
kg_readreply longer than the client keeps whole (45,000 UTF-16 units in Claude Code, 36,000 bytes in Codex) is split;kg_read(session_id, more=true)returns the next part. Only what a delivered part showed counts as seen. - Oversized graphs degrade predictably. A full read hides the lowest-scored archived anchors first, then the lowest-value edges, with counts and a search pointer.
The 50,000-character figure is a target, not an absolute cap: active gists are preserved even if they alone exceed it. The newest nodes (the fresh tier, up to 30% of a level's budget) are never archived, so a level holding unusually long gists can render past its target until a maintenance pass tightens them.
These live in server/core/constants.py. You can change them in a fork, but they are not exposed as settings, and several are part of the budget arithmetic above.
| Setting | Value | Why |
|---|---|---|
| Storage layout | <storage-root>/projects/<slug>/graph.json |
Plain JSON; the root is configurable |
| User graph path | <storage-root>/user.json |
One shared file |
| Sessions path | <storage-root>/sessions.json |
Central session tracking |
| Session TTL | 24 hours | Sessions expire after inactivity |
| Session ID length | 8 hex characters | Short enough for tool arguments |
| Budget per level | 22,000 characters | Compaction threshold, in exact rendered characters |
| Full-graph read target | 50,000 characters | Can be exceeded when active gists alone exceed it |
| Search output ceiling | 10,000 characters | Hard cap on a kg_search result |
| Compaction target ratio | 0.8 | Archiving compacts down to this share of the budget; refill fills back up to the same ceiling |
| Fresh tier | 30% of the budget | The newest nodes that are never archived |
| Archived budget ratio | 0.30 | Most of the budget archived anchor lines may take before the lowest-scored become orphaned |
| Archived-edge weight | 0.2 | Connectedness weight of an edge to an archived neighbour (active = 1.0, orphaned = 0) |
| Scoring formula | 0.25×recency + 0.40×connectedness + 0.35×usefulness |
Tie-aware percentile ranks. Connectedness = max(weighted in×0.66 + out×0.33, 0.5×log1p(all incident edges)). Endorsements decay with a 90-day half-life. Recency is the latest write, read or credit |
| Endorsements per session | 5 guidance, 10 hard cap |
kg_useful is for what helped, not for traffic; past five the reply says how far over |
| Maintenance credits | 1–3 per node, 15 per pass |
kg_useful(credits=…) in a maintenance session; fades like an endorsement |
Background upkeep ("chores") is configured in ~/.knowledge-graph/chores.json. It is off by default because it spends your quota. kg setup --only upkeep creates the file from the plugin's chores/chores.example.json with "enabled": true; you can also edit it by hand. The server re-reads the file when it changes, so no restart is needed. Every decision, including each refusal and its reason, is logged in ~/.knowledge-graph/chores.jsonl.
There are two tiers. A chore fixes one kind of debt on one or two nodes (a cluster of up to five when lifting episodes into a lesson). A pass is the full /kg-maintain runbook, due by time since the last pass, and runs only when the week is on course to leave quota unspent.
| Key | Default | Meaning |
|---|---|---|
enabled |
false |
The switch (KG_CHORES overrides it) |
runner |
auto |
Which harness runs the agent, and whose quota it spends: auto (Claude Code if installed, else Codex, else Antigravity), claude, codex, antigravity
|
model |
claude-sonnet-5 |
Model for Claude Code runs |
codex_model |
Codex's default | Model for Codex runs |
codex_reasoning_effort |
low (chore), medium (pass) | Effort for Codex runs |
antigravity_model |
the CLI's default | Model for Antigravity runs; its model group picks the quota bucket |
antigravity_effort |
low (chore), medium (pass) | Effort for Antigravity runs |
antigravity_allow_credits |
false |
Allow a run while useG1Credits could spend paid credits |
budget_notices |
true |
Wrap-up notices from the session's own quota gauge (works whether upkeep is on or off) |
min_interval_s / graph_cooldown_s / max_per_day
|
2700 / 21600 / 8 | Spacing between chores overall, per graph, and per day |
debt_floor |
0.12 | A graph below this debt is left alone |
max_5h / max_7d
|
55 / 80 | Quota gates for a chore (percent used) |
gauge_max_age_s |
5400 | A quota reading older than this refuses the run |
timeout_s |
420 | A chore still running after this is killed, with everything it started |
pass_interval_days / pass_max_per_day
|
21 / 1 | When a graph is due a full pass |
pass_max_5h / pass_max_7d / pass_pace_max
|
40 / 70 / 1.0 | Pass gates: a pass also needs the week to be running under its quota pace |
pass_timeout_s |
1500 | Pass time limit |
A configured claude_bin, codex_bin or antigravity_bin pins its runner, even if the path is wrong: that shows up as "binary missing" instead of silently spending another subscription's quota.
Chores need a fresh quota reading from their runner. For Claude Code that is the status-line gauge kg setup installs (Installation#session-limits-and-the-status-line-recommended); Codex and Antigravity readings come from their own session logs and /usage. The kg-ops skill has the full detail on runners, permissions and safety rules.
kg editor starts the editor (and the memory server, if it is down) and points it at the server kg uses. The editor reads its own variables:
| Variable | Default | Description |
|---|---|---|
EDITOR_PORT |
8766 |
Editor web server port |
EDITOR_HOST |
127.0.0.1 |
Editor bind address |
MCP_SERVER_URL |
http://127.0.0.1:8765 |
The memory server for REST requests and live updates. kg editor sets it from KG_HTTP_HOST and KG_HTTP_PORT
|
See Visual Editor.