Admin authentication
Configure Axiom Overwatch admin authorization for human operators and machine clients.
Admin authorization is the separate operator gate for sensitive Axiom Overwatch API operations. It is not an API-key tier and it is not cron authentication. An authorized request can be made by a signed-in Supabase user whose identity or trusted role is configured for admin access, or by a configured machine key.
Admin authorization is implemented by the shared requireAuth helper. The
endpoints in Admin-gated API operations call that
helper directly; it does not look up or authorize database API-key tiers.
Authorization evaluation order
requireAuth evaluates an admin request in this order:
- It applies the admin-attempt rate limit: more than 10 attempts per minute
from the same source IP receive
429. - It checks that at least one of the four
ADMIN_*authorization paths below is configured. If none is configured, it returns403. - It reads the current Supabase session user and checks the user's ID against
ADMIN_USER_IDS. - It checks the user's trusted
app_metadataroles againstADMIN_ROLE_CLAIMS. - It checks the supplied machine key against
ADMIN_MACHINE_KEYS. - It checks the same machine key against the legacy
ADMIN_API_KEYfallback.
The first matching path authorizes the request. A nonmatching request returns
401; the protected route does not continue into its handler.
For a machine actor, the helper reads X-API-Key first. If that header is
absent, it reads Authorization: Bearer <key>. Supplying both headers uses the
X-API-Key value.
Human actors
Human authorization starts with the authenticated Supabase user for the
request. ADMIN_USER_IDS matches the user's immutable ID. Role authorization
reads only app_metadata, not user_metadata: the latter must not grant admin
access because it is commonly user-writable. The accepted role fields are
role, roles, admin_role, admin_roles, app_role, and app_roles; each
may be a string or an array of strings.
Machine actors
Machine automation can send either supported key header. Machine-key comparison
uses a digest and constant-time equality check. ADMIN_MACHINE_KEYS is checked
before the legacy key, so an automation key present in both configurations is
resolved as a machine-key authorization rather than a legacy authorization.
There is no route-specific scope grammar in the current implementation. Every
matching value in ADMIN_MACHINE_KEYS authorizes every operation in the table
below. A value such as scope:imports is only a literal key value unless it
exactly matches the configured secret; it does not limit access to imports.
Environment variables
The four admin settings are comma-separated lists where noted. Empty list items are ignored and surrounding whitespace is removed. The examples below are deliberately fake placeholders, not usable credentials.
| Variable | Format and fake example | Authorization semantics |
|---|---|---|
ADMIN_USER_IDS | Comma-separated Supabase user IDs. Example: user-placeholder-001,user-placeholder-002 | User ID entries are lowercased after trimming and compared to the signed-in user's ID. A match is the first authorization path. |
ADMIN_ROLE_CLAIMS | Comma-separated role names. Example: ops,admin | Entries and candidate role strings are lowercased. A match must be in the signed-in user's app_metadata; user metadata is ignored. |
ADMIN_MACHINE_KEYS | Comma-separated machine-key values. Example: example-machine-key-a-not-a-secret,example-machine-key-b-not-a-secret | Entries are trimmed but remain case-sensitive. Any matching entry authorizes all admin-gated operations; the value can arrive in X-API-Key or Authorization: Bearer. |
ADMIN_API_KEY | One legacy machine-key value. Example: legacy-example-not-a-secret | A nonempty exact match is considered only after the three preferred paths. It remains supported for compatibility, but is the broad legacy fallback. |
CRON_SECRET is deliberately separate. Cron routes use it only through
isAuthorizedCronRequest, which accepts Authorization: Bearer <cron secret>;
it does not authorize the admin-gated operations on this page.
Machine-key requests
Use one of the supported headers. The following command uses a fake placeholder and the audit-log operation as an example.
curl -X GET \
-H "X-API-Key: example-machine-key-not-a-secret" \
https://www.axiomoverwatch.io/api/v1/admin/audit-logADMIN_MACHINE_KEYS accepts more than one comma-separated value. This lets an
operator configure a replacement machine key before removing an old one, but
each configured value has the same full admin access described above.
Admin-gated API operations
These are the current direct call sites of requireAuth. Methods not listed
here may have their own API-key, session, analyst, or public-access rules, but
they are not protected by this admin helper.
Operations and configuration
| Method | Path | Admin operation |
|---|---|---|
GET | /api/v1/admin/audit-log | Read audit records, with filters and optional CSV output. |
POST | /api/v1/calibration | Create or replace a manual vessel-type calibration. |
POST | /api/v1/congestion/refresh | Request a manual congestion-index refresh. |
POST | /api/v1/freight-rates | Create or update a freight-rate record. |
GET | /api/v1/keys | List API-key metadata; key hashes are not returned. |
POST | /api/v1/keys | Create a free, starter, or pro API key. This route does not create admin-tier keys. |
POST | /api/v1/pi-coverage/import | Bulk-import P&I coverage from a plain-text CSV body. |
GET | /api/v1/webhooks | List configured webhooks without their secrets. |
POST | /api/v1/webhooks | Create a webhook. |
POST | /api/v1/webhooks/slack-test | Send a test request to a Slack webhook URL. |
Dashboard admin surfaces
The /dashboard layout requires a signed-in Supabase user. Within that
authenticated dashboard, the current code has two dedicated admin surfaces:
/dashboard/admin/audit-logaccepts a key in its client form and sends it asX-API-KeytoGET /api/v1/admin/audit-log./dashboard/admin/pi-importaccepts a key in its client form and sends it asX-API-KeytoPOST /api/v1/pi-coverage/import.
Those page components do not add a separate user-ID or role check before they
render. The admin decision happens when the protected API operation calls
requireAuth. The P&I page also loads /api/v1/pi-clubs without a key; that
directory request is session-authenticated for a signed-in dashboard user and
is not one of the admin-gated operations listed above.
Failure behavior
Admin authorization is fail-closed. The helper returns an error response before the route handler performs its sensitive work.
| Status | Response body | When it occurs |
|---|---|---|
429 | { "error": "Too many auth attempts" } | More than 10 attempts in one minute from the same source IP. |
403 | { "error": "Admin access not configured" } | None of ADMIN_USER_IDS, ADMIN_ROLE_CLAIMS, ADMIN_MACHINE_KEYS, or ADMIN_API_KEY is configured. |
401 | { "error": "Unauthorized" } | A configured admin path exists, but the session user and supplied machine key do not match any allowed path. |
For example, a machine request with an unconfigured key receives:
Response
{ "error": "Unauthorized" }The individual operation can return additional validation or service errors after authorization succeeds. Those responses are specific to the endpoint and do not mean that admin authorization was granted to an unauthenticated caller.
Moving off ADMIN_API_KEY
ADMIN_API_KEY remains in the code as a compatibility fallback, but the
preferred configuration is an immutable user ID or trusted app-metadata role
for people and ADMIN_MACHINE_KEYS for automation.
Configure the preferred paths
Add the required human identities to ADMIN_USER_IDS and/or trusted role names
to ADMIN_ROLE_CLAIMS. Add each automation credential to
ADMIN_MACHINE_KEYS.
Update machine callers
Send the new configured machine value through X-API-Key or
Authorization: Bearer. When both ADMIN_MACHINE_KEYS and ADMIN_API_KEY are
present, the machine-key check is evaluated first.
Remove the legacy fallback
After callers no longer depend on the legacy value, remove ADMIN_API_KEY.
At least one remaining admin path must be configured or all protected routes
will return 403.
Admin & Billing
API key management, billing, subscription plan endpoints, and workspace administration controls for the Axiom Overwatch maritime intelligence platform.
SDKs & MCP
Client libraries and AI agent integration for Overwatch — Python and TypeScript SDKs plus an MCP server for connecting LLMs to maritime intelligence directly.