axon.config.ts
Every agent has one. It declares what capabilities the agent loads and what the capsule is allowed to do. Everything else in the folder is discovered automatically — this is the one file Axon explicitly reads at boot.
export default defineAgent({
modules: [
"@axon/fs",
["@axon/github", { owner: "acme", repo: "backend" }],
],
policy: {
fs: { read: ["./**"], write: ["./src/**"], deny: [".env"] },
net: { allow: ["api.github.com:443"] },
shell: { allow: ["git", "bun"], args: { git: { deny: ["push --force*"] } } },
},
workspace: true,
})
Everything in one TypeScript file means the agent is readable at a glance, and one changed
file is one diff to review. It also means config can hold real logic — the escalate
callback does pattern matching and context-sensitive decisions that a JSON file could not
express.
model
Which model drives the agent's cortex. A string, and a preference rather than a constraint.
export default defineAgent({
model: "codex:gpt-5.6-terra", // a specific route
// model: "gpt-5.6-terra", // whichever route supplies it
})
Resolution tries the pin first and falls back to ordinary ranking when the user's providers cannot supply it — so a published agent stays runnable by someone who has no Codex connection, rather than carrying its author's account setup as a hard dependency.
Applies to the cognet's primary role only. Which model fills a percept or compression role is resolution's business; choosing those by hand is the wiring Axon exists to abolish.
Most agents omit this too. Declare it when the agent genuinely reads better on a particular model.
providers
Where inference comes from. Most agents declare nothing here — and that is the intended state.
A user's providers live on their profile, and every agent they run inherits them:
// ~/.axon/profiles/<you>/profile.config.ts — the inference you HAVE
export default defineProfile({
providers: [Axon(), Codex(), Ollama()],
})
// axon.config.ts — usually nothing at all
export default defineAgent({})
That split is what makes installing an agent a download rather than a setup. The user says what they have; the cognet says what roles it needs; the runtime matches them at boot. There is deliberately no way to say "use this model for that role" — a role name belongs to one brain's private vocabulary, and typing it into a profile would couple a user's machine to the internals of whatever cognet they happen to run.
An agent declares providers: only for a source its user would not otherwise have —
a self-hosted endpoint it ships against, a local daemon it assumes:
export default defineAgent({
providers: [Ollama({ url: "http://box.local:11434" })],
})
Additive, never a replacement. The agent's entries are appended after the profile's and deduplicated by provider name, first wins. An installed agent can add a source; it can never displace or remove one, because the machine belongs to the person running it — an agent quietly rerouting their inference is the failure this split exists to prevent.
A profile that has never declared providers: gets the managed route (Axon()), which
needs no setup beyond being signed in. An explicitly empty array is a real answer and
is honoured: the boot then fails loudly, naming the roles nothing could fill.
See engines for every option each factory takes.
engine: is removed. It named one model for the whole agent, which could not survive
a cognet declaring several roles — and it put the choice in the wrong place besides.
Replace engine: Codex({ model: "gpt-5.6-terra" }) with model: "codex:gpt-5.6-terra",
and move the source itself to your profile's providers:.
modules
The capabilities this agent loads. An array of entries: a module name, or a
[module, options] tuple.
export default defineAgent({
modules: [
"@axon/fs", // no options
["@axon/github", { owner: "acme", repo: "backend" }], // with options
["@axon/slack", { channel: "#eng-alerts" }],
],
})
A local module's config can be imported directly instead of named:
import DiscordModule from "../modules/discord/module.config"
modules: [[DiscordModule, { mentionOnly: true }]]
Options are validated against the module's declared schema and passed to its setup() as
ctx.options. A missing required option fails boot with a clear error.
Only put configuration the module declares as an option here. Not secrets — those belong
in .env. Not logic — modules handle their own setup.
This block applies to modules already present in modules/. To add one:
axon install @acme/github
That copies the source in and patches axon.config.ts with any policy the module needs.
See axon install.
policy
What the agent process may do — filesystem, network, commands, plus resource limits
and an escalate callback for decisions that need context.
export default defineAgent({
policy: {
fs: {
// Only these exist inside the box — nothing to deny, because
// anything ungranted is already absent.
read: ["./src"],
write: ["./src"],
},
net: { allow: ["api.github.com:443"] },
shell: { allow: ["git", "bun"], args: { git: { deny: ["push --force*"] } } },
},
})
A missing block means unrestricted access in that domain — no fs block and the
capsule reads and writes anywhere. For production agents, always declare explicit rules.
policy is the full reference: every field, both enforcement layers, escalation, and the profile ceiling that sits above this block.
server
The agent's HTTP server. Relevant when it runs with one — axon dev, axon deploy, or
Axon({ server: { port } }).
export default defineAgent({
server: { auth: "axon" },
})
auth | Behaviour |
|---|---|
"axon" | Enforce Axon Cloud JWT. Requires AXON_JWT_PUBLIC_KEY in env. |
false | No auth. All routes public. |
| a function | Custom handler — throw to reject. |
Omitted, the runtime auto-detects: JWT auth if AXON_JWT_PUBLIC_KEY is set, otherwise
open, which is what makes local dev work with no extra setup.
server: {
auth: async (event) => {
const token = getHeader(event, "x-api-key")
if (token !== process.env.MY_API_KEY) throw createError({ status: 401 })
},
}
The handler receives an H3 event, so any H3 utility works inside it. Throw to reject, return normally to allow.
deploy
How the agent is packaged and where it runs. All fields optional — omitting deploy
entirely uses Axon Cloud defaults.
export default defineAgent({
deploy: {
target: "axon",
scaling: { min: 0, max: 3 },
runtime: { packages: ["git", "ripgrep"] },
},
})
target — "axon" (default) deploys to Axon-managed Cloud Run, one command, no
infra. "self" makes axon bundle produce a .agent/ folder with a Dockerfile and a
source tarball, to run anywhere that takes containers. See Deploy.
scaling — Cloud Run instance count, target: "axon" only. min: 0 scales to zero
when idle (default); min: 1 keeps one warm, trading cost for cold-start latency.
runtime.packages — system packages installed into the container via apk, for
anything your tools need beyond a standard Node environment. Installed at build time, so
adding one requires a redeploy.
connections
External triggers that invoke the agent. Currently cron schedules.
export default defineAgent({
connections: {
schedule: [
{ name: "daily-digest", cron: "0 9 * * *", timezone: "Europe/London" },
{ name: "hourly-check", cron: "0 * * * *" },
],
},
})
Axon provisions a Cloud Scheduler job per entry; at the given time the platform POSTs to
/_axon/inbox/{name}. Handle it in a server route:
// server/api/_axon/inbox/daily-digest.post.ts
export default defineEventHandler(async () => {
const { stream } = axon.scripts.stream("run-digest")
for await (const _ of stream) {}
})
name must be unique within the agent — it becomes both the inbox path and the scheduler
job name, so no spaces or special characters. Schedules apply only when deploy.target is
"axon"; axon dev ignores them.
workspace
export default defineAgent({ workspace: true })
Opts the agent into the nearest .agents/ folder up the directory tree. Axon walks upward
from the working directory, finds the workspace layer, and merges its tools, prompts,
scripts and modules into the agent's runtime. See Workspace
Agents.
Environment
Environment variables are not declared here. The agent's .env is the source of
truth, like any other project — putting a key there is the explicit act of granting the
agent that key. Which vars the capsule can actually see is a
policy question. See Environment &
Secrets.