Availability
API keys are always available. The framework owns a dedicated key store, so there is no plugin or manifest entity to declare. The store persists to thePYLON_SESSION_DB SQLite file, or stays in memory when that variable is unset.
Management endpoints are mounted under /api/auth/api-keys.
What you get:
/api/auth/api-keysendpoints to create, list, and revoke keys- Bearer-token resolution: a request with
Authorization: Bearer pk.<...>resolves to the key’s owner instead of looking up a session
What’s stored
The key store keeps an internal record per key (not a manifest entity):
Lists show only the
prefix. The full key appears once at creation time.
Create a key
scopes is an optional free-form string. expires_at is an optional unix timestamp (seconds). API-key auth cannot create keys; a real session is required (403 API_KEY_AUTH_FORBIDDEN otherwise). A leaked key cannot create more keys.
Response (200):
key field is shown once. Copy it now, or revoke and re-create the key. The server stores only the hash.
Use a key
Use it like a session, withAuthorization: Bearer <key>:
AuthContext with the key owner’s userId. Pylon does not interpret or enforce the key’s scopes (see below).
Scopes
Scopes are an opaque, application-defined label. Pylon stores the string you pass and shows it back when you list keys. Pylon does not parse or enforce it. There is no built-in scope vocabulary and no automatic read/write gating. Pylon does not pass the scope string to your function or policy code. Treat it as metadata for a person who audits the key list, not as an access-control mechanism. Two things constrain what a key can do:- It resolves to the key’s owner
userId. A key can do whatever that user can do, subject to your entity policies. - It does not load the owner’s roles.
auth.hasRole(...)isfalsefor key-authenticated requests, so a key can’t exercise role-gated policies its owner otherwise could.
expires_at. Do not rely on the scopes string to
enforce anything.
List keys
Rotate
There is no dedicated rotate endpoint. To rotate, create a new key and revoke the old one:Revoke
404 NOT_FOUND otherwise). The response is {"revoked": true}. Any later request with that key gets 401 INVALID_API_KEY.
Expiry
Setexpires_at (unix seconds) to make a key auto-expire:
expires_at, Pylon rejects the key as 401 INVALID_API_KEY. The last_used_at field updates on every successful request. List your keys and compare last_used_at to the current time to find dormant keys you can revoke.
Security
- Keys are CSPRNG-generated — a 256-bit random secret, wire format
pk.key_<id>.<secret>. - Hashed with HMAC-SHA256 server-side. Unlike passwords (Argon2id), API-key secrets are full-entropy random, so a fast keyed hash is safe here and adds no per-request latency. A slow KDF adds no benefit.
- Constant-time comparison — no timing leak on key resolution.
- Shown once — the plaintext key never touches the database; only the hash is stored.
- Owner-scoped management — creating, listing, and revoking keys all require a real session, never an API key.
Common patterns
Per-integration keys
One key per third-party integration. Stripe, SendGrid, your CI pipeline, and your monitoring agent each get a key scoped to the functions it calls.Per-environment keys
CI key for staging, separate key for prod. Rotate independently. If a CI build leaks the staging key, prod isn’t affected.Time-limited keys for handoffs
A consultant needs access for two weeks? Create a key withexpires_at 14 days out. Enforce read-only in your functions if you tag it with a read scope. The key expires on its own, so no one must remember to revoke it.