# Safety Centre

> What your A to E safety grade means, what Guv checks, and how to fix each finding.

Every project has a safety grade from **A** to **E**, worked out from a checklist of things that commonly go wrong with web apps, especially ones built quickly or with AI. Every finding is written in plain English and comes with a one-click fix, or an AI-written pull request.

| Grade | Means |
| --- | --- |
| **A**, Excellent | Nothing important to fix. Your app follows good security practice. |
| **B**, Good | A few minor suggestions. Nothing urgent. |
| **C**, Fair | Some issues worth fixing soon. |
| **D**, Poor | Important issues. Fix these now; most have a one-click fix. |
| **E**, At risk | Critical problems. Your app or data may be exposed. Act today. |

## What Guv checks

| Check | An example finding |
| --- | --- |
| Secrets in your code and git history | "A Stripe key was found in `src/config.ts` (commit a1b2c3). It has been exposed; rotate it now." |
| Secrets in your client bundle | "Your `SUPABASE_SERVICE_ROLE_KEY` is visible to anyone visiting your site." This one **blocks the deploy**. |
| Secrets printed to logs | "Your app logged `DATABASE_URL` at startup." |
| Database exposure | "Your database is reachable from the internet." or "Row-level security is off on table `users`." |
| Vulnerable dependencies | "Next.js 15.x has a known critical vulnerability. Upgrade to 15.y." |
| Security headers | Missing HSTS, CSP or frame protection, with a suggested configuration. |
| Unprotected admin pages | Obvious admin routes without sign-in (an optional crawl of your public site). |
| Backups | "Backups on. Last restore test passed 3 hours ago." |
| Domains and certificates | Certificate status, CAA records and dangling DNS records. |
| Access | Members without passkeys, over-scoped agent tokens, tokens nobody has used for months. |
| Data location | "All data stored in London, UK." |

## Blocked deploys

One check stops a deploy outright: a secret value found in the files your app sends to browsers. If that happened, every visitor could read the key. The API returns [`deploy_blocked`](/errors/deploy_blocked/) and the dashboard shows the file and the variable.

To fix it, move the value into server-side code, rotate the key with its provider (assume it has been seen), and deploy again. If a value really is meant to be public, such as an analytics key, mark it so in `guv.yaml`:

```yaml
env:
  NEXT_PUBLIC_POSTHOG_KEY: { value: phc_xxx, public: true }
```

## Share your grade

You can show a **Secured on Guv** badge in your README or site footer. It links to a public summary of your grade, never to the findings themselves.
