Build Draftbit
KINDAreplaces $24/mosaves $288/yrback to the verdict
A constrained app builder whose hard problems are an emitter whose output is stable enough to diff, a regeneration path that cannot destroy hand-written code, and a data connector whose credentials never leave your server.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the builder and its database with one command, which is the difference between a tool you use and a tool you set up once.
Get it Install Docker Desktop or Docker Engine with the compose plugin. open ↗
Verify
docker compose version prints - installfree
Why Builds the builder and runs the generated project.
Get it nodejs.org, then the Expo tooling in the generated project. open ↗
Verify
A freshly generated project starts and loads on a device - have readyfree
Why Layouts that look fine in a browser preview are routinely wrong on a real screen, and the awkward-record problem only shows up there.
Get it Install the development client on your own phone and connect it to the local server.
Verify
A generated screen renders on the phone with the sample data - API keyvaries
Why One connector done properly is the whole lesson; the credential is also the thing this build exists to keep off the device.
Get it An API key for a service you control, with the narrowest permissions that serve the screens you want.
Verify
The proxy returns mapped fields and the key appears nowhere in the generated tree - decidefree
Why Every regeneration decision follows from this line, and deciding it after the emitter exists means deciding it badly.
Get it Write down which directories the generator owns outright and which it must never write to.
Verify
The rule is in the README and enforced by the merge step
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- projects(id, name, expo_sdk, rn_version, emitter_version, created_at) · the framework versions are recorded because generated code is only valid against the versions it targeted - screens(id, project_id, name, route, tree_json, updated_at) · a screen is a tree of vocabulary components and nothing else - components(name, version, category, props_schema, emitter_ref) · the vocabulary, versioned so a project pinned to an older one still generates - sources(id, project_id, kind, base_url, credential_ref, field_map_json) · credential_ref points into the server's secret store and is never serialised into a screen tree - bindings(id, screen_id, node_path, source_id, field, transform) · which component property reads which field - app_tokens(id, project_id, subject, issued_at, last_used_at, revoked_at) · what the installed app actually holds - generations(id, project_id, at, emitter_version, files_written, files_skipped, conflicts_json) · every regeneration, including what it refused to touch
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_URLsecret | required | postgres://builder:...@db:5432/builder | The builder's own database. Screens, bindings and generation history. |
SECRET_STORE_KEYsecret | required | base64:...redacted | Encrypts connector credentials at rest. Losing this means re-entering every source credential. |
CONNECTOR_BASE_URL | required | https://tools.internal.example/connector | Where the generated app sends data requests. The only address it knows. |
APP_TOKEN_TTL_DAYS | required | 30 | How long an app token lives before it must be reissued. |
GENERATED_ROOT | required | ./generated | Where projects are emitted. The generator owns this tree and nothing above it. |
BASELINE_ROOT | required | ./generated-baseline | The last generated tree, kept as the merge base. Deleting this disables safe regeneration. |
The build, in order
A vocabulary, not a canvas
A screen is a tree of a small number of typed components, and anything the vocabulary cannot express is refused rather than approximated.
The schema is the contract between the builder and the emitter.
Files
src/vocabulary/components.tstests/vocabulary.test.tsAn invalid tree reaches the emitter and becomes invalid code.
Files
src/screens/validate.tsOtherwise adding a component changes every existing project.
Files
src/db/migrations/001_vocabulary.sql
done when · tick each as it passeswatch out- Adding free positioning and an open style object the first time a layout is awkward turns this into a worse design tool whose generated code is a wall of unreadable inline styles, which defeats the only reason to generate source rather than render a runtime. Keep the vocabulary closed, and when something genuinely cannot be expressed, add a named component to the vocabulary rather than an escape hatch.
Screens that bind to fields
A component property can read a field from a data source, and the builder shows real shapes rather than placeholder text.
A typo in a field name should be a validation error, not an empty screen.
Files
src/bindings/validate.tstests/bindings.test.tsPlaceholder data flatters every layout.
Files
src/preview/sample.tsLong, empty and null are the three that break layouts.
Files
src/sources/samples.ts
done when · tick each as it passeswatch out- Previewing every list against three rows of neat placeholder text produces layouts that fall apart the moment a real record has an empty name, a forty-character title or a null image, and you discover all three after the app is on somebody's phone. Capture a real sample response per source, preview against it, and include at least one deliberately awkward record in the sample.
An emitter you can diff
The same screen tree always produces byte-identical source, formatted and readable.
A diff nobody reads is a generator nobody trusts.
Files
src/emit/serialise.tstests/deterministic-emit.test.tsGenerated names that shuffle are the same bug wearing a hat.
Files
src/emit/names.tsSo you can tell what produced a file you are staring at in six months.
Files
src/emit/header.ts
done when · tick each as it passeswatch out- Serialising properties in whatever order they happen to sit in memory means two runs over an unchanged project produce a thousand-line diff of reordered attributes, and after the second time nobody looks at a generated diff again, which is exactly when the generator starts quietly breaking things. Sort every collection before emitting, derive names from stable identifiers, and assert byte equality across two runs in the test suite.
Regeneration that keeps your edits
Running the generator over a project you have edited by hand never silently loses a line.
Without it you cannot tell your edit from the generator's.
Files
src/generate/baseline.tsSilent overwriting is the failure people never forgive.
Files
src/generate/merge.tstests/merge.test.tsThe report is what makes regeneration something you dare to run.
Files
src/generate/report.ts
done when · tick each as it passeswatch out- Regenerating straight over the project directory destroys every hand edit the first time somebody fixes a layout in the code, and because generated files look regenerable nobody has them in a separate commit, so the work is simply gone. Keep the previous generated tree, compare three ways, and make an unresolvable difference a reported conflict rather than a write.
The credential stays homeproduct builder
The installed app holds a revocable token and nothing else, and the whole project can be taken away from the builder.
The bundle is public; your server is not.
Files
src/connector/proxy.tstests/no-secrets-in-bundle.test.tsRotating a bundled key means shipping an app update.
Files
src/tokens/issue.tsA builder you cannot leave is a builder you should not adopt.
Files
src/export/project.tsdocs/standalone.md
done when · tick each as it passeswatch out- Putting the source credential into the generated app's environment because it is faster than writing a proxy ships that credential to every installation, and rotating it then means shipping an app update and waiting for people to install it, which is days during which the old key must stay valid. Keep the credential on the server from the first screen, and give the app a token you can kill in a second.
That is the whole plan for Draftbit. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Arbitrary user-authored code execution.
- A broad connector marketplace.
- App store builds and signing.
- Pixel-level visual design with a freeform canvas.
- visual mobile builder, device previews, integrations, collaboration, and build tooling
- hundreds of connectors
- pixel-perfect app design
- mobile app-store builds
- enterprise permissions and scale
- A second data source kind behind the same field map, once the proxy has proved the pattern with one
- A vocabulary component that wraps a hand-written file, which is the honest way to add an escape hatch without opening the canvas
- A generated test per screen that renders it against the stored sample, which costs little and catches the awkward record
Need the files? The project pack on the verdict page hands your agent the whole brief · more no-code apps.