Skip to main content

Solo Builder: from vibe-coded app to live product

This arc is for one person with an app that already exists — built by hand or built with an AI coding tool — and no cloud experience to go with it. The nine stories follow Sam from a landing page sitting in a repository to a product with customers, a database, a chat channel and a mobile app, and they build on each other in order; each one also stands alone, so start wherever you recognise yourself. The through-line never changes: every change to your infrastructure arrives as a pull request — a proposed change you read and approve before anything is created — with the estimated cost attached to it. Undoing a change is the same button as undoing code, because the change was code.


1. Ship your first site​

The win: Your site is live in about five minutes, and the price was on the change before you said yes.

Who this is forA solo builder with a frontend in a repository and nowhere to put it
DifficultyBeginner
Time~5 minutes
What you need firstA repository containing a static or buildable frontend. You do not need a Google Cloud project ready — Infrastream can create one for you.
What it costsNothing per visitor if the site keeps no secrets — it serves from the cache. If one secret is involved, that adds one small always-on component, and the estimate appears on the change before you approve it.

In plain words​

Sam has a landing page in a repository and nowhere to put it. Every tutorial about "deploying to the cloud" opens with a wall of acronyms before a single pixel goes live, so Sam skips all of it: point Infrastream at the repository, and it reads the project, proposes a deployment, and shows the estimated monthly cost inside the pull request, before anything is provisioned. Sam approves it, and a few minutes later the site is live behind a global CDN — a network of caches around the world that holds a copy of your files close to whoever asks for them. The number on the first invoice was the same number that was on the pull request.

Why the price is not always the same number

It comes down to one question: does anything your frontend does need to keep a secret?

A secret is something like a paid API key — a password your app uses to call somebody else's service. Anything your site sends to a visitor's browser can be read by that visitor: they open the browser's developer tools and there it is. So a secret cannot live in the browser, which means the call that uses it cannot happen in the browser either.

If nothing needs a secret — a plain HTML/CSS/JavaScript site, or an app that only calls public services, or services that accept a token that is safe to hand out because it is short-lived and scoped to that one visitor — then every file is just handed out from the cache. Nothing has to be switched on and waiting between visits. That is the cheapest shape, and it is still fully public.

If one thing needs a secret, that one call has to move off the visitor's machine and onto a computer you control, which holds the key and makes the call on your app's behalf. Infrastream provisions exactly one small piece of software running on a server for that — not a whole backend, just the one place the secret lives. Your site stays cached and cheap; that single piece is the only thing added, and it only appears when there is genuinely a secret worth protecting.

Sam's landing page has no secrets to hide, so it stays in the cheaper shape. The day it needs to call a paid API with a key attached, that one call moves behind the small server-side piece, and the estimate on the pull request changes before the invoice does.

What you actually write​

One Application manifest — a manifest is simply the plain-text file that describes what you want — naming the build that produces your site and the project it runs in.

# .../application-set/samsapp/application/landing-page.yaml
apiVersion: lowops.manifests.v1
kind: Application
metadata:
name: landing-page
application-set: samsapp
release-track: main-track
organizational-unit: web
organization: samsapp
spec:
description: "Marketing site and waitlist landing page."
buildDefinition: landing-page # the BuildDefinition that builds this repository
target: CLOUD_RUN # CLOUD_RUN | KUBERNETES | COMPUTE
meshStrategy: DISABLED
project: samsapp-prod

A matching DeploymentConfig sets the per-environment details — which version is live, scaling floor and ceiling, ports and health checks — so the same application can run differently in development and production without touching the manifest above.

What the platform handles for you​

  • The build pipeline that turns your repository into a runnable image, so you never write CI configuration by hand.
  • The global CDN and TLS certificate in front of the site, so visitors get an HTTPS address that loads quickly wherever they are.
  • A generated hostname for the service, so the site has a working address on day one and the custom domain becomes an optional step rather than a blocker.
  • The cost estimate attached to the pull request, so approving the change and approving the price are the same action.

Learn more​


2. Get your own domain, without leaving your repo​

The win: A real domain from one merged line — and nothing to renew by hand in six months.

Who this is forAnyone whose site works but whose URL is not presentable
DifficultyBeginner
Time~10 minutes
What you need firstStory 1 done, and a domain you already own at a registrar
What it costsNothing extra beyond the front door you already have; the certificate is issued and renewed by the platform.

In plain words​

The generated URL works, but sams-landing-page-4821.run.app is not going on a business card. Sam owns samsapp.dev, bought years ago for a project that never shipped. Adding it is a one-line change: Infrastream opens a pull request that binds the domain, creates the DNS zone — the place that answers the question "which server is samsapp.dev?" — and requests the HTTPS certificate. Sam merges it, points the domain's nameservers at the zone the platform created, waits for the certificate to issue, and the domain resolves. There is no control panel with seventeen tabs, and nothing to renew manually later.

What you actually write​

A PublicIngress manifest — the public front door for everything in this project — with the domain you own.

# .../project/samsapp-prod/public-ingress/front-door.yaml
apiVersion: lowops.manifests.v1
kind: PublicIngress
metadata:
name: front-door
project: samsapp-prod
environment: production
organizational-unit: web
organization: samsapp
spec:
description: "Public front door for the marketing site."
domain:
name: samsapp.dev # base domain; the platform creates the DNS zone and certificate
txtRecords: # optional extras kept at the zone apex, e.g. domain verification
- "google-site-verification=xxxxx"

Routes attach to this front door and decide which path goes to which application — that is the next story.

What the platform handles for you​

  • A managed DNS zone with the records the platform needs, so you are not hand-copying record values between two dashboards.
  • The TLS certificate and its renewals, so the padlock keeps working without a calendar reminder.
  • The external load balancer the domain points at, so every route you add later inherits the same address and certificate.
  • Your extra TXT records are re-applied on every run, so platform-managed records never quietly erase the ones you added for domain verification or email.
One manual step remains

You must delegate the domain at your registrar by pointing its NS records at the nameservers of the zone Infrastream created. Until you do, the certificate cannot validate and the domain will not resolve.

Learn more​


3. Put a login on exactly one page​

The win: A private /admin page for a one-person project, with no authentication code written.

Who this is forA builder who needs one private page and does not want to build a login system
DifficultyBeginner
Time~15 minutes
What you need firstStory 1 done, and a Google Workspace or compatible identity you can sign in with
What it costsNothing extra — the gate rides on the front door and the application you already run.

In plain words​

Sam wants an /admin page for editing content without redeploying, but building an authentication system for a one-person side project is how side projects die. This is an internal-access problem, not a customer-facing one, so Infrastream puts Identity-Aware Proxy — a checkpoint that sits in front of the app and refuses to pass the request along until it knows who you are — in front of /admin only, tied to Sam's own account. The rest of the site, the part strangers actually visit, stays exactly as public as it was. There is no session logic to write and no password to forget.

What you actually write​

Two rules on one route. The first rule names the identity that may pass; the second has no authentication block at all, which is what keeps the public site public.

# .../public-ingress/http-route/site-routes.yaml
apiVersion: lowops.manifests.v1
kind: HttpRoute
metadata:
name: site-routes
public-ingress: front-door
project: samsapp-prod
environment: production
organizational-unit: web
organization: samsapp
spec:
rules:
- matches:
- prefixMatch: /admin # only this path is gated
authentication:
- type: IDENTITY_PROVIDER
tenants: [admin-login] # an IdentityProvider manifest
action:
destinations:
- deploymentConfig: landing-page
port: 8080
- matches:
- prefixMatch: / # no authentication block: stays public
action:
destinations:
- deploymentConfig: landing-page
port: 8080

Who is allowed through is named on the front door's iapPermissions block, which lists OrganizationUserGroup manifests rather than raw email addresses — so revoking access later is removing a person from a group, in a diff anyone can read.

What the platform handles for you​

  • The Identity-Aware Proxy checkpoint and its IAM bindings, so an unauthenticated request never reaches your container at all.
  • Per-path enforcement, so one page can be private while the rest of the site stays open — you are not choosing between "all public" and "all private".
  • The hosted sign-in page, so there is no login screen to design or session cookie to get wrong.
  • Group-based access, so "who can reach the admin page" is a reviewable line rather than a setting buried in a console.

Learn more​


4. Turn your site into a real app​

The win: What used to be four tickets to a platform team is one merged pull request.

Who this is forA builder whose site now needs to remember things between visits
DifficultyIntermediate
Time~30–45 minutes
What you need firstStory 1 done, backend or API code you can deploy (or the willingness to scaffold one), and a decision on the database engine
What it costsScales with the size you pick for the database, and a database is exactly what the off-hours schedule can pause — see Controlling Costs.

In plain words​

The landing page did its job and people signed up for a waitlist. Now Sam needs somewhere to put them. The usual version of this involves provisioning an API service, then a database, then the private network path between the two, then the permissions that let one talk to the other — four separate pieces of work that each have to agree with the other three. Instead, Sam describes the shape of the app once: a frontend, an API, and a managed PostgreSQL database. Infrastream provisions all of it as one connected graph, where the API already knows how to reach the database and the database already trusts only the API.

What you actually write​

A Database manifest declares the instance and the logical databases on it. The API then asks for access in its own manifest, and that request is what wires the two together.

# .../application-set/samsapp/application/waitlist-api.yaml
apiVersion: lowops.manifests.v1
kind: Application
metadata:
name: waitlist-api
application-set: samsapp
release-track: main-track
organizational-unit: web
organization: samsapp
spec:
description: "Waitlist and account API."
buildDefinition: waitlist-api # the BuildDefinition that builds this repository
target: CLOUD_RUN
project: samsapp-prod
accessControl:
database:
name: accounts-db # the Database manifest in this project
schema: accounts # a logical database declared in its spec.databases
privileges: [USAGE, CREATE]
secretSource:
envVar: DATABASE_URL # credentials injected at runtime, never written to Git

The companion Database manifest sizes the engine — for AlloyDB (PostgreSQL), cpuCount and clusterSize — and lists the logical databases applications may connect to by name.

What the platform handles for you​

  • The private network path between the API and the database, so the database is never reachable from the public internet.
  • A dedicated database user for this application with exactly the privileges listed, so a mistake in one service cannot rewrite everything.
  • The credentials themselves: created, stored in Secret Manager, and injected as DATABASE_URL at runtime, so no password is ever committed to your repository.
  • The ordering between the pieces, so the database exists and trusts the application before the application starts looking for it.

Learn more​


5. Let your customers sign in too​

The win: Real customer accounts without hand-rolling a single session.

Who this is forA builder whose app now has users, not just visitors
DifficultyIntermediate
Time~30–45 minutes
What you need firstStory 4 done, and a decision about which sign-in methods matter — email and password, Google, Apple, and so on
What it costsScales with sign-ins rather than running all the time; the estimate appears on the change before you approve it.

In plain words​

Story 3 solved a narrower problem: keeping Sam's own /admin page private. Now that there is a real backend and a database, actual customers need accounts — sign up, log in, and stay logged in between visits. This is a customer identity provider, not an internal gate restricted to Sam's own account: it issues real end-user identities through whichever sign-in methods Sam picks. Infrastream wires that provider into the same graph as the API and the database, so every request the frontend makes carries a verified user the backend can trust. The app knows who is asking, and Sam wrote none of it.

What you actually write​

An IdentityProvider attached to the front door from Story 2. The providers block is the list of buttons your customers will see on the sign-in page.

# .../project/samsapp-prod/public-ingress/customer-login.yaml
apiVersion: lowops.manifests.v1
kind: IdentityProvider
metadata:
name: customer-login
public-ingress: front-door
project: samsapp-prod
environment: production
organizational-unit: web
organization: samsapp
spec:
description: "Customer accounts for the web app."
displayName: "Sign in to Samsapp" # shown on the hosted login page
logoUrl: "img/logo.svg"
iconUrl: "img/icon.svg"
mode: REDIRECT # REDIRECT | POPUP
providers:
password: true # email and password
google: true # Google sign-in

Declaring the provider builds the login experience, but nothing is actually gated behind it until a route says so — an HttpRoute rule with type: IDENTITY_PROVIDER and this provider in its tenants list is what puts requests through it.

What the platform handles for you​

  • The hosted sign-in page, branded with your display name, logo and colours, so there is no login screen to build.
  • The federated providers themselves, so adding Apple or Microsoft later is one more line rather than an OAuth integration project.
  • Token verification at the edge, so your API receives requests that already carry a verified user instead of validating sessions itself.
  • Signup rules — such as an email-domain allowlist or blocklist — so who may create an account is declared rather than policed after the fact.

Learn more​


6. Let AI run your database​

The win: Schema changes you read over coffee, instead of typing them into a production shell at 11pm.

Who this is forA builder who needs to change the database and would rather not do it by hand
DifficultyIntermediate
Time~20 minutes to wire up, then ongoing
What you need firstStory 4 done, and pull-request access granted to the pvot agent on your repository
What it costsNothing extra for the migration itself — it runs once per deployment and stops. The database it changes is the ongoing cost, and it is a good candidate for the off-hours schedule — see Controlling Costs.

In plain words​

Sam's schema needs a new column, which normally means connecting to a production machine at 11pm, running a migration by hand, and hoping nothing breaks. Instead, Sam describes the change in plain language and pvot — the platform's AI agent — drafts it as a pull request: the actual SQL, the rollback plan, and the difference against the current schema. Sam reads it over coffee before merging. The database changes exactly when Sam says yes, never before, and never because somebody typed into a production shell.

What you actually write​

Add two lines to the application — a job named migrate with type: BEFORE, which blocks the deployment until it succeeds — and one JobConfig per environment describing how the migration runs.

# .../project/samsapp-prod/job-config/waitlist-api-migrate.yaml
apiVersion: lowops.manifests.v1
kind: JobConfig
metadata:
name: waitlist-api-migrate # <application-name>-<job-name>
project: samsapp-prod
environment: production
organizational-unit: web
organization: samsapp
spec:
version: "1.4.0" # the image tag the migration runs from
container:
command: ["/migrate.sh"] # the entrypoint that applies your migrations
env:
- name: MIGRATION_MODE
value: "up"

The job reuses your application's own container image, so the migration always runs the version of your tooling that matches the code being deployed.

What the platform handles for you​

  • The one-off job and its ordering, so the main service is only deployed after the schema change succeeds — a failed migration stops the release instead of half-applying it.
  • The same private path and credentials the application uses, so the migration never needs a separate admin password.
  • The review gate, so an agent-authored change is still a diff a human approved — the agent proposes, it does not apply.
  • A history of every schema change as commits, so rolling one back is the same action as rolling back code.

Learn more​


7. Meet your users where they already are​

The win: A second front door, not a second backend.

Who this is forA builder whose users would rather send a message than open a browser
DifficultyIntermediate
Time~30 minutes
What you need firstStory 4 done, and a WhatsApp Business API or Meta developer account with a number you control
What it costsOne more small component that scales with message volume; the estimate appears on the change before you approve it.

In plain words​

Half of Sam's waitlist signups come from a region where nobody opens a browser to check an order status — they text. Adding WhatsApp as a channel does not mean standing up a second backend: the piece that receives messages is wired to the exact same API and the same database the website already uses. A user sends a message, the same business logic answers, and Sam's app quietly has two front doors that behave identically because underneath they are the same app.

The WhatsApp side of the arrangement — registering the number and the webhook with Meta — happens in the Meta developer console, not in a manifest. What Infrastream declares is the service that receives those messages and the credential it uses to reply.

What you actually write​

A second application in the same set, holding the WhatsApp token as a secret and reading the same logical database as the website.

# .../application-set/samsapp/application/whatsapp-channel.yaml
apiVersion: lowops.manifests.v1
kind: Application
metadata:
name: whatsapp-channel
application-set: samsapp
release-track: main-track
organizational-unit: web
organization: samsapp
spec:
description: "Webhook that turns WhatsApp messages into API calls."
buildDefinition: whatsapp-channel # the BuildDefinition that builds this repository
target: CLOUD_RUN
project: samsapp-prod
accessControl:
secrets:
whatsapp-token: # a Secret manifest; the value lives in Secret Manager
envVar: WHATSAPP_TOKEN
database:
name: accounts-db # the same database the website uses
schema: accounts
privileges: [USAGE] # no CREATE: this service cannot alter the schema
secretSource:
envVar: DATABASE_URL

What the platform handles for you​

  • A separate service identity for the channel, so what the chat front door may do is declared independently of what the website may do.
  • The WhatsApp token as a managed secret, injected at runtime and never committed, so rotating it does not mean editing code.
  • The same private database path as the API, so the second channel cannot drift onto a different copy of your data.
  • The public route and certificate for the webhook address, so Meta has a stable HTTPS endpoint to deliver messages to.

Learn more​


8. Give your WhatsApp bot the same identity as your app​

The win: One customer across every channel — and the bot can only do what its own leash allows.

Who this is forA builder running more than one channel who does not want two copies of each customer
DifficultyIntermediate
Time~30–45 minutes
What you need firstStory 5 done (customer identity) and the WhatsApp channel from Story 7 in place
What it costsNothing extra to run; phone verification is charged per message sent, so the region allowlist below is a cost control as well as a fraud control.

In plain words​

A customer messaging the WhatsApp number and that same customer logged into the website should resolve to one account, not two silos that happen to share a database. When a phone number is verified, it belongs to the same customer identity from Story 5, so order history and preferences follow the person rather than the channel. The bot answering those messages, though, is not the customer: it needs its own identity, scoped to only the actions it is allowed to take on someone's behalf and kept separate from that person's own credentials. Who the customer is, and what the bot may do for them, are two different declarations.

What you actually write​

Extend the Story 5 provider so the same tenant also accepts a verified phone number. Phone sign-in requires an SMS region policy — the platform refuses the configuration without one.

# .../project/samsapp-prod/public-ingress/customer-login.yaml  (Story 5, extended)
apiVersion: lowops.manifests.v1
kind: IdentityProvider
metadata:
name: customer-login
public-ingress: front-door
project: samsapp-prod
environment: production
organizational-unit: web
organization: samsapp
spec:
displayName: "Sign in to Samsapp"
logoUrl: "img/logo.svg"
iconUrl: "img/icon.svg"
providers:
password: true
google: true
phone: true # the same tenant now accepts a verified phone number
smsRegionPolicy: # required whenever phone sign-in is enabled
allowedRegions: ["BR", "IN", "NG"]

The bot's own leash is the accessControl block on the channel application from Story 7: it lists exactly which database, which privileges and which secrets that service may use, and nothing else.

Linking a phone number onto an existing email account is behaviour of the identity platform rather than a manifest field. Ask the pvot agent to draft this one — see Using pvot.

What the platform handles for you​

  • One identity tenant across every channel, so a person who arrives by chat and a person who arrives by browser are the same account.
  • The SMS region allowlist, so verification messages can only be sent to regions you named — the standard defence against toll fraud.
  • A distinct service identity for the bot, so "what the bot may do" is granted to the bot and never borrowed from the customer.
  • Custom claims on the user's token, so your API can act on facts about the user that the platform attached at sign-in rather than facts your code guessed.

Learn more​


9. Ship it to mobile too​

The win: The third surface costs an hour, not a third authentication system.

Who this is forA builder adding a mobile app to a product that already has a web and a chat surface
DifficultyIntermediate
Time~1 hour
What you need firstStory 5 done (customer identity) and a mobile app project, new or existing
What it costsNothing extra on the platform side — the mobile app runs on your users' devices and calls the API and identity provider you already run.

In plain words​

By now Sam's app has a website, a WhatsApp bot and real customer accounts. The last ask is a mobile app, and the instinct is to dread it: a third login flow, and a third set of API contracts to keep in sync with the other two. Instead, the mobile app signs users in against the same customer identity from Story 5 and calls the same API from Story 4. Three surfaces — web, chat and mobile — share one identity, one backend, and one place where changes are made.

What you actually write​

Nothing new for the mobile app itself. What matters is that the API route names the same identity tenant the website uses, so a token issued to a phone is accepted exactly like a token issued to a browser.

# .../public-ingress/http-route/api-routes.yaml
apiVersion: lowops.manifests.v1
kind: HttpRoute
metadata:
name: api-routes
public-ingress: front-door
project: samsapp-prod
environment: production
organizational-unit: web
organization: samsapp
spec:
description: "The API every surface calls — web, chat and mobile."
rules:
- matches:
- prefixMatch: /api
authentication:
- type: IDENTITY_PROVIDER
tenants: [customer-login] # the same tenant the website signs into
action:
destinations:
- deploymentConfig: waitlist-api
port: 8080

If the mobile project is also built by the platform, it gets a BuildDefinition of its own like any other repository — the build toolchain is selected by the definition's type.

What the platform handles for you​

  • One identity tenant serving all three surfaces, so there is no second user table to reconcile later.
  • Token verification at the edge for every surface, so the mobile client gets the same guarantees as the browser without extra API code.
  • One API and one database behind all of it, so a change to your business logic reaches every surface at once instead of three times.
  • The same review-and-price flow for any new surface you add, so surface number four is the same kind of pull request as this one.

Learn more​


Where to go next​

If the project outgrows one person — a second engineer, a staging environment, requests for small infrastructure changes landing in your messages — continue with the Growing Team arc, stories 10 to 14, which picks up exactly where Sam leaves off here. If you would rather start building now, the Getting Started guide walks through your first repository end to end, and the Manifest Reference documents every field used on this page.