Authentication
Oyster uses Bearer tokens for authenticated routes plus public access
for blob reads and infrastructure probes. There are two tiers of Bearer
token, distinguished by which routes they unlock; both share the same
Authorization: Bearer <hex> wire format. Admin keys belong to an app, which
owns the accounts it creates and manages them through admin routes.
Which routes need authentication?
| Route pattern | Auth mode | Purpose |
|---|---|---|
| Bucket CRUD; blob write, list, and delete; wallet | API key | Data operations |
GET .../blobs/{key}, GET /blobs/by-blob-id/... | Public | Blob reads |
POST /accounts, key management under /accounts/{id}/... | Admin key | Admin operations |
/health, /ready, /metrics, /api/docs | Public | Infrastructure |
How Oyster tells them apart: the URL prefix selects the credential table. Admin routes look up the Bearer token in the
app_admin_keystable (one app per key); data routes look it up in theapi_keystable (one account per key). Both tokens are 64-character hex; the hash check happens on whichever table the route statically uses.
How to authenticate with an API key
Include your API key in the Authorization header:
Authorization: Bearer <api-key>
Example:
curl -s \
-H "Authorization: Bearer $API_KEY" \
"$OYSTER_URL/api/v1/buckets"
Key properties
| Property | Value |
|---|---|
| Size | 32 bytes, hex-encoded (64 characters) |
| Hash algorithm | BLAKE2s-256 (Oyster stores only the hash) |
| Prefix | First 8 characters, used to identify keys without exposing the secret |
You provision API keys through the Admin API (see
Admin). Oyster shows the full secret only once at
creation time. You cannot recover a lost key. Create a replacement with POST /api/v1/accounts/{account_id}/api-keys (see Create API key) and revoke the lost key with DELETE /api/v1/accounts/{account_id}/api-keys/{key_id}.
Errors
| Status | Condition |
|---|---|
401 Unauthorized | Missing, malformed, or invalid API key |
How to authenticate with an admin key
Admin endpoints require a per-app admin key that the server operator issues:
Authorization: Bearer <admin-key>
The server operator generates admin keys server-side with
oysterd app issue-admin-key <app_id>. They are not available through
a public API. Oyster supports multiple admin keys per app (AWS-style two-key
rotation, no cap).
Key properties
| Property | Value |
|---|---|
| Size | 32 bytes, hex-encoded (64 characters) |
| Hash algorithm | BLAKE2s-256 (Oyster stores only the hash) |
| Prefix | First 8 characters, used to identify keys in listings without exposing the secret |
| Lifetime | Long-lived; no expiry. Rotation is voluntary issue-then-revoke. |
Account ownership enforcement
An app can only manage accounts it created. Attempting to access another app's accounts returns 403 Forbidden:
{ "error": "forbidden: account does not belong to this app" }
How to rotate admin keys
Admin keys do not expire. The recommended pattern is AWS-style two-key overlap:
- Operator issues a new key alongside the old one
(
oysterd app issue-admin-key <APP_ID>). - Callers swap to the new key.
- After confirming nothing still uses the old key, the operator revokes
it (
oysterd app revoke-admin-key <OLD_KEY_ID>). Revocation takes effect immediately, with no caching.
oysterd app list-admin-keys <APP_ID> shows all keys (active and
revoked) so an operator can audit before revoking.
Errors
| Status | Condition |
|---|---|
401 Unauthorized | Missing, malformed, revoked, or unknown admin key |
403 Forbidden | Valid admin key but accessing another app's resources |
Which endpoints are public?
The following routes require no authentication:
- Blob reads:
GET /api/v1/buckets/{bucket_name}/blobs/{key}andGET /api/v1/blobs/by-blob-id/{blob_id} - Infrastructure:
/health,/ready,/metrics,/api/docs
Example:
curl -s "$OYSTER_URL/api/v1/buckets/my-bucket/blobs/hello.txt"
Security notes
- API keys and admin keys: Oyster stores only the BLAKE2s-256 hash.
You cannot recover a lost key. For a lost API key, issue a replacement
through the Admin API (see Create API key).
For a lost admin key, the server operator issues a new one with
oysterd app issue-admin-key. - Admin-key compromise: A leaked admin key gives full app-admin
access (account creation, key issuance, S3 access keys) until you revoke it.
Treat it like a long-lived service credential and rotate it periodically and
on personnel changes through
oysterd app issue-admin-keyandoysterd app revoke-admin-key. - TLS: Always terminate TLS in front of Oyster in production.
What is oysterd?
oysterd is the Oyster server binary. It runs the HTTP and S3 server (oysterd serve, the default when you give no subcommand). It also provides the oysterd app subcommands for managing apps and admin keys. These subcommands work through direct database access, so the server operator runs them on the server side. For the full oysterd command reference, read Admin. For the environment variables that configure the server, read the Oyster README.