Why I Put a Bearer Token on APX’s Local Daemon
I kept running into the same problem while building APX: the moment you add a daemon, you create a shared control surface. The CLI, the web admin, Telegram hooks, future tools, and even debugging helpers all end up talking to the same local API. That is useful. It is also where things get risky.
My first instinct was the usual one: "it runs on localhost, so it is fine." That sentence sounds safe, but it is too loose. Localhost is not a trust boundary. It is just a network address. If some other process under the same OS user can reach the daemon, then "local" does not mean "harmless."
That is why APX now requires a bearer token on its daemon API.
The implementation is small, but the design choice matters. APX writes a fresh token at startup to ~/.apx/daemon.token with 0600 permissions. The CLI reads it automatically, so normal use stays boring. The only unauthenticated endpoint is GET /health, which the CLI can use to check whether the daemon is alive before it reads the token file.
That gives me the security property I actually wanted: the daemon is still easy to use, but it is no longer an open local control socket.
Why this was worth doing
APX is not a toy process. It manages projects, agents, sessions, message logs, routines, and runtime state. It can also execute work through connected runtimes and plugins. Once a tool does that much, the cost of a weak boundary grows fast.
The threat model here is not a remote attacker on the internet. It is something more mundane and more realistic: a compromised npm dependency, a stray script, a malicious helper, or any other process running as the same user. If that process can call the daemon API, it can do more than read a status page. It can trigger actions, inspect memory, and interact with project state.
That is not a theoretical concern. It is the kind of mistake local developer tools make when they assume "same machine" means "same trust."
What changed in practice
The token let me keep the APX workflow simple without leaving the API exposed.
The developer experience is still basically one command path:
apx init
apx run sofia --runtime claude-code "Review the open PRs and summarize them"
apx messages tail
The CLI handles the token automatically, so I do not have to think about auth during normal use. That is the important part. Security controls that require memory will drift. Security controls that are invisible in the common path tend to survive.
The daemon also stays the same kind of thing from the user’s point of view: a local runtime. I did not want a login wall, a remote account model, or a cloud dependency. I wanted a minimal check that protects the shared API while keeping the machine-local workflow intact.
Why localhost was not enough
This is the part I think a lot of local tooling gets wrong.
A local API feels private because the browser cannot see it from the outside world. But APX is not only a browser app. It is a daemon with commands that can read memory, tail messages, start work, and route actions across surfaces. That means the real question is not "can the internet reach it?" The real question is "what other code on this machine can reach it?"
Once you ask that question, the answer changes.
A bearer token does not solve every problem. It does not stop a user from doing something they are allowed to do. It does not make the daemon invulnerable. But it does raise the bar from "any process that can guess a port" to "a process that can read the token file."
That is a meaningful improvement.
The tradeoff I accepted
Every security boundary adds friction somewhere. The wrong kind of auth makes a local tool feel heavy. You end up typing credentials for things that should be instant.
I did not want that.
So the token had to be automatic, short-lived per daemon start, and invisible in normal CLI use. That keeps the safety cost low. The daemon still starts locally, the CLI still feels immediate, and the browser admin still talks to the same local backend. The boundary is there, but it does not become the product.
That is the pattern I keep preferring in APX: keep the control plane local, keep the state local, and add only the smallest guard that makes the design honest.
The bigger lesson
Building APX keeps teaching me the same thing in different forms: the hard part is not making a tool powerful. The hard part is making the power legible.
A daemon without auth is easy to build. A daemon with auth that still feels local takes more care. But that extra care pays off because it makes the architecture coherent. APC can stay focused on portable project context. APX can stay focused on execution. And the bridge between them can have a real boundary instead of a wish.
That is why I put a bearer token on the APX daemon.
It is a small change. It is also the kind of small change that keeps the rest of the system sane.
If you want the source context, start with the APX repo and its security notes:
Top comments (0)