The last mile of AI-assisted coding is a signup form — Trusty Squire<br>← BlogThe last mile of AI-assisted coding is a signup form<br>2 July 2026<br>AI has made most of my solo development two to three times faster. I spend my time on product decisions now, not boilerplate — the agent writes the integration, the migration, the test. But there’s one part of the loop it kept handing back to me, and it broke my flow every single time.<br>You know the moment. You ask for email, or push notifications, or a database. The agent writes the integration in thirty seconds and then stops:<br>Add your RESEND_API_KEY to .env.<br>So I alt-tab out of the editor, find the dashboard, sign up, click through onboarding, verify an email, create an API key, copy it, paste it into a .env— and now there’s a live secret sitting in plaintext that I have to remember not to commit, and that the agent forgets about and asks me for again next session. Every service. Every project. It’s the last manual bottleneck in AI-assisted coding, and for a while it was the only wall the agent couldn’t get through for me.<br>I tried the obvious tools first — OpenAI’s Operator, browser-use, a couple of others. They can drive a browser, but they’re built as autonomous general-purpose bots, and that’s not the shape of the problem. I don’t want to watchan agent sign up for Resend. And in practice they’d stall on real signup flows anyway. The insight I eventually landed on: the coding agent I already have (Claude Code, Codex, whatever) is a perfectly good planner. What it’s missing is a driver — a scoped browser and a safe place to put what it finds.<br>So I built that driver. It turned into two genuinely hard engineering problems, and the second one is the one I actually care about.<br>Problem 1: getting through the door<br>Modern SaaS does not want a bot filling in its signup form. Cloudflare Turnstile, Stytch, Clerk, DataDome — signup pages are now some of the most aggressively bot-gated surfaces on the web. This became a multi-week rabbit hole, and the most useful thing I can share is how wrong I was, repeatedly.<br>Every time a signup got blocked, I had a confident theory:<br>“It’s the IP reputation.” Datacenter IP, obviously flagged. Falsified: a fresh residential IP failed identically.<br>“It’s the fingerprint / no GPU.” Headless Chromium has no real GPU, tells everywhere. Falsified: a real laptop with a real GPU and a real display failed identically.<br>“It’s the CDP-level automation tells.” navigator.webdriver, Runtime.enable, mainWorld isolation. This one was real but not sufficient — I moved to patchright, a Playwright fork that closes the CDP tells the stealth plugins can’t, and it made things better and still didn’t clear Turnstile.<br>I kept re-deriving “it’s the environment” and kept getting proven wrong by experiment. The discipline that eventually saved me was writing every falsified hypothesis down in a STATE.md — form a hypothesis, name the experiment that would falsify it, run it, record the result. It reads like a graveyard, and it stopped me from cargo-culting the same three wrong answers over and over.<br>The actual cause of the Turnstile wall, when I finally ran a controlled matrix, was almost stupid: Playwright’s launchPersistentContext. Not the IP, not the GPU, not the fingerprint. The way Playwright launches and attaches to a persistent browser profile is itself a detectable signal. The fix was to self-launch a normal Chrome process and attach over CDP (connectOverCDP) instead of letting Playwright launch it. Same IP, same machine, same fingerprint — token issued.<br>The rest of the anti-bot layer is less surprising but earns its keep: behavior simulation for invisible/scored challenges (bezier-curve mouse paths, variable typing speed with thinking pauses, post-load dwell), click-and-wait for visible checkbox challenges, and — because headless Chromium gets gated on sight — running headed against an on-demand virtual display (Xvfb) so there’s a real surface to render against, which the user never sees.<br>None of this is a “wall.” Every block I called a wall turned out to be a specific, fixable tell. That mindset — a block is a diagnosis problem, not terrain — is most of the job.<br>Problem 2: keeping the key once you have it<br>This is the part I actually built the thing for. Getting the key is a means; the interesting question is where it goes.<br>The default answer — a .env file — is genuinely bad, and everyone reading this has felt it. .envfiles get committed to GitHub. They get lost. They get pasted into three services and rotated in none of them. And in the AI-coding era there’s a new worst case: the API key ends up in the agent’s context window, which is the single least contained place a secret can be.<br>So the design principle is: the raw secret is never handed back to the agent, and never lands in your repo. Concretely —<br>The vault is write-only . When the driver extracts a key off the dashboard, it goes straight into an encrypted store. The agent can’t read it back out. There is...