Docs
Authentication
Sessions, Personal Access Tokens, scope-restricted bearers, and audience-scoped HMAC tokens for machines.
Authentication
Hangar speaks two auth schemes. Pick the one that matches your caller.
Sessions (browsers)
The dashboard uses Better Auth DB-backed
sessions. The session cookie is named hangar.session (or
__Secure-hangar.session in production). You don't need to do
anything — the login page sets it.
Personal Access Tokens (everything else)
Every non-browser caller — MCP clients, agents, CLIs, CI jobs — uses
a Personal Access Token. PATs are bearer tokens prefixed with oss_.
Mint a token
Visit /dashboard/settings/tokens or
POST /api/tokens:
curl -X POST https://hangar.so/api/tokens \
-H "Cookie: hangar.session=…" \
-H "Content-Type: application/json" \
-d '{
"name": "Claude Desktop",
"scopes": ["mcp:*"],
"expiresInDays": 90
}'
The response includes the plaintext token. It is shown ONCE. Store it securely; we keep only a hash on the server.
{
"id": "tok_...",
"name": "Claude Desktop",
"tokenPrefix": "oss_aBcD...",
"plaintext": "oss_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
"scopes": ["mcp:*"],
"expiresAt": "2026-08-10T00:00:00Z",
"createdAt": "2026-05-12T00:00:00Z"
}
Use a token
Send it as a Bearer header on every request:
curl https://hangar.so/api/instance/status \
-H "Authorization: Bearer oss_..."
List tokens
curl https://hangar.so/api/tokens \
-H "Authorization: Bearer oss_..."
The plaintext is never returned again — only the prefix and metadata.
Revoke
curl -X DELETE "https://hangar.so/api/tokens?id=tok_..." \
-H "Authorization: Bearer oss_..."
Scopes
PAT scopes are explicit. The default mint is mcp:* (every MCP tool).
For least-privilege tokens, mint with explicit scopes:
| Scope | Tools |
|---|---|
mcp:wallet.read |
wallet.balance, wallet.transactions |
mcp:wallet.write |
wallet.checkoutTopUp |
mcp:instance.read |
instance.status |
mcp:instance.write |
instance.pause, instance.resume, instance.refreshEnv, instance.setModel |
mcp:personas.read |
personas.list |
mcp:personas.write |
personas.activate, personas.deactivate |
mcp:skills.read |
skills.list, skills.show |
A read-only Claude token typically wants:
{
"name": "Read-only Claude",
"scopes": [
"mcp:wallet.read",
"mcp:instance.read",
"mcp:personas.read",
"mcp:skills.read"
]
}
Failure modes
401 unauthorized— token missing, malformed, or revoked. Mint a new one.403 forbidden— token lacks the scope. Mint a token that includes the scope you need.- An invalid Bearer token does not fall back to the cookie session. This prevents typos from "still working" via the user's browser session.
Audience-scoped HMAC tokens (machine-side)
When Hangar provisions a Fly Machine, it injects three short-lived HMAC tokens, each scoped to a single audience:
llmToken—/api/llm/proxy(wallet-billed LLM calls).filesToken—/api/agent/files(first-boot config sync).agentToken—/api/agent/{prompt,context,observe,feedback}.
These are not user-facing. They exist so a leaked LLM token cannot read the secret vault, and a leaked files token cannot run billed LLM calls. Audit-driven separation, not theatre.