ConnectOnionAll Posts

Design Journal · August 15, 2026 · ConnectOnion 1.6.5

The Agent That Was Itself, and Billed Someone Else

A deployed agent now runs on its own account. Getting there meant deciding what a process should inherit when it leaves the machine that made it.

In 1.6.5, co deploy --to stops sending the operator's identity to the server. The agent that comes up on the other side has its own address, its own mailbox, and its own balance.

The decision

A deploy carries the project. It does not carry the person who deployed it.

The problem, and why nobody saw it

co init writes four identity lines into a project's .env on purpose. They are what makes a project run as you while you are building it on your laptop. That is the right behaviour locally, and it is the whole difficulty: the file that makes a project yours is the file a deploy was copying to the server verbatim.

What travelledEffect on the server
AGENT_EMAILOverrode the mailbox the agent derives, so it sent mail as its author
OPENONION_API_KEYBilling keys off the JWT, so every model call charged its author
AGENT_ADDRESSSet nothing, but it is what people read to learn who an agent is
IS_EMAIL_ACTIVETravelled with the rest

So the deployed agent was cryptographically itself and financially its author. It signed with its own key, answered on its own address, and passed every check an operator would think to run. Then it sent mail from its author's mailbox and charged every model call to its author's balance.

Nothing errored. There was no failed request to notice, no warning line in a log, no reconciliation that came out wrong. One agent ran that way for nine days. The reason it lasted nine days rather than nine minutes is the part worth keeping:

Every observable signal agreed with itself. The identity checks passed because the identity was real. The billing was consistent because it consistently charged one account. A wrong answer that is internally coherent survives far longer than one that is merely wrong.

It also could not be undone after the fact. usage_logs has no column naming the machine that made a call, so once two agents bill the same account there is no query that separates them. The cost of the bug was fixed at the moment it started, not at the moment it was found.

Alternatives we considered

Strip nothing, and document it

Tell operators to remove the identity lines before deploying. This is what the situation already implied, and it is why the bug existed: a rule that lives in documentation is enforced by whoever remembers it at 2am. The default has to be correct, because the default is what runs.

Strip the whole .env

Simple, and wrong in the other direction. Projects legitimately carry configuration a deployed agent needs — provider keys for tools, endpoints, feature flags. Refusing to send any of it would trade a silent billing error for a loud broken deployment, which is better but still not right.

Withhold the identity keys and substitute the agent's own

What shipped. _env_for_server holds back the four identity lines and supplies the agent's own credentials in their place, derived from the key the deploy is already using to reach the machine. Ordinary configuration travels untouched. The distinction is not "secret vs not secret" — it is whose a value is.

The tradeoff we accepted

A deployed agent now needs an account of its own, which means a deploy can fail for a reason it never used to: the agent has no balance. We think that is the correct failure. An agent with no funding should say so, rather than quietly spending someone else's. But it is a real change for anyone who deployed expecting the operator's balance to cover it, and the release notes say so plainly.

Evidence

The regression test was checked in the only way that proves anything: it was run against the unpatched code first. Reverting the three source files and keeping the test fails ten tests; restoring the fix passes them. A test that has never been red is a test that has never demonstrated it can catch the thing it is named after.

What would make us revisit this

If usage_logs ever records the machine alongside the account, the "cannot be separated afterwards" half of this problem goes away, and a more permissive model — a deliberately shared account across an operator's fleet — becomes defensible. That is a real deployment pattern and we are not ruling it out. It just cannot be the default, and it cannot be reached by accident.

Upgrading

pip install --upgrade connectonion   # 1.6.5 or later, stable
co deploy --to prod

Existing deployments keep working. Redeploy to move an agent onto its own account.

See also Why Alpha, Beta, and RC Come Before ConnectOnion 1.7 LTS for why this is a 1.6 patch rather than part of the 1.7 preview train.

Star us on GitHub

If ConnectOnion saves you time, a ⭐ goes a long way — and earns you a coffee chat with our founder.