Guide

AI app builder state limits, in four layers

The phrase “state limits” gets used like it’s one number. It isn’t. AI app builders have four distinct layers of state, each with its own ceiling, and only one of them has a hard wall. Most articles on this topic flatten all four into “AI apps can’t persist”. That framing is a holdover. Here is the breakdown for mk0r, with the file paths.

M
Matthew Diakonov
7 min read

Direct answer (verified 2026-05-06)

On a sandbox-based AI app builder, the only hard state ceiling is the sandbox VM’s idle timeout. On mk0r that’s exactly 3,600,000 milliseconds (one hour), declared as E2B_TIMEOUT_MS on line 33 of src/core/e2b.ts. Past that, the sandbox pauses; it does not reset. Database rows, git history, sent emails, and tracked events all live outside the sandbox and persist indefinitely.

Component state, browser storage, and the database tier behave normally. None of those have a builder-imposed cap; they hit the same ceilings any React app hits.

The four layers, ordered by where they break first

When someone says “AI app builder state limits” they usually mean one of these four things, but they don’t say which. The ceilings are very different.

  1. 1. Component state (React)

    Ceiling: none. This is just useState and useReducer.

    Lives for the page load. Resets on refresh. The agent uses this for the first version of any prototype: the active screen, form values, the selected item. If your app forgets things on refresh, you are looking at this layer and you want the next one.

  2. 2. Browser storage (localStorage / IndexedDB)

    Ceiling:the browser’s default quota (typically 5 to 10 MB for localStorage, hundreds of MB for IndexedDB).

    Survives refresh, tab close, and reboot. Cleared when the user clears site data. Per-device. Useful for personal todo lists, drafts, settings. Useless for anything that needs to be shared between users or read from another device.

  3. 3. Sandbox session (the agent’s VM)

    Ceiling: 3,600,000 ms idle, then pause. This is the only hard wall in the whole system.

    The sandbox is where your project files live during agent edits. Every successful turn calls commitTurn and lands as a real git commit; the SHAs are pushed onto an unbounded historyStack array (line 100 of src/core/e2b.ts). After one hour idle, the sandbox pauses. When you come back, it resumes from the same disk and the same git HEAD. Nothing is lost; you just wait a few seconds for the reconnect.

  4. 4. Database (Neon Postgres, pre-provisioned)

    Ceiling:whatever Neon’s plan limits are. Effectively none for a prototype.

    This is the layer most articles claim doesn’t exist for AI app builders. It exists. Every project that mk0r generates in VM mode has a Postgres connection string pre-wired into /app/.env from turn zero. The agent reads it, writes migrations, inserts rows. Data lives outside the sandbox; it survives the 1-hour pause, the project being abandoned for weeks, and the user moving between devices.

The .env that gets written when the sandbox boots

You can verify layer 4 yourself. The instruction file at docker/e2b/files/app/CLAUDE.md tells the agent which services are pre-provisioned. The values are written into /app/.env before the agent reads its first prompt. A redacted version of what you’d see in a real session:

/app/.env

That’s a real Postgres URL, a real Resend API key, a real PostHog project, and a real GitHub remote. The agent does not have to ask you for credentials, and you do not have to sign up for any of them. Your app’s data layer exists from the first prompt.

The one constant that matters

Layer 3 is the only layer with a builder-imposed ceiling, and it is a single number. Below the ceiling: nothing about your project state is capped. The undo history is a plain array, not a circular buffer. The turn log is not truncated. The transcript is not summarized. Every turn is a real commit on a real git history you can pull and read.

src/core/e2b.ts

What survives a sandbox idle pause

The 1-hour idle timer sounds scary if you read it as a deadline. It isn’t one. When the sandbox pauses, this is what you keep:

Preserved across the pause / resume cycle

  • Every file in /app/, your source tree is on a real disk
  • Every git commit from every turn, historyStack is a string array of real SHAs, not a transcript
  • Every row in Neon Postgres, the database lives outside the sandbox entirely
  • Every Resend send and every PostHog event, those left the box already
  • Your project name, mode, residential IP setting, and active commit pointer, mirrored to Firestore by persistSession

The pause is not a deletion event. It’s a hibernation. The sandbox VM goes cold, the disk image is preserved, and the next time you send a prompt the runtime calls Sandbox.connect and re-arms the timer. From your perspective: the editor reloads, the preview comes back, the chat history is intact, the git log shows the same SHAs in the same order. The agent does not have to be told what you were working on; the working tree is already there.

Where the “AI apps can’t keep state” framing comes from

The framing is a true sentence about a different category of tool. One-shot HTML generators (Claude Artifacts, the Quick mode in mk0r, most chat-based code outputs) really don’t have layer 3 or layer 4. They produce a single self-contained HTML file with no execution context. Anything beyond what fits in localStorage is gone on refresh. For that category, “no real database” is accurate.

Sandbox-based builders are a different shape. They give the agent a real Vite + React project, a real filesystem, a real git history, and (in mk0r’s case) a real Postgres URL on day one. The state ceiling is one constant in source, the database is in a different service that survives independently, and the failure mode of a long idle period is “wait three seconds for the reconnect”, not “your data is gone”.

When someone reads the genre-level claim (“AI app builders can’t persist”) and applies it to every tool in the space, they end up overestimating their problem and reaching for heavyweight tools they don’t need. The honest position is: ask which of the four layers you’re hitting. If it’s 1 or 2, you’re fine. If it’s 3, you’ll wait a few seconds on resume. If it’s 4, the connection string is already in your .env.

When you actually outgrow this

A builder that has all four layers is enough for prototypes, internal tools, hobby apps, demo backends, and the first few users of a real product. It is not enough for a production multi-tenant system with strict row-level security, paid SLAs, complex permissions, and many concurrent writers. At that point the handover is straightforward: pull the GitHub repo down, point a production Postgres instance at the same schema, deploy the React app somewhere with a real CI pipeline. The state model you’ve been using all along (component, browser, database) maps cleanly onto whatever you migrate to. The sandbox layer is the only one you’re leaving behind, and you don’t need it once the agent isn’t doing your edits.

Got a state-shape question?

Book a 15-minute call and we'll work out which of the four layers you actually need, before you reach for a tool that's heavier than your problem.

Frequently asked

Frequently asked questions

What's the actual hard limit on state in mk0r?

One hour of idle time on the sandbox VM. The constant is E2B_TIMEOUT_MS = 3_600_000 milliseconds, declared on line 33 of src/core/e2b.ts. After an hour with no activity, the sandbox pauses on the E2B side. Nothing is lost: the filesystem is real disk, the git history is real commits, and the database lives in Neon (which is a separate service entirely). When you come back, ensureSessionLoaded reconnects the sandbox and the timer resets. There is no turn cap, no token cap on saved state, and no cap on how many SHAs the historyStack can hold.

Don't AI-built apps have a 'no real database' problem?

That's true for one-shot HTML generators (Claude Artifacts, the Quick mode in mk0r) where the whole app is a single self-contained file with no backend. It's not true for sandbox-based builders. mk0r's VM mode boots every project with a real Neon Postgres URL pre-written into /app/.env. The agent reads it, calls the database directly from the React app, and your data persists across sessions, across browser refreshes, across the sandbox's 1-hour idle pause, and across the project being abandoned and resumed weeks later. The 'no real DB' framing is a holdover from an earlier era of AI app generation.

What's the difference between component state, browser state, and sandbox state?

Component state is whatever sits inside React (useState, useReducer). It lives for the page load and resets on refresh. Browser state is localStorage, IndexedDB, or cookies; that survives refresh and tab close, until the user clears site data. Sandbox state is the project's filesystem inside the VM where the agent runs. That survives across your editing sessions until the 1-hour idle timer fires (then it pauses, then resumes byte-exact). Database state is your Neon Postgres rows; those live in a separate cloud service and persist independently of everything above.

Is the 1-hour timeout reset every time I send a prompt?

Yes. Every turn the agent runs ends with persistSession writing the latest state to Firestore, and the active sandbox timer is reset to E2B_TIMEOUT_MS via setTimeout (line 337 in src/core/e2b.ts). As long as you keep sending prompts, the sandbox stays warm. If you walk away for an hour, it pauses; the next prompt re-attaches via Sandbox.connect and resumes from the same disk and the same git HEAD.

How big can the undo history get before it breaks?

There is no enforced ceiling. historyStack is declared as a plain string[] on line 100 of src/core/e2b.ts. Every successful turn calls commitTurn (around line 1747), which runs git add -A and git commit inside the VM and pushes the resulting SHA onto the stack. Undo runs git checkout on the previous SHA and restores every file in /app byte-exact. We've run sessions with hundreds of commits without hitting limits; the practical bound is the size of the git pack on disk, not the array length.

What happens if I want my app to share state across users?

Use the Postgres database. Read DATABASE_URL out of the .env, write a small server route or a serverless function, and let users hit it. The same connection that's wired in for your own development is what the agent uses to write rows for users. mk0r's VM mode is a real Vite + React project; you can install any backend library you want. The constraint is not the builder, it's whether you actually want to operate a backend.

Where do most articles on this topic go wrong?

They flatten the four layers into one and write 'AI apps can't keep state.' That's a true sentence about Claude Artifacts and most one-shot HTML generators because those produce a single file with no execution context. It's a misleading sentence about sandbox-based builders that boot every project with a Postgres URL, an analytics key, an email API, and a git remote. The honest framing is: your agent's session has a 1-hour idle ceiling; everything that matters to your users (database rows, sent emails, tracked events, source code on GitHub) lives outside that ceiling.

Can I see this for myself without signing up?

Yes. Open mk0r.com, switch to VM mode, type a prompt that asks for a database-backed feature ('a notes app where each note is saved to Postgres and listed on load'). Watch the agent read /app/.env, write a small DB client, run a migration, and write rows. Refresh the page. The notes are still there. Walk away for an hour and come back. The notes are still there. The whole thing runs without an account.

mk0r.AI app builder
© 2026 mk0r. All rights reserved.