Skip to content

Mint a dev token

Quayside’s authentication for v1 is JWT-based. The dashboard, CLI, and any direct API caller authenticate by passing a JWT in the x-api-key header, prefixed with qsk_.

In dev mode the JWT is minted by a dedicated endpoint with no user-facing OIDC flow. Production deployments will replace this with a real OIDC integration.

Quick — via the dashboard

Open /developers, fill in the three fields, click Mint token. Copy the resulting qsk_… string.

The dashboard isn’t doing anything special — it’s making the same POST you can make from curl.

Via HTTP

Terminal window
curl -X POST http://localhost:8000/api/v1/auth/dev/mint-token \
-H "Content-Type: application/json" \
-d '{
"principal_id": "alice@example.com",
"class_slug": "eng/code-reviewer",
"tenant": "dev"
}'

Response:

{
"api_key": "qsk_eyJhbGciOiJIUzI1NiIs...",
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 3600,
"principal_id": "alice@example.com",
"class_slug": "eng/code-reviewer",
"tenant": "dev"
}
  • api_key is the value you pass to the proxy as x-api-key
  • token is the raw JWT (api_key without the qsk_ prefix)
  • expires_in is seconds; default is 3600

Via the CLI

Terminal window
quayside login -p alice@example.com -c eng/code-reviewer -u http://localhost:8000

The CLI POSTs to the same endpoint and stores the result. Read it back with:

Terminal window
quayside whoami

What the token carries

A Quayside JWT has these claims:

ClaimNotes
subThe principal id
class_slugWhich class this token authorizes
tenantTenant id
issquayside
iatIssued-at unix epoch
expExpires-at unix epoch

The signing key is QUAYSIDE_JWT_SECRET. In dev_mode=true deployments without an explicit secret, a deterministic dev secret is used. Never run production with dev mode on.

Using the token

The proxy reads it from x-api-key:

Terminal window
curl -X POST http://localhost:8000/api/v1/proxy/anthropic/v1/messages \
-H "x-api-key: qsk_eyJhbG..." \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet-latest",
"max_tokens": 50,
"messages": [{"role":"user","content":"Hello"}]
}'

The proxy:

  1. Strips the qsk_ prefix
  2. Validates the JWT signature, issuer, and expiry
  3. Extracts sub (principal), class_slug, and tenant from the claims
  4. Resolves identity, runs the policy cascade, forwards upstream

What happens when the token is bad

SituationResponse
Missing x-api-key and X-Quayside-* headers400
x-api-key without qsk_ prefix401 api key missing 'qsk_' prefix
Malformed JWT401 token malformed
Wrong signature401 token signature invalid
Expired401 token expired
Issuer mismatch401 token issuer mismatch
Missing required claim401 token missing required claim: …

The CLI’s cached state expires too — quayside whoami after expiry warns you to log in again.

Production swap-out

When you wire a real IdP:

  1. Disable dev_mode (QUAYSIDE_DEV_MODE=false)
  2. Set a strong QUAYSIDE_JWT_SECRET
  3. The dev mint endpoint refuses (403)
  4. Your auth provider mints tokens via your chosen flow (OAuth/PKCE, SAML, etc.) and stamps them with the same claim shape

The proxy’s validation path is unchanged. Only the issuing flow changes.