I’m Ken. I keep payment systems operational in the cloud, build AI agents that occasionally go off the rails, and spend an unreasonable amount of time thinking about how to keep systems secure. I write about cloud architecture, agentic AI, authn/z, and the quiet dread of getting paged at an hour with a single digit in it. Everything here is my own opinion — my employer has better ones.

Your Auth Server Now Fetches URLs Strangers Pick
Every OAuth client you have ever built began with a small act of bureaucracy. A human opened a developer console, clicked “Register New Application,” and received a client_id. That string is now a row in a database you don’t own, on a server you’ll never see, maintained by people you’ll never meet. It is proof of identity for exactly one reason: somebody wrote your name down in advance. This act is known as client pre-registration. ...

A Toaster Can Log In Now, Apparently
You’re SSH’d into a box with no display. No X11, no Wayland, $DISPLAY unset, as browser-capable as a toaster. Claude Code, running against your LLM gateway, needs a fresh JWT, and the script backing its apiKeyHelper is about to try to webbrowser.open() a URL into a void where a browser should be. It doesn’t. It notices the void, shrugs, and prints a six-character code to your terminal instead — with instructions to go type it into a browser on some other device. Your phone. Your laptop. Whatever’s within arm’s reach that isn’t the toaster you’re SSH’d into. You do that, tap approve, and the headless box — the one that, and I cannot stress this enough, has no browser and never will — gets a real, human-approved OAuth login anyway. ...

I Gave Claude Code a Static API Key. Then I Took It Back.
I got Claude Code talking to my self-hosted gateway in about fifteen minutes. Mint a scoped API key, export ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN, done. It worked immediately, which should have been my first warning — security problems that work immediately are the ones that stick around. The key I minted was budget-capped and scoped to a few models, not the master key to the kingdom. Bounded risk, technically. But it lived in plaintext in ~/.claude/settings.json, which meant anyone who could read a file on my machine — a compromised dependency, a misconfigured backup, a cat fat-fingered into the wrong Slack channel — now had a usable credential for as long as I forgot to revoke it. I noted the risk, decided it was acceptable for now, and moved on with my life, the way you do with a smoke detector that’s beeping every ninety seconds and you just haven’t gotten around to the battery yet. ...

Building an Agent on Bedrock AgentCore, Part 5: Whose Notes Are These?
Last time I gave the agent real tools behind a real Gateway, then told you — in bold — that the front door was propped open with a brick. Anyone with the URL could list every note, write garbage, run your Bedrock bill for sport. “The fix has earned its own post,” I said. This is that post. But the lock isn’t even the interesting part. The interesting part is what comes after the lock: alice and bob become two different people instead of one undifferentiated “demo-user” sharing a single pile. The agent knows who’s talking to it, their notes are theirs, and the model had absolutely no say in making that happen. ...

Deploying the Claude Apps Gateway on Bedrock: A Scar Map
Part 1 was for the person who signs the invoice. This one is for the person who gets handed the Jira ticket afterward. If you read that post and thought “yes, fine, I want this, how hard can it be” — this post is the honest answer to that question. It’s a deployment walkthrough with the bad parts left in. The code is at github.com/kenkitts/claude-apps-gateway-cdk. Five Python CDK stacks, a Docker image that fetches and verifies the claude binary at build time, and enough inline comments to reconstruct most of the decisions made under pressure. Clone it now if you want to follow along; the scars will make more sense with the code open. ...

Do You Actually Need an LLM Gateway? A Field Guide for People Who Sign the Invoice
Your developers are already using Claude Code. Right now. On personal Anthropic accounts or individual API keys, expensed on a T&E line that says “software tools,” with your source code as the payload. You can’t see the spend, can’t say who’s spending it, can’t cap it, and if you asked your CISO whether any of this is compliant with your data-handling policy, she would say something unprintable. That is the status quo an LLM gateway is reacting to. Not a hypothetical future where AI tools proliferate — the present, where they already have. ...

Building an Agent on Bedrock AgentCore, Part 4: The Notes Were Always Ephemeral
Last time the agent learned to remember you: durable conversations, your preferences trailing you across sessions. It knows you like terse summaries. It knows the Q3 doc is in the shared drive. It does not have the Q3 doc. It has never had your notes. For three posts add_note has been dropping them into a Python list that lives in RAM and dies with the process, like a goldfish keeping a diary. The agent remembers the conversation about your notes. The notes themselves were a rumor it was too polite to correct. ...

Building an Agent on Bedrock AgentCore, Part 3: Giving It a Memory That Outlives the Process
Last time we put the agent in the cloud behind a real endpoint. It works, it scales, two users don’t trample each other — and it has the retention span of a mayfly. Notes live in a Python list that evaporates the instant the instance recycles. It remembers your note right up until it doesn’t, which is arguably worse than a goldfish: at least the goldfish never promised. This post gives it AgentCore Memory. Two kinds, in order of how much they’ll surprise you: ...

Building an Agent on Bedrock AgentCore, Part 1: The Loop Was Never the Hard Part
Previously I wrote a whole series about building an agent from scratch. The big reveal was almost insultingly simple: An agent is a while loop. The model asks for a tool, your code runs the tool, you hand the result back, repeat until it stops asking. Six hundred lines of Python, a babysitter wrapped around a language model, and you’ve got something useful, if not dangerous. I stand by every word of it. If you’ve never hand-rolled the loop, go read that series first — this one assumes you already know what a tool-call looks like coming back from the model. ...

Building an Agent on Bedrock AgentCore, Part 2: Teaching It to Run Somewhere I Can Close My Laptop
Last time we built a notes agent with Strands and ran it in a terminal. It worked. It also ran exactly as long as I left the terminal open, which is to say: it was production-ready in the same sense that a campfire is central heating. This post fixes the “on my laptop” problem and nothing else. Same agent. Same tools. Same build_agent() from Part 1, not one line changed. The only new thing is where it runs — and the surprising part is how little code that takes. ...