LayerKick LayerKick Journal
How It Works Pricing Journal 63ms TTFB Join Waitlist →
Waitlist →
Building / Entry №09 / ★ Flagship

Production needs my thumbprint.

LayerKick production lives in a Cloudflare account no agent has write credentials for. The agents build, test, and merge against a second account all day, and the promote to a live store takes my thumbprint. Labels, soak windows, and a path denylist handle the rest.

Craig Ruks, Founder · April 21, 2026 · 7 min read · AI-drafted, founder-edited

Most of LayerKick’s code is written by AI agents, and their work reaches production every week. What it can’t do is get there on its own. Every deploy to a live storefront ends with one physical act that only I can perform: a biometric prompt on my own machine, where I approve the promote with my thumbprint. No agent, no CI runner, and no GitHub secret can take that step, because the key that writes to production lives behind that prompt and nowhere else.

I want to be precise, because “the agents can’t touch production” is the kind of thing people say loosely, and it isn’t quite what I mean. Agent-authored code ships to real stores constantly. It just never crosses the final line without me physically approving it. This isn’t a permissions policy that a misconfiguration could widen, and it isn’t a system prompt asking the model nicely. It’s a boundary drawn in credentials and a thumbprint, and the rest of this entry is how it’s built and what it lets me relax everywhere downstream.

Two accounts

Start with the threat model, because the design only makes sense against it. Prompt injection is real: a malicious string in a page, an issue, or a dependency can convince an agent to do something its operator never asked for. Agents also make ordinary mistakes, and they make them at machine speed, faster than I can read. And I’m one person, so I can’t review every line at agent throughput even when nothing’s wrong.

The usual answer is role-scoped tokens: give the automation a credential that can only do specific things. That helps, but inside a single account it still leaves you one compromised or over-scoped secret away from a live storefront, and “one mistake away” isn’t the property I want on the thing customers actually see. So I moved the boundary up a level, from a permission inside an account to the account line itself.

Account A (production)Account B (staging + sandbox)
ServesLive client storefrontsStaging and sandboxes
Databaseproduction DBstaging + sandbox DBs
KVproduction namespacestaging + sandbox namespaces
Token in automationread-onlyfull access
Who holds write accessMy local keychain, nobody elseGitHub secrets, agents, CI

Two separate Cloudflare accounts, with separate credentials that can’t reach across. The agents, CI, and every GitHub secret live entirely in Account B. The only production token that exists in any automation is read-only, purely for health checks, and it can’t change anything. The write token for Account A sits in my local keychain and nowhere else.

The nice consequence is that the worst an agent can do is wreck staging, and staging is disposable by design: it’s rebuilt from code, so a bad night there costs a redeploy, not a customer. The blast radius of a fully compromised agent is a thing I can recreate with one command.

One more detail keeps the boundary from leaking in practice: the tooling defaults to staging, not production. Every deploy and CLI command resolves to the staging account unless I explicitly name production, and naming production is the exact thing that triggers the thumbprint. So the easy path, the one you get by not thinking about it, is the safe one. Reaching a live store takes deliberate intent twice over: you have to ask for it by name, and then approve it with a fingerprint.

The auto-merge contract

Physical isolation handles damage, but it does nothing for throughput. I still can’t hand-review every agent PR, and a queue of unreviewed PRs is its own kind of failure, so merging runs off a contract enforced in CI rather than off my attention.

Every agent PR has to carry exactly one agent: label, declaring which system opened it. A PR with no agent label is treated as human, and human PRs are never auto-merged, full stop. That’s the same safe-default logic as the tooling above: the absence of an explicit signal resolves to the cautious behavior, not the permissive one.

Each agent type gets its own soak window before it’s eligible to merge, a deliberate waiting period, and the window scales with how much the PR changed. A small source-only change soaks briefly. Anything larger, or touching several packages at once, waits longer. The dependency-update bots get the shortest leash because their diffs are mechanical and easy to reason about.

The workflow re-evaluates the PR on every completed check suite and every label change, and it only merges (squash, so history stays linear) when all the gates pass at once: required checks green, no human-review label present, no denied paths in the diff, and the PR older than its soak timer.

The soak isn’t superstition; it buys concrete things. Agent PRs tend to arrive in bursts, and a few minutes is enough for the advisory reviewer bot to leave comments, for CodeQL (GitHub’s static analysis, which looks for security bugs in the code) and secret scanning (which looks for leaked credentials) to finish and report, and for me to glance at a phone notification before the merge is real. It turns “merged instantly” into “merged after the cheap automated checks have actually had time to speak.”

The denylist

Auto-merge is also scoped by path, because not all files carry the same risk. Agents are pre-authorized to auto-merge only the everyday low-risk surface, where a mistake is caught by tests and bounded by the account boundary anyway.

Against that sits a denylist of path patterns that always require a human, no matter which agent opened the PR and no matter how green every check is: anything infrastructure-shaped, the deploy pipeline, anything credential-shaped, and the CI workflow definitions themselves.

That last category is the one I care about most. The auto-merge contract is itself a workflow file, which means an agent editing workflow files could, in principle, loosen its own leash. Putting the workflows on the denylist closes that loop: any change to the rules the agents run under has to go through me, so the system can’t quietly rewrite its own constraints.

There’s one deliberate omission from the denylist that surprises people: database migrations. That sounds reckless right up until you remember the account boundary. A merged migration only ever applies to sandbox and staging, both in Account B; production migrates when a human runs the promote. The physical separation upstream is exactly what lets the policy downstream relax, and that’s the general shape of the whole design. Make the floor solid and you can afford softer walls.

The layers are ordered by trust. The account boundary assumes the auto-merge contract will fail. The contract assumes the checks will fail. The checks assume the agent has already failed. Each layer only has to catch what the one below it missed.

What a human still does

Merging isn’t shipping, and keeping those two things distinct is what the human ceremony protects.

Before a merge, every PR is smoked against staging, a real run against real infrastructure, and the results post back as a PR comment. After the merge, the build auto-deploys to staging in Account B, still nowhere near a customer. Nothing reaches a live storefront until I run the promote recipe from my own machine, and that recipe stops on a biometric prompt: the production write token only unlocks with my thumbprint.

That’s the entire human step: one deliberate command and one fingerprint, on hardware the agents don’t control, after staging has already exercised the build. It’s small on purpose, because a ceremony you perform constantly is one you stop paying attention to, and this one needs to stay meaningful.

The other reason I keep it manual is that it tells me exactly when something hits production. The moment I run the promote, I know to go look, and I do a quick spot check right then, especially on the pages I know a particular client cares about at that moment. An automated deploy is something you find out went wrong later. A deploy I perform is one I’m already watching.

There’s an escape hatch running the other way too, for when the agent is the cautious party. An agent unsure about its own change applies the human-review label, which blocks auto-merge, and comments on exactly what needs a human call. I’d much rather field a question than unwind a bad merge, so asking is cheap and getting it wrong is not.

Next in the log is the other half of the agent story. Isolation covers what agents are allowed to touch; the more interesting problem is orchestrating a whole fleet of them at once without babysitting each one, and that machinery gets its own entry.

If you're curious
See it on your own store.

LayerKick layers onto your existing Shopify theme and serves it from Cloudflare's edge. If anything goes wrong, traffic passes through to Shopify like we were never there. The fastest way to understand it is to watch it run on your own storefront, and the waitlist is the way in.

Join the waitlist → $200/mo beta · billing starts two weeks after signup
← Previous entry
Predicting customer behavior.
Next entry →
Running with the lights off.