Authentication
Two credentials, deliberately separate: a session for a person at a browser, a bearer token for a machine. The session mints tokens; the token cannot mint anything.
Magic link
Section titled “Magic link”POST /v1/auth/request { "email": "[email protected]" } → 202GET /v1/auth/verify?token=… → 200 + Set-CookieThe link is single-use and lives 15 minutes.
Rate limits. Five requests per 15 minutes per address, twenty per 15 minutes per IP. Both answer
429 with Retry-After and the same body — which limit you hit is not part of the answer.
The session cookie
Section titled “The session cookie”Set-Cookie: plune_session=…; Max-Age=604800; Path=/; HttpOnly; Secure; SameSite=LaxSeven days, HttpOnly (script cannot read it), Secure, and SameSite=Lax — which is what stops a
cross-site POST from carrying it. The value is signed; a cookie with the wrong signature is a 401,
not an unknown user.
API tokens
Section titled “API tokens”POST /v1/tokens → 201 { "token": "plune_…" } ← the only time the value existsGET /v1/tokens → the list, metadata onlyDELETE /v1/tokens/{id} → 204Only a hash of the token is stored, so the list can never show you the value again and a database
leak yields nothing usable. Send it as Authorization: Bearer plune_….
Ninety days. Long enough to survive the thing a token exists for — a pipeline configured once and left alone — and short enough to be distinguishable from forever. The expiry is shown twice: beside the value when you create it, and in the list afterwards. The second one matters more, since “why did my CI stop?” arrives months after anyone thought about the token.
Ten live tokens per account. Expired ones do not occupy a slot. The list says “N of 10”, and
past the ceiling the Generate button is disabled with the way out named — revoke one; the API
answers 409 with the same explanation. Minting has a three-second cooldown: a second request
inside it answers 429 with Retry-After: 3, and the dashboard keeps the button down for those
three seconds so you do not see that answer by accident.