privacy-audit
Privacy Audit
Overview
The rule is explicit: a privacy policy is required if any user data is collected, and the team must know exactly where it's stored. This skill produces that inventory and checks it against reality — not against what the privacy policy claims, but against what the code and infra actually do. Apply it first to anything public-facing that collects user data. Internal or proprietary services need the same discipline whenever they touch partner or customer data, even where no public policy is required.
Workflow
-
Enumerate data collection points. Grep the codebase for the shape of intake, not just obvious forms:
- Signup/auth forms (NextAuth v5 — check
providersconfig and any customsignIn/sessioncallbacks for extra fields captured). - Any Zod schema in a route handler with fields beyond
id/createdAt—grep -rn "z.object" --include="*.ts"and read what each schema captures. - Stripe customer/subscription metadata (
stripe.customers.create,metadata:blocks) — Stripe stores whatever you attach. - PostHog
capture()/identify()calls — checkproperties:payloads for anything beyond anonymous event data (email, name, IP if not stripped). - File uploads (campaign assets, avatars) — where do they land (S3/R2/local disk) and is there EXIF/metadata scrubbing?
- BYOK products: the user's own Anthropic/OpenAI API key — this is the most sensitive field in the product. Confirm it is encrypted at rest, never logged, and never returned in any API response after initial save.
- Signup/auth forms (NextAuth v5 — check
-
Build the data map. For every field found, record: field name → source (which form/event) → storage location (table.column, PostHog event property, Stripe metadata key, log line) → retention (indefinite / N days / until account deletion) → who else sees it (third-party processor: Stripe, PostHog, email provider, Sentry/Better Stack).
- Table format works well here — one row per data category (email, name, BYOK key, IP, campaign content, payment method) with those five columns.