Skip to main content
Use short-lived, per-device sessions for users. Use long-lived, independently revocable API keys for server-to-server calls such as Stripe webhooks, external cron jobs, and CI scripts.

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 the PYLON_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-keys endpoints 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):
The 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, with Authorization: Bearer <key>:
The runtime resolves the key with a constant-time hash comparison. It produces an 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(...) is false for key-authenticated requests, so a key can’t exercise role-gated policies its owner otherwise could.
For finer per-key restriction, create the key under a service user with few privileges, and check expires_at. Do not rely on the scopes string to enforce anything.

List keys

Response:
Pylon does not return the full key. The response has only the prefix and metadata.

Rotate

There is no dedicated rotate endpoint. To rotate, create a new key and revoke the old one:
The old key stops working the instant it’s revoked.

Revoke

Pylon checks ownership first. You can only revoke your own keys (404 NOT_FOUND otherwise). The response is {"revoked": true}. Any later request with that key gets 401 INVALID_API_KEY.

Expiry

Set expires_at (unix seconds) to make a key auto-expire:
After 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 with expires_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.

Webhook signature backup

Even with HMAC-signed webhooks, an added API-key requirement means a leaked HMAC secret alone is not enough. This adds a second layer of defense.

Differences from sessions

When to use a session instead

You can do server-to-server auth with a long-lived session token. You then lose per-integration tracking, explicit expiry, and clean revocation. Prefer keys for any non-user caller.