Reference
Tokens and the open web API
The daemon serves a REST API and a WebSocket. Both are how Beacon and the iOS app talk to it, and both will answer anyone who can reach the port. One token stands between those two facts, and it is worth knowing exactly what it does before you expose anything.
Check first
oara token show on the daemon's machine answers the only question that
matters. Here is what an unprotected install says — verbatim, from a fresh one:
oara token show No web API token is set — the web API is OPEN (no auth). Set one with: oara token rotate (env file: ~/.config/prometheus/env, var: PROMETHEUS_API_TOKEN)
It says OPEN rather than something softer, and that is the right word. With no token, anything that can reach port 8005 can drive your agent — read conversations, run tools, start coding runs.
web.enabled ships as true, but the daemon mints a
PROMETHEUS_API_TOKEN into the env file on first start when none is set. So a
normal install is authenticated without you doing anything. The open state above is
reachable — by clearing the variable deliberately — and oara token show is how
you find out which one you are in.
Setting and rotating
oara token rotate Generates a new token, writes it to ~/.config/prometheus/env, and prints it once.
Rotating invalidates the old token immediately. Every client holding the old one stops working until you give it the new one, which in practice means Beacon on each machine and the iOS app on each device.
The two transports authenticate separately
This is the detail that produces confusing half-working states. REST sends the token as a bearer header. The WebSocket sends it in the first frame of the handshake. They are different code paths, and it is entirely possible for one to succeed while the other fails — Beacon's Connection settings panel tests each independently for exactly this reason.
| Port | What | Auth |
|---|---|---|
| 8005 | REST API — web.api_port | Bearer header |
| 8010 | WebSocket — web.ws_port | First frame of the handshake |
If chat loads but nothing streams, suspect the WebSocket. If nothing loads at all, suspect REST or the address itself.
Where the token lives
~/.config/prometheus/env, mode 0600, on the daemon's machine. Not in
prometheus.yaml — that file gets copied between machines and pasted into
issues, which is the whole reason for the split. See Configuration.
On the client side, Beacon stores it in your OS keychain rather than in a config file. The address is stored locally; the token is not written anywhere Beacon can read back as plain text.
Before you expose a port
Four things, in order, and the last one is the one people skip:
- Run
oara token show. Confirm it does not say OPEN. This takes five seconds and is the whole ballgame. - Prefer a private network to a public one. A tailnet or a LAN means the port is not reachable from the internet at all, and no token failure can change that. This is a stronger guarantee than authentication.
- Check
oara doctor. It reports the token state alongside everything else, so you get the answer without having to remember to ask. - Read what the agent is actually allowed to do. A token controls who can drive the agent. It says nothing about what the agent may do once driven — and the defaults there are more permissive than the name of one setting suggests. Configuration has the three layers and which of them does not cover
bash.
What a token does not do
It authenticates the transport. It is not a per-user identity, there are no roles, and there is no audit trail of who used it — one token, one level of access. If you need to revoke access for one person while keeping it for another, rotating is the only mechanism and it revokes everyone.
It also does not encrypt anything. The REST API is plain HTTP on a dev port; if you are carrying it across a network you do not control, put it inside something that does encrypt — which is another argument for a tailnet over a port forward.