An AI app builder where the backend exists before your first prompt.
A common failure mode of tools that claim the phrase in this title: the backend is a checkbox. You still sign up somewhere, paste a url, choose a region, and hope. mk0r treats the backend as a precondition of the session. By the time the agent reads your first message, a dedicated Postgres project, a restricted email key, a per-app analytics tag, and a private GitHub repo are already written to /app/.env inside the sandbox. This page is a close read of why that single design choice is the difference between a generated frontend and a generated app.
The first-turn test
Open any AI app builder that advertises a backend. Type this prompt on turn one:
Build me a habit tracker. The streaks should survive a page refresh, even if I close the browser.
Watch what happens. Most builders respond with a question: which database do you want, do you have a Supabase project, can you paste an anon key. Some generate a frontend with localStorage and call it done. A smaller number attempt to stand up a shared database that goes to sleep five minutes later, when you show it to a friend, right as they try to add their first habit.
mk0r does something different. It reads process.env.DATABASE_URL, writes a habits table and a check_ins table into the Neon project that was created during session boot, runs drizzle-kit push, and serves the first response with streaks already persisted. The agent never asked for a database because the database was already there.
What the sandbox actually inherits
The uncopyable part of this is a file on disk. Not a connector URL, not a plugin config, a real /app/.env file that the agent can read with a shell call. Here is what it looks like when the session is ready:
Eleven variables. Three services on the left (Neon, Resend, and PostHog), one repo pointer from GitHub. The values are not placeholders; they are live. The Neon endpoint accepts connections. The Resend key sends mail. The PostHog id groups events for this specific app. The GitHub repo already exists. None of this was typed by the user.
How three upstream APIs become four live environment groups the agent inherits
The numbers that make this a precondition, not a feature
These are not marketing figures. They are counts from the repository as of today.
The session boot sequence, unabridged
This is what happens between the moment you open mk0r.com and the moment the agent starts reading your first sentence. Each step has a progress() marker in src/core/e2b.ts, so you can grep the file for the exact lines if you want.
vm_boot
A pool sandbox is claimed or a new E2B VM is spun up. This is the slow step; the warm pool (claimPrewarmedSession) exists to amortize it.
acp_init + acp_session
The Agent Coding Protocol server inside the VM is initialized, and a new session id is created. This is what your later turns will address.
model_setup
The default coding model is set to Haiku via /session/set_model, so the first reply streams fast.
git_setup
ensureSessionRepo initializes the in-sandbox git repo, so every turn's diff can be committed, rolled back, or jumped to a prior sha.
service_provisioning
provisionServices(sessionKey) runs. PostHog is synchronous. Neon, Resend, GitHub run in Promise.allSettled. The resulting env vars are written to /app/.env via execInVm. This completes before the session is persisted to Firestore.
first user turn
Only now does your prompt reach the agent. By this point every credential it needs to persist data, send email, tag analytics, or commit code is already on disk.
A real session, as it would appear in the log
If you attach a debug viewer to the structured log stream coming out of src/core/e2b.ts, this is roughly the sequence of events for a habit tracker prompt. The exact prefixes ([provisioning], [e2b.session], etc.) are plog lines you can grep for in the Cloud Run logs.
What this actually unlocks on turn one
Because persistence, email, analytics, and a repo are preconditions rather than later questions, your first prompt can ask for full-stack behavior. Five concrete first-turn prompts that work in mk0r and do not work in a builder that waits until turn three to ask you for a database url:
First-prompt persistence, without the paste step
Type 'make the streaks survive a refresh' and the agent writes a habits table into the Neon project that was already created for this session. No 'which database should I use' question on turn one.
First-prompt email, without the SMTP dance
Ask for a waitlist that notifies the owner and the agent posts through RESEND_API_KEY into RESEND_AUDIENCE_ID, both already in /app/.env. No ask for a Postmark token, no ask for a verified domain.
First-prompt analytics, already tagged
VITE_POSTHOG_APP_ID is populated, so the first posthog.capture call lands grouped under this specific app, not mixed into a shared stream.
First-commit push, to a repo that already exists
GITHUB_REPO is set to m13v/mk0r-<slug>. The agent can commit the scaffold and push to a private repo without stopping to create one.
Promise.allSettled, so one outage is not your problem
If Neon is slow the Resend call still happens. The orchestrator records a failure in errors[] and keeps going instead of blocking the session.
Side by side with the usual 'with backend' shape
The honest comparison is not mk0r against some abstract 'AI app builder'. It is the file that exists inside mk0r against the wizard flow you are asked to complete inside most other tools before your first prompt is even accepted.
'With backend' vs. 'with backend already in /app/.env'
| Feature | Most 'with backend' builders | mk0r |
|---|---|---|
| What 'with backend' usually means | A Supabase or Firebase wizard you configure yourself | A /app/.env file already populated with four live credentials |
| Database | None on first load, or a shared sandbox cluster | Dedicated Neon project per session (aws-us-east-2, pg_version 17) |
| Email provider | Add your SMTP or Postmark key later | Restricted Resend API key scoped to a per-app audience |
| Analytics | Paste your PostHog key into a settings screen | VITE_POSTHOG_APP_ID injected, events tagged by app id |
| Source control | Download a zip, create a repo yourself, push | Private GitHub repo created under m13v/ before turn one |
| First-prompt capability | Pure frontend state, no persistence until later | Can write a users table, insert rows, and send welcome email |
| Accounts you create before your first prompt | Builder account, database account, email account, repo host | Zero |
Why each choice is deliberate
Dedicated Postgres, not shared. Neon's pricing at the project level is cheap, and per-session isolation means one app's messy schema never touches another's data. The call is a real POST to /projects, not a schema carveout on a single shared cluster. Region is pinned to aws-us-east-2, postgres version is pinned to 17, so agent code never writes SQL the server cannot parse.
Resend API key with sending_access only. If a generated app leaks its key (and generated apps leak things), the damage stops at unwanted email through this app's own audience. The key cannot read other audiences, create new keys, or touch the mk0r Resend account root. That is the difference between a key as a capability and a key as a credential.
Shared PostHog project, per-app id. PostHog is the one service that does not get a per-session isolation. Every generated app writes into the same mk0r PostHog project, but every event is tagged with VITE_POSTHOG_APP_ID of the form mk0r-<slug>. Dashboards filter by app id. Expensive isolation, cheap tagging, correct tradeoff.
Private GitHub repo, not public. The default is private: true with auto_init: false. The agent becomes the repo's first real commit. No empty README, no license file you did not ask for. If you want the code out, you flip visibility on your own terms.
Where to verify every claim on this page
- The provisioning orchestrator is at
src/core/service-provisioning.ts, 389 lines. Read it end to end. - The call site in the session boot flow is at
src/core/e2b.ts:1296-1329, insidecreateSession. - The actual write-to-disk is one line,
src/core/e2b.ts:1305, anexecInVmcall that runsprintf '%s' > /app/.env. - The agent-facing documentation of these services is at
src/core/vm-claude-md.ts:1177-1191(a markdown table the agent sees on every load) andsrc/core/vm-claude-md.ts:69-71(the instruction to check /app/.env before asking for credentials). - Every line above lives in the public appmaker repo. Nothing on this page is derived from a private document.
Want to see /app/.env populate in real time?
Fifteen minutes. A live session boot, a first prompt that writes to a brand-new Postgres, and your questions about any of the details above.
Book a call →Frequently asked, specifically about this
What does 'with backend' actually mean in most AI app builders?
In most products it means the tool has a Supabase or Firebase connector. You sign up for the builder, sign up for the database, paste a URL and an anon key, pick a region, and hope nothing crashes when the preview runs a query against a free-tier cluster that has already gone to sleep. The backend is something you wire up, not something that arrives with the app.
What does it mean in mk0r specifically?
There is a single file, src/core/service-provisioning.ts, 389 lines long, that runs before the agent receives your first prompt. It asks the Neon API for a dedicated Postgres project (aws-us-east-2, pg_version 17, org_id org-steep-sunset-62973058), asks the Resend API for a new audience and a restricted sending-access API key scoped to that audience, generates a per-app PostHog app id, and POSTs to the GitHub API to create a private repo under m13v/. The connection uri, the API key, the app id, and the repo url are written to /app/.env inside the sandbox at src/core/e2b.ts:1305 via execInVm('printf %s > /app/.env').
Why does pre-provisioning change what you can build on turn one?
Because the agent's system prompt at src/core/vm-claude-md.ts:69-71 tells it to check /app/.env for pre-provisioned services before creating accounts or asking the user for credentials. On turn one, the agent can write a users table, insert rows, send a welcome email, and push the commit to a private repo, without asking you for a database url, a SMTP host, or a repo name. First-prompt persistence stops being a multi-step dance.
Is the database shared across every mk0r app?
No. The Neon call creates a dedicated project per session. The POST body specifies a new project name in the form mk0r-<twelve char session slug>, and Neon responds with its own connection_uri, its own default database, its own role, and its own endpoint host. That is different from a schema-per-tenant model on a shared cluster. If two apps both write to a 'users' table they are writing to two physically separate databases.
Why bother with a restricted Resend key per app?
Blast radius. If a generated app leaks its Resend API key (and generated apps sometimes do), the worst case is unwanted mail sent through that app's audience. The key cannot list other audiences, rotate keys, or read data from other apps. The provisioning call uses permission 'sending_access', explicitly scoped by the audience it was created alongside. That is a choice the code makes at src/core/service-provisioning.ts:126-138.
What happens if one of the upstream APIs is down?
Promise.allSettled in provisionServices means one failed upstream does not block the others. If the Neon API is throwing 5xx when your session boots, you still get a working app, a Resend key, a PostHog id, and a GitHub repo. The missing DATABASE_URL gets recorded as an entry in errors[], and e2b.ts logs a provisioning_partial warning. The session continues. You would just not be able to ask for persistence on turn one, but you could still ask for it on turn four after requesting a retry.
Is there a signup step before I get all of this?
No. Open mk0r.com and type a description of the app. The sandbox boots from a warm pool (claimPrewarmedSession in src/core/e2b.ts), provisioning runs while the VM is starting, and /app/.env is populated by the time the agent reads your message. There is no account creation, no credit card gate, no wizard that asks which backend you want. The credentials come from mk0r's upstream service accounts, not yours, so your first prompt arrives in an environment where persistence, email, analytics, and source control are already legal operations.
How does the session know about /app/.env before the first user message?
createSession orchestrates it. Look at src/core/e2b.ts around line 1170-1329: vm_boot runs, then acp_init, then acp_session, then model_setup (Haiku), then residential_ip if requested, then git_setup, then service_provisioning. Only after service_provisioning.done does the session get persisted to Firestore and become available to the user-facing API routes. The .env file is a precondition for the session being considered ready, not something that happens lazily on first query.
Can I verify this without running the full pipeline?
Yes. The appmaker repo is public. Read src/core/service-provisioning.ts end to end. Read the call site at src/core/e2b.ts:1296-1329. Read the agent-facing doc at src/core/vm-claude-md.ts lines 1177-1191 which lists every pre-provisioned service in a markdown table the agent sees on first load. All three together are the full story.
What if I want to bring my own database or email provider later?
The .env file is just a plain text file inside the sandbox. The agent can read and write it via its shell tool, so you can tell it 'use my Supabase url instead' and it will overwrite DATABASE_URL with yours. The pre-provisioned values are a starting position, not a lock-in. You get a working backend on turn one; you replace it whenever you want.