Agentfile Core Profile
Version: 0.1.0-draft.6 — Working Draft
Status: Working Draft (# syntax=agentrc.agentfile/v0.1)
Date: 2026-06-30
Audience: compiler / frontend authors (the agentrc BuildKit frontend and the agentrc / arc CLI)
The normative keywords MUST, MUST NOT, SHOULD, and MAY follow RFC 2119. This profile is the compile-side companion to the specification; where the two disagree, the spec wins.
Purpose
The Core Profile defines what it means to read a Dockerfile-shaped Agentfile and
compile it into an OCI artifact — namely, the ai.agentrc.* image labels plus
the layers that carry the agent’s /mnt tree. It is the contract a compiler
implementation claims: recognize the four agentrc keywords (IDENTITY,
CAPABILITY, SOP, POLICY) and the standard Dockerfile keywords, and emit the
labels and layers exactly as the spec’s translation tables require.
This profile covers build, not run time. What a platform does with the labels
(grant / narrow / reject, enforce via Cedar, project /mnt) is the
Platform Conformance Profile and the
Enforcement (Cedar) Profile; how the labels and package are
shaped is the OCI Labels & Package Profile.
A small core is deliberate. The Agentfile adds only four keywords to a model developers already know; everything else is a standard Dockerfile keyword. Keeping the new surface tiny is what lets two independent compilers — the BuildKit frontend and the native CLI — produce byte-for-byte identical artifacts.
Keyword summary
There are four new keywords. Everything else is a standard Dockerfile keyword, used as-is or with a documented extension. A conformant compiler MUST recognize all of these.
| Keyword | Origin | Role in agentrc |
|---|---|---|
IDENTITY |
New | Who / what the agent is: name, version, author, description. |
CAPABILITY |
New | What modalities / patterns the agent supports (text, streaming, multimodal, …). |
SOP |
New | The agent’s system prompt / objective / standard operating procedure. |
POLICY |
New | Typed, namespaced request for a resource, model, or operational constraint. |
FROM |
Dockerfile | Base image and agent inheritance (FROM another-agent). |
CMD |
Dockerfile | The agent’s invocation surface — the framework / loop to run. |
COPY |
Dockerfile | Add local tools, skills, MCP bundles, or an SOP file into the /mnt tree. |
ADD |
Dockerfile (extended) | Add remote resources via --remote plus delivery flags. |
HEALTHCHECK |
Dockerfile | Liveness probe; MAY invoke a projected tool. |
LABEL |
Dockerfile | Standard OCI metadata; available for hand-authored ai.agentrc.* metadata. |
ENV / ARG / WORKDIR / USER / EXPOSE / RUN |
Dockerfile | Standard semantics; available, unchanged. |
There is no
TOOL,MCP,SERVER,FUNC,CRED,SECRET,AUDIT,MOUNT,MEMORY, orRATELIMITkeyword. Tools / skills / MCP are added withCOPY/ADD; memory / context / model / CPU arePOLICYrequests; audit rides onagent.hooks.*; secrets are deferred. Any earlier draft that recognized those keywords is stale and MUST NOT be revived by this profile.
The /mnt projection layout
The destination path under /mnt is what tells the compiler what a resource is.
There is no per-type keyword; a file copied to /mnt/tools/x is a tool, one
copied to /mnt/mcp/x is an MCP bundle, and so on. The compiler MUST use this
layout:
/mnt
├── tools/ # each tool is an executable; argv in, structured output out
├── skills/ # skill bundles (a directory of instructions/scripts/resources)
├── mcp/ # MCP server bundles or configs
├── proc/ # runtime-populated (live policy, identity, budgets, audit tail)
└── SOP # the agent's system prompt, as a readable file
/mnt/proc is populated at run time by the platform, not by the compiler. See the
/mnt Projection Profile for the full layout.
Required behavior
An implementation claiming this profile MUST:
- read a text file named
Agentfileor an explicitly supplied path (-f Agentfile); - ignore blank lines and comments (
#), while honouring the# syntax=agentrc.agentfile/v0.1parser directive on the first line; - parse the four agentrc keywords and the standard Dockerfile keywords listed above, case-sensitively;
- capture an
SOP <<EOF … EOFheredoc verbatim, without interpreting its inner lines as Agentfile instructions, and embed it as the readable file/mnt/SOP; - accept the inline and file-backed
SOPforms and embed each at/mnt/SOP, emitting a pointer + digest label — never the full prompt text; - honour the
ADD --remotedelivery flags —--cached(default) /--runtimeand--fail-if-unavailable(default) /--warn-if-unavailable, plus standard--chmod/--chown— fetching--cachedresources at build and recording--runtimeresources as references; - use the destination path under
/mntto classify eachCOPY/ADDresource as a tool, skill, MCP bundle, or SOP; - translate authored intent into
ai.agentrc.*labels and/mntlayers exactly per the spec §9 tables (reproduced below); - auto-derive an explicit, attributed
networkegress label from any URL inagent.hooks.*oragent.interrupt_endpoint, never a silent egress; - report line numbers for parse failures, and reject unknown agentrc keywords rather than silently dropping them.
A POLICY line is authored in short form (no ai.agentrc. prefix); the
compiler prepends the namespace when it emits the label. The compiler never
evaluates a POLICY — it only records the request. Whether a request is granted,
narrowed, or rejected is a platform decision.
Unknown substrate.<token>.* keys parse; foreign platform keys are ignored.
A platform-scoped request substrate.<token>.* (spec §8.7) is a KEY under the
existing substrate.* namespace, not a new namespace. The compiler MUST accept
any platform token — including tokens it does not recognize — and emit the
corresponding ai.agentrc.substrate.<token>.* label verbatim; an unknown token
is NEVER a parse error (a linter MAY warn). At enforcement time a platform simply
ignores keys scoped to a different platform. The compiler does not judge which
tokens are “real”; it records the request and lets the platform pick the keys
scoped to itself.
Build translation (compile targets)
These are the tables the compiler MUST implement. They are reproduced from the specification so the compile-side contract is self-contained.
POLICY → labels
Each POLICY <key> <value> becomes ai.agentrc.<key>=<value>.
| Authored | Emitted label |
|---|---|
POLICY agent.idle_timeout 5m |
ai.agentrc.agent.idle_timeout=5m |
POLICY agent.context.type autocompressed |
ai.agentrc.agent.context.type=autocompressed |
POLICY agent.hooks.pre https://hooks.internal/pre-step |
ai.agentrc.agent.hooks.pre=https://hooks.internal/pre-step (+ auto-derived egress) |
POLICY agent.sub_agents.max 5 |
ai.agentrc.agent.sub_agents.max=5 |
POLICY substrate.runtime.memory 8gb |
ai.agentrc.substrate.runtime.memory=8gb |
POLICY substrate.ptty true |
ai.agentrc.substrate.ptty=true |
POLICY model.name claude-opus-4 |
ai.agentrc.model.name=claude-opus-4 |
POLICY model.capability vision |
ai.agentrc.model.capability.vision=true |
POLICY network dns:api.github.com:443 |
ai.agentrc.network.dns.api.github.com=443 |
For an auto-derived egress, the compiler MUST emit both the egress label and its attribution, so it is never a silent network hole:
ai.agentrc.network.dns.hooks.internal=443
ai.agentrc.network.dns.hooks.internal.source=auto:agent.hooks.pre
IDENTITY / CAPABILITY / SOP → labels
| Authored | Emitted label(s) |
|---|---|
IDENTITY name=claims-triage version=1.0 |
ai.agentrc.identity.name=claims-triage, ai.agentrc.identity.version=1.0 |
CAPABILITY streaming |
ai.agentrc.capability.streaming=true |
SOP … / COPY ./sop.md /mnt/SOP |
ai.agentrc.sop=/mnt/SOP, ai.agentrc.sop.sha256=<digest> (pointer + digest, never full text) |
Resource delivery → layers + labels
| Authored | Build behaviour | Emitted label(s) |
|---|---|---|
COPY ./tools/x /mnt/tools/x |
Embed file as a layer. | ai.agentrc.tool.x=local |
ADD --remote --cached <url> /mnt/tools/x |
Fetch at build, embed as a layer. | ai.agentrc.tool.x=<digest> + ai.agentrc.tool.x.origin=<url> |
ADD --remote --runtime <url> /mnt/mcp/x |
Do not embed; record reference. | ai.agentrc.mcp.x=runtime:<url> |
For every embedded MCP server or skill, the compiler MUST emit both the resolved digest and the origin reference, so a platform can re-point to a mirror at deploy time without rebuilding:
ai.agentrc.mcp.github=sha256:abc123...
ai.agentrc.mcp.github.origin=https://registry.agentrc.io/mcp/github:latest
Policy encoding
The compiled request set MUST be retrievable in either form, and the build MAY
support both, selected by --policy-mode:
- inline — values live directly in labels (best for small request sets);
- digest — labels carry a digest of a structured manifest embedded as a layer (best for large request sets).
Which form is the default for arc build is
open decision #1 — the --policy-mode inline|digest flag is the seam.
Identical output from both front doors
agentrc has two build paths — the BuildKit frontend invoked by the
# syntax=agentrc.agentfile/v0.1 directive, and the native agentrc / arc CLI
(see the CLI page). They are two front doors to the same compiler.
A conformant implementation MUST produce identical OCI artifacts — same layers, same
ai.agentrc.*labels, same digest — from the same Agentfile, whether built via the frontend or viaarc build. This is the single most important conformance property of this profile and is exercised adversarially by the conformance suite (build-labels-identical).
Worked translation
A minimal Agentfile:
# syntax=agentrc.agentfile/v0.1
FROM python:3.11-slim
IDENTITY name=claims-triage version=1.0 author=acme
CAPABILITY text
CAPABILITY streaming
SOP You are a claims-triage specialist. Escalate anything ambiguous to a human.
CMD claude --print
COPY --chmod=755 ./tools/file_read /mnt/tools/file_read
POLICY model.name claude-opus-4
POLICY network dns:api.github.com:443
compiles to layers carrying /mnt/tools/file_read and /mnt/SOP, plus these
labels in the image config:
ai.agentrc.identity.name=claims-triage
ai.agentrc.identity.version=1.0
ai.agentrc.identity.author=acme
ai.agentrc.capability.text=true
ai.agentrc.capability.streaming=true
ai.agentrc.sop=/mnt/SOP
ai.agentrc.sop.sha256=<digest>
ai.agentrc.tool.file_read=local
ai.agentrc.model.name=claude-opus-4
ai.agentrc.network.dns.api.github.com=443
The platform reads those labels — never the Agentfile source.
Validation recommendations
A compiler / linter SHOULD warn when:
IDENTITY name=is missing in an artifact intended for publication;FROMuses a mutable tag such aslatest;- an
ADD --remoteuses--runtimewithout a clear failure mode, so the default--fail-if-unavailableis recorded explicitly; - a
COPY/ADDtargets a path outside/mntfor a resource that is meant to be projected (it will not be classified as a tool / skill / mcp); - a removed keyword (
TOOL,MCP,CRED,MOUNT,RATELIMIT, …) is encountered — it is stale and MUST be rejected, with a pointer to theCOPY/ADD/LABEL/POLICYreplacement.
Compatibility note
The reference compiler — the BuildKit frontend and the arc CLI — is in progress.
This profile defines the target compile semantics; an implementation SHOULD
publish a support matrix rather than implying it emits every label in the tables
above. Do not advertise a profile you do not pass.