64 docs indexed

Authentication

API key format, hash method, and how to authenticate against the data plane and Management API.

Data plane

All data-plane requests must include a tenant API key:

Authorization: Bearer sk_<48 hex chars>

Example:

curl http://localhost/v1/chat/completions \
  -H "Authorization: Bearer sk_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"model": "my-model", "messages": [...]}'

Key format

ComponentValue
Prefixsk_
Secret part48 hex characters (24 random bytes)
Total length51 characters
Display prefixFirst 18 characters

Key storage

obleth never stores the raw key. On creation:

  1. The gateway generates 24 cryptographically random bytes.
  2. It hex-encodes them and prepends sk_.
  3. It computes SHA-256(full_secret_bytes) to produce the key hash.
  4. It stores the hash in Postgres (api_keys.key_hash) and Redis (obleth:key:{hex_hash}).
  5. It returns the full secret to the caller once.

On each request, the gateway re-hashes the incoming bearer token and looks up the hash. If Redis is cold or unavailable, it uses the in-process cache when possible.

Optional hash pepper

If OBLETH_API_KEY_PEPPER is set, it is mixed into the hash so a leaked config database alone can't be used to confirm guessed keys without also stealing the pepper (which lives out of band). When unset, hashing is byte-for-byte identical to the unpeppered scheme, so existing keys keep working. Changing or adding a pepper invalidates all previously issued keys — they must be rotated.

Management API

All requests to the Management API (:9180) must include the admin bearer token:

Authorization: Bearer <OBLETH_ADMIN_TOKEN>
curl http://localhost:9180/api/v1/tenants \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN"

OBLETH_ADMIN_TOKEN has no default and is compared in constant time. There is no per-user RBAC for the Management API \u2014 the admin token is all-or-nothing. Protect it accordingly; see the Security guide.

Creating a key

curl -X POST http://localhost:9180/api/v1/tenants/$TENANT_ID/keys \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app-key"}'

Response:

{
  "key": {
    "id": "...",
    "tenant_id": "...",
    "name": "my-app-key",
    "key_prefix": "sk_a1b2c3d4e5f6a1b",
    "disabled": false
  },
  "secret": "sk_a1b2c3d4e5f6..."
}

Key lifecycle

ActionEndpoint
CreatePOST /api/v1/tenants/{id}/keys
ListGET /api/v1/keys
DisablePUT /api/v1/keys/{id}/disabled with {"disabled": true}
DeleteDELETE /api/v1/keys/{id}

A disabled key returns 403 Forbidden. A deleted key returns 401 Unauthorized.