Agent, Tool, and Security ABI
Layer boundary:
model = pure inference endpoint
tool = executable capability endpoint
agent = policy-bound orchestrator process
By default, a model has no:
tool permission
filesystem write permission
project context
long-term memory
task planning
chroot/mount policy
MCP/skill
cluster scheduling
An agent owns Linux uid/gid/groups, label, home, root, cwd, mounts, policy, context, and tool execution decisions. Real file writes, tool calls, and shared-space access are attributed to the agent, not to the model.
Tool boundary:
model may emit tool_call events
model must not execute tools
agent decides whether to execute tools
agent policy decides whether execution is allowed
Agent as File
/ctx/agent/
coder
coder.sock
coder.d/
owner
uid
gid
groups
perm
label
iso
parent
life
root
cwd
env
path
mount
model
tools
abi
system.md
prompt.template.md
policy
status
pid
log
meta.json
hooks/
pre.d/
post.d/
Control files:
owner owning Linux user uid
uid runtime uid; defaults to owner
gid runtime gid
groups supplementary groups, one gid per line
perm coarse file/shell ceiling: r=fs.read/list/stat, w=fs.write/replace, x=shell.exec/bash/tmux/zellij
label CortexFS agent label, for example user_u:agent_r:coder_t:s0
iso isolation profile: shared, uid, or userns
parent parent agent, session, or run that created this agent
life lifecycle ownership, default owned
system.md user-editable agent instructions/persona. This is prompt text, not authority.
prompt.template.md user-editable system prompt template. This is prompt text, not authority.
abi required executable-agent launch ABI: sdk-envelope-v1
approval hosted SDK direct-native mode: auto or ask; missing means auto
The abi control is required and accepts only sdk-envelope-v1; it must not be
inferred from executable contents or other controls. approval=ask uses this
host-mediated exchange.
The optional tools control declares the agent's static direct-native tool set.
It is empty when missing or empty and otherwise contains one canonical tool
name per line with a final newline. Blank, whitespace-padded, duplicate,
invalid, and reserved tsh entries are rejected. Declaration is not authority:
direct execution = declared name AND matching agent perm bit AND CTX_PATH hit AND agent policy AND tool policy AND Linux/mount permission
Every call re-derives this intersection and opens the selected executable
without following symlinks. tsh load/pin cache state is dynamic prompt
context only and never admits a direct-native call.
For hosted SDK agents in ask mode, approval occurs after this full authority
intersection and nofollow open, before process spawn. It is an additional
single-call gate, not an authority grant and not Codex-equivalent coverage for
all operations. Clients without an approval handler fail closed.
Multiple agents may share one Linux uid. The uid expresses the user boundary. The label expresses the agent security boundary.
meta.json may exist for longer descriptions such as purpose, creation time,
or issue number. Policy decisions must not depend on meta.json.
system.md is the user-editable identity and instruction text for the agent.
prompt.template.md is rendered as the first system message sent to the model.
The template supports simple {{name}} variables including agent,
current_time_unix, agent_instructions, rules, skills, tool_injection,
history_messages, and runtime_contract.
The rendered prompt combines system.md, discovered AGENTS.md rules, bounded
skill metadata, optional tool-injected context, optional historical message
context, and the immutable CortexFS runtime contract. Prompt text must not grant
tool, model, network, filesystem, or session authority; those remain controlled
by policy, path, mount, uid/gid, and Linux mode bits.
Skill metadata contains only name, description, and SKILL.md path. Full
SKILL.md files are read only after a skill is selected. The skill metadata
section may use at most 2% of the model context window; when the window is
unknown, the hard cap is 8,000 characters. If the list is too large,
descriptions are shortened first; if it still exceeds the cap, some skills are
omitted and a warning is included.
Agent startup:
1. Read /ctx/agent/<name>.d/*
2. Set CTX_ROOT, CTX_HOME, and CTX_PATH
3. Merge agent/<name>.d/env
4. Establish runtime identity from uid/gid/groups/label
5. Create the mount namespace
6. Apply bind mounts from mount
7. chroot to root
8. cd to cwd
9. exec the agent runtime
Agent View
An agent view is the set of files, tools, models, sockets, and shared spaces visible to an agent.
It is derived from:
root
cwd
mount
path
model
perm
policy
Linux uid/gid/groups/mode bits
CortexFS label
Resource tiers are distinct:
/ctx/model system models, visible to all users by default
/ctx/agent system agents, visible to all users by default
/ctx/tool system tools, visible to all users by default
/ctx/home/<uid>/model user-specific models and aliases
/ctx/home/<uid>/agent user-specific agent state and user agents
/ctx/home/<uid>/tool user-specific tools
Those directories are durable resources. They are not the same thing as an
agent's runtime view. At runtime, CortexFS/FUSE projects the tools visible to
one agent in memory from agent/<name>.d/path, policy, mount, uid/gid, and
mode bits. Do not create placeholder files or symlink copies merely to express
that a system tool is visible to an agent.
CortexFS does not define MCP config formats, skill formats, project rule formats, or prompt package formats. Those are ordinary files.
Examples:
/home/alex/.codex/config.toml /home/agent/.codex/config.toml ro bind,nosuid,nodev,noexec
/home/alex/project/.mcp.json /work/.mcp.json ro bind,nosuid,nodev,noexec
An agent may read those files only if they are visible inside its chroot or mount namespace and allowed by Linux permissions. Executing any capability derived from them still requires CortexFS tool policy.
Skill files are ordinary files visible through the agent mount namespace. CortexFS does not define skill file formats. Skill visibility is determined by mount visibility, Linux permissions, and policy. Skills do not grant authority.
Lifecycle:
start
ready
busy
idle
stopping
dead
The stable ABI does not introduce a global daemon. Prefer each agent process owning its own socket, pid, log, and sessions. A future supervisor is implementation detail. It must not add another root directory.
Agent Home
An agent does not use the user's home directly:
/ctx/home/1000/
agent/
coder/
root/
session/
data/
cache/
log/
tool/
model/
Recommended config:
/ctx/agent/coder.d/root = /ctx/home/1000/agent/coder/root
/ctx/agent/coder.d/cwd = /workspace
Runtime environment:
CTX_ROOT=/ctx
CTX_HOME=/ctx/home/1000
HOME=/home/agent
PATH=/usr/bin:/bin
USER=coder
LOGNAME=coder
SHELL=/usr/bin/bash
TERM=xterm-256color
LANG=C.UTF-8
The runtime starts from an empty environment and sets only the allowlisted
variables above. User session variables, desktop state, provider secrets, and
human MCP secrets must not be inherited by default. CTX_PATH is intentionally
absent from the process environment unless explicitly granted; tsh then uses
CTX_HOME/.tshrc or its default tool path.
The sandbox should also mask host shell startup files that commonly repopulate
environment variables, such as /etc/profile, /etc/bash.bashrc, and
/etc/profile.d.
The runtime-visible tool directory can be a filtered in-memory FUSE projection
of candidate tool tiers for this agent. A tool being present in /ctx/tool
means it is installed system-wide; it does not by itself grant any agent
execution authority.
Mount File
/ctx/agent/<name>.d/mount format:
source<TAB>target<TAB>mode<TAB>options
v0 parser rules:
source and target must be absolute paths
source and target must not contain TAB or newline; return EINVAL if they do
mode is only ro or rw
options is a comma-separated list of small words
unknown option returns EINVAL
Fixed v0 option set:
bind
rbind
nosuid
nodev
noexec
-
- means no extra option. Except for -, options must not repeat. bind and
rbind are mutually exclusive.
Example:
/ctx /ctx ro rbind,nosuid,nodev
/ctx/home/1000/agent/coder /home/agent rw rbind,nosuid,nodev
/home/me/project /work rw rbind,nosuid,nodev
/ctx/shared/project-a /shared/project-a rw rbind,nosuid,nodev
/tmp /tmp rw rbind,nosuid,nodev
Agent Creation
An agent can create another agent only through normal CortexFS objects and
policy checks. There is no root-level spawn/, factory/, or
agent-template/.
architect is the ordinary root agent for lineage:
/ctx/agent/architect
/ctx/agent/architect.sock
/ctx/agent/architect.d/
architect is not a template namespace and does not add inheritance semantics.
It is a normal agent object with a normal label, mount table, policy, socket,
home, and session state. New top-level agents should be created by
agent.create with parent=agent:architect. Child agents created by other agents
must still be attenuated from their direct parent.
base is a retired reference-agent name. ctx bootstrap reports any remaining
base object as would_skip and retains it for manual review; legacy trees
have no manifest that can prove ownership and full control-tree integrity, so
update must not delete it automatically.
The child appears as ordinary agent ABI:
/ctx/agent/reviewer
/ctx/agent/reviewer.sock
/ctx/agent/reviewer.d/
owner
uid
gid
groups
perm
label
iso
parent
life
root
cwd
env
path
mount
model
policy
status
pid
log
parent is a small text file. Keep it simple:
agent:architect
agent:coder
or, when needed:
agent:coder session:default run:01H...
Do not turn lineage into a separate tree.
Child defaults:
owner = parent owner
uid = parent uid
gid = parent gid
groups = subset of parent groups
perm = parent perm
iso = shared
life = owned | temp
A temp child uses the same defaults except:
life = temp
Every agent should have a distinct CortexFS label unless it is intentionally the same security domain. A child that reuses the parent's label is the same security subject for policy purposes.
Agent Tool Visibility
An agent can see and execute only the intersection of:
user-visible scope
CortexFS security context
User-visible scope is derived from ordinary Linux and mount facts:
agent uid/gid/groups
tool file owner/group/mode bits
agent mount table
mount mode and noexec option
CTX_PATH search order
CortexFS security context is derived from stable agent controls:
agent label subject, for example coder_t
agent/<name>.d/perm
agent/<name>.d/policy
tool/<tool>.d/policy
shared/session/mount policy where relevant
Both sides must allow access. A tool that is executable and mounted but not
allowed by policy is invisible for execution. A tool allowed by policy but not
visible to the agent uid/gid/groups or blocked by noexec is also invisible.
Prompts, skills, MCP config files, schemas, and model output never expand this
set.
perm is a coarse ceiling, not a replacement for the other checks. Its
canonical values are the eight Unix-style triplets from --- through rwx.
The r bit gates fs.read, fs.list, and fs.stat; w gates fs.write and
fs.replace; x gates shell.exec and host-like shell/terminal tool objects.
Unknown domain tools remain governed by their normal declaration and policy.
A permitted shell can still perform any operation allowed by its sandbox,
mounts, and Linux identity, so x must not be treated as a read/write sandbox.
The agent terminal path is:
ctx agent start launches bwrap
bwrap starts ctxterm
ctxterm starts tsh
tsh resolves tool names through CTX_PATH
humans observe through ctx agent watch
humans join through ctx agent attach
By default, ctx agent start binds the caller's current directory to
/workspace with read-write access and starts the agent terminal there. The
agent sees the project through the sandbox path, not the host path. Additional
host paths must be declared as sandbox mounts; paths that are not mounted are
not visible to the agent at the Linux filesystem layer.
Filesystem access is granted only when both layers allow it:
sandbox mount exposes the path
CortexFS policy/mount context authorizes the tool or ABI operation
Agents should be granted the tsh terminal capability as their primary shell
tool. tsh is not a host command shell and must not fall back to PATH.
Interactive behavior such as bash, tmux, or zellij is provided by
ordinary visible tool objects with those names.
Child agent attenuation is mandatory:
child permissions must be a subset of parent effective permissions
child policy must be a subset of parent effective policy unless a supervisor grants more
child groups must be a subset of parent groups unless a supervisor grants more
child mounts must be derived from parent-visible mounts
Mount attenuation:
parent rw may become child ro
parent visible may become child hidden
parent ro must not become child rw
parent hidden must not become child visible
A child mount must not expose paths invisible to the parent. For example, a
parent that sees /work and /shared/project-a may grant the child read-only
views of those paths, but not /home/user, /etc, /var/log, or
/shared/project-b unless a supervisor authorizes them.
Owned child agents are cancelled when the parent dies. Parent death cancels the child runtime, not the child's session history. See ctx-coreutils.md for handoff, result, and lifecycle rules.
Names should stay short:
coder
reviewer
planner
runner
worker1
fix-123
rev-123
Put longer descriptions in agent/<name>.d/meta.json.
Tool, Shared, Policy, and Logs
Tool ABI, MCP-projected tools, shared-space access, policy v0, and log placement are normative in tool-policy-abi.md.