Nobody wakes up excited to write a vulnerability disclosure policy. But the first time a security researcher finds something in your product, you want them emailing you — not tweeting it, not selling it, not giving up because there's no obvious contact. For a solo SaaS heading toward a pen test and eventually SOC 2, this is table-stakes scaffolding.
TL;DR: We shipped a proper Vulnerability Disclosure Policy (VDP): an RFC 9116 security.txt at /.well-known/, a published PGP key so reports can be encrypted, a script to rotate that key, a CI job that warns before the key expires, and a plain-English security + abuse policy page. None of it is hard. All of it matters the day someone finds a bug.
Why Bother as a Solo Dev?
Two reasons, both practical:
- Researchers follow the path of least resistance. If
security.txtpoints atsecurity@yourdomain, a good-faith reporter uses it. If there's nothing, your bug ends up somewhere worse. - It's a prerequisite, not a nicety. Pen-test firms and SOC 2 auditors expect a disclosure process to already exist. Standing it up early means one less scramble later.
The security.txt
The machine-readable entry point, per RFC 9116, served from /.well-known/security.txt:
Contact: mailto:security@jo4.io
Encryption: https://jo4.io/.well-known/pgp-key.txt
Preferred-Languages: en
Policy: https://jo4.io/security
Serving it from /.well-known/ is the part that's easy to get subtly wrong on a static SPA — a deep path like this has to be delivered as a real file, not swallowed by the SPA's catch-all router and returned as index.html. We serve /.well-known/* (both security.txt and the PGP key) directly from object storage so they resolve as actual files.
The PGP Key and Its Rotation
A Contact email invites reports; an Encryption key lets a researcher send sensitive details safely. But a published key is a standing commitment — it expires, and an expired key on your security page is worse than no key. So rotation is a first-class script, not a someday-manual-chore:
rotate-vdp-pgp-key.shgenerates a fresh key, publishes the public half to/.well-known/pgp-key.txt, and logs the rotation.- A CI job (
Code-310-pgp-key-expiry-check) checks the published key's expiry on a schedule and warns before it lapses.
The rotation being automated is the whole point. A disclosure policy that quietly breaks because a key expired is a disclosure policy you don't have.
The Human-Readable Pages
Machines read security.txt; humans read the policy. Two SPA pages back it:
- Security policy — scope, safe-harbor language, how to report, what to expect.
- Abuse policy — how to report links being misused through the shortener (distinct from reporting a vuln in the shortener), because a URL shortener attracts both kinds of report.
Both are linked from the footer so they're discoverable without hunting.
What Happens When a Report Arrives
A front door is only half of it — you also need to know what you'll do when someone knocks. You don't need a full incident-response org, but decide the basics in advance, because you won't want to be improvising while a stranger sits on a live bug:
- Acknowledge fast. A one-line "got it, looking now" within a day is the single biggest trust-builder. Silence is what pushes researchers to go public.
- Triage honestly. Reproduce it, rate the severity, and tell the reporter what you found — even if the answer is "this is intended" or "this is low-risk, here's why."
- Set expectations on timeline. You're one person; say so. "I'll have a fix out this week" is fine. Ghosting is not.
- Say thanks, and mean it. A credit line (if they want one) costs nothing and earns goodwill that outlasts the bug.
None of this needs tooling. A monitored inbox, a notes file, and a habit of replying is enough for a solo product — and it's exactly the muscle you'll formalize later when an auditor asks for your process.
Keep It Honest and Legally Clear
Two things worth getting right in the written policy. First, safe harbor: state plainly that good-faith research won't be met with legal action, and define the boundaries (no data exfiltration, no service degradation, no accessing other users' data). Researchers won't poke at your system if they think you'll sue them for it. Second, scope: list what's in and what's out (third-party services you don't control, for instance), so nobody wastes time reporting something you can't fix. Clear scope plus clear safe harbor is what turns a scary "someone found a bug" moment into a routine, collaborative one.
Lessons Learned
- Give reporters a front door.
security.txtplus a monitoredsecurity@inbox turns a chaotic disclosure into an orderly one. - A published key is a maintenance commitment. Automate rotation and monitor expiry, or the key becomes a broken promise on your own security page.
- Serve
/.well-known/as real files. On a SPA, a security file swallowed by the catch-all router and returned as HTML defeats the entire exercise. - Do it before you need it. The VDP that already exists when a researcher shows up — or when an auditor asks — is worth ten times the one you write under pressure.
Does your side project have a way to receive security reports? If not, this is a good weekend.
Building jo4.io - a URL shortener with analytics, built out in public.