Production audit

Vibe coding production limits, in three constants you can read

Most posts on this topic talk about context windows and rate limits. Those are the wrong ceiling. The real ceiling for a vibe-coded app in mk0r is three named values in source: a one-hour sandbox auto-pause, a publish handler that throws on custom domains and emails a human, and a free tier pinned to Haiku. Each one has a file path, a line number, and a clear right move when you graduate past it.

M
Matthew Diakonov
7 min

Direct answer (verified 2026-05-01)

In mk0r, the production ceiling of a vibe-coded app is three things, all readable directly in the application source: the runtime is an E2B sandbox that auto-pauses every 1 hour (E2B_TIMEOUT_MS at src/core/e2b.ts line 33), custom-domain publishing is human-in-loop because src/core/e2b.ts line 1870 throws and the deploy route emails a person instead, and the free tier is pinned to Haiku because FREE_MODEL = 'haiku' at src/app/api/chat/model/route.ts line 5 with paid models gated by an active subscription. None of those prevent shipping a working prototype. All three are what production-bound apps eventually graduate past.

Limit one. The runtime pauses every hour.

A production app needs to be reachable while you are not looking at it. The mk0r preview URL is reachable while you are looking at it. The difference is one constant.

src/core/e2b.ts line 33 sets E2B_TIMEOUT_MS = 3_600_000. That value is passed into the createSandbox call as timeoutMs: E2B_TIMEOUT_MS at line 308 with lifecycle: { onTimeout: 'pause', autoResume: true } so the sandbox suspends after one hour rather than terminating. The next request resumes it via Sandbox.connect and the window resets via setTimeout(E2B_TIMEOUT_MS) at line 337. Pool entries are even shorter: POOL_MAX_AGE_MS at line 1896 is 45 minutes, recycled on staleness.

For prototype iteration this is invisible. For a public mobile app that other people are pinging at random hours, this is the limit. The runtime is not always-on, it is on-demand. Treat the live preview as a working prototype, not as the production hosting.

3,600,000ms

The sandbox lifecycle is one hour. After that it pauses, then auto-resumes on the next prompt.

src/core/e2b.ts line 33 and line 308

Limit two. Custom-domain publishing is a human.

Every other guide on this topic glosses over the publish step and frames it as a future-feature gap. The truth is more interesting and is written into the code on purpose.

startDomainVerification at src/core/e2b.ts line 1862 takes a session key and a domain, and at line 1870 it throws the literal string "Domain publishing is not yet supported on the E2B backend."completeDomainVerification at line 1873 throws a similar string. getPublishState at line 1879 returns null fields. unpublishSession at line 1887 is a no-op.

The deploy button still works because the actual work happens in src/app/api/publish/route.ts. NOTIFY_TO at line 11 is set to "i@m13v.com". The POST handler validates ownership and then calls sendEmail with the domain, project name, session key, VM ID, user email, timestamp, and the live preview URL on the mk0r preview subdomain for that sandbox. A real person reads that email and finishes the DNS mapping. Until they do, your app lives at the preview URL and serves traffic from your sandbox.

You can decide whether human-in-loop publish is a limit or a feature. Either way, it is not a bug, and it is not opaque. The code shows you exactly what happens when you click deploy.

Limit three. The free tier runs Haiku.

src/app/api/chat/model/route.ts is twenty-six lines. Line 5 sets const FREE_MODEL = "haiku". The POST handler at line 7 reads sessionKey and modelId from the body, then at line 17 it checks: if the requested modelId is not the free model and the user is not anonymous, it reads billing and returns 403 with error: "subscription_required" unless an active subscription exists. Otherwise it calls setSessionModel and returns ok.

The mk0r boot path explicitly defaults the new session to Haiku. src/core/e2b.ts line 1268 has the comment "Set default model to Haiku" and line 1275 posts modelId: "haiku" into the ACP set_model endpoint. So a never-signed-in visitor opens the site, types a sentence, and gets Haiku for $0 with no Anthropic account.

For prototype work this is rarely binding. Haiku 4.5 is fast at HTML, CSS, and JS, and the iteration loop benefits from latency more than from raw planning depth. It becomes a production limit when you want the agent to refactor across multiple files with stronger reasoning. That is the one ceiling of the three you clear without leaving the product, sign in, subscribe, switch model.

The production-readiness checklist, honestly

Run through what a production-bound app needs and what the tool gives you out of the box. The X marks are not failures, they are the line where the tool hands off to the next layer.

Does mk0r meet the production bar by itself?

  • Always-on runtime (no auto-pause). mk0r ships a 1-hour E2B sandbox at src/core/e2b.ts line 33.
  • Automated custom-domain mapping. src/core/e2b.ts line 1870 throws and the publish route emails a human.
  • Production database tier with backups and pooling. The Neon project provisioned per session is sized for prototypes.
  • Authenticated multi-user state out of the box. Anonymous Firebase carries the loop, real auth is a layer you add.
  • Free model access for unlimited agent iterations. FREE_MODEL = 'haiku', stronger models gated by billing.

The counterargument: when these limits do not matter

The three limits assume the goal is production hosting. For a large fraction of apps that get vibe-coded, that is not the goal. A one-page expense calculator the founder shares in a Loom for a partner to look at, a mobile recipe randomizer the indie hacker posts on a Sunday, a demo of an idea that needs to live for the forty minutes a job interview takes, none of those need always-on hosting, none of them need a custom domain, and none of them benefit from a stronger model.

The qualification list in mk0r's product config is honest about this: the right user has a specific app or prototype idea they want to ship within a week or two, is fine with mobile-first HTML/CSS/JS as the starting point, and is willing to iterate with plain words. The disqualification list excludes apps that need native iOS or Android, real-time multi-user state, or backend logic that generated HTML and JS cannot cover. If your idea fits inside the qualification list, the production limits do not bind.

The mistake is treating "cannot run in production" as a flaw of vibe coding rather than a positioning choice. mk0r is the prototype-to-shareable-link bridge. Production hosting is a downstream layer the user can add on a real host when the prototype is good enough to deserve it.

Graduating: the three steps when the prototype is good enough

  1. 1

    Lock the prototype

    Stop iterating in the chat. The git history is real, every turn was a commit, the head SHA in Firestore is the source of truth.

  2. 2

    Move the runtime

    Deploy the generated repo to an always-on host. The 1-hour sandbox limit no longer applies once your app runs outside the E2B sandbox.

  3. 3

    Send the publish email

    If you want a custom domain on the live preview, click Publish, type the domain, and expect a human reply rather than a status spinner.

The work itself is portable. Per-turn git commits via commitTurn, a real Neon Postgres 17 project provisioned at session start, and a private GitHub repo at m13v/mk0r-<slug>. The runtime is what the limits live in. The repository is what you take with you.

Want to walk through your idea against these three limits?

Twenty minutes, your specific app, an honest read on which side of the prototype-to-production line it sits on.

Frequently asked questions

What are the actual production limits when you vibe code an app in mk0r?

Three concrete ceilings, all visible in source. First, the runtime: E2B_TIMEOUT_MS at src/core/e2b.ts line 33 is set to 3,600,000 milliseconds, so the sandbox your app runs in auto-pauses every hour. Second, custom-domain publishing: src/core/e2b.ts line 1870 throws 'Domain publishing is not yet supported on the E2B backend.' The product still has a deploy button, but src/app/api/publish/route.ts line 11 sets NOTIFY_TO = 'i@m13v.com' and the deploy action sends an email rather than mapping DNS automatically. Third, model access: FREE_MODEL = 'haiku' at src/app/api/chat/model/route.ts line 5, and the route returns subscription_required if a non-anonymous user without billing tries to switch to a stronger model. None of these stop you shipping a real prototype. All three are what you graduate past when the prototype turns into a production app.

Is the one-hour sandbox timeout a hard kill or a pause?

It is a pause, not a kill. The createSandbox call passes lifecycle: { onTimeout: 'pause', autoResume: true }, so when E2B_TIMEOUT_MS elapses the sandbox suspends rather than terminating. The next request to the same session calls Sandbox.connect, the runtime resumes, and the timeout window resets via setTimeout(E2B_TIMEOUT_MS) at src/core/e2b.ts line 337. The pool side is shorter: POOL_MAX_AGE_MS at line 1896 is 45 minutes, which is when the warm-pool entry is considered stale and recycled. Practically, short sessions never see the timeout, long marathons get a brief reconnect pause, and if a session goes truly cold the project is restored from the head SHA in Firestore on the next prompt. Production apps that need to be reachable continuously by other users do not sit well inside this lifecycle, which is the limit being honest about.

Why is custom-domain publishing not automatic?

Because the publish-to-real-domain step is human-in-loop on purpose. src/core/e2b.ts line 1870 in startDomainVerification throws 'Domain publishing is not yet supported on the E2B backend.' The deploy handler in src/app/api/publish/route.ts catches the user's intent earlier: it accepts the domain string, builds an email payload that includes the domain, project name, session key, VM ID, user email, and the live preview URL on the mk0r preview subdomain, and ships that to NOTIFY_TO = 'i@m13v.com'. A real person reads the email and finishes the domain mapping. The honest framing: the iteration loop is fully automated, the production publish is gated by a human review. If you want one-click DNS for any domain, you have outgrown the product at this seam.

What does the free-tier model limit really mean for production?

FREE_MODEL = 'haiku' at src/app/api/chat/model/route.ts line 5 means an unsigned visitor or a signed-in user without an active subscription can keep using the default model for free, but the route returns 403 with error: 'subscription_required' if a non-anonymous user without billing tries to set a different modelId. For prototyping that is rarely a binding constraint, Haiku 4.5 is fast and good at HTML/CSS/JS apps. It becomes a production limit when you start asking the agent to plan multi-file refactors that benefit from a stronger model, at which point you authenticate, subscribe, and switch. The model itself does not change what the running app can do, it changes how thoroughly the agent reasons about edits. That is the one of the three production limits you can clear without leaving the product.

If these are the limits, what kind of app should I actually use mk0r for?

The product description is explicit: full HTML, CSS, and JS mobile apps from a single sentence prompt. The qualification list is also explicit: the right user has a specific app or prototype idea to ship within a week or two, is fine with mobile-first HTML/CSS/JS as the starting point, and either does not write code or wants a disposable first draft before investing engineering time. Within those bounds the tool carries you through the entire iteration loop without infrastructure friction. Outside them, native iOS or Android, real-time multi-user state, complex backend orchestration, the same three limits stop being abstract and start being daily blockers. The win is to know which side of that line your idea sits on before the first prompt.

How do I move past these limits when the prototype is good enough to ship?

Each limit has a different right move. For the sandbox lifecycle, deploy the generated HTML/CSS/JS or the Vite/React project to a real always-on host, the prototype repository is genuinely portable. For the publish flow, send the deploy email with the domain you want and expect a human to respond, that is what the route is designed to do. For the model gate, sign in with Google and start a paid plan if you want the stronger model in the agent. None of those steps require throwing away the work already done, which is why source-level honesty about limits is more useful than a blanket 'AI builders cannot do production'.

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