Blog / Product
Deploy first, claim later
Who owns an app an agent built? Our answer is "nobody, for an hour". Here is how anonymous deploys, the clock, claim codes and token revocation fit together.
Every hosting product answers the question "whose is this?" before it answers anything else. You can't deploy until there is an account, a project and usually a payment method. The ownership question comes first because, for human developers, it is cheap to answer. You know who you are.
Agents break that ordering. An agent knows what it built, but it doesn't know whose it should be, and it shouldn't be the one deciding. So we flipped the order: deploy first, decide ownership later, and make "nobody" a perfectly valid answer for a while.
This post is about the choices behind that, including a few we argued about.
Most agent-built apps should die#
This sounds bleak. It isn't. When an agent builds something, most of the output is a draft. You look at it, you learn something, you ask for a change, and the draft gets replaced. If every one of those drafts became a permanent project in your account, the account would turn into a junk drawer within a week.
So the default outcome of an anonymous deploy is that it goes away. A stack an agent deploys without credentials runs for one hour and then stops. Nobody has to remember to clean it up, and nobody gets a bill for a preview they forgot about.
An hour is long enough to click around, show it to someone, and decide. It is short enough that abandoned stacks don't pile up. If the agent needs more time, it can extend the clock, but the important thing is that there is a clock, and that keeping something takes a human's decision.
Expiry is not deletion#
The obvious problem with a short clock is that people are busy. You ask an agent for something before lunch, it deploys at 12:10, and you look at it at 14:00. If expiry meant deletion, you would have lost the work for being at lunch.
It doesn't. When an unclaimed stack expires, its processes stop, but the built artifacts and every service's
$DATA_DIR stay on disk for seven days. The claim link keeps working during that time. Claiming an expired
stack brings it back from the snapshot, without rebuilding, at the same URLs and with the same data.
So there are really two clocks. The short one decides how long a thing runs without anyone owning it. The long one decides how long you get to change your mind. Mixing them up is how you end up either deleting people's work or running a free hosting service for spam.
Five minutes before the short clock runs out, the stack emits a stack.expiring event with a plain
message: ask your human to claim this stack, or extend it. An agent that is still in the conversation can pass that
along. One that isn't can at least have it delivered to a webhook.
The claim code is a capability#
When a stack is created anonymously, the response includes a claim code and a claim URL built from it:
{
"status": "live",
"claim_code": "CLM-7KQM-4XHT-R9WD",
"claim_url": "https://agentserve.sh/claim/CLM-7KQM-4XHT-R9WD",
"note": "Unclaimed stacks expire. Give claim_url to your human so they can keep this stack."
}
(That code is made up; yours will differ.) The code is the only thing that grants ownership. Whoever opens the URL and finishes the form gets the stack. There is no "request access" step and no approval queue. That's deliberate: the agent and the human are usually in the same chat, and the simplest secure channel between them is the chat itself.
A few details we cared about:
- Codes survive being read aloud. The alphabet leaves out 0, O, 1, I and L. The claim page
accepts the code in any case, with or without dashes or spaces, with or without the
CLMprefix. Codes end up in screenshots, voice notes and Slack messages, and we'd rather not lose a stack to a typo. - Codes are single-use. Claiming clears the stored hash in the same database update that sets the owner, so two people racing on the same link can't both win.
- Codes aren't stored. The server keeps an HMAC of each code and token, never the value. The response that creates the stack is the only time the code exists in plaintext on our side.
- Codes can be narrowed. If the agent knows who should own the stack, it can pass
claim_email(or set it in the manifest). Then only an account with that email can claim it, even if the link leaks.
If you don't have an account yet, the claim form creates one: an email and a password. If you do, it signs you in. Either way the stack moves into your account, the expiry is cleared, and a new account gets its API key, shown once.
The agent keeps a key, and you can take it back#
The agent that deployed the stack holds a separate secret, the manage token. It's what lets the agent redeploy,
read logs and check status. The CLI and the MCP server keep it in .agentserve/stack.json inside the
project, with a .gitignore next to it, and the MCP server strips it from tool results so it doesn't sit
in the transcript.
Here's the part we debated: after you claim a stack, the agent's token still works.
The argument for cutting it off at claim time is tidy: new owner, new keys. The argument against it is how people actually use this. You claim the app in the middle of a conversation, then you say "great, now add dark mode", and you expect the agent to redeploy. If claiming silently locked the agent out, the very next step would fail for reasons nobody in the chat could see.
So claiming changes who owns the stack, not who can work on it. When you do want the agent out, the project's
settings page in the console has a button that revokes the agent's token. That emits
stack.agent_revoked, and from then on only your API key can manage the project. It's one click, and
it's explicit, which we think beats a surprise.
Agents should never claim on their own#
An agent could, in principle, claim a stack over the API. We let it, under exactly one condition: it has to present the account's API key.
agentserve claim CLM-7KQM-4XHT-R9WD # only works with $AGENTSERVE_API_KEY set
Without a key, the API refuses. The skill file we give agents says it plainly: never claim a stack yourself unless the user gave you their key. The point of the whole design is that a person decides what to keep. An agent that signs up for accounts on its own to keep its work alive would be missing that point.
If you trust an agent enough to give it your key, there's a shorter path anyway: with
AGENTSERVE_API_KEY in its environment, its deploys go straight into your account, with no claim code and
no clock. Anonymous is the default, not the only option.
What we gave up#
This model has costs, and we'd rather name them.
A leaked link is a lost stack. Until it's claimed, anyone with the claim URL can take it. Binding
to claim_email fixes that, but only if the agent knows your email.
The agent's network pays the limits. Since there is no account, the cap on running unclaimed stacks is per network: three at a time by default. A busy agent will hit it and have to destroy or get something claimed first.
We don't email you. The claim link reaches you through the agent, or through a webhook you set up. If the agent forgets to pass it on, the only copy is in its project directory.
We think those are the right trades for now. They keep the first deploy at zero ceremony, they keep the default outcome tidy, and they keep the decision to keep something with a person. The details, including the exact claim flow and every event, are in Claiming.