An agent server. Every AI product runs one. Nobody ships you one.
Before Apache, serving a website meant writing your own server. Everyone building an agent today writes their own harness. contenox is the harness as infrastructure: you install it, and the agent becomes the thing you author.
You don't build an agent. You declare one.
Markdown with a YAML frontmatter header — the same file Claude Code reads, and the same one Copilot, Cursor, OpenCode and Antigravity keep. Drop it in and the next run picks it up: no build step, no plugin API, nothing to convert.
Installs contenox. Run contenox init to scaffold a workspace.
The argument
Apache made serving something you install. HTML became something you author.
In 1994 you did not serve a website, you wrote a server: parse the request, hold the connection, decide what to send — all of it welded to the content it was there to deliver. Then the server became a thing you installed, and the two came apart. HTML was authored by people who never touched a socket, and the web that followed was built by them.
Every team building agents today is back on the wrong side of that line, hand-rolling the same machine: the loop, the tool gate, the approval flow, session persistence — welded to one prompt, rewritten at the next company. contenox makes that machine infrastructure and the declaration the artifact. The engine is not the interesting part; who holds its controls is.
Apache, 1995
contenox, now
You install the server.
You install the runtime.
You author HTML. The server never writes a page for you.
You declare agents in Markdown. The server ships no agent of yours.
Modules extend it. Apache shipped no websites.
MCP servers and the editor's own capabilities supply tools. You bring the ones you need.
It served whatever HTML you already had.
It reads the declarations you already keep — Claude Code, Copilot, Cursor, OpenCode, Antigravity.
Connection handling is infrastructure. Nobody writes their own.
The durable ask is infrastructure: a run stops, checkpoints, survives a restart, resumes when a human answers.
Five agents ship in the box. They are the default page, not the product — an example of the artifact, there to be replaced by yours.
The artifact
An agent is a file. A workflow is a directory.
A declaration is Markdown with a YAML frontmatter header: a name, the tools it may call, the model, and a body that becomes its system prompt. Put declarations in a directory and the directory is the workflow — the agent.md at the top reads the request, answers with one label, and the branch of that name takes it from there, with its own instruction, its own tools, its own budget.
default: in the router's frontmatter names the branch an unrecognised answer falls to — the narrowest one, never the most capable. contenox compiles the directory into the chain that runs it and the policy it runs under, into .generated/, on the next run. Both are JSON Schema-validated, both are yours to read, and neither is yours to maintain.
- →A branch is a specialist because of what it holds, not how it is phrased: name its tools and every other tool is withheld where calls execute, rather than merely left out of what the model was offered.
- →A support desk sends billing questions to a read-only branch holding the billing API, and account changes to one that may write but stops for a person.
- →A back office sends “summarise this” and “extract these fields” to branches with different output contracts, so callers get parseable output without asking for it every time.
- →A regulated deployment sends anything touching regulated data to a branch under a stricter envelope, however the request was phrased.
You write the rules
How much agency? You decide — in writing.
Every run is bounded by an envelope: a JSON policy naming what passes silently, what pauses for a human, and what is denied outright — plus hard ceilings on tool calls and tokens, and a pin on which models and backends the run may use. No hidden prompt decides for you. The file lives in your repo and gets reviewed like any other change.
{
"default_action": "approve",
"compute": { "maxToolCalls": 300, "maxTokens": 2000000, "onExhausted": "finish_stuck" },
"rules": [
{ "tools": "local_fs", "tool": "*", "action": "deny",
"when": [{ "key": "path", "op": "glob",
"value": "**/{.ssh,.aws,.kube,.config/gcloud}/**" }] },
{ "tools": "local_fs", "tool": "read_file", "action": "allow" },
{ "tools": "local_fs", "tool": "write_file", "action": "approve" },
{ "tools": "local_shell", "tool": "local_shell", "action": "allow",
"when": [{ "key": "command", "op": "command_prefix_allowlist",
"value": "go test,go vet,ls,cat,grep,npm test,pytest" }] },
{ "tools": "local_shell", "tool": "local_shell", "action": "approve" }
],
"attention": { "allowAgentAnswers": false }
} Trimmed from the shipped default preset. Anything no rule matches fails closed — it asks a human. Six presets ship with contenox init, and contenox vet checks your policy before anything runs under it.
The durable ask
The run stops. The process exits. Days later, one answer resumes it.
Any harness can pause for a human while it holds the connection open. Holding a connection is not the hard part — surviving the wait is. A run under contenox checkpoints where it stopped, releases the process, and picks up from that exact point when someone answers, from anywhere.
No babysitting a terminal, no approval that expires because you were asleep, no silent change while you were away. The envelope decided what could run unattended, the declaration decided nothing beyond it, and one answer resumed the run exactly once.
Tools
Bring the tools you need. Nothing else.
Apache shipped modules, not websites. contenox owns the tool boundary and you decide what stands on the other side of it. Every tool you do not need is tokens burned on every turn, and one more thing to govern. Tools cross that boundary two ways, and both of them are yours to choose.
From the client
An ACP client — your editor — negotiates fs/* and terminal/* as capabilities. contenox forwards the call and the client performs it, in the workspace you already have open. local_fs is five tools: read_file, write_file, edit_file, sed, read_file_range. Listing and search go through the shell, on the client's side of the line.
From the operator
Attach any MCP server — stdio, SSE or HTTP — or any HTTP service with an OpenAPI spec. It registers once and its tools become policy-scoped tools an agent names like any other. A declaration can also bring its own, reachable by no other agent and retired when you delete the file.
Whichever side a call comes from, it crosses one boundary and is checked against one envelope before it runs.
Two shapes
A subprocess in your editor, or a host on a box.
Same runtime, same declarations, same envelope, same session state. What you approved in the editor is what runs from cron.
Launched by your editor
Zed, JetBrains, AionUi or OpenClaw starts contenox as an ACP subprocess over stdio — no plugin lock-in, and approvals route through the editor's own permission UI. /pair makes that session reachable from the app, so an ask that lands after you shut the lid still gets an answer.
contenox serve
A host: the same runtime with no editor in front of it. It holds the relay connection open and stays up until you stop it — a headless box that takes missions and reaches the MCP servers you attached. A headless acpx session gets the hardened profile, where writes and network are denied outright rather than offered for approval.
Yours by default
Your machine, your state, your models.
- →Sessions, config, run logs, and captured execution state live on your machine. No account, no contenox service in the loop — unless you pair with the optional relay below. Telemetry is opt-in and off by default.
- →Inference is configuration: local Ollama or vLLM when nothing may leave your network, or OpenAI, Anthropic, Gemini, Bedrock, and Vertex on your own keys, pinned to a region you choose. Swapping later is a config change, not a rewrite.
- →Secrets resolve from your environment at request time and never land in config on disk. Every envelope denies .ssh, .aws and .kube under every permission setting, including the loosest one.
Optional: the hosted relay
Your session, on your phone.
Everything above runs without an account. When you want a running session reachable from elsewhere, pair the machine with the hosted relay: one typed key from the contenox app, no browser on the machine. The machine dials out and sends exactly two things — the key and its hostname — and from then on you read the transcript and answer approvals from your phone. Free for you and three teammates, one machine each — and an install that never pairs contacts no relay at all.
We also provision and run contenox agents for you, managed and on your terms. Tell us what the work is and we will get you set up: talk to us see the hosted app
Get started
Declare an agent. Fire your first mission.
Questions? hello@contenox.com · Issues and PRs on GitHub
Built on
Local models connect through Ollama or vLLM over their local APIs; hosted providers over their public APIs. No bundled inference runtime, no GPU drivers to ship.