What User Data Is My App Actually Storing, and How Do I Find Out?
6 min read
Before real users arrive, you should be able to answer one plain question: what personal data does my app store, and where does it live? For most vibe-coded apps the honest answer is "I'm not sure," and the way to fix that is a short, deliberate inventory. The good news: you can build one in an afternoon by reading your own database schema and your list of connected services.
Why this happens
You asked your AI agent for features, not for a data map. When you said "let
users sign up," it added an email column, maybe a phone number, an IP address in
a log, a created_at timestamp, and a session record. When you added "save their
profile," it created more columns. Every third-party tool you wired in, analytics,
error tracking, email, payments, quietly started keeping its own copy of some of
it. Nobody wrote any of this down, because the agent built exactly what you
asked and nothing you didn't, including no inventory.
The result is normal and fixable. It is just invisible until you go looking.
How to check
Work through three places. Write down every field of personal data you find.
-
Your database schema. This is the source of truth. On Supabase or any Postgres, list every table and column:
select table_name, column_name, data_type from information_schema.columns where table_schema = 'public' order by table_name, ordinal_position;On Firebase, open Firestore and note every collection and the fields inside a few sample documents. Look for anything that identifies a person: email, name, phone, address, IP, avatar URL, free-text notes, uploaded files.
-
Your logs. Search your server logs and your error tracker (Sentry, LogRocket, etc.) for real email addresses or tokens. Agents often log entire request bodies, so personal data ends up sitting in a log you forgot exists.
-
Your connected services. List every third party that touches user data: analytics, email, payments, your LLM provider, file storage. Each one is a place your users' data physically lives, outside your database.
The fix
- Make one table. A simple spreadsheet with columns: field, where it lives (table or service), why you collect it, is it sensitive. One row per piece of personal data.
- Fill it from the checks above. Include the quiet ones: IP addresses, device info, anything in logs, anything a third party stores on your behalf.
- Flag the sensitive rows. Mark which fields count as PII, and which are extra-sensitive (health, financial, location, anything about children).
- Delete what you don't need. For every row, ask "would beta break if I stopped collecting this?" If not, drop the column and stop storing it. The safest data is the data you never keep.
- Keep the table current. Update it whenever you or your agent add a feature. This document is what you'll reach for the first time a user emails "what do you have on me?"
The trap to avoid
Do not build your inventory from memory or from the app's screens. What the UI shows is not what the database stores. Agents frequently keep hidden columns, soft-deleted rows, and log entries that never appear in the interface. If you only write down what you think you collect, you'll miss exactly the data that gets you in trouble later. Read the schema, not the app.
Where this fits
Knowing what you store is the foundation for nearly every other data decision: deletion, export, consent, and keeping personal data out of your LLM prompts and logs. The free Readiness Report runs this inventory against your real project, so you see the tables and fields you forgot about rather than the ones you remember. If you'd like the map built and the unnecessary data cleaned up for you, that's part of the Finishing Pass.