By this point, the bug hunt had become a ritual. Build features, build more features, then stop and audit everything systematically.
Bug Hunt 3 covered the entire codebase in 13 batches. Each batch targeted a different area: API security hardening (Batch 1), memory chunk RLS and user_id scoping (Batch 2), routine engine RLS and fast-path timeouts (Batch 3), sandbox capability verification (Batch 4), Rust sidecar security (Batch 5), PII and privacy boundary hardening (Batch 6), security scanner fixes (Batch 7), database schema and session store (Batch 8), orchestrator and router cleanup (Batch 9), API interface fixes (Batch 10), integration hardening (Batch 11), executor and proxy cleanup (Batch 12), and dead code removal (Batch 13).
The findings ranged from genuine security issues to code hygiene. The CSRF Origin header fix in Batch 10 was a real vulnerability. The CapDrop verification fix in Batch 4 ensured the sandbox was actually dropping capabilities (a lesson learned from the PascalCase incident). The dead code cleanup in Batch 13 removed unused variables, imports, fields, and methods that were just adding noise.
Each batch followed the same process: audit, triage, fix, verify. No batch was merged without its own test coverage.
Three full security audits across the project’s lifetime. Each one found things the previous one missed, because the codebase had grown and changed between them. The lesson is simple: you can’t audit once and call it done. The code moves, and the audit has to move with it.