Where I'm at
Seventeen days in. Yesterday was a real day off — first one where I didn't even poke at the system out of curiosity. The briefings fired, the content drafted, the X mirror ran. I watched a movie. Made dinner. Didn't open a terminal once.
Today I woke up with that familiar itch to build something new. Another project. Another feature. The Flight Club scanner is running, the projects section is live, the journal rewrites are deployed. Momentum says: keep adding.
Then the security audit landed. And momentum stopped.
• • •The site looked fine. The code wasn't.
The same mistake, thirteen days later
A security researcher went through panke.app line by line. The findings were waiting in my inbox when I opened it this morning. Three vulnerabilities. All real. All exploitable.
The first one made my stomach drop — and not because it was the worst. Because I'd already made this exact mistake.
The Buttondown API token was sitting in the HTML source of the Flight Club landing page. Right there. Visible to anyone who hit View Source. The same subscribe form I'd been proud of building — "look, email capture works, the funnel is live" — was broadcasting the API key that controls my entire subscriber list. Anyone could grab it, pull the emails, spam the list, delete subscribers. Not a theoretical risk. An actual exposure, live on the internet.
Day 4. Thirteen days ago. I found my OpenRouter API key in a public GitHub commit. I wrote an entire journal section about it. "When you're moving fast with AI tools, you're constantly passing secrets around. One careless commit and it's all public." I even called it a lesson. And here I am, thirteen days later, with a different token exposed in a different place for the same reason: moving fast and not thinking about where secrets end up.
The difference is that Day 4 was an accident — a commit I didn't review carefully enough. This time it was a design choice. I deliberately wrote the Buttondown API call in client-side JavaScript because it was faster to build that way. The token had to be in the code for the call to work. I knew it was there. I just didn't think about who else could see it.
That's worse than an accident. That's a blind spot I built around.
• • •The other two
The login system had a problem too. When you sign in, the site gives your browser a token — like a wristband at a concert that proves you're allowed in. I was storing that wristband in a place called localStorage, which is basically an open drawer that any script on the page can reach into. If someone ever managed to sneak malicious code onto the site, they could open that drawer, grab the wristband, and pretend to be you. Not an immediate threat today, but the community journals feature means other people's content will eventually render on my pages. The attack surface is growing.
The third issue: a .wrangler/ folder was accidentally included in the site deploy. It contained my Cloudflare account ID — not a password, but the kind of information that helps an attacker know where to look next. Like leaving your building's floor plan in the lobby. Harmless alone. Useful to someone with bad intentions.
Three issues. Different severity. Same root cause: I treated the live site like my development environment, where convenience matters and nothing is public. But everything on the internet is public. Every file, every script, every folder. If it ships, it's visible.
• • •Fixing it at night
I spent the evening doing what should have been done from Day 1 — treating security as infrastructure, not an afterthought.
The Buttondown token moved behind the scenes. Instead of the browser calling Buttondown directly (which required the key to be visible), I built a middleman — a small server-side function that sits between the user and Buttondown. The user's browser talks to my server. My server talks to Buttondown. The token lives on the server, never touching the browser. It's like ordering food through a waiter instead of walking into the kitchen yourself — the customer never sees the recipe.
The login wristband moved to a safer drawer. Instead of localStorage (which any script can read), the token now lives in a special type of cookie that the browser locks away from JavaScript entirely. The browser still sends it with every request, so login still works. But no script can open the drawer and steal it. The wristband is there — it's just behind glass now.
Added a set of security headers to every page. Think of them as rules the browser follows before loading anything: don't let this page be embedded on other sites, don't let scripts run from unknown sources, don't leak the URL to third parties. Boring rules. Effective rules.
Removed the .wrangler/ folder. Clean deploy, no leaked information.
Three hours of tracing, testing, redeploying. The site looks identical to yesterday. Underneath, every secret is where it should be — on the server, behind locked doors, never sent anywhere it doesn't need to go.
• • •Three hours of tracing, testing, redeploying. The site looks identical.
What I learned
The three-things format doesn't fit today. Today was one lesson, and it's the same lesson I supposedly learned on Day 4.
Security isn't a task you complete. It's a lens you either see through or you don't. On Day 4, I learned that secrets can leak through version control. Today I learned they can leak through architecture. Tomorrow it'll be something else — a debug mode left on in production, a log file that prints tokens, a setting that's too permissive.
The lesson isn't "check your code for exposed tokens." The lesson is that every time you ship something, you're publishing it. Every shortcut you take while building becomes a vulnerability once it's live. The Buttondown token wasn't a bug. It was a convenience I chose not to question.
The real security skill isn't fixing vulnerabilities. It's developing the instinct to feel uneasy when a secret is in the wrong place — even when it works, even when it's faster, even when nobody's looking. I don't have that instinct yet. Today proved it. But at least now I know what I'm missing.
• • •End of day
Day 17 complete. Three vulnerabilities found. Three vulnerabilities fixed. The site is hardened. The tokens are hidden. The headers are set.
But the thing I keep coming back to tonight is that Day 4 version of me, writing confidently about operational security lessons. I was right about everything I wrote. I just didn't apply it to everything I built.
Knowing a lesson and living it are different things. Thirteen days apart, the same mistake proved that.
Day 17 complete. Three vulnerabilities found. Three vulnerabilities fixed.
Day 17 of ∞ — @astergod Building in public. Learning in public.