---
title: "Can you vibe code safely?"
description: "A security checklist for vibe-coded apps, in any stack: what goes wrong, how to check for it, and how @popcomputer/web settles each risk for you."
url: "https://web.popcomputer.com/vibe-coding-safely"
updated: "2026-10-30"
---

# Can you vibe code safely?

Yes, if the risky decisions aren’t left to the AI. In an app on @popcomputer/web, who may see a record, how a form is checked and how the database is written are settled by the framework. The type checker, the lint rules and the tests then catch the mistakes AI tools tend to make, before they reach anyone. Whatever you build with, the checklist below shows how to look for them.

## Where vibe coding goes wrong

An AI writes code that works when you try it. The trouble is what you can’t see by clicking around: a page that shows someone else’s order when you change a number in its address, a form that lets a visitor set a field they shouldn’t, a query built from whatever was typed, a secret key pasted into the code.

None of these break anything you’d notice, and each is one forgotten line. The fix isn’t a more careful AI. It’s a framework where that line doesn’t have to be written in every feature.

## A security checklist for AI-built apps

Nine checks, for an app built with any stack and any AI. Each says what goes wrong, how to check for it yourself, and how @popcomputer/web settles it. Go through them before you first deploy, and again after any big change.

## 1. Who can see each record

- What goes wrong: a page loads a record by the number in its address and never asks who’s looking. Change the number, and you’re reading someone else’s order, invoice or messages.
- How to check: sign up as two people. Open one person’s record, copy its address, and open it as the other person, then signed out. Do the same for every page that edits, deletes or downloads.
- How @popcomputer/web settles it: each table says, once, who may load its records, and the lookup’s own query enforces it, so no page has to remember. Another person’s record is the same “not found” as one that doesn’t exist, and a table that doesn’t say is a lint error. [Who may load a record](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/route-models.md#who-may-load-a-record).

## 2. Fields a form shouldn’t accept

- What goes wrong: the code saves whatever fields arrive. A visitor adds one the form never showed, such as a role of admin or someone else as the owner, and it’s saved.
- How to check: in your browser’s developer tools, add a field to a form, such as role or userId, and send it. Then look at the record: if the field changed, the form takes more than it shows.
- How @popcomputer/web settles it: each form is a schema, and a field it doesn’t declare never reaches your code. The framework’s rules tell your AI to take the owner from the signed-in user, never the form, and Better Auth’s sign-up refuses a user field you haven’t named as one visitors may set. [Validating input](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/validation.md).

## 3. SQL built from text

- What goes wrong: your AI builds a query by joining in what someone typed. A name with a quote mark in it then changes what the query does, and a crafted one can read or change data it was never meant to.
- How to check: search the code for SQL put together from pieces of text, and type a quote mark into every search box and form. An error page where you expected results means the input reached the SQL.
- How @popcomputer/web settles it: queries go through Drizzle, which sends values apart from the SQL. A lint rule reports SQL built from text before it’s committed, and another reports an update or delete with no condition, which would change every row. [Lint rules](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/lint-rules.md).

## 4. Secrets

- What goes wrong: an API key is pasted into the code, then pushed to GitHub or sent to every visitor’s browser inside the page’s JavaScript, where anyone can read it and use it.
- How to check: search the code and its history for keys, such as anything starting sk_ or whsec_, and search the JavaScript your browser downloads. Replace any key you find with a new one, then move it out of the code.
- How @popcomputer/web settles it: secrets are declared in one file and set on Cloudflare, never written in the code. Each copy of your app checks them before it serves, the template’s deploy stops while one is missing, and a lint rule stops a page, or since 1.8.0 one of its components, importing server code that could carry a secret to the browser. [Vars and secrets](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/env.md).

## 5. Uploads

- What goes wrong: a file is taken whatever its type or size, then served back from your app’s own address. An HTML or SVG file with a script in it runs there as if your app wrote it.
- How to check: upload an HTML file and an SVG, each with a small script, and open them from your app. Try a file far bigger than you’d expect, and another person’s file by its address.
- How @popcomputer/web settles it: each form names the types and the largest size it takes, and “any image” leaves out SVG. A request over 1 MiB is refused unless the form raises the limit. Served back, only images other than SVG, PDFs, plain text, JSON, audio and video show as they are; anything else is sandboxed. [Serving an upload back](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/responses.md#serving-an-upload-back).

## 6. Sign-in rate limits

- What goes wrong: nothing slows down someone trying password after password. A limit for each address doesn’t help when the guesses at one account come from thousands of addresses.
- How to check: sign in to one account with a wrong password twenty times in a row. If the twentieth is answered like the first, nothing is counting.
- How @popcomputer/web settles it: it counts each address at Cloudflare’s edge, and each account in your database, before the password is checked. After 10 failures, that account’s sign-in is refused for a minute, doubling with each further failure up to 15 minutes. [Limiting password guessing](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/auth.md#limiting-password-guessing).

## 7. Dependencies

- What goes wrong: your AI adds a package to save itself work: an old version with a known hole, or a name you’ve never heard of, from someone nobody can vouch for.
- How to check: read the list of packages in each change, look up any you don’t know, and run your package manager’s audit, such as npm audit, before you deploy.
- How @popcomputer/web settles it: partly. The versions it accepts of Hono, Drizzle and Better Auth start at the first release with the security fixes for the parts it uses, and its tests check those floors. The packages your AI adds are still yours to check. [Installation](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/installation.md).

## 8. Error pages that leak

- What goes wrong: when something fails, the page shows a stack trace, a query or a setting, each telling an attacker how your app is built. Logs and error reports quietly keep passwords and tokens.
- How to check: on the live app, open an address that doesn’t exist, send a form full of nonsense, and change an id in an address to a word. Read what comes back, then read your logs.
- How @popcomputer/web settles it: a failure your code can’t answer is a 500 with a plain message in production. The detail shows only while you develop, and goes to your error reporting. A failed form’s report leaves out what was typed, such as a password, and every report leaves out the values in a URL, such as a reset token. [Failures an action cannot answer](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/actions.md#failures-an-action-cannot-answer).

## 9. Deploys and rollback

- What goes wrong: a change goes straight to the live app. It breaks, or a migration drops a column, and there’s no quick way back to yesterday’s code or yesterday’s data.
- How to check: before launch, find out three things: where you can try a change before it’s live, how to put the last version back, and how to get your data back from an hour ago. Know each before you need it.
- How @popcomputer/web settles it: [the template’s deploy](https://github.com/PatrickOgilvie/popcomputer-web-demo#deploying) checks each new version answers before it goes live, and rolls back in one command. A rollback doesn’t undo the database: D1’s [Time Travel](https://developers.cloudflare.com/d1/reference/time-travel/) puts it back to any minute in the last 30 days on the paid plan, or 7 on the free one.

## Decided once, not in every feature

The decisions that keep an app safe are made in one place, where your AI can’t forget them on the next page:

- Who may load a record is said beside its table. Every page that names it gets the same check, from the lookup’s own query.
- Each form’s fields are a schema. A field it doesn’t declare, such as who owns a record, never reaches the database.
- Secrets are declared in one file, checked when the app starts and before each deploy, and never written in the code.
- Rules two people could break by saving at once, such as stock that can’t go below zero, go in the database itself, as the framework’s rules tell your AI to, so they hold however the code is written.

## Mistakes reported, not hoped against

The type checker names what’s missing at the line that needs it: a service the app doesn’t provide, a failure nothing answers, an address that isn’t declared. The framework’s lint rules report what types can’t see, such as a database write with no condition that changes every row, SQL built from text, a page that reads input without checking it, or server code imported into a page or one of its components, which would send it to the browser.

Each message names what to use instead. Your AI reads it and fixes the code, without you having to know the rule.

## A fence around your AI

Asking an AI to run the checks works until it skips them. The [agent fence](https://github.com/PatrickOgilvie/popcomputer-web/blob/main/docs/agent-fence.md), added in 1.6.0, makes them a rule it can’t skip:

- The commit gate runs the type check, the lint rules and the tests before every commit.
- A hook refuses commands that skip the gate, and asks you first before your AI deploys, changes live data or secrets, publishes or pushes.
- The debt report counts every rule turned off, so the count can only shrink.

## Deploys you can check and undo

The template’s deploy uploads each new version beside the live one, and makes it live only once it answers. A preview gives you an address to try a change on before anyone else sees it, and a rollback puts the version before back in one command.

## Your part

The framework takes most of the risk away. A few habits take care of the rest:

- Start from the template, so your AI reads its rules first.
- Ask for one change at a time, and try each one.
- Never paste a password or an API key into the chat. Ask your AI how to set it as a secret.
- Read what changed before you deploy, even if only the list of files.
- Keep two-step sign-in on for your Cloudflare and GitHub accounts.

## Questions people ask

### Is it safe to vibe code a web app?

It can be, if what keeps it safe isn’t left for your AI to remember in every feature. The holes to look for are the nine in the checklist above, from someone else’s records to secrets in the code. On @popcomputer/web the framework settles most of them, and the checks report the usual mistakes before they’re committed.

### How do I deploy a vibe-coded app securely?

Go through the checklist first. Then deploy so that each new version is checked before it goes live, keep secrets in your host’s settings rather than the code, know how to roll back the code and restore the data, and keep two-step sign-in on for your hosting and GitHub accounts. The template’s deploy checks each version and its secrets for you, and rolls back in one command.

### How do I stop AI-generated code from having security holes?

Don’t rely on the AI remembering. Build on a framework that makes the risky decisions once, not in every feature, and have checks that run themselves before every commit: a type checker, lint rules for known mistakes, and tests. [The vulnerabilities @popcomputer/web is tested against](/security/tested-against) lists bugs that hit other frameworks, and the test that tries each on this one.

### What is vibe coding?

Building software by describing what you want in plain words and letting an AI write the code. Andrej Karpathy named it in early 2025.

### Which AI tools work with @popcomputer/web?

Claude, ChatGPT, Claude Code and Cursor. Any AI that can run commands on your computer can follow the setup prompt. We recommend Claude with Opus 5.5.

### Do I need to know how to code?

Not to start. It helps to read what your AI changes, but the checks are what tell it what’s wrong, so you don’t need to know every rule yourself.

### Can my AI deploy without me?

Not with the agent fence on. Deploys, changes to live data, secrets and pushes wait for you to say yes.

## Read next

- [How the security works.](https://web.popcomputer.com/security)
- [How to get started with vibe coding.](https://web.popcomputer.com/vibe-coding)
- [A web framework built for coding agents.](https://web.popcomputer.com/coding-agents)
- [The vulnerabilities popweb is tested against.](https://web.popcomputer.com/security/tested-against)
