← Field NotesAI engineering, applied to cyber

Cloud Security For The Accidental SaaS Founder

The grown-up conversation about the cloud layer — RLS, IAM, and secrets management in plain English. Plus a pre-launch checklist you can run through before the next user signs up.

Illustration for Cloud Security For The Accidental SaaS Founder — Part 3 of 5

You had an idea. You vibe-coded a prototype. Two weeks later someone paid you real money for it and now you’re a SaaS founder, apparently.

Congratulations, and I’m sorry. Because the moment real users turn up, the rules change. Your weekend project is now infrastructure, and infrastructure has rules.

This article is the grown-up conversation about the cloud layer — the bit underneath your code where most vibe-coding disasters actually happen. We’ll talk through the three things that trip everyone up (RLS, IAM, and secrets), stay firmly in plain English, and finish with a checklist you can run through before you let the next user sign up.

First, a quick recap of how Moltbook went wrong

From Part 1: Moltbook exposed 1.5 million API keys, 35,000 email addresses, and a load of private messages. The code wasn’t the problem. The configuration was. One setting on their Supabase database — Row Level Security — was switched off, and that one setting turned a “safe to publish” API key into an admin password.

That’s the theme of this whole article. In cloud-native applications, your code might be flawless but you can still leak everything. The vulnerabilities often live in the gaps between the services — in the settings you didn’t know to check.

Let’s go through the three big ones.

Concept 01 · Database

Row Level Security (RLS): the bouncer on your database

What it is

RLS is a feature in modern databases (PostgreSQL, Supabase, and others) that lets you write rules like “users can only read rows where the user_id column matches their own ID.” Those rules are enforced inside the database. It doesn’t matter what the API says, what the frontend says, what the request looks like. If the rule says no, it’s no.

Why it matters so much for vibe-coded apps

Supabase and Firebase and their ilk are genuinely brilliant. They give you a full backend — database, auth, storage, APIs — in about five minutes. The cost of that convenience is that the “public” API key they hand you is literally published in your frontend JavaScript. Anyone who opens the browser’s dev tools can see it.

If RLS is on and properly configured, that’s fine. The key is just a project identifier. Every query still has to pass the rules.

If RLS is off, that key is a master key to your entire database. To everyone. Forever. Which is exactly how Moltbook happened.

What to do about it

  1. Turn it on for every table. In Supabase this is a single toggle per table. Do not deploy anything to production with RLS disabled.
  2. Write policies that are restrictive. A policy of true (meaning “always allow”) is worse than no policy, because it lulls you into thinking you’ve done the job. A real policy looks like auth.uid() = user_id — the authenticated user can only see rows that belong to them.
  3. Test by breaking in. Open an incognito browser. Go to your app without logging in. Open the dev tools and try to query your database using the public API key. If you get data back that you shouldn’t have access to, your policies are wrong.

The subtle bit everyone misses

RLS policies need to be written for every operation: SELECT, INSERT, UPDATE, DELETE. I’ve seen apps that correctly restrict reads but let anyone insert rows. An attacker who can insert rows into your posts table can impersonate your CEO, spam your users, or corrupt your analytics. Do all four. Test all four.

Concept 02 · Identity

IAM: the principle of least privilege

What it is

Every service in your cloud account has an identity and a set of permissions. The app server has an identity. The background worker has an identity. Your personal account has an identity. Each of those identities can do a specific set of things.

IAM is the system that decides who can do what. AWS, GCP, Azure, and every modern cloud provider has its own flavour.

Why vibe coding makes this harder

Because the AI tends to produce code that works, and “works” is easiest to achieve when the code has permission to do everything. Ask for a script that uploads to S3 and you’ll often get one configured with admin access to your entire AWS account. It works. It also means that if that script ever gets compromised, the attacker has admin access to your entire AWS account.

The rules of thumb

  • Least privilege, always. Every identity gets the minimum permissions it needs. If your app only reads from one database table, give it read-only access to that one table. Not the whole database. Not every table. That one.
  • No personal credentials in code. Your personal AWS access key should never, ever be in an application. Services authenticate as services, with their own IAM roles that they assume when they start up. If the AI hands you code that starts with “use my AWS credentials,” push back.
  • Separate environments have separate identities. The role your staging app uses should not be able to touch production data. Ever. This sounds obvious; it’s violated constantly.
  • Audit what’s out there. Every cloud provider has a tool that lists unused or over-privileged identities (AWS Access Analyzer, GCP Policy Troubleshooter, and so on). Run it at least quarterly. Be ruthless about pruning.

Horror story

The over-privileged AI assistant

In February 2026, BeyondTrust’s Phantom Labs found a critical command-injection vulnerability in the cloud environment of a well-known AI coding assistant. The attack exposed sensitive GitHub credentials that had been granted to the service for “convenience.” The credentials had way more access than the assistant actually needed.

When the vulnerability was exploited, attackers didn’t just compromise the assistant — they got a foothold into every repository the credentials could touch. Least privilege genuinely matters.

Concept 03 · Secrets

Secrets management — because nothing ever goes in the code

What counts as a secret?

  • API keys
  • Database passwords
  • OAuth client secrets
  • Third-party tokens
  • Webhook signing keys

Anything that, if leaked, would let someone impersonate you or your service.

Where secrets should live

In a secrets manager. Not in your code. Not in environment variables on your laptop that get committed to .env files and pushed to GitHub (the single most common way secrets leak, by miles). Not in config files. Not in CI logs. In a secrets manager.

The major ones: AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, HashiCorp Vault, Doppler, 1Password Secrets Automation. Pick one and use it.

In development, environment variables loaded from a .env file that is .gitignore’d are acceptable. In production, use a proper secrets manager that supports rotation, audit logging, and access control.

The rotation rule

Every secret should have a rotation schedule. Every. Secret. Thirty days is common; ninety is the outer limit. If a secret has been active for a year, it has almost certainly leaked somewhere you don’t know about.

This is boring, I know. It is also the difference between “we had a breach and had to notify 30,000 users” and “we rotated last week — the leaked key is already dead.”

What to do if a secret leaks

Assume they do, occasionally. When one does:

  1. Rotate it first. Before you investigate, before you commit the fix, before you do anything — rotate the key. The leaked version is now useless.
  2. Check the audit logs. Was it actually used by an attacker? Most cloud providers log API calls by credential.
  3. Scrub the git history. Tools like git-filter-repo or BFG Repo-Cleaner can remove the leaked secret from historical commits. Then force-push. (This is a nuclear option on a shared repo, so coordinate with your team.)
  4. Learn why it leaked. Was it committed by accident? Was it in a log file that got uploaded somewhere? Fix the root cause, not just the symptom.

A few more things that’ll bite you

Public buckets

Cloud storage buckets (S3, GCS, R2, Supabase Storage) default to private these days, but it’s frighteningly easy to make one public by accident — and some AI-generated code genuinely does this to make uploads “just work.”

Rule: every bucket is private unless you’ve consciously decided otherwise and you understand what that decision means. Signed URLs exist for a reason. Use them.

CORS and the frontend

Cross-Origin Resource Sharing rules tell browsers which websites can talk to your API. A common AI-generated pattern is Access-Control-Allow-Origin: * because it makes the “works locally” problem go away.

That also means any website in the world can make authenticated requests to your API from a logged-in user’s browser. Set CORS to your actual domain(s). No wildcards.

Billing alerts

A fun subgenre of disaster: someone finds your API key, spins up cryptocurrency mining on your cloud account, and you get a five-figure bill at the end of the month.

Turn on billing alerts. Set a soft cap at your usual monthly spend plus 20%. Set a hard cap at 10× that. Better to have your service go down than to wake up to a £50,000 AWS bill.

Logs, metrics, and the 3am problem

You want to know about bad things while they’re happening, not weeks later. At minimum:

  • Log every authentication attempt — success and failure.
  • Log every privilege escalation — role changes, admin actions.
  • Alert on unusual patterns: a spike in 401s, a single IP making thousands of requests, a service account suddenly reading from a table it’s never touched.

Cloud providers will do most of this for free if you turn it on. Modern managed log platforms (Datadog, Grafana Cloud, Honeycomb, Axiom) make it easy. If your answer to “how would I know if we got breached right now?” is “erm…”, that’s your next weekend project.

The pre-launch checklist

Before you let real users sign up, run through this:

  • Every database table has RLS enabled and policies for all four operations.
  • You’ve tested the app without being logged in and confirmed you can’t read anything you shouldn’t.
  • No credentials, keys, or secrets in the code or in Git history.
  • Every service identity has the minimum permissions it needs.
  • Secrets are in a proper secrets manager, with rotation scheduled.
  • Buckets are private by default; uploads use signed URLs.
  • CORS is configured to your actual domain(s).
  • Billing alerts are live, with sensible thresholds.
  • Logging covers auth, privileged actions, and anomalies.
  • You know who gets the 3am page, and they know the service exists.

If all ten are ticked, you’re in better shape than 90% of vibe-coded SaaS products currently in production. Which is both reassuring and slightly terrifying.

The bottom line

Cloud security isn’t about being clever. It’s about doing the boring things, in the boring order, and checking that the boring settings are boring in the way you meant.

The code your AI wrote is most probably fine. The gaps around the code are where the Moltbooks happen. Close the gaps, and you’ve done more for your users’ safety than any fancy framework can do for you.


Next in the series — Part 4 of 5

Setting up a free security pipeline in an afternoon — scanners, linters, and the hooks that tie them together, so the boring stuff happens automatically on every commit.