Lovable and OurTools: which one does what
Lovable builds the app, OurTools puts it behind your company login, and this is what that actually means in the request path.
Someone on your team described a tool in Lovable and it works. Now the finance team wants to use it, and someone in IT wants to know who can open it. Those are two problems, and they belong to two different products.
Two different jobs
Lovable builds apps. You describe what you want in chat and you get something you can click. That loop, from an idea to a working screen, is the part that used to take a week, and it is the part Lovable is good at.
OurTools builds nothing. It takes an app that already exists, as a folder, a zip or a GitHub repository, and puts an address in front of it that checks who is asking before the request arrives. If you are happy building in Lovable, keep building in Lovable. This is about the step after that.
One thing worth saying plainly, because it gets missed: lovable.app is on the Public Suffix List. That is a deliberate and correct decision. It is what makes a browser treat acme.lovable.app and rival.lovable.app as separate sites, so two customers can never share cookies. It is real engineering on Lovable's part, and it is also, incidentally, the reason Google will not let us point a site-restricted query at that domain.
What a company access rule actually asks for
Start by checking what your own publishing settings already offer. If they meet the rule your company has, you are finished and you can stop reading. The rule is worth writing out, though, because it is rarely what people first assume, which is that the address should be hard to guess. Written out, it usually reads like this:
- Only people with a work account at this company can open it.
- Some apps go to three named people, not to everyone.
- Access can be removed in seconds by someone who is not the person who built the app.
- There is a record of who was given access and when, and it can be handed to whoever asked for it.
- The data sits somewhere the company has already agreed to.
That is a hosting and identity requirement rather than a building one. It is why these two products sit beside each other rather than against each other.
What behind company login actually means
The phrase gets used loosely, so here is the mechanism. Each app gets a subdomain of ourtools.app. The load balancer sends the apex to the web app and every other subdomain to a Go proxy. The proxy looks up the app by its slug, works out who is asking, decides whether they are allowed, and only then forwards anything upstream.
Identity comes from an ID token verified against the project's public keys, with the issuer, the audience and the expiry all checked. Sign-in is email and password, Google, or Microsoft.
The consequence is the part worth reading twice. Your app contains no auth code, and it is never asked to. It receives a request that has already been decided, with the verified caller in headers it can read:
X-OurTools-User-Email: [email protected]
X-OurTools-User-Id: 5f3c9a1e-...
X-OurTools-Permission: edit
X-OurTools-App-Slug: quarterly-plannerThree deletions are what make those headers worth trusting. Every inbound header whose name starts with x-ourtools- is deleted from the outbound request before the platform sets its own, so a caller who sends X-OurTools-User-Email is sending it into a bin. The platform session cookie is dropped, so customer-written code never sees a live token belonging to the person using it. The Authorization header is replaced with the proxy's own token. All three live in apps/proxy/forward.go.
The app also has no public address of its own. Tenant services are deployed with ingress INGRESS_TRAFFIC_INTERNAL_ONLY and an IAM policy holding exactly one binding, roles/run.invoker for the proxy's service account. If Cloud Run hands back a service with any other ingress, the builder refuses to publish it rather than carrying on.
The app your team built has no auth code in it, and it never sees the session cookie.
Per person, per company, or one link
Four kinds of rule, set from the dashboard or the command line:
- A named person, by email address.
- Everyone in the workspace.
- Everyone with an address at a domain you name, so all of acme.com gets in with their work login.
- A share link, for the contractor who has no account.
Each rule carries view or edit. When more than one applies the order is fixed: a rule naming the person beats a workspace rule, which beats a domain rule, which beats a link, and anything left over is a denial. Within one rank, edit beats view. That order exists twice, in TypeScript for the dashboard and in Go for the proxy, and each has its own table of cases written by hand rather than generated from one shared fixture, so a mistake in one is not copied into the other.
Share links are handled with a little care. The token in the query string is swapped for a signed cookie and the browser is redirected to the address without it, so it does not sit in history or leak through a Referer header. Only the hash of the token is stored, so a link shown once cannot be recovered from the database later.
ourtools share quarterly-planner --user [email protected] --permission edit
ourtools share quarterly-planner --domain acme.com
ourtools share quarterly-planner --privateThe audit log
The audit_events table is append-only. The control plane has one insert and no update and no delete anywhere in it, so the only way history changes is somebody typing SQL against the database by hand.
It records deploys, changes to who can open an app, settings changes, rollbacks, kills, sign-ins and database changes. Metadata is scrubbed on the way in: anything called value, secret, token, connectionString or databaseUrl is dropped rather than written, because this log is read by admins and exported out of the product.
The export is a CSV with six columns: when in UTC, the event, what happened in plain words, who, which app, and the details. Every cell is neutralised against spreadsheet formula injection, so a reason someone typed starting with an equals sign arrives in Excel as text rather than as something Excel runs. Audit log with CSV export sits on the Business plan at $199 a month.
Where it runs
The region is chosen per app when you create it, not once per account. There are 43 to pick from across 29 countries, and a team in one country and a team in another can each keep their tool close to home under one workspace and one bill. Change your mind and redeploy somewhere else.
ourtools deploy ./app --name quarterly-planner --region europe-west4Bring the database you already have
Bringing your own connection string is the first of four database paths and the default, and most apps should never reach the other three. If your project already keeps its data in Supabase, that is the whole migration: set DATABASE_URL, or whichever variable your app reads, and OurTools hosts the app and touches nothing else. Firebase, MongoDB Atlas, Neon, PlanetScale and the database your company already runs work the same way.
Values are encrypted before storage and never come back out. The API returns the literal string *** where a value would be, so the dashboard can show what an app is connected to without ever showing the credential.
If you have no database, ask and the app gets a managed Postgres, MySQL or SQLite one with the connection string already in its environment. SQLite comes with its limit stated rather than hidden: single instance only, a copy of the file saved every 60 seconds and when the app shuts down normally, and writes from the last minute lost if the machine dies suddenly.
Getting the project across
Four stacks are supported, detected from the upload itself: a static site with an index.html at the root, Vite, Next.js, and single-file Python with Flask or FastAPI. A Vite project is recognised by a package.json that depends on vite, which is what most exports of this kind look like.
Three ways in. Drag a folder or a zip into the dashboard. Run one command. Or connect the GitHub repository, through a GitHub App installed on the repositories you choose rather than an OAuth app holding everything you can see, with installation tokens minted on demand and never written down.
ourtools login
ourtools deploy ./my-appThere is also an MCP server, so the coding agent that wrote the app can deploy it, share it and read its build log without you leaving the editor.