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) and Taking AI-built code to production (hardening). 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.
- Never commit
.env*. Put.env*in.gitignore. This alone prevents most accidents. - Understand the
NEXT_PUBLIC_trap. In Next.js, a value with theNEXT_PUBLIC_prefix gets baked into the client bundle in plaintext at build time. Put aservice_rolekey or an API secret here and it's exposed. The mechanism, the typed env boundary, and how to useserver-onlyare explained with real code in Next.js environment variables and preventing secret leaks — the NEXT_PUBLIC_ trap, so I won't repeat them here. It's a must-read even for solo dev. - 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). In addition,
npx @aegiskit/cli scancatches hardcoded keys/tokens and the "NEXT_PUBLIC_× secret key" combination on shape (pattern) alone.
# インストール不要。いまのプロジェクトを静的解析する
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). 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 and Managing vulnerabilities with Dependabot.
- For a quick local check,
npm audit. Even just running it once before you commit reduces the number of known CVEs you bring in.
# 既知脆弱性のある依存を即席で確認
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).
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).
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.
-- 危険:ログインさえしていれば「全ユーザーの全行」が読める
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.
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. 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, 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 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 DEFINERfunction that doesn't pinsearch_path(a breeding ground for privilege escalation)
-- 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). 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 initadds 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.
# ヘッダー/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), and how to proceed so that even handing large-scale implementation to AI doesn't break, in Spec-driven development × Claude Code. 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 auditbefore committing - Run
npx @aegiskit/cli scanto list missing horizontal controls and suspected vertical risk - Check the "shape" of your RLS with
/aegis/rls-checker, and verify the intent of eachUSING(true)one by one - Confine the
service_rolekey 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's free scan, and if you're short on hands, get in touch.