Guide

The Forms Generator HTML That Brings Its Own Backend

Every other forms generator stops at the markup layer. mk0r is the only one that provisions a Postgres database, a Resend audience, and a PostHog project in parallel the moment you describe a form, then wires the submit handler to all three.

Describe a Form
m
mk0r
7 min
4.8from 10K+ builders
Zero signup required
Postgres + email provisioned per session
Submit handler tested by Playwright

The thing every SERP result about “forms generator html” leaves out

Search the phrase and you get Basin, Jotform, BeautifyTools, Faary, ConvertFlow, and a dozen more. They are all structured identically: drag fields onto a canvas, set validation rules, copy some HTML. The HTML works. The form renders. The submit button even does something.

Then you close the generator and realise the submit button does nothing useful. You still need a place for the rows to land. You still need an email service to confirm receipt. You still need analytics to know anyone filled the thing in. The gap between “generated markup” and “working feature” is your problem.

mk0r closes that gap by provisioning the backend inside the same request that boots your sandbox.

What happens in the 4 seconds after you open mk0r

Before you even describe the form, four separate systems are already being wired up on your behalf. All of this happens in parallel via Promise.allSettled inside src/core/service-provisioning.ts:

Per-session backend provisioning

Session start
Slug generated
provisionServices()
Neon Postgres
Resend audience
PostHog app ID
GitHub repo

The anchor fact: exactly what lands in your VM’s .env

This is the part you can verify if you read the source. Open src/core/service-provisioning.ts, jump to the provisionServices function, and trace the last 30 lines. This is the concrete handoff between “services got created” and “your form can talk to them.”

src/core/service-provisioning.ts

Those env vars are written to /app/.env and read by Vite at dev time. When the AI agent scaffolds your form’s submit handler, it references the same names directly. No configuration step. No “connect your database” modal.

The Resend audience has a very specific shape

This matters for security. The API key mk0r bakes into your form is not an owner-level key. It is a restricted key created per session, and the audience name contains your session slug so logs stay readable.

What provisionResend() actually creates

  • Audience named literally 'mk0r-{slug} Users'
  • API key with permission: 'sending_access' only
  • Key scoped to this session, not your personal Resend account
  • Errors bubble up through Promise.allSettled, not thrown
  • Audience ID returned as RESEND_AUDIENCE_ID env var
  • Logged via plog() with audienceId + apiKeyId for audit

What the agent writes for a waitlist form

The project’s CLAUDE.md includes a canned Waitlist Pattern at src/core/vm-claude-md.ts (line 1403). When you ask for a waitlist or signup form, the agent follows this shape. The handler touches all four provisioned services:

src/api/waitlist.ts

Watching the provisioner run

If you tail the Cloud Run logs while a new session boots, the provisioner emits structured events at each step. Here is a trimmed trace from a real boot. Notice the provisioning.completed line lists which services landed:

mk0r session boot

End-to-end flow

Each step below is something a reader could watch happen live in the screencast pane of mk0r. There is no hidden “deploy” step later.

1

Open mk0r.com, no account

No signup screen. No email confirmation. The session key is generated from a cookie and the E2B sandbox begins booting from a pre-warmed snapshot.

2

provisionServices() fires in parallel

PostHog configures synchronously. Resend, Neon, and GitHub run under Promise.allSettled so a slow step cannot block the others. Any failures are logged but do not kill the session.

3

Env vars land in /app/.env

RESEND_API_KEY, RESEND_AUDIENCE_ID, DATABASE_URL, VITE_POSTHOG_KEY, GITHUB_REPO, plus Neon host/role/password fields. Vite picks them up on next reload.

4

You describe the form

Example: 'waitlist form with email only, save to Postgres and send a welcome email.' The agent reads CLAUDE.md, follows the Waitlist Pattern, writes the tsx component, and wires the submit handler.

5

Playwright fills the form and checks the DB

@playwright/mcp@0.0.70 drives a real Chromium inside the VM. It submits a test email, then shells into Node and queries the emails table to confirm the row landed. If the row is missing, it edits the handler and re-runs.

6

You publish or take the code

The private GitHub repo (GITHUB_REPO_URL) is already there. You can publish the app to a subdomain or keep iterating by sending follow-up prompts.

Numbers that make the difference concrete

0Services provisioned per session
0Env vars injected into /app/.env
pg_0Postgres version on Neon
0Signup steps before provisioning

Those are not marketing numbers. They come from reading provisionServices (4 calls), counting the envVars[...] assignments (11 keys), the Neon project request body (pg_version: 17), and the fact that there is no auth step in front of /api/vm/ensure-session.

Try it yourself

Describe a form. Watch Neon boot, Resend mint a key, and the AI wire the submit button, all without a signup screen. Book time with us if you want a walkthrough.

Book a 15 min walkthrough

Forms generator comparison

A direct comparison against the SERP incumbents. “Form generator” as a category is crowded. “Form generator with a backend it wired itself” is not.

FeatureDrag-and-drop generatorsmk0r
Generates HTML markupYesYes
Stores submissionsNo (user wires endpoint)Yes (Neon Postgres auto-provisioned)
Sends confirmation emailNo (user wires SMTP)Yes (Resend audience + restricted key)
Tracks submissions in analyticsNo (user adds pixel)Yes (PostHog app ID injected)
Source code you ownHTML snippetFull React + TS repo on GitHub
Requires signupYes, all of themNo
Tests the form before deliveryNoYes (Playwright MCP in VM)

Why the provisioning model matters for form security

If you bake a form into a drag-and-drop tool, you typically paste an owner-level API key for your email provider into some “webhook URL” field. That key has blast radius the size of your entire account.

mk0r avoids that failure mode by construction. The Resend key your form uses is minted per-session with permission: "sending_access". Even if your generated bundle leaked, the key cannot list audiences, cannot create more keys, and cannot modify account settings. Neon is scoped to the project mk0r just created; there is no shared database to pivot to.

Blast radius, by source

What an attacker could do with the credentials baked into a form.

Owner-level Resend key (typical setup)Entire account
SMTP creds in form action URLYour mail server
mk0r per-session sending_access keySending from this one form

Forms you can ship in one prompt

These all use the same provisioned backend. The agent picks which services to wire based on what the form actually needs.

Waitlist signupContact form + CRM entryNewsletter double opt-inJob application + file uploadBug report with PostHog tagProduct feedback with ratingBeta access requestEvent RSVP with reminder emailCustomer support intakeSurvey with conditional logic

How it compares numerically

The gap between mk0r and any other forms generator is not polish, layout, or validation rules. It is infrastructure.

0

accounts to create with any third party

0

env vars the provisioner writes for you

0

prompt to a form that stores, emails, and tracks

Frequently asked questions

What does 'provisions its own backend' actually mean?

When your session starts, mk0r runs provisionServices in src/core/service-provisioning.ts. In parallel it creates a Resend audience named 'mk0r-{slug} Users', a Neon Postgres project on pg_version 17 in aws-us-east-2, a scoped PostHog app ID, and a private GitHub repo. The credentials are written to /app/.env as RESEND_API_KEY, RESEND_AUDIENCE_ID, DATABASE_URL, VITE_POSTHOG_KEY, and GITHUB_REPO before the AI agent writes a single line of your form.

Do I pay for the database or email account?

No. There is no signup. The Neon project, Resend audience, and PostHog app ID are created under mk0r's infrastructure and scoped to your session. Your form's submit handler reads them from process.env inside the VM.

Is the Resend API key in my form a full account key?

No. provisionResend creates a per-app key with permission set to 'sending_access' only. It cannot list contacts outside the mk0r-{slug} Users audience, cannot create more keys, and cannot modify account settings. If someone scrapes your form's bundle, the blast radius is limited to sending from the key's allowed domains.

Can I get just standalone HTML with no backend wiring?

Yes. Ask for 'a plain HTML form' or use Quick mode. You get a single self-contained file with inline CSS and JavaScript in under 30 seconds, which you wire to your own endpoint. If you want the backend too, stay in VM mode and the agent scaffolds the submit handler against the provisioned services.

What database does mk0r use and how do I query it?

Neon serverless Postgres, accessed via the @neondatabase/serverless driver. The connection URI is injected as DATABASE_URL. The Waitlist Pattern section in the project's CLAUDE.md shows the exact shape: `const sql = neon(DATABASE_URL); await sql\`INSERT INTO ...\`;`. The agent follows that pattern when you ask it to store submissions.

Do I own the code after generation?

Yes. The source is in a private GitHub repo that mk0r creates for you and the URL is exposed as GITHUB_REPO_URL inside the VM. You can also export the project or continue iterating with follow-up prompts.

What happens if Resend or Neon provisioning fails?

provisionServices uses Promise.allSettled so partial failures do not block the session. The errors array is logged to the VM and the env var for that service is simply omitted. The AI agent checks `if (RESEND_API_KEY)` before wiring the email step, so your form still renders and the failed service is skipped gracefully.

Can the AI iterate on the form after the first generation?

Yes. Send a follow-up like 'add a country dropdown with validation' or 'log submissions to a separate table.' The agent edits the tsx file, Vite HMR reloads, and Playwright re-fills the form to verify the new field before reporting back.

Stop gluing forms to backends you have to build

Describe the form. mk0r provisions Postgres, Resend, and PostHog for you, then wires the submit button.

Generate a Form With a Backend