Launch a starter app and remix it live
This is the fastest path from nothing to a working, deployed app, and the template for the "watch your agent build it" demo. You pick a curated starter, scaffold it in one call (its database is provisioned for you), then remix it live by editing the app and redeploying. When you're ready for a real app, the same loop grows it: add a secret the agent never sees, lock an admin route, attach a custom domain.
Audience: agents. These are tool calls in order, with what "done" looks like at each step. Everything here is drivable over
POST /mcp. No dashboard required.
The curated starters
Four DB-backed starters exist specifically to be launched-and-remixed. Each is a real, useful app that runs inside the Explore (free) floor:
| Stack | What it is | Good for showing |
|---|---|---|
kanban | Trello-style board (columns, cards, labels) | the best live-edit demo |
todo | To-do / task list (lists, due dates) | the safest, fastest first deploy |
link-in-bio | Linktree-style public links page | sharing a *.deploymill.app link |
event-rsvp | Event page + public RSVP + guest list | a shareable, time-bound page |
All four are Hono + Postgres. They declare a managed database in their manifest, so start_project provisions Postgres and injects DATABASE_URL before the first build. The app comes up working in a single call, with no follow-up reconcile_project.
Discover them at runtime. Don't hardcode the list:
list_templates()
// → includes { key: "kanban", … }, { key: "todo", … },
// { key: "link-in-bio", … }, { key: "event-rsvp", … }
Step 1: Launch it
start_project({ name: "my-board", stack: "kanban" })
// Creates the repo, scaffolds the app, PROVISIONS managed Postgres + injects
// DATABASE_URL, deploys main, and attaches the prod domain — one call.
// → { ok: true, url: "https://my-board.deploymill.app", … }
Done when: list_deployments shows the latest deploy done and the url serves the seeded app (the board has its three columns and example cards). The build is the slow part (~1–3 min); if your client times out, the deploy keeps running server-side. Poll list_deployments or re-run start_project with the same name (it's idempotent).
The committed .deploymill/project.json already records the database, so the first reconcile_project is a no-op. Nothing else to wire.
Step 2: Remix it live
This is the loop the demo shows off: change the app, redeploy, watch it update.
- Read the contract first. The scaffold ships an
AGENTS.mddocumenting theentrypoint, the schema, and the "make it yours" knobs. Read it before moving files. The
Dockerfile, not convention, decides what ships. - Edit the source. Use
get_file/push_filesto changesrc/index.js(the entrypoint: UI, schema, routes). To add a field, add an
ALTER TABLE … ADD COLUMN IF NOT EXISTS …in the boot schema, add the input to the form, and render it. - Redeploy.
deploy({ applicationId })builds the new source andhealth-gates it; a failed
/healthzauto-rolls-back, so a broken edit can't take the app down. - Verify.
get_app(itshealthblock) / load theurl.
Repurpose freely: the kanban board becomes a CRM pipeline or content calendar by renaming columns; the todo list grows tags and priorities; link-in-bio gets new themes; event-rsvp gets a capacity cap. Each is a small, local edit. See the starter's AGENTS.md for the specific knobs.
The Explore (free) floor: design inside the box
The curated starters are built to run free, and a remix should stay inside the same envelope:
- State lives only in managed Postgres. No volumes, no object storage, **no
file/image uploads**. Images are pasted URLs stored as strings. The schema init + seed run on boot; there's no migration tooling.
- No outbound calls / no third-party API keys. No email, Stripe, OpenAI, or
webhooks on the free tier (egress is locked). A browser-side redirect is fine.
- Subdomain only (
*.deploymill.app). Forlink-in-bioandevent-rsvpthe shareable subdomain link is the point.
- Keep
/healthzfast and returning200. Make it tolerate a not-yet-ready DBon first boot so a warming database never trips a false rollback.
If a remix needs something outside this box, it's a paid-tier capability. Make it an explicit upgrade prompt, not a half-built feature. Every gate returns a coded upgrade_required with an upgradeUrl.
Step 3: Grow it into a real app (Builder)
When a card is on file (the $10/mo Builder base), the whole envelope lifts at once: custom domains, the REST API, object storage, open egress, editable build recipe, arbitrary import. That's what turns a starter into a production app. The worked example is a waitlist that emails its signups:
- Start from a starter or a fresh app, give it a
subscriberstable, and apublic form that inserts an email. (On the free
event-rsvpstarter you already have the collect-and-list half.) - Add the email provider's API key (the agent never sees it). Use the
secret hand-off:
request_secretreturns a one-time entry link the human opens to paste the key;bind_secretinjects it into the app's env as, say,RESEND_API_KEY. The value never reaches the agent or any tool response. (Seedeploymill://guides/secrets.) This needs open egress (Builder). - Write the send code against the injected env var, and deploy.
- Lock the admin page. Put the "view all signups + mass-email them" view on
a route and protect it:
set_app_protectiongates it behind a password (seedeploymill://guides/site-protection). The public form stays open; the admin route doesn't. - (Optional) Schedule a digest. Declare a
schedulesentry and handle thetick in
POST /_system/tickto send on a cadence (seedeploymill://guides/schedules).
The two memorable beats, the agent wired up a credential it can't read and locked a production route, are the guardrails that make it safe to hand an agent prod. That's the demo worth walking a new user through.
Recovery & idempotency
start_projectis idempotent onname: re-running reuses the repo/app andwon't double-provision the database (a managed-DB record short-circuits it).
- If a step fails it returns
{ ok: false, failedAt, partial }. Fix the cause(usually the Dockerfile/build, read
get_logs) and re-run, or call the failed primitive directly. - Tear down with
delete_appwhen the demo's done. Make teardown part of thescript so it cleans up after itself.
Related guides
deploymill://guides/project-config: the.deploymill/project.jsoncontract(the
databaseblock, health, domains).deploymill://guides/database/node: the Postgres client pattern (pooling,the
/dbhealth probe) if you go deeper than the starter's wiring.deploymill://guides/secrets·deploymill://guides/site-protection·deploymill://guides/schedules: the Step-3 guardrails.