contenox

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.

.contenox/
agents/
reviewer.md # one agent, one file
triage.md # another
agents.toml # the knobs a declaration cannot reach
.claude/agents/ # already have agents? read where they are

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.

$ curl -fsSL https://contenox.com/install.sh | sh
PS> irm https://contenox.com/install.ps1 | iex

Installs contenox. Run contenox init to scaffold a workspace.

any model one binary no account your rules in a file you can read
GitHub stars Latest release Apache-2.0

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.

.contenox/agents/
triage/
agent.md # reads the request, answers with one label
code/
agent.md # the branch that label routes to
recovery.md # its second attempt, when the first stops short
docs/
agent.md # tools: Read, Glob, Grep — it cannot write
failure.md # what it says when every branch has given up

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.

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.

Terminal demo: a destructive rm command stops at a human approval gate before it runs

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.

# fire a declared agent under a strict envelope
$contenox mission fire billing "reconcile the vendor invoices flagged this week"
mission fired · envelope hitl-policy-strict.json
the agent pulls the ledger and the invoices — allowed rules pass silently
⏸ the agent asks: three invoices disputed, credit notes drafted — send them?
no answer: run checkpointed, ask saved, process released
# close the laptop. restart the box. days pass. then, from anywhere:
$contenox approvals list
ask 8f3c · send the drafted credit notes? · mission 01jm…
$contenox approvals respond 8f3c --answer "yes, send them"
checkpoint resumed — exactly once. mission landed.
$contenox inbox list
report · billing: three disputes, credit notes sent — waiting for you

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.

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.

$ curl -fsSL https://contenox.com/install.sh | sh
PS> irm https://contenox.com/install.ps1 | iex

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.

Esc to close