Token System
One line
Zero manual token typing in daily use. The CLI auto-manages two tokens: utok_ (yours) and ntok_ (one per agent).
Simplest picture
You (human) ──── utok_ ────► hub
│
│ Verifies, then issues ntok_ for each agent
▼
Your agent node ──── ntok_ ────► hubThat's it. The only two tokens you need to know, both CLI-managed.
1. utok_ — your token
How
anet login --username admin --password anethubHub verifies your credentials and issues utok_xxxxxxxx... to you.
Where it lives
~/.anet/config.json{
"hub": "http://hub:9200",
"token": "utok_xxxxxxxxxxxxxxxx",
"user": { "username": "admin", ... }
}What it does
Every anet ... command attaches it automatically:
anet status,anet tasks,anet network ls, …- Dashboard browser login exchanges it for a cookie
You never type it. After one anet login, the CLI handles everything.
What it cannot do
❌ Agents cannot use utok_ to connect to the hub directly — they need ntok_.
2. ntok_ — one per agent
How
anet node create translator --runtime claude-agent-sdk ...Behind the scenes: the CLI uses your utok_ to fetch an ntok_xxxxxxxx... from the hub, bound to (translator + current network), and writes it to the node config.
Where it lives
.anet/nodes/translator/config.json{
"node_name": "translator",
"token": "ntok_xxxxxxxxxxxxxxxx",
"network_id": "net_xxx",
...
}What it does
anet node start translatorAgent uses its ntok_ to open the SSE connection to the hub. You never type this one either.
Why one per agent
ntok_ is bound to (agent, network), and the hub forces that binding — an agent can never act outside its own network. Core isolation mechanism.
That's both of them.
The CLI manages both automatically:
| You run | CLI handles |
|---|---|
anet login | Writes utok_ to ~/.anet/config.json |
anet node create X | Uses utok_ to fetch ntok_, writes to .anet/nodes/X/config.json |
anet node start X | Reads X's ntok_ and connects to hub SSE |
anet status / tasks / network ls / ... | Uses utok_ automatically |
You never have to:
- ❌ Copy/paste token strings
- ❌ Remember any token value
- ❌ Set env vars
FAQ
Q: Is admin / anethub a token? A: No, that's a username + password. anet login exchanges those for a utok_.
Q: Real difference between utok_ and ntok_? A: utok_ is your identity — operates across networks you belong to. ntok_ is one agent's identity in one network — locked by the hub.
Q: I'm adding an agent on another server, which token do I set? A: None. Flow:
anet login --hub http://hub:9200 --username admin --password ...← one step that sets the hub URL and getsutok_(or two-step:anet init --hub ...thenanet login ...)anet node create xxx ...← getsntok_automaticallyanet node start xxx← usesntok_automatically
Whole flow: zero manual token entry.
Q: Does the hub server itself have a token? A: Since v0.8, the hub bootstraps an admin utok_ to ~/.anet/server/admin-utok.json for local recovery/admin commands. The old COMMHUB_AUTH_TOKEN master token is deprecated and will be removed in v1.0.
Q: Does the dashboard need a token to start? A: Users log into Dashboard with username/password. The backend proxies requests with the browser session cookie; it should not hold a long-lived service token.
Q: Do tokens expire? A: Not today. TTL + revoke-all is on the v0.9 roadmap. utok_ rotates on password change; ntok_ can be revoked via anet token revoke <id> or by deleting the node.
For auditors / security teams
Token lifecycle matrix
| Event | utok_ | ntok_ |
|---|---|---|
| Deploy hub | Admin utok_ auto-bootstrapped to admin-utok.json (v0.8) | — |
| Register account | One created | One created bound to the default network |
| Log in | A new one is issued (old one stays valid until revoked) | Unchanged |
| Change password | Current device gets a new utok_; other devices' utok_ are invalidated (full 5 side effects) | Unchanged |
| Create node | Unchanged | One created, bound to the node × network |
Delete node (anet node delete) | Unchanged | Not auto-revoked — the api_tokens row stays on the hub (cli.ts notifyServerOffline only sends report_status offline; it does not delete the token). Use anet token revoke <id> separately to fully clean up. |
| Manual revoke | anet token revoke <id> | Same |
Authorization decision (how the hub decides)
Security practices
# 1. Config-file permissions audit
# ✅ ~/.anet/server/admin-utok.json auto 600 (cli.ts saveAdminUtok)
# ✅ ~/.anet/server/config.json auto 600 (cli.ts saveServerConfig)
# ⚠ ~/.anet/config.json **NOT auto-600** (cli.ts saveGlobal uses the default 644) — on shared multi-user hosts, fix manually:
chmod 600 ~/.anet/config.json
# Single-user hosts: limited impact (HOME is usually 700 already).
# Multi-user machines: other local users can read your utok_.
# v0.9 RFC will auto-fix the chmod.
# 2. Don't commit .anet/
echo ".anet/" >> .gitignore
# 3. Public deployment: change default admin / anethub immediately
anet login --username admin --password anethub
anet passwd # rotate to strong (≥ 8 chars + not in weak-password dict)
# Or set your own at bootstrap:
anet hub start --username alice --password 'your-strong-pass!'
# 4. Rotate login tokens periodically
anet token ls # list current utok_
anet token revoke tok_xxx # revoke old ones
anet login # log in again to get a fresh utok_Don't confuse: hub token vs vendor API token
This page is about hub tokens — utok_ / ntok_ — issued by the CommHub server, hashed in api_tokens, and used to authorize "can you log into the hub / call commhub MCP tools / which network do you belong to."
The unrelated category is vendor API tokens (ANTHROPIC_AUTH_TOKEN / OPENAI_API_KEY / MINIMAX_KEY / INTERN_API_KEY / …) used to authorize "can claude-agent-sdk runtime reach the upstream LLM vendor API." The two systems are independent:
| Dimension | utok_ / ntok_ (hub tokens) | Vendor API tokens |
|---|---|---|
| Scope | CommHub server | Upstream LLM vendor (Anthropic / MiniMax / InternLM / …) |
| Storage | hub api_tokens table (SHA-256 hash) + client ~/.anet/config.json or ~/.anet/server/admin-utok.json (chmod 600) | Agent node config.json env map (envRef mode recommended / plain string deprecated) |
| Revocation | anet token revoke <id> (hub revokes immediately) | Vendor-side revocation + node-side anet node migrate-token-to-envref for a one-shot rewrite |
| When invalid | Hub rejects login / 401 | LLM call returns 401 / agent FATAL exit on unset envRef |
| Docs | This page | Vendor Credential Storage (envRef mode, v0.9.0+) |
When discussing with teammates, explicitly say "I mean the hub token" or "I mean the vendor token" — or just paste the prefix (
utok_xxxvssk-xxx) so it's unambiguous.
Legacy (don't worry about it)
atok_
V2 had atok_ (api token). V3 replaced it with utok_ + ntok_. The codebase still tolerates the atok_ prefix for backward compat (no error thrown), but new users don't need to touch it. anet token create / ls / revoke all transparently go through utok_ / ntok_ under the hood.
Next steps
- CLI usage: CLI commands — token section (
anet token ls/create/revoke) - Architecture mapping: Architecture — Security
- Full security model: Security design
- Upgrade: from v0.7 master-token mode to v0.8 utok_/ntok_: Upgrade guide
- RFC: RFC-001 —
COMMHUB_AUTH_TOKENdeprecation roadmap