API Authentication
Mint Bearer tokens from the local CLI and use them against Downcity APIs
API Authentication
Downcity now uses a single external auth model:
- no username/password sign-in
- no browser session layer
- all external auth goes through Bearer tokens
- tokens are managed by the local CLI on the same machine
The currently exposed endpoints are:
GET /api/auth/statusGET /api/auth/meGET /api/auth/token/listPOST /api/auth/token/createPOST /api/auth/token/delete
First-time setup
If you start Console through:
city start --consolethe first startup automatically creates a local token and prints the plain token once in the startup output. The startup output looks like this:
$ city start --console
city version: 1.0.0
✅ city runtime started
pid: <runtimePid>
log: <cityRuntimeLogPath>
✅ Console started
pid: <consolePid>
url: http://127.0.0.1:5315
log: <consoleLogPath>
✅ Console token initialized
subject: local-cli
name: console-bootstrap
token: dc_xxx
next: paste the Bearer Token above into Console UI / ExtensionIf you want to mint tokens manually, run:
city token create my-tokenThe name is only for your own identification and management. It does not represent a different token type or channel scope.
If you prefer an interactive flow, you can also run:
city tokenThe interactive flow then helps you:
- copy the newly issued token immediately
Use the token
Send the token through the Authorization header:
curl http://127.0.0.1:3000/api/auth/me \
-H 'Authorization: Bearer dc_xxx'If the token is valid, the endpoint returns the current subject, roles, and permissions.
Manage tokens with CLI
List tokens on the current machine:
city token listCreate a new token:
city token create my-tokenDelete a token:
city token delete <tokenId>You can also manage tokens through HTTP with an existing Bearer token:
curl http://127.0.0.1:3000/api/auth/token/list \
-H 'Authorization: Bearer dc_xxx'curl -X POST http://127.0.0.1:3000/api/auth/token/create \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer dc_xxx' \
-d '{
"name": "chrome-extension"
}'curl -X POST http://127.0.0.1:3000/api/auth/token/delete \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer dc_xxx' \
-d '{
"tokenId": "token_record_id"
}'DC_AUTH_TOKEN
Main rule first:
- only external HTTP calls need a Bearer token
- local
city service,city plugin,city task,city chat, and similar commands use IPC by default and do not need a token first
For direct API usage, the important rule is:
- external calls always authenticate through
Authorization: Bearer <token>
Inside the CLI/runtime, the only auth env var most users need is:
| Variable | Intended For | Purpose |
|---|---|---|
DC_AUTH_TOKEN | users, scripts, CI | explicitly choose which token a CLI/API invocation should use |
Only explicit remote HTTP calls use this resolution order:
--tokenDC_AUTH_TOKEN
So:
DC_AUTH_TOKENis the explicit override- local IPC calls do not need a token
- explicit remote HTTP calls normalize
DC_AUTH_TOKENinto the Bearer header
If you see Missing bearer token:
- do not start by suspecting
sessionId - first confirm that the invocation is actually using remote HTTP
- then check whether the current process has any valid token source
- if this happens inside an agent/Bash/ACP subprocess, first check whether the call should be local IPC instead of remote HTTP
Current scope
The current model is intentionally token-only:
- username/password login removed
bootstrap-adminremovedloginremovedpassword/updateremoved- Console UI, Chrome extension, and external scripts all use explicitly minted Bearer tokens
Future integrations will stay on this Bearer token model instead of reintroducing a separate login session layer.