← Back to blog

The Haven App Builder - describe the app your team is missing, and an AI writes it

Karsten Lehmann · 23.09.2026

Karsten Lehmann 23 September 2026 09:00:00

Yesterday’s post was about peer-to-peer sync - two Haven clients converging directly, with no server in the middle. This one is about the other end of the product: not how the data moves, but who gets to build the thing that uses it.

Haven has been extensible through apps since the App Store shipped. There are two established ways in. You write the app yourself against the MindooDB App SDK, or you hand a coding agent our context files - llms.txt and llms-full.txt, which is the whole SDK reference in one file - point it at the reference app, and let it write the thing. People do both, and both work.

But both of them also hand you a pile of decisions that have nothing to do with the app you wanted. Where does the code live. Who hosts it. Which build command. How does the built thing get an address, and how does that address get registered in Haven, with which databases, at which permissions. None of it is hard. It is just a queue of small, unrewarding steps standing between “we could really use a shift plan” and having one - and that queue is where most internal tools quietly die.

So the App Builder removes the queue. It is an app in Haven’s own App Store, and after a one-time setup it just needs a name, a sentence, and a description of what the app should do.

The App Builder open in Haven, headed "App Builder - Describe an app. Get it into Haven." A card titled "What do you want to build?" explains "Describe your app the way you would explain it to a colleague. The AI turns this into working software - no technical wording needed", above an illustration of a pencil, a document and a tag. Below it a field "Name your app" containing "Team Poll" with the hint "This is the name you will see in Haven", a field "In one sentence, what is it for?" containing "A modern collaborative polling app for teams", and a large "Describe what it should do" field holding four paragraphs of brief: a collaborative polling app where users create polls with a question, description, multiple answer options and an optional deadline, supporting single-choice and multiple-choice polls, anonymous voting and hidden results until the poll ends; a request for a screenshot-friendly voting-wall look with large poll cards, bold typography, participant avatars and animated result visualisations rather than a generic administration-dashboard look; a clean overview of active and closed polls with immediate, interactive voting; and a prominent "Create Demo Data" button plus a "Clear Database" button with a confirmation step. Underneath, the hint "This is the brief the AI works from - the more concrete, the better. Think about who uses it, what they see on screen, and what they can do. You can always ask for changes afterwards"

Why an app an AI wrote is safe to run at all

This is the part that makes the rest defensible, so it goes first.

A Haven app is an isolated web application that runs inside its own sealed frame in the Haven client. The browser keeps it there: it cannot read Haven’s own data, it cannot reach into the page around it, and it cannot navigate Haven away from under you. Everything it does with data goes through the SDK bridge, which is the only channel between the app and Haven.

The bridge is not just a pipe, it is where access is decided. Three things matter here:

  • Apps ask for databases by name, not by location. An app is written against a name like the Team Poll database; which tenant and which physical database that name points at is something Haven holds, and something you can change later. Moving an app from a test tenant to a shared production one is a setting in Haven, not a change to the app.

  • Permissions are granted per database, by the user, at install time. Writing, deleting, reading the change history, attachments, views - each is a separate approval, and an app that was not given one has to cope with that rather than assume it.

  • Useful services come from Haven rather than from the app. The hard parts are the host’s job, and using one of them buys the app no extra access:

    • The document scanner. One call opens Haven’s camera capture with perspective correction; you photograph a single page or a whole stack, and the app gets them back straightened as one PDF, attached to the record it asked for. Anything that files paperwork - an expense log, a delivery note, a lab form - gets a scanner without anybody having written one.
    • File preview, for Word, Excel, PowerPoint, PDF, Markdown, images, text, audio and video, so an app that stores a file does not have to bring a viewer for it. The video case is my favourite bit of hidden work in there: an mp4 video that was never prepared for streaming would normally have to be downloaded whole before it plays, and Haven repackages it on the fly instead, so it starts immediately and can be scrubbed like any other.
    • Drag and drop between apps. Two apps on the same workspace page are two iframes that cannot see each other - and you can still drag something out of one and drop it into the other, because Haven brokers the gesture. The source hands over typed text (plain text, Markdown, JSON, or a reference to a MindooDB document) and a PNG of what is being dragged; Haven carries it across the page, works out what is under the pointer, and delivers it to the target. No markup, no code and no reach into each other’s page goes with it.

Tightening the sandbox

Over the last few weeks we tightened the level above that. With hosted bundles as the deployment target, the finished app is downloaded into Haven and served locally rather than fetched from the web each time - which puts Haven in the middle of every request the app makes, and that is what lets Haven enforce a network allowlist. An app declares the addresses it needs; anything else it tries to call (e.g. via fetch or external script or image references) simply does not happen, and it cannot wander off its own pages either. When something is blocked, you are told:

Haven blocked a network request to {host}.

That is the property that makes generated code acceptable. An app whose brief was “a shift plan” has no business calling an address nobody declared, and if it tries, the call does not happen and you find out which host it was aiming at.

And it is not an option the App Builder leaves to chance: every app it creates is published as a hosted bundle, which is the strictest of the two deployment modes, with an allowlist that starts out empty. That is why the install dialog further down this article says None. This application cannot reach the network. An AI wrote that app, and it begins with no way to send anything anywhere - if a later version genuinely needs to call an address, that address has to be declared, and you have to approve it.

Connect three accounts, once

The only technical part is the first five minutes, and it happens once for every app you will ever build: a setup page where you connect three services and approve a couple of installs. Three accounts, and only one of them costs anything:

  • GitHub holds your app’s code, so nothing is locked in.
  • Cloudflare publishes it at its own web address and rebuilds it on every push.
  • Cursor is the AI that writes the code. Its free Hobby plan does not include the cloud agents the builder starts - that needs a paid plan, from $20 a month.

GitHub and Cloudflare are free, and both connect through their own consent screens, so there is no account id or owner name to look up anywhere. Cursor has no consent flow, so you paste an API key from its dashboard.

Where those credentials end up is worth being precise about, because this is a browser app asking for tokens that can create repositories and deploy code. They live in one document in your own App Builder database, sealed for you personally (all your devices) - shared database or not, nobody else can read it. The GitHub token never leaves the browser tab, because GitHub lets the page talk to it directly. The Cloudflare token and the Cursor key do reach the builder’s server, for the calls a browser is not allowed to make (no CORS support), and each request carries the one it needs and nothing is kept afterwards. And no credential is ever handed to the coding agent: giving a cloud machine a token that can deploy would have been the convenient design, and publishing instead runs through Cloudflare’s own Git integration, which needs no hand-off at all.

If you would rather the Cursor key did not pass through a server you do not run, the builder is open source and a local copy is three commands, served at http://127.0.0.1:4400 and added to Haven through App Store → New app → From URL. Same application, key never leaves your machine.

A name, a sentence, and a brief

The brief in that screenshot is the whole input, and it is worth reading as an example of what the form actually wants: what the app is for, who uses it, what appears on screen, what a person can do - plus, in this case, a request for a demo-data button, because an app you are going to show somebody needs something in it. No technical wording anywhere.

Everything else on the form has a default that works. The web address is derived from the name, the repository is private, and the app gets one database of its own with a sensible set of permissions. Two collapsed sections - Web address and privacy, Database and access - let you change any of that, and most of the time there is no reason to.

Then one button: Build my app.

Ten steps, and what each one actually does

The wait is about two minutes, and the builder narrates it rather than showing a spinner, because a silent two minutes feels like a hang.

The App Builder progress panel headed "Working on it...", listing completed steps with their results: "Check the name is still free - klehmann/team-poll is available", "Create your project on GitHub - klehmann/team-poll", "Write in your app's name and brief - 4 files named for Team Poll", "Check Cloudflare can see the project - Cloudflare can read the repository", "Reserve your web address - https://team-poll.karsten-lehmann-69a.workers.dev", "Set up automatic publishing - Pushes to main now deploy themselves", "Publish the app - Cloudflare is building main", then the running step "Wait for the app to go live - Attempt 1: The origin did not answer yet", and two pending steps, "Add the app to Haven" and "Start the AI developer"

Since the labels are deliberately non-technical, here is what is happening behind each of them:

StepWhat happens
Check the name is still freeThe name has to serve as the project, the web address and the app’s identity all at once, so it is checked before anything is created
Create your project on GitHubA new private project, created from our starter template: a small working app, the SDK wiring, the hosting configuration, and a rules file the AI is required to read first
Write in your app’s name and briefYour name and your brief are written into the project itself, so the AI’s instructions live with the code rather than in a chat window
Check Cloudflare can see the projectGitHub does not report what Cloudflare is allowed to see, so the builder asks Cloudflare whether the new project is visible to it
Reserve your web addressThe address is registered and switched on before there is anything to serve at it
Set up automatic publishingCloudflare is wired to the project, so from here on every change that lands is rebuilt and published with nobody deploying anything
Publish the appThe first build, started explicitly, because the app’s code arrived a moment before the thing that watches for new code existed
Wait for the app to go liveThe builder asks the new address every few seconds, for up to five minutes, until the app answers with its own description
Add the app to HavenThe finished address is offered to Haven - the one extra permission the builder itself holds, and an offer rather than an install
Start the AI developerA Cursor cloud agent is set to work on the project, on the branch that publishes itself

Haven asks before it installs

The handover is not a privileged back door, but an SDK feature. The builder passes Haven a URL, and Haven fetches the app’s description from that address itself and puts the result in front of you before it downloads anything:

The Haven dialog "Install this application?" with the note "App Builder asks Haven to install an application it has prepared." It names Team Poll, "A modern collaborative polling app for teams", its address https://team-poll.karsten-lehmann-69a.workers.dev, version 0.1.0 and the tenant it will belong to. Under "Databases it will use" it lists Team Poll, app_team_poll, with Write, Delete, History, Attachments, Views, Directory and the note "will be created". Under "Addresses it may call" it says "None. This application cannot reach the network." A yellow warning reads "Haven read this description from the address above. Continue only if you trust that address: the application will be able to read and write the databases listed here." Cancel and "Install application" buttons sit at the bottom

Databases, permissions, and network access - the last of which reads None. This application cannot reach the network here, because a polling app has no reason to call anything. Note also what the same mechanism means later: a new version cannot widen any of this. It can change the app’s description, its version and its files, and it cannot add a database, widen a permission or add an address it may call - because whoever controls the web address controls that description, and a hosting account that got taken over must not be able to quietly grant itself databases and permissions you never agreed to. Haven names the extra request and keeps what you approved; granting it is a deliberate act in the app’s settings, or a remove and reinstall - and removing an app does not delete its data.

Watching the AI work

By now the app is published and installed, and the interesting part is only starting. The brief went to a cloud agent, and you can open its session and watch.

A Cursor cloud agent session titled "Application readiness" on the repository klehmann/team-poll. The visible instructions read "Read AGENTS.md in the repository root first: it states the platform rules and links the documentation that matches the pinned SDK version. Then implement TASK.md. Keep public/haven-app.json in step with the databases the app actually opens. Generate a 512x512 PNG that matches the app's theme and write it to public/appicon.png - Haven uses that file as the workspace icon after a hosted-bundle install. Run pnpm test and pnpm build before you finish, and commit the pnpm lockfile that the first install produces. Commit and push to main directly. Do not create a pull request, and do not open a draft." Below are the agent's steps - a diff hunk, "Re-run tests and build after preview fix", "Commit and push preview-mode bridge fix", "Reload Team Poll preview - Worked for 28s", "Started screen recording", "Exercise Team Poll flows". On the right, a Desktop tab shows the agent's own browser with the running Team Poll app and a "Take control" button

Those instructions are not a demo prompt, they are the contract. The rules file ships in the starter template, so the agent learns the rules of the platform before it reads your brief. It draws an icon for the app, which is why a new app arrives in Haven looking like a real one rather than a placeholder. It has to leave the project’s tests and its build passing. And it commits to the branch the hosting watches, so everything it finishes republishes the live app on its own.

The right-hand pane is the part that is fun to watch: the agent opens the app in its own browser and clicks through it - and at the end it records what it built:

A minute of the finished app, recorded by the thing that wrote it.

What comes out

An ordinary web app at its own address - which is where Haven picked it up from, and where anybody else’s Haven can pick it up too.

The generated Team Poll app filling the screen. A warm header reads "TEAM POLL - Hey John" over the line "A live voting wall for the questions that actually move a team. Big cards, honest tallies, no spreadsheet energy", with New poll, Create Demo Data and Clear Database buttons. Filter chips read All 6, Live 5 and Closed 1. Below, pastel poll cards: "Which Friday ritual would we miss most?" tagged LIVE and SINGLE, with Demo theater at 50%, three options at 17% each, a donut chart repeating the 50%, six voter avatars, "6 teammates voted", Luca Moretti and a Close poll link; and "Who should MC the all-hands?" tagged LIVE, SINGLE and HIDDEN TALLY, ending in 4 days, whose four options each read "Results drop when this poll ends"

And in Haven it lands on the workspace’s Start tab, an icon among everything else that lives there - wearing the icon the agent drew for it:

The Haven Workspace page on its Start tab, headed "Workspace" with the note "Arrange databases, applications, notes, web pages, videos, and diagrams as draggable tiles across pages - like home screens on a phone". Signed in as John Wayne/Acme. A first row of tiles holds Setup wizard for a new environment, Haven App Store, Sync with server, Quick Scan, Virtual Views and Preferences; a second row holds the applications - 3D Pac Man, App Builder, Mindoo Teacher's Desk, Mindoo TeamSketchbook, Mindoo Vega, SDK Example App and Team Poll, each with its own generated icon and the tenant it belongs to; below them a userdirectory database tile marked "On Local"

From there it opens full screen, or sits as a tile on a workspace page beside other apps:

A Haven workspace page named Workspace 2 with two app tiles side by side. On the left, Team Poll with its "Hey John" header and the "Which Friday ritual would we miss most?" poll, Demo theater leading at 50%. On the right, a 3D Pac Man app headed "MINDOO ARCADE", signed in as cn=John Wayne/o=Acme, with a score, high score and lives display over an isometric blue maze of glowing pellets, ghosts and Pac-Man, an overlay reading "Press Enter or Space to start", on-screen arrow controls, and the footer lines "Isometric arcade - Haven ready" and "Ghosts think. Pellets glow. You run." Each tile has its own reload and overflow buttons

Phone, tablet or desktop too - the app is a Haven app, so it inherits all of that. What runs there is the copy Haven downloaded, which is also why it keeps working when the network does not. The second tile there is a 3D Pac Man, and we had that one built the same way - a name, a description, one button. It is not a serious business application, which is exactly why it is a good illustration: nobody wrote a line of it, and it is now sitting in a workspace next to a polling app that was made the same afternoon.

Coming back to it later

The builder opens on the list of what you have built, and every app keeps a page of its own - its address, its repository, its hosting, its agent.

The App Builder start page. A card "Your apps" with the note "Describe what you need, and it gets built, published, and added to Haven" and a "New app" button lists two entries, "Team Poll - A modern collaborative polling app for teams" and "3D Pac Man - 3D Pac Man Game", each with a blue "Live, added to Haven" badge and a delete button. Underneath, a "Connections and setup" link explains that the tokens and keys entered there are saved and live in one document in your own App Builder database, encrypted so that only you can read them - not even someone you share that database with - and that the App Builder is open source, so you can run your own copy and add that to Haven instead

Which matters because the first version is rarely the last one. Asking for the next feature is another brief in the same session - or a sketch, or a screenshot of what it should look like - and the agent changes the code, pushes, and Cloudflare republishes. No programming knowledge is needed for that loop either. It is the same conversation as the first one.

And Cursor is replaceable. What the builder created is an ordinary Git repository in your own GitHub account with a documented starter app in it. Point Claude Code or Codex at it, or open it in an editor and write the change yourself. Cloudflare republishes whatever arrives, whoever wrote it.

Handing an app to somebody else

The app’s address is public, and that turns out to be the whole distribution mechanism. Send the URL to a colleague or to another organisation’s Haven, and they use App Store → New app → From URL: Haven reads the app’s description from that address, shows them the databases, permissions and network access it asks for, they confirm, and it is installed in their tenant against their data.

Public address, private data. An app carries no data of its own - a stranger who opens the URL gets the empty application and nothing behind it, because everything it ever reads or writes arrives through Haven, under permissions a person granted it. Which is why sharing an internal tool between two organisations is a link rather than an export.

Limits, stated plainly

The cloud agent needs a paid Cursor plan, from $20 a month. GitHub and Cloudflare are free. The agent is also the last step rather than a prerequisite, so you can leave that account for later and still have a published app.

Cloudflare and Cursor have to be able to see the new repository, and that is the one setup choice worth a thought. Allowing them all repositories is what keeps app-building down to a single button, because a repository the builder has not created yet cannot be picked from a list. Approving each new project by hand works too - the builder stops and tells you when that is due. If all repositories feels too broad, keep a GitHub account just for these apps.

A brief is an input, not a wish. The agent writes what the description says, so a vague description gets a vague app. The brief at the top of this article asks for specific interactions, a specific visual treatment and a demo-data button, and that is why it produced something demonstrable. This is also why the loop matters more than the first run: the second brief is always better than the first.

The hosted builder’s server sees your Cursor key on the calls a browser cannot make. It is not stored, and it is not the kind of secret you have to hand over for good either: Cursor lets you give a key an expiry date when you create it, and delete it from its dashboard whenever you like. If a server you do not run is still one server too many, run your own copy.

An update cannot widen a grant. If a later version of your app needs a second database, the user has to grant it - in the app’s settings, or by reinstalling. Worth knowing before you ask the agent for a feature that needs one.

What this adds up to

The App Builder completes the platform. Everything else was already in place - the collaboration, the history, the encryption, the app model with its permissions and its allowlist - and every bit of it assumed that somebody would write the apps. A description is now enough, and what comes out is a real app with its own repository and its own address, not a prototype.

The App Builder is in Haven’s App Store now, and it is open source, as is MindooDB under the Apache 2.0 licence. Haven Community is free for private and commercial projects at haven.mindoodb.com, there is more on how apps work at mindoodb.com/haven/apps, and everything else is at mindoodb.com.