# A security checklist for solo dev — the bare minimum before release (all free)

> A pre-release security checklist for solo developers with no time and no budget. It walks how to mechanically close out secrets, dependency vulnerabilities, authorization (RLS), and input validation with all-free tools — Secret scanning, Dependabot, npx @aegiskit/cli scan, /aegis/rls-checker, and more.

- Published: 2026-07-07
- Author: 友田 陽大
- Tags: セキュリティ, バイブコーディング, AI駆動開発, Next.js, Supabase
- URL: https://tomodahinata.com/en/blog/personal-project-security-checklist-solo-dev-guide
- Category: Vibe coding: shipping AI-generated code safely
- Pillar guide: https://tomodahinata.com/en/blog/vibe-coding-what-is-tools-risks-production-guide

## Key points

- The winning move for solo dev is to 'let all-free tools mechanize the horizontal controls (which apply uniformly across the app) and concentrate yourself only on the vertical risk (authorization / RLS design).' If you don't split these, one person can't defend it all.
- The pre-release check is five items — (1) don't leak secrets (2) don't leave known dependency vulnerabilities unaddressed (3) don't settle authorization with a 'UI if-statement' (4) validate input at the boundary (5) stop recurrence in CI. All free, one hour the first time, automatic from then on.
- Secrets and dependency vulnerabilities are entirely the machine's job — never commit `.env`, plus GitHub Secret scanning (free and automatic for public repos), plus Dependabot (free), plus `npx @aegiskit/cli scan` catch them on shape (pattern) alone.
- The one thing a tool can't fix is authorization (vertical risk). The typical case is RLS that 'authenticates but doesn't scope rows to the owner'; USING(true) is correct for a public table but an accident anywhere else. /aegis/rls-checker reads migrations and warns on suspicious spots.
- Honest scope: free tools automate horizontal controls, and for vertical risk they only 'detect and warn.' They can't claim complete protection. The final judgment on business logic and tenant isolation stays with human design.

---

Let me state the conclusion first. **The key to defending security in solo dev is to "let all-free tools mechanize the horizontal controls (measures that apply uniformly across the app) and concentrate yourself only on the vertical risk (authorization / RLS design)."** In one-person development with no budget, no time, and no reviewer, measures that rely on your attention will always spring a leak. So let the machine stand guard over what can be delegated to it with a single command, and spend your own time only on the design that only a human can judge — this is the only way to fight that scales.

Working solo × generative AI (Claude Code), I've built a B2B SaaS that won the Minister of Economy, Trade and Industry Award and a payment platform with zero double-charges in production. So I can say this with confidence — **for all the speed AI gives you in building, left alone, vulnerabilities pile up at the same speed.** But don't worry. The holes you need to close before release in solo dev almost all show up in "fixed places," so **you can run through them with all-free tools — one hour the first time, automatic from then on.** This article is that practical checklist.

---

## 1. Solo dev especially should "let the machine stand guard" — split horizontal and vertical

Security measures split into two kinds of a different nature. This line is the whole strategy for solo dev.

| | Horizontal controls | Vertical risk |
|---|---|---|
| What it is | Measures that apply uniformly across the app | Depends on the app-specific "who owns what" |
| Examples | Secrets, dependency vulnerabilities, headers, input validation | Authorization / IDOR, RLS design, tenant isolation, business logic |
| Who can fix it | **The tool (can be mechanized for free)** | **You (closes only through design and review)** |
| Solo-dev policy | Automate with one command, then don't think about it | Pour your time here |

Solo-dev failures come from mixing these two and "looking at everything by sheer willpower." Try to do even the horizontal controls by hand and you burn out and leak, leaving no concentration for the vertical risk (authorization) that really deserves your head. **What can be left to the machine, to the machine; only what only a human can do, to the human.** The big picture from the client's perspective is covered in [Vulnerability assessment of AI-generated code (close it in four layers)](/blog/ai-generated-code-vulnerability-assessment-vibe-coding-security-guide) and [Taking AI-built code to production (hardening)](/blog/vibe-coding-ai-generated-code-production-hardening-guide). This article distills that thinking into **a practical, free-tools-only procedure for the solo developer who builds.**

---

## 2. The pre-release checklist (in priority order, all free)

First, the overview. From the top down, ordered by damage size × ease of mechanization.

| # | Item | What it prevents | Free tools | First time |
|---|---|---|---|---|
| 1 | Don't leak secrets | Publishing keys/tokens to the whole world | `.gitignore` / GitHub Secret scanning / `npx @aegiskit/cli scan` | 15 min |
| 2 | Known dependency vulnerabilities | Leaving known CVEs unaddressed | Dependabot / `npm audit` | 10 min |
| 3 | Authorization (vertical risk) | Reading/writing others' data (IDOR) | `/aegis/rls-checker` / `npx @aegiskit/cli scan` | 20 min |
| 4 | Validate input at the boundary | Injection / malformed data | Zod / `npx @aegiskit/cli init` | 10 min |
| 5 | Stop recurrence in CI | A once-fixed hole coming back | GitHub Actions (Aegis / Dependabot) | 10 min |

1, 2, and 4 are entirely horizontal (the machine's job). 3 is the only vertical one (your job), and here alone the tool can go no further than "detect and warn." Below, we close them out one at a time.

---

## 3. Don't leak secrets (most frequent, worst)

This is the one with the largest damage and, on top of that, the one that happens most often. The moment a secret key enters the repository, you should assume it's been distributed to the whole world (it stays in the history, so deleting it is too late = **always rotate it**).

There are three things to do.

1. **Never commit `.env*`.** Put `.env*` in `.gitignore`. This alone prevents most accidents.
2. **Understand the `NEXT_PUBLIC_` trap.** In Next.js, a value with the `NEXT_PUBLIC_` prefix gets baked into the client bundle in plaintext at build time. Put a `service_role` key or an API secret here and it's exposed. The mechanism, the typed env boundary, and how to use `server-only` are explained with real code in [Next.js environment variables and preventing secret leaks — the NEXT_PUBLIC_ trap](/blog/nextjs-env-secret-leak-prevention-public-vars-guide), so I won't repeat them here. It's a **must-read** even for solo dev.
3. **Let the machine watch.** GitHub's Secret scanning runs **free and automatically for public repositories**, detecting and warning on credentials that slipped in ([GitHub Docs](https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning)). In addition, `npx @aegiskit/cli scan` catches hardcoded keys/tokens and the "`NEXT_PUBLIC_` × secret key" combination **on shape (pattern) alone.**

```bash
# インストール不要。いまのプロジェクトを静的解析する
npx @aegiskit/cli scan
```

One honest scope note. What the tool can mechanize is detecting "that a secret **slipped in.**" The judgment of "whether that value may be public" is human design that depends on the app's meaning, and it can't be automated. In other words, **you can stop the slip-in, but you can't decide whether it may be public.**

---

## 4. Don't leave known dependency vulnerabilities unaddressed

Even if the code you wrote is perfect, if a dependency library has a known hole, you'll be stabbed through it. This too is entirely the machine's job.

- **Enable Dependabot (free).** Enable Dependabot alerts in the repository settings and it notifies you of vulnerable dependencies against the GitHub Advisory Database and automatically opens PRs to the fixed versions ([GitHub Docs](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts)). Keeping "dependencies up to date" by hand in solo dev is unrealistic, so put it in first. The practicalities of setup and operation are covered in [The Dependabot practical adoption guide](/blog/dependabot-production-guide) and [Managing vulnerabilities with Dependabot](/blog/dependabot-security-updates-alerts-vulnerability-management-guide).
- **For a quick local check, `npm audit`.** Even just running it once before you commit reduces the number of known CVEs you bring in.

```bash
# 既知脆弱性のある依存を即席で確認
npm audit --omit=dev
```

As a further trap specific to the AI era, there's "slopsquatting," where generative AI suggests a **nonexistent package name** and an attacker pre-registers that name. Get in the habit of confirming a package's existence and download track record before you `npm install` (the mechanism and countermeasures are detailed in [Vulnerability assessment of AI-generated code](/blog/ai-generated-code-vulnerability-assessment-vibe-coding-security-guide)).

---

## 5. Don't settle authorization with a "UI if-statement" — the one vertical risk

This is the main keep, and **the one item free tools won't fix for you.** Both AI and humans tend to settle authorization with "show / don't show the button on the screen," but if the API or DB is hit directly, the UI if-statement is meaningless. Broken object-level authorization (IDOR / BOLA) has for years remained the **No. 1**, most frequent risk on the OWASP API Security Top 10 ([OWASP](https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/)).

### 5-1. Suspect "authenticates but doesn't authorize"

The most frequent accident pattern is RLS that **requires login but doesn't scope "who owns that row."** On Supabase, a policy like this is a danger signal.

```sql
-- 危険：ログインさえしていれば「全ユーザーの全行」が読める
create policy "read" on notes
  for select to authenticated
  using ( true );

-- 正しい：自分が所有する行だけに絞る
create policy "read own" on notes
  for select to authenticated
  using ( auth.uid() = user_id );
```

`USING (true)` is the **correct choice if that table truly is meant to be public (anyone may read it).** The problem is writing it on a table of private data. The distinction that "authentication" and "authorization" are different things is explained in detail in [Supabase RLS: authenticated and authorized are different](/blog/supabase-rls-authenticated-vs-authorized-owner-scope-guide).

This isn't a hypothetical worry. In a primary survey of 1,000 public Supabase apps, about 9.2% of the apps that had RLS had a policy that "authenticates but doesn't scope rows to the owner" (a lower bound, since it's a public-repository sample; un-scoped by owner doesn't mean instantly vulnerable — it needs confirmation). The method and the actual data are in [the Supabase RLS security field study](/blog/supabase-rls-security-field-study). And a case where an app was published with RLS unconfigured and others' data got read and written without authentication is actually registered as a CVE ([CVE-2025-48757](https://nvd.nist.gov/vuln/detail/CVE-2025-48757), CVSS 9.3 CRITICAL).

### 5-2. Machine-detect mistakes of "shape" for free

The correctness of meaning can be judged only by a human, but **mistakes of "shape" can be machine-detected for free.** [/aegis/rls-checker](/aegis/rls-checker) and `npx @aegiskit/cli scan` read Supabase's `migrations/**.sql` and warn on the following.

- Tables with RLS **unconfigured**
- Unconditional `USING (true)` (OK if publicly intended, otherwise needs fixing)
- A missing `WITH CHECK` (readable, but the ownership check on the write side is absent)
- A `SECURITY DEFINER` function that doesn't pin `search_path` (a breeding ground for privilege escalation)

```sql
-- SECURITY DEFINER 関数は search_path を必ず固定する
create function public.get_my_data()
returns setof notes
language sql
security definer
set search_path = ''          -- ← これが無いと path 経由で乗っ取られうる
as $$ select * from public.notes where user_id = auth.uid() $$;
```

### 5-3. Where you keep `service_role` becomes the "defensive boundary"

Supabase's `service_role` key runs with PostgreSQL's `BYPASSRLS` and **ignores RLS entirely** ([Supabase Docs](https://supabase.com/docs/guides/database/postgres/row-level-security)). In other words, the moment you leak this key to the client or the edge, however carefully you wrote your RLS, it's all void. It means **the defensive boundary moves to "where the key is kept."** Treat `service_role` as a server-only secret and protect it together with the secret hygiene from Section 3.

This is the one place that "a tool won't close," so it's the point the solo developer should think hardest about. `/aegis/rls-checker` goes **as far as warning** on suspicious spots — closing it is your design judgment.

---

## 6. Validate input at the boundary

Data that comes from outside (forms, APIs, queries) is all validated at the boundary on the assumption that it "might be adversarial input." Here we're back to horizontal (mechanizable).

- **Boundary type-validation with Zod.** At the entrance of a Route Handler or Server Action, always parse external input with Zod before using it. Don't wave it through just because "it worked."
- **Runtime hardening in one command.** `npx @aegiskit/cli init` adds security headers / CSP, rate limiting, CSRF, and a typed env boundary all at once as a single middleware. Rather than hand-writing them every time in solo dev, put it in first and solidify the foundation — that's faster.

```bash
# ヘッダー/CSP・レート制限・CSRF・型付きenv境界をミドルウェアで導入
npx @aegiskit/cli init
```

---

## 7. Stop recurrence in CI (don't let the checklist end at "once")

The measures so far **will always rot if you do them once and call it done.** Every time you have AI add a feature, a new hole gets in, so the checks only deliver value once they're "automatic on every push." Even in solo dev, don't skip this.

- Add Aegis's CI step (`aegis ci`) to GitHub Actions, and only high-confidence detections stop the build, with the SARIF left in GitHub code scanning (a design that doesn't break the build on false positives).
- Dependabot and Secret scanning keep working on every push from the moment you enable them.

The idea itself of "running the same quality gate through the machine every time" is covered in [Quality gates for AI-driven development (CI, types, tests, security)](/blog/ai-driven-development-quality-gates-ci-type-safety-test-security), and how to proceed so that even handing large-scale implementation to AI doesn't break, in [Spec-driven development × Claude Code](/blog/spec-driven-development-claude-code-ai-agent-production-workflow). **Don't slow the generation, but slot one layer of verification in before shipping** — this is the only form that keeps quality even for one person.

---

## 8. Honest scope — the remainder free tools "don't close"

Finally, the most important caveat. **"I added all the free tools, so it's secure" is the worst kind of complacency.** A product that advertises that is, if anything, dangerous; honest tools — `npx @aegiskit/cli scan` included — state their own limits up front.

- What free tools **automate is horizontal controls** (secrets, dependencies, headers, input validation).
- For vertical risk (authorization / IDOR, RLS design, tenant isolation, business logic), they **only detect and warn.** Closing it is your design.
- A clean result means "you haven't stepped on the common traps," not "this code is secure."

Even so, **the value of being able to mechanically close the most frequent holes is greatest precisely in solo dev.** Because it frees one person's worth of attention from the work the machine can do, so you can concentrate on the genuinely hard judgment (authorization design).

---

## Summary: the one-hour pre-release checklist

- [ ] Put `.env*` in `.gitignore`, and never commit secrets (if it leaks, **rotate**)
- [ ] Enable GitHub Secret scanning (free and automatic for public repos)
- [ ] Enable Dependabot + run `npm audit` before committing
- [ ] Run `npx @aegiskit/cli scan` to list missing horizontal controls and suspected vertical risk
- [ ] Check the "shape" of your RLS with `/aegis/rls-checker`, and verify the intent of each `USING(true)` one by one
- [ ] Confine the `service_role` key to server-only
- [ ] Boundary validation with Zod + runtime hardening with `npx @aegiskit/cli init`
- [ ] Make it an automatic check on every push in CI (`aegis ci` / Dependabot)

It's all free. **Horizontal to the machine, vertical to yourself.** Keep just this division and even one person can lift "the demo works" up to "it doesn't leak in production." When you need a design review to actually close the detected vertical risk (authorization / RLS design), start from [Aegis](/aegis)'s free scan, and if you're short on hands, get in touch.
