Authentication
The bus and lead tokens, the callers and their rights, the KRAD_ADE variables, the api.json discovery file and the security model.
Tokens
The app makes two random 128-bit tokens, written as 32 hex digits, each time it starts, and compares them in constant time.
| Token | Who gets it |
|---|---|
| The bus token | Every terminal the app opens, as KRAD_ADE_TOKEN or, on SSH hosts, as an owner-only file named by KRAD_ADE_TOKEN_FILE; it is also written to api.json |
| The lead token | Kept by the app. An envelope whose from is {"Lead":...} must carry it; with the bus token it is refused with unauthorized ("only a lead the user named can call as a lead"). No terminal receives it: a lead calls as its own terminal and the app recognises it. |
The tokens are never logged: the types that hold them print [REDACTED].
Callers
from names the caller, and the app answers according to it.
from | Who | Scope and rights |
|---|---|---|
"Client" | A program outside the app's terminals | Hello, List and ListAll across every open environment, and SubscribeAgents. Anything else is refused with forbidden. |
{"Terminal":{"environment":E,"terminal":N}} | A terminal or chat of environment E; the app sets KRAD_ADE_TERMINAL=E:N | Its own environment and group. Every request except those that direct Workers. |
The same, when terminal N is a session's lead chat | A lead | For Spawn, Send, Close, CloseSession and PickVariant, and for Read and Wait on its own Workers, the app treats it as the lead of that session. Leads are the only callers allowed to direct Workers; anyone else gets forbidden. |
{"Git":"main"} | The app's own git credential prompts | Internal; only the askpass request |
The CLI fills from from KRAD_ADE_TERMINAL inside the app, and with "Client" when it found the endpoint through api.json.
Inside the app's terminals
The app sets these variables in every terminal and chat process it starts:
| Variable | Value |
|---|---|
KRAD_ADE_ENDPOINT | 127.0.0.1:<port>; on an SSH host, the forwarded address there |
KRAD_ADE_TOKEN | The bus token, on this computer |
KRAD_ADE_TOKEN_FILE | The owner-only file holding the token, on SSH hosts, instead of KRAD_ADE_TOKEN |
KRAD_ADE_TERMINAL | The caller handle, environment:terminal |
PATH | Prefixed with the app's own folder, where krad and krad-ade-agents live |
The API discovery file
Outside the app's terminals, programs find the API through api.json, which the app leaves in its config folder while it runs: $XDG_CONFIG_HOME/krad-ade, else ~/.config/krad-ade, else %APPDATA%\krad-ade.
{
"api_version": 1,
"endpoint": "127.0.0.1:52144",
"token": "6f1c0d9e2b7a4c58e3f09a1d7b6c2e40",
"pid": 48211,
"started_at": "2026-11-10T09:12:00.000Z"
}- It is written atomically, a temporary file renamed into place, and owner-only (
0600on Unix). - It is replaced each time the app starts, and removed when the app exits cleanly, but only by the process whose
pidit names. - Settings › General › "Let other programs find the agents API" turns it on and off. It is on by default, and turning it off removes the file at once.
- The variables win over the file: the CLI reads
api.jsononly whenKRAD_ADE_ENDPOINTandKRAD_ADE_TERMINALare not set. - The static Linux build of the CLI that runs on SSH hosts has no discovery file and only uses the variables.
Stale files
A file left by a crash can name a dead endpoint, or a port another program now holds.
| The file is | When |
|---|---|
| Live | Hello at its endpoint answers with the same pid |
| Stale | Nothing answers, or another process does |
krad api info makes that check and exits with 2 on a stale file. It prints where the API is and how it was found; --show-token adds the token, and --json prints {"endpoint","api_version","build","discovery_file","token"}, the last two only when they apply.
krad api info --jsonSecurity model
The caller is declared, not proven
from is what the client says it is. The token proves the client runs as the user, since it could read the file or a terminal's environment; it does not prove which terminal sent the request. A program holding the token could name any terminal of any environment, a lead's included. The lead token keeps the Lead caller itself out of reach, but the rights the app gives a lead's own terminal follow from the name.
- Loopback only. Nothing outside this computer can connect, except through the SSH forwards the app itself opens to the hosts of its environments.
- A token per run. Every request needs the bus token. It reaches only the app's terminals and the owner-only
api.json, it changes each time the app starts, and it is never logged. - Narrow rights outside. A
"Client"caller can only list and watch agents. Turning the discovery file off leaves the API to the app's own terminals. - No contents on the wire by default. Agent events and app events carry ids, titles and states, never terminal contents. Reading a screen (
Read) or a group's patch (Changes) needs a terminal caller of that environment.
The trade-off is convenience over isolation from other processes of the same user: anything that runs as you can already read your files and your terminals' environments, and the API does not try to protect the app from it. It protects the app from other users and other machines.