Skip to main content

CortexFS Architecture

Normative ABI detail lives under spec/. Visual identity lives in DESIGN.md (Google Labs DESIGN.md format). This file is the engineering design entry: what CortexFS is, where state lives, and what must not become root ABI.

One-page model

/ctx is a FUSE filesystem interface for agent runtimes.
model is a pure inference file.
agent is the policy-bound orchestrator.
tool is a capability endpoint.
session is ordinary file history.
policy is a minimal SELinux-like allowlist.
Rig removes provider and API-format differences.
CortexFS does not express provider/API formats as root ABI.
MCP servers are tool sources; MCP capabilities are ordinary tools.
CortexFS controls agent visibility, execution, and sharing, not framework config formats.

Frozen root rule

root only contains stable object classes
root never mirrors provider, database, workflow, memory, or orchestration internals
MCP must not become a root namespace
MCP configs, skills, project rules, and prompt packages are ordinary visible files

Forbidden root namespaces (examples):

skill/ memory/ mcp/ workflow/ chan/ job/ hook/ audit/ control/

Those concepts may exist as object-local files, session data, or tools. They must not become new root classes.

Core invariants

Context is a working set, not the full history.
Raw history is durable.
Prompt context is disposable and rebuildable.
Compaction must not destroy raw messages.
Independent tasks should run in child agents.
Child agents are owned by their parent unless explicitly detached by policy.
Owned children die when the parent dies.
Prompt text and skill metadata never grant authority.
Policy, path, mount, uid/gid, and mode bits grant authority.
Mechanism enforces principal, path, mount, and Linux constraints; an injected
policy evaluator may only further restrict that authority.

Identity, lifetime, and transport

CortexFS uses four different identities. They must not be collapsed into an "agent daemon" or duplicated in a second lifecycle tree:

LayerStable identityOwner
Definitionagent/<name> + agent/<name>.d/reference tree
Runtime instancesupervisor unit + invocation receiptruntime/supervisor
Sessionhome/<uid>/agent/<name>/session/<session>/durable files
Runentropy-backed run id in session eventssession recorder

The definition says how an Agent may run. A runtime instance says which processes currently realize that definition. A session owns durable human and Agent history. A run correlates one bounded execution inside that session.

agent/<name>.d/meta.json may retain the latest receipt-bound supervisor facts needed for inspection and safe cleanup. status, pid, and log are summary projections. None of those files changes the Agent's definition identity, and none is an independent process supervisor.

Do not add instances/ merely to mirror process state already owned by systemd and receipt metadata. A future multi-instance feature must first define an identity that cannot be expressed by the existing agent/session/unit/run tuple, then nominate one authoritative lifecycle owner and migration path.

Sockets are transports. Live sockets belong under /run; paths such as agent/<name>.sock and session/<session>/terminal/main.sock are stable ABI entries or aliases used to discover those transports. Socket presence does not define object identity, session durability, or process ownership.

A terminal is a durable resource below a session. Its resource directory owns metadata and replayable events; a runtime PTY and socket are replaceable mechanisms for the process and attachments. The first terminal resource slice uses:

home/<uid>/agent/<agent>/session/<session>/terminal/<terminal-id>/
meta.json state status owner cwd events.jsonl

The root remains frozen: this session-local path does not add /ctx/terminal. A top-level terminal class requires a separately versioned root ABI decision.

The compact rule is:

object defines identity
supervisor receipt defines process lifetime
ordinary files define durable state
socket provides optional transport
terminal resource owns PTY history; socket is only a live transport

Model and context boundary

A model object is the stable provider/model identity. Its driver control selects replaceable adapters for each use case; cap and limit project only provider-neutral facts. Agents and context code consume those projections and must not branch on provider names, API formats, or model branding.

Capability data is conservative. Hard limits use the precedence defined by the Model ABI: explicit per-model host configuration, then the validated catalog, then unknown. Stable cap words are adapter projections; unsupported or untrusted facts are omitted. A future per-model capability override or host-side probe requires a versioned Model ABI change. It must not become a model-call side effect, a background watcher, or a second configuration store; accepted evidence would enter the same validated cap/limit projection or remain diagnostic-only.

Context construction uses the selected model's hard limit and the Agent's attenuating window control. Raw session history remains intact while the rendered prompt may use a recent tail, summaries, rules, skills, and loaded tool metadata. Changing models therefore rebuilds prompt context from durable facts; it does not rewrite history or teach each Agent a table of model-specific cases.

Where things live

The packaged host keeps versioned durable trees under /var/lib/cortexfs/storage/generations/<generation> and exposes the selected tree through the atomic /var/lib/cortexfs/storage/current symlink. On a systemd restart, ctx storage update clones the current generation, applies and validates the next bin/cortexfs.bootstrap.json tree_version, then switches current. A failed stage leaves current unchanged. This is a restart boundary, not a watcher, poller, or hot reload; the /ctx ABI shape remains unchanged. The package generates root files locally; generations are not distributed artifacts. The systemd restart path, after stopping consumers, explicitly uses --prune to remove non-current generations. There is no background generation GC. The mount and agent runtime resolve current once at process startup and keep that concrete generation for their full lifetime, including mount cache refresh. Short-lived object-runner invocations may resolve the then-current generation each time.

PlacePath shapeRole
Control/ctx/agent/<name>.d/*policy, mount, cwd, system.md
Agent home/ctx/home/<uid>/agent/<name>/session, data, cache, log
Session.../session/<session>/messages, events, context, load snapshots
Runtime IPC/run/user/<uid>/cortexfs/...terminal sockets only

Sandbox mapping (typical):

/ctx/home/<uid>/agent/<name> → HOME=/home/agent (rw)
caller project cwd → /workspace (rw, default cwd)
/ctx → /ctx (often ro)

/run holds sockets. Agent cwd is usually /workspace. Private session files live under agent home, not under /run.

Prompt load observability

When the object runner builds a run prompt, it best-effort writes:

/ctx/home/<uid>/agent/<agent>/session/<session>/AGENTS.md
/ctx/home/<uid>/agent/<agent>/session/<session>/SKILLS.md
AGENTS.md merged rules snapshot (same text as {{rules}})
SKILLS.md skill metadata only (name, description, path)

Ordinary session files, not authority. Full skill bodies stay at listed SKILL.md paths. Implementation: agent/prompt/snapshot.rs.

Engineering taste

short names over long phrases
one clear job per module
reuse before inventing helpers
no parallel enums for Empty/Missing/Invalid
no second root ABI for orchestration
no background watchers, polling, or hot-reload subcommands
Git commit (or process restart) is the development refresh boundary
atomic rename for control-plane writes
ordinary files for history and snapshots

Module naming: naming-guide.md. Prefer single-token stems (snapshot.rs); no new - / _ in module file stems.

Internal code architecture

Product rules above freeze what /ctx is. How the Rust tree is layered (process roles, crate/feature splits, module dependency direction, error tiers, migration phases) lives in internal-architecture.md.

Read that document before large refactors (crate splits, executor error migrations, FUSE vs object boundary changes). Do not “improve structure” by adding root ABI classes, workflow engines, or background watchers.

Read the specs in order

spec/README.md
spec/root-abi.md
spec/fuse.md
spec/object-abi.md
spec/model-abi.md
spec/session-abi.md
spec/agent-tool-security.md
spec/agent-runtime.md
spec/tool-policy-abi.md
spec/ctx-coreutils.md
spec/rolling-upgrades.md

Stable ABI red line

Do not let /ctx become a directory mirror of an AI platform database.
It should stay small, hard, boring, and scriptable.