Downcity
Security & Permissions

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/status
  • GET /api/auth/me
  • GET /api/auth/token/list
  • POST /api/auth/token/create
  • POST /api/auth/token/delete

First-time setup

If you start Console through:

city start --console

the 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 / Extension

If you want to mint tokens manually, run:

city token create my-token

The 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 token

The 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 list

Create a new token:

city token create my-token

Delete 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:

VariableIntended ForPurpose
DC_AUTH_TOKENusers, scripts, CIexplicitly choose which token a CLI/API invocation should use

Only explicit remote HTTP calls use this resolution order:

  1. --token
  2. DC_AUTH_TOKEN

So:

  • DC_AUTH_TOKEN is the explicit override
  • local IPC calls do not need a token
  • explicit remote HTTP calls normalize DC_AUTH_TOKEN into 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-admin removed
  • login removed
  • password/update removed
  • 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.