The conversation
You describe what the team needs, in your own words. If showing is faster than describing, attach it — a screenshot of the report somebody wants, the spreadsheet the data comes out of, a PDF of the form being replaced. Images and PDFs are read directly; a spreadsheet is handed over as a file the builder can open and read.
You can interrupt at any point, change your mind, and it picks up with the full history intact. Asking for something in the middle of a build is the ordinary case, not an interruption it recovers from.
Things that are not code can be asked for in the same sentence — call this one the first release and put it on test. Naming a version, connecting a domain, adding a person, testing an integration and deploying are all things the builder can do, acting with your permissions, not its own. A viewer who asks it to deploy is refused exactly as a viewer pressing the button would be.
Asking for settings
The conversation reaches the whole of the platform, not only the code. Somebody who does not know where a setting lives does not have to find out — they ask for the outcome.
- Its address
- Rename the subdomain, connect or disconnect a domain you own for either environment, and check the DNS record.
- How it introduces itself
- The browser tab, the icon, and what a link to it looks like pasted into a message. Icons are chosen from files you have published.
- Redirects
- Add and remove them, including everything under a path.
- Configuration values
- Create and remove the named values the application reads. It will set the name and tell you to paste a real secret in yourself.
- Integrations
- Connect, disconnect and test email, SMS and Google.
- Who can reach it
- Open, one shared password, or accounts — and it hands you the generated password, because it cannot be read back afterwards.
- The application’s own users
- Add somebody, reset a password, and switch on one account across every application — that last one only if you are an owner.
- Search engines
- What crawlers are told, per environment.
- Versions and shipping
- List them, name one, and deploy to test or live.
It is the same surface the Settings panel covers, reached a different way — so nothing is only possible by asking, and nothing is only possible by pressing. Where the two disagree they cannot: both go through the same code.
Three rules make that safe to have.
- It acts as you. A run carries the identity of whoever sent the message, and the role is resolved and enforced exactly as it is for the buttons. A viewer cannot deploy by asking nicely, and an owner-only action stays owner-only however it is phrased.
- It does not ship things you did not ask for. Deploying is not a finishing touch to add after a change. An ambiguous request gets finished and asked about, and a deployment records the reason in the words of the person who ordered it.
- It is never given your secrets. It can read every setting except a secret's value, and it can create a configuration value by name — but for a real credential it will tell you to paste it into Settings yourself, because a value typed into a conversation stays in that conversation.
What a build is
A real application is written into its own workspace, given its own database, cache and container, and then worked on: code written, run, the output read back, and what broke fixed. Each step is published as it happens and translated into plain language.
What you are shown is filtered on the platform, not in the browser. Command output, file contents and build logs never leave the server. A failure arrives as one sentence about what went wrong. This is not to make anything inconvenient to find — it is so that a product manager is never handed a stack trace and made to feel unqualified to own their own application.
Whatever reached the workspace is committed at the end of a run — whether it succeeded, failed or you stopped it. Work that exists is work somebody may want to go back to. A run that hits its step limit pauses with its work saved, and continue resumes it.
Versions
Every build becomes a version automatically. Not a convenience — people do not save their own work, so the version that mattered is always the one nobody thought to save. Nobody can lose a build here by forgetting to press something.
Each is named after what was asked for — Add a stage filter, not Version 7 — because that is what somebody searches for six weeks later. Naming one yourself is how you mark the build you will want to find again.
Storage is incremental, so fifty versions of an application cost about as much as one, and what changed between the version we tested and the one about to go live is a real difference you can read. The deploy dialog shows it.
Environments
Three, and each has its own database, cache and storage bucket. That separation is the point: a version being tested must never be able to write to what the business is running on.
A deployment unpacks a version, installs it, builds it, starts the server and waits for it to answer — and only then moves the address. A version that does not build never replaces the one that is live; it just fails to, and says so in the same feed as a build.
You do not have to know versions exist. Put the latest on test saves a version from the current state and deploys it in one press. Deploying twice with nothing changed in between reuses the version rather than making a near-identical one.
Live is reached from test by default, because that is the copy a person has actually looked at. Going straight from the workbench is there, underneath, labelled as the second option it is.
Domains and addresses
Your workspace can own a domain, and every application in it becomes a subdomain of it — crm.acme.com live, crm-uat.acme.com for testing. A workspace that sets none falls back to ours, so an application always has an address.
On top of that, any environment of any application can have a domain you own — one for live, a different one for testing. Two DNS records: one points the address at us, one proves the domain is yours. The certificate is then issued in the background, before the first visitor arrives, rather than on the first request while somebody waits.
The platform address never goes away when you connect your own. It stays as the way in while DNS propagates, and as the fallback if the domain is later pointed somewhere else.
Who can reach it
Three answers, set per application, and all three are enforced in front of the application rather than inside it. That covers every page, every API route and every published file, including ones added by a later build; it holds while the application is mid-deploy or its build is broken; and the application cannot turn it off by accident, because it is not the application's to turn off.
- Open
- Anyone with the address. A public site.
- One shared password
- Everyone types the same password once. Nothing to set up per person — right for showing a client their application before it is public. Changing the password ends every session opened with the old one.
- People sign in
- An account per person, with two-step sign-in by email code or authenticator app. The business adds and removes its own people; the platform stores the passwords, so the application never does.
The people who sign in to your application are your customers, not ours. They live apart from platform accounts, cannot reach the workbench, and a session belongs to the application it was made for.
One user base, or one per application. A workspace setting decides whether an account works across every application there or has to be granted per application. Turning it on grants nothing retroactively and asks nobody to re-register — it changes the question from is this person a member of this application to is this person in this workspace, so the accounts that already exist simply start working. A session still belongs to the application it was made for: one application's cookie is never another's.
Your business manages its own people. Somebody added by address gets an account, a generated password shown once, and an email with it if the application has email connected. Somebody already in the workspace is granted this application instead of getting a second account, and keeps the password they already had.
Integrations
An application does not hold an email provider's key or speak an SMS vendor's API. It asks the platform, and the platform does the work with whatever your workspace configured.
- Postmark, Mailjet, Mailgun, Resend, SendGrid, or your own SMTP
- SMS
- Telnyx, Twilio
- Drive, Docs, Calendar — connected per application, by consent
Three things follow. Somebody who is not a developer configures a service, not a library. Switching provider is a settings change, with no rebuild and no agent run, because the application only ever knew "send an email". And credentials never reach the application — an application that is compromised cannot walk off with your email account or your Drive.
Testing a connection is a real send, because a check that passes and then fails on the first customer is worse than no check. Email goes to an address that accepts and discards it, so nothing lands in anyone's inbox. There is no equivalent for SMS, so that one asks for a number and costs one message — which is said plainly rather than charged silently.
The cost of this design is real and worth stating: an integration we have not implemented cannot be used at all. That constraint is what the rest of it is bought with.
Files
Upload a document and it gets a permanent address on your application's own domain — a price list, a brochure, a form to print, terms to download:
https://crm.acme.com/_files/price-list.pdf
These belong to the business rather than to the code, so they need no build to change and no deployment to appear. Up to 100 MB each, up to 200 per application. Three properties follow from the platform serving that address rather than the application:
- the link is the same in test and live;
- it keeps working while a deployment is building, and while a build is broken;
- it answers before anything has been deployed at all — publishing a price list should not require a deployment.
Replacing a file keeps its address, which is the whole point: a link already sitting in a customer's inbox goes on working and now points at this quarter's prices. Published files sit behind the access setting, so on a protected application a file is not a way around it.
Two other kinds of file are kept separate because they have different lifetimes. Files in the conversation are how you show the builder what you mean. Files the application's own users upload go to its own storage bucket, which reaches nothing else.
Settings, not source
The browser tab, the icon, what a link to it looks like pasted into a message, whether it can be added to a phone's home screen, old addresses that still get visited, what search engines are told, and the named values the application reads — all of these are settings.
Everybody notices these only when they are wrong, nobody remembers to ask for them, and written into the source the next build would overwrite them. As settings they take effect on a deployment with no code written, and a build cannot undo them.
Configuration values can apply to every environment or only to live or test, because a live payment key in the test application is how test data reaches real customers. Secrets are encrypted and read back only as a hint: once saved, a secret can be replaced but never re-read.
Who is using it
The first question anybody asks about something they had built, and one you should never have to wire anything up to answer. Every deployed application reports its own page views: visitors today, visitors and pages over a period, a daily average, a day-by-day chart and the most-opened pages — separately for test and live, because a busy round of testing is not customers using it.
Counted without identifying anyone. A visitor is a daily one-way hash that cannot be reversed into an address and does not carry from one day to the next or from one application to another. No identifier is stored, no cookie is set, and there is nothing to put a banner up about.
Workspaces
A workspace is an organisation: its people, its applications, its domain, its design systems. Four things are shared across every application in one, and they are the four that would otherwise be re-specified per application:
- The domain. Set it once and every application becomes a subdomain of it —
crm.acme.com,portal.acme.com— with no further work per application. - The design systems. Your brand, authored once and available to everything built there.
- The people who build, with their roles.
- The user directory — everybody who signs in to the applications built there, one list behind all of them, optionally with one login across every application.
Working for clients, use a workspace per client. Their brand, their domain, their people and their applications' users are all separate from every other client's, and nothing of one is visible from another. It is also the unit we suspend, bill and report spend against, so a client is a clean line in every one of those.
One account can belong to several, and the role is held per workspace — an owner of their own, a builder in one client's, a viewer in another's. A session is bound to one workspace at a time and switching starts a new one, so one client's work is never one mis-click away from another's.
Your first workspace comes from us: public signup is closed, and that is how access is granted. After that you make your own whenever you take on a client, and you are its owner.
Handoff and exit
Every application is ordinary Nuxt source with ordinary git history. Download source exports it as a repository that runs with pnpm install && pnpm dev on any developer's machine. It sits with the workspace owner.
That escape hatch is what makes the rest of the constraints safe to accept. The technology stack here is deliberately fixed — one framework, one way to store data, one way to send email — and the reason a team can agree to that is that the result is never locked in behind it.
Every screen and setting, written up
The documentation covers all of this in the order you would meet it, with the exact steps.