Skip to content

Where Chimera keeps your data

Chimera writes to two places and only two: a user-scope root (~/.chimera by default) and a project state directory (<project>/.chimera). Every store inside them is declared in one registry, chimera/config/paths.py. If a directory is not in that registry, Chimera did not put it there.

That registry is what makes the rest of this page possible: a single place to relocate everything, a single place to declare retention, and a structural guarantee that no lifecycle tool can touch a path the registry never named.

If you have never configured anything, nothing has moved. With no environment variable and no config file, every path resolves exactly where it always did. You can stop reading here.

Two ways, highest precedence first:

Terminal window
export CHIMERA_HOME=/data/chimera # 1. environment
# ~/.chimera/config.toml # 2. config
[storage]
root = "/data/chimera"

Otherwise the root is ~/.chimera. ~ is expanded in both forms.

[storage] is read from the same chain as every other Chimera setting — XDG (~/.config/chimera/) < user (~/.chimera/) < project (<project>/.chimera/) — so a project can pin its own root, and any of config.toml, config.yaml, config.yml, config.json will do (TOML is canonical).

One deliberate exception: config.toml itself is always discovered at ~/.chimera/, never under a relocated root. A file cannot live inside the directory it relocates — reading it back would be circular. So with root = "/data/chimera" set, your config stays at ~/.chimera/config.toml while your sessions, cohorts, and datasets move to /data/chimera/.

Project state (<project>/.chimera) is never relocated. It belongs to the project by definition.

StoreScopePath under its rootWritten byPrunable
datasetsuserdatasetschimera/eval/datasets.pynever
cohortsusercohortschimera/tui/cohort.pyyes
sessionsusersessionschimera/cli/code.pyyes
eventlogusereventlogchimera/sessions/eventlog/yes
historyuserhistorychimera/cli/code.pynever
projectsuserprojectschimera/tools/todo.pyyes
function_synthesisuserfunction_synthesischimera/function_synthesis/never
tasksusertaskschimera/tools/task_tool.pyyes
experiment-runsuserexperiment-runsscripts/experiments/yes
exportsuserexportschimera/sessions/share.pyyes
sharesusershareschimera/otter/share_cmd.pyyes
snapshotsusersnapshotschimera/otter/snapshot.pyyes
worktreesuserworktreeschimera/otter/worktree.pyyes
teamsuserteamschimera/cli/agent_teams.pyyes
plansuserplanschimera/stoat/plan_mode.pyyes
cacheusercachechimera/skills/discovery.pyyes
cronusercronchimera/tools/cron_tools.pynever
learninguserlearningchimera/learning/store.pynever
tokensusertokenschimera/mcp/oauth.pynever
runuserrunchimera/otter/server_pidfile.pynever
agentsuseragentschimera/agents/team_roles.pynever
skillsuserskillschimera/skills/discovery.pynever
completionusercompletionchimera/cli/completion.pynever
profilesuserprofileschimera/ferret/cli.pynever
badgeruserbadgerchimera/badger/slash.pynever
ferretuserferretchimera/ferret/subcommands/mcp_manage.pynever
shrewusershrewchimera/shrew/model_profiles.pynever
stoatuserstoatchimera/stoat/hooks.pynever
project-stateproject(the dir itself)chimera/commands/builtins.pyyes
project-sessionsprojectsessionschimera/assembly/coding_agent.pyyes
project-agentsprojectagentschimera/agents/team_roles.pynever
project-checkpointsprojectcheckpointschimera/env/local.pyyes
project-snapshotsprojectsnapshotschimera/commands/builtins.pyyes
project-memoryprojectmemorychimera/core/memory.pynever
project-promptsprojectpromptschimera/core/prompt_template.pynever
project-skillsprojectskillschimera/skills/discovery.pynever

Single files also live at the root and are not stores: config.toml, mcp.json, settings.json, permissions.json, credentials.json, sessions.db, persistent_memory.json, loop_detector_state.json.

chimera doctor --section storage prints this table with sizes and ages, plus any directory it finds that the registry does not claim — see Seeing and reclaiming your storage.

Six categories are structurally exempt, meaning no config file can opt them in:

  • datasets and function_synthesis — deliberately staged benchmark inputs and synthesised model artifacts. Expensive to rebuild, sometimes impossible for a pinned revision.
  • agents, skills, profiles, project-memory, project-prompts, project-skills — things you authored. Input, not output.
  • tokens — credentials.
  • cron — pruning a job file silently unschedules work.
  • run — live pidfiles.
  • history — a single readline file, not a directory; readline caps it itself.

Write [storage.datasets] retain = 1 and it is read, ignored, and the store keeps everything.

Retention is opt-in and off by default. Declaring it changes nothing on its own — chimera gc is the only thing that acts on it, and its dry run is the default.

[storage.sessions]
retain = 200 # keep the newest 200 (absent = keep forever)
max-age-days = 90 # and/or drop anything older
[storage.eventlog]
retain = 50

Both keys accept the underscore spelling (max_age_days). A missing, zero, negative, or unparseable value disables that knob rather than guessing. Store names containing a dash also answer to underscores, so [storage.experiment_runs] and [storage.experiment-runs] are the same table.

Cohort retention shipped before [storage] existed. Both spellings work:

[storage.cohorts] # preferred
retain = 20
max-age-days = 30
[tui.cohorts] # legacy alias — still read
retain = 20
max-age-days = 30

[storage.cohorts] wins if both are present. Nothing you already wrote needs to change.

These predate the registry and are honored exactly as before:

VariableEffect
CHIMERA_HOMEThe storage root. Beats [storage] root.
CHIMERA_DATASETS_DIRRelocates the datasets store alone. Beats the root.
CHIMERA_FS_HOMERelocates the function_synthesis store alone.
CHIMERA_PB_RUNSRelocates the pb-runs subtree of experiment-runs, not the store.
CHIMERA_TEAMS_HOMERelocates the teams store for one run.
CHIMERA_CRON_DIRRelocates the cron store for one run.
CHIMERA_CONFIG_HOMEWhere config.toml is read from (config only, not storage).

Per-benchmark dataset overrides (CHIMERA_TAU_BENCH_PATH and friends) are unchanged; when unset, those benchmarks now resolve under the datasets store, so CHIMERA_DATASETS_DIR relocates them consistently with chimera bench-fetch.

from chimera.config.paths import (
all_stores, chimera_home, project_state_dir, store_path, store_retention,
)
chimera_home() # PosixPath('/Users/you/.chimera')
store_path("sessions") # .../.chimera/sessions
store_path("project-skills", "/repo") # /repo/.chimera/skills
project_state_dir("/repo") # /repo/.chimera
store_retention("sessions").retain # 200, or None when unconfigured
for store in all_stores():
print(store.label, store.writer, store.prunable, store.note)

Nothing here creates a directory — callers mkdir when they are about to write. Resolution happens on every call, never at import, so setting CHIMERA_HOME in a test or an embedding host is honored by code that was imported earlier. An unknown store name raises UnknownStore rather than resolving to a plausible-looking path.

Add a row. It is data, not a code path:

Store(
name="my-store",
scope="user",
rel="my-store",
writer="chimera/mypackage/writer.py",
prunable=True,
note="What a reader needs that the columns cannot say.",
)

Then use store_path("my-store") at the write site. Do not compose Path.home() / ".chimera" / ... — a directory the registry does not name is reported as an orphan, and the one-definition property is the whole point.

  • It does not delete anything. No code path in the registry removes files.
  • It does not prune on its own. Retention is read here and acted on only by chimera gc, explicitly, dry-run first.