# Scripting Hammerhead: API audit Everything the dashboard does goes through the JSON API (`server.go` lists the routes). Callers authenticate with the `hh_session` cookie or `Authorization: Bearer hhk__`. ## Credentials | Credential | Reaches | Made at | |---|---|---| | Organization key | that organization's `/api/orgs/{org}/...` routes (admin rights), `GET /api/orgs`, `GET /api/edges` (edges the org may use) | `POST /api/orgs/{org}/apikeys` | | Server key | every organization, plus the server-admin routes: edges, `/api/admin/alerts`, users, settings, `POST /api/orgs` | `POST /api/admin/apikeys` (signed-in server admin only) | The secret is shown once, in the `key` field of the create response. A key cannot mint keys at server level, and a server key has no user, so `/api/me` and other per-user routes refuse it. ## Gaps found, and what was done | Gap | Resolution | |---|---| | Edges, server alerts, `POST /api/orgs` required a browser session (`withServerAdmin` refused keys) | server keys, accepted by `withServerAdmin` | | No way to mint an unattended server credential | `GET/POST/DELETE /api/admin/apikeys` | | `GET /api/edges` was session-only | accepts keys; an org key sees the edges its org may use | | No way to list orgs with a key | `GET /api/orgs` | | Key creation did not return the key's ID | create response now has `id` as well as `key` | | `createOrg` added the caller as a member | skipped for server keys (they have no user) | ## Known remaining gaps (not blocking scripting) - No per-resource `GET`: sites, domains, ports and alert channels are read from `GET /api/orgs/{org}/state` and `GET /api/orgs/{org}/alerts`. Clients filter by ID. - Alert channel secrets (webhook URL, Sandbar key) are never returned, so a client can't detect drift in them; a blank value on `PUT` keeps the stored one. - Keys are all-powerful within their scope (no read-only or per-resource scopes), and have no expiry. - `PUT` on ports replaces the whole object; omitted fields reset. - Site tokens are returned on create and, for admins, in `state`. - Deleting an edge/site does not check for ports that use it (the server cleans up or refuses; clients should order deletes).