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.

Authentication — Hangar