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
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_keyis the value you pass to the proxy asx-api-keytokenis the raw JWT (api_keywithout theqsk_prefix)expires_inis seconds; default is 3600
Via the CLI
quayside login -p alice@example.com -c eng/code-reviewer -u http://localhost:8000The CLI POSTs to the same endpoint and stores the result. Read it back with:
quayside whoamiWhat the token carries
A Quayside JWT has these claims:
| Claim | Notes |
|---|---|
sub | The principal id |
class_slug | Which class this token authorizes |
tenant | Tenant id |
iss | quayside |
iat | Issued-at unix epoch |
exp | Expires-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:
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:
- Strips the
qsk_prefix - Validates the JWT signature, issuer, and expiry
- Extracts
sub(principal),class_slug, andtenantfrom the claims - Resolves identity, runs the policy cascade, forwards upstream
What happens when the token is bad
| Situation | Response |
|---|---|
Missing x-api-key and X-Quayside-* headers | 400 |
x-api-key without qsk_ prefix | 401 api key missing 'qsk_' prefix |
| Malformed JWT | 401 token malformed |
| Wrong signature | 401 token signature invalid |
| Expired | 401 token expired |
| Issuer mismatch | 401 token issuer mismatch |
| Missing required claim | 401 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:
- Disable
dev_mode(QUAYSIDE_DEV_MODE=false) - Set a strong
QUAYSIDE_JWT_SECRET - The dev mint endpoint refuses (403)
- 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.