Skip to main content
Someone asks for a guestbook and gets back a live link. Behind it, the agent built the app and gave it its own Postgres database with Claimable Neon, no Neon account or token needed. The database is free for 72 hours, and theirs to keep once they claim it. This guide is the website builder pattern with a database added: one instance per user, a public port per app, and a publish endpoint in your app, plus a claim flow your server drives through exec.
Paste this into your coding agent

app-builder: this guide as a working app

Everything on this page, runnable: create agents, chat with starter prompts, and get back live apps with an Apps panel that shows each database, its time left, and a Claim button. Hono on the server, React from a CDN in the browser, no build step. Clone it, add your key, npm start.

Who owns what

Three parties touch an app, and each holds only what it needs:
  • Your server holds the sk_live_ key. It decides when a port goes public and drives the claim flow.
  • The agent creates the database and builds the app. It never sees your key, and it cannot publish on its own: it asks your server.
  • Neon holds the database. Until it is claimed it belongs to no one and expires on its own; once claimed, it belongs to the user’s Neon account.
Your server needs no Neon account, API key, or provisioning code. The only database detail it ever handles is the user’s new connection string after a claim, which it passes to the instance once.
1

Create the instance with a publish token

Create it from the agent37-hermes template, exactly like the website builder: env carries the raw publish token into the container, and metadata stores its SHA-256 so your server can verify it later without keeping state.
node
The app tag in metadata marks the instance as this app’s. Check it before every per-instance action (listing, chat, exec, delete), so your server never touches instances another app created in the same workspace. The instance has Node and npx on the path, so the Neon CLI runs without installing anything. Building an app with a database takes more turns than a static page, so this grants a $2 managed-LLM budget (see Budgets).
2

Brief the agent

The gateway has no system-prompt field on POST /v1/responses, so your server prepends the brief to the first turn of each session. It gives every app a folder, a port, and its own database:
the brief, prepended server-side
neon claim create provisions a Neon project and writes three variables into the app’s .env: DATABASE_URL (pooled), DATABASE_URL_UNPOOLED, and NEON_BRANCH. It also leaves a .neon file in the folder that names the project, and saves the credential that lets this machine claim the project later under ~/.config/neon. Both live under ~, the instance’s persistent disk.The brief rides only on a session’s first turn, so any URL in it is fixed for the life of that session. That is fine for a deployed server. If your server’s address can change, as a dev tunnel’s does on every run, have your server write it to a file on the instance before each session’s turns and have the brief read it (curl -s -X POST "$(cat ~/.app-builder/publish-url)/api/publish" ...); the example app does exactly that, so old chats keep publishing after a move.start.sh loads .env with set -a; . ./.env rather than node --env-file=.env: --env-file never overrides a variable already set in the container, so an instance-level DATABASE_URL would silently win. The one script is what the boot hook runs, what the agent reruns after an edit, and what your server reruns after a claim.
3

Verify and publish in your endpoint

Your endpoint checks the token against the instance’s metadata hash, applies your port policy, and creates the public port. Labeling the port with the app’s slug is what later lets you match a URL to an app folder:
node
The agent reaches this endpoint over the internet, so in local dev give your server a public URL first (cloudflared tunnel --url http://localhost:3000, plus --protocol http2 on networks that block QUIC).
4

Show each app's database

Every app folder with a .neon file is an app with a database, and neon claim status reports where its claim stands. Your server reads both from the instance with exec, so it stores nothing:
200
project_expires_at is 72 hours after creation: that is when Neon deletes an unclaimed project. Show it next to the app’s URL so the user knows how long they have.
5

Let the user claim it

A claim moves the project into the user’s own Neon account, data included. Your server mints a claim code on the instance and gives the user the link:
stdout
Open verification_url for the user. They sign in to Neon (or sign up) and accept the project. The code is good for 15 minutes, and minting a new one cancels the previous unused code, so a second click is harmless. Pass the project id explicitly, as here, and the command works from any directory.Treat the link like a password: whoever opens it first and signs in gets the database, data included. Show it only to the user who owns the app, and keep it out of logs. The credential that mints it sits on the instance, so the agent could mint one too; that is one more reason the agent’s instance should belong to that one user.state moves from unclaimed to pending (a code is out), then accepted and reconciled. A code nobody used reads expired once its 15 minutes pass; that is the code, not the database, which stays claimable until project_expires_at. Once a claim reconciles, the CLI deletes its local credential for the project, and neon claim status on the instance fails from then on with “identity assertion … is missing”, so record the last state you saw if your UI needs to keep saying “claimed”.
6

Reconnect the app after the claim

Claiming revokes the temporary password: the app’s old DATABASE_URL now fails with password authentication failed. The user’s Neon account issues a new connection string for the same database, same host, new password, in the Neon Console. Write it into the app’s .env and rerun start.sh, and the app is back on the database the user owns, with every row it had:
node
The check is what keeps this safe. Without it, a pasted value such as postgresql://u:p@h/db"$(command)" would run command on the instance the next time start.sh loads .env. The command also drops DATABASE_URL_UNPOOLED, since it carried the revoked password.

Worth knowing

  • Unclaimed means temporary. Neon deletes an unclaimed project 72 hours after creation, and caps it at 100 MB of storage and 1 GB of transfer. Before a claim, a project has Postgres (plus Neon Auth and the Data API when requested); Functions, Object Storage, and AI Gateway need a claim first. See Claimable Neon for the current limits.
  • The claim credential is on the instance’s disk. Deleting the instance loses the ability to claim its unclaimed databases, which then expire on schedule; claimed databases belong to the user and are untouched. A fork copies the credential along with the files, so the fork can claim the same project.
  • Pin the CLI for unattended runs. The guide uses npx neon@latest, but your server runs the CLI on every user’s instance with that instance’s environment. The example app pins the exact version it was tested with, so a new release reaches your users only after you have tried it.
  • Keep your server private. Only /api/publish has to be reachable from the internet. The example app listens on 127.0.0.1 and accepts writes only as application/json, which forces a CORS preflight it never answers, so other sites cannot drive it from a browser.
  • The app survives restarts. The boot hook reruns every start.sh, and the files are on the persistent disk, so a published URL keeps serving across restarts, updates, and recovery. The home directory and the database together are the app’s whole state.
  • Public means public. Anyone with an app’s URL reaches it, and a request wakes a sleeping instance, which bills compute. See rules and limits.
  • Chat, streaming, and sessions are the standard chat app wiring; the publish flow is the website builder’s.