Back to DevLog

From CSRF Hell to Social Post Automation: A Wild Dev Session

3 min read

Just wrapped up one of those sessions where everything that could go wrong... did go wrong. But hey, that's the fun of building in public, right?

Started the day thinking I'd knock out some quick security fixes for EpsteinScan. Famous last words.

The CSRF Protection Rabbit Hole

First up was adding CSRF protection with Flask-WTF. Should be simple - install the package, add CSRFProtect(app), throw some tokens in forms. Done in 20 minutes, right?

Wrong.

Then I decided to add rate limiting to auth endpoints because, you know, security. Flask-Limiter seemed perfect. But here's where things got spicy:

  • First attempt: Decorators. Nope, CSRF fired before rate limiter could do its thing
  • Second attempt: before_request handler with in-memory storage. Nope, gunicorn workers don't share state
  • Third attempt: Shared file with fcntl locking in /tmp/. Finally!

5 POST requests per 60 seconds on login endpoints. The hackers can wait.

Secrets Management (Finally)

I've been putting off cleaning up secrets scattered across supervisor configs and crontabs. Created a proper .env file with chmod 600 and a wrapper script to source it. No more plaintext API keys lying around like digital confetti.

15 secrets moved to safety. My future self will thank me.

The Fun Stuff: Social Post Automation

Here's where it got interesting. Added a "Generate Social Posts" feature that:

  1. Takes monitor items (news articles we're tracking)
  2. Extracts proper nouns from titles
  3. Searches our 1.4M document database for connection counts
  4. Sends everything to Claude API for social post generation
  5. Spits out content for Facebook, Twitter, Reddit, Instagram, TikTok

The key was making sure we only use real data - no fabricated connections. Just cold, hard document counts.

Added a purple "Monitor" badge to distinguish these from blog-sourced posts. Purple because... why not?

Multi-Agent Workflow Magic

Used my Manager/Builder/Reviewer pattern via agent-bridge MCP. Having AI agents delegate, implement, and verify with actual curl commands is still wild to me. The rate limiter took 3 iterations to get right, but the agents caught the multi-worker state sharing issue before I deployed broken code.

The Small Wins

  • Fixed og:url meta tags (was using request.url, now properly canonical)
  • Added 301 redirects for /ai/ai-analyst and /key-findings/findings
  • Built a newsletter unsubscribe endpoint with HMAC token verification
  • Soft delete on unsubscribes (preserves history)

Production Deploy

Synced everything to production. The security fixes are live, but keeping the social post generation in dev for now. Want to monitor those document searches - 1.4M records with LIKE queries might need some FTS indexing love.

What's Next

The unsubscribe endpoint returns 200 on invalid tokens instead of proper 400/403 responses. It works, but my inner perfectionist is twitching. Also need to watch those document search performance metrics.

Sometimes the best dev sessions are the messy ones where you learn three new things while fixing one "simple" issue. Today was definitely one of those days.

Building in public means showing the beautiful failures alongside the wins. Rate limiting with multi-worker WSGI apps? Now I know. CSRF token ordering? Learned that too.

Onward to the next rabbit hole! 🕳️

Share this post