DOCS · ADMIN · AUTH

Who gets in.

Six config blocks in /etc/qnt/server.toml decide how people sign in to your dashboard and how ops tooling talks to the control plane. The auth model: per-user JWT access tokens plus a rotating refresh cookie, with the static admin token reserved for machines.

1 The JWT signing key

The [web_auth] block governs the dashboard's end-user login. Its keystone is the signing key: every access token issued to a browser is signed with it.

Generate the key

Write a random secret to a file and lock it down. Point jwt_secret_path at it. If the path is empty, the entire /auth/* surface is disabled.

head -c 48 /dev/urandom | base64 | tr -d '\n' > /etc/qnt/jwt.key
chmod 600 /etc/qnt/jwt.key

Wire it into server.toml

[web_auth]
jwt_secret_path = "/etc/qnt/jwt.key"
dashboard_base_url = "https://login.example.com"
  • jwt_secret_path — file holding the JWT signing key. Empty disables /auth/*.
  • dashboard_base_url — base URL for the dashboard, used in OAuth redirects and emails. Has a default.

Access tokens are HS256 with a 15-minute TTL; the long-lived half is an opaque, rotating refresh token in an HttpOnly cookie. Passwords are hashed with argon2id. Optional TOTP MFA backed by 10 recovery codes is available per user.

What breaks if you lose or rotate it

The signing key is the root of trust for every session. Replace the file and every issued access token is invalidated at once — all logged-in users are signed out and must re-authenticate. Treat rotation as a deliberate, announced event, and back the key up alongside your other secrets.

2 The admin token

The [api] block guards the control-plane API. By default it demands bearer auth on every /api/* route except /health.

Prefer a file over an inline string

The static admin token authenticates service-to-service and ops callers. Keep it in a file — one line, whitespace-stripped — and point admin_token_path at it.

[api]
require_auth = true
admin_token_path = "/etc/qnt/admin.token"
  • require_auth — require bearer auth on /api/* except /health. Defaults to true in production; set false only for local dev.
  • admin_token_path — file containing the static admin token.
  • admin_token — optional inline override. Avoid in production; prefer the file.
  • allowed_origin — optional CORS allowed origin.

This token is ops-only and is never sent by browsers. Dashboard users authenticate with their per-user JWT; the admin token is for machines and operators talking to the API directly.

3 Google OAuth

Social login lives under [oauth], with one sub-table per provider. Google is the standard flow.

Configure [oauth.google]

[oauth.google]
client_id = "your-google-client-id.apps.googleusercontent.com"
client_secret_path = "/etc/qnt/google-oauth.secret"
  • client_id — leaving it empty disables Google login.
  • client_secret_path — path to the file holding the client secret.

Register the redirect URI

At the Google provider, register the callback against your dashboard_base_url. The path shape is /api/auth/<provider>/callback:

https://login.example.com/api/auth/google/callback

4 GitHub OAuth

GitHub is the second provider sub-table under [oauth]. Same shape, one wrinkle.

Configure [oauth.github]

[oauth.github]
client_id = "your-github-client-id"
client_secret_path = "/etc/qnt/github-oauth.secret"
  • client_id — leaving it empty disables GitHub login.
  • client_secret_path — path to the file holding the client secret.

GitHub OAuth uses no PKCE; Google is the standard flow.

Register the redirect URI

As with Google, the callback points at your dashboard_base_url with the /api/auth/<provider>/callback shape:

https://login.example.com/api/auth/github/callback

5 Email (Resend)

Signup sends a one-time verification code, so transactional email has to work for self-service registration to complete. The [resend] block wires it up.

Configure [resend]

[resend]
api_key_path = "/etc/qnt/resend.key"
from_address = "noreply@example.com"
from_name = "21tunnel"
  • api_key_path — path to the Resend API key file. Empty turns off the email features of /auth/*.
  • from_address — a verified sender address.
  • from_name — the display name on outgoing mail.

Without a working key, signup OTP and verification emails can't be delivered, so email-based registration won't complete.

6 Cookie + CORS knobs

Two final settings tune how the refresh cookie is set and which origin the browser API will talk to.

secure_cookies and allowed_origin

[web_auth]
secure_cookies = true

[api]
allowed_origin = "https://login.example.com"
  • secure_cookies (in [web_auth]) — defaults to true and requires HTTPS. Set it false only for local dev over plain HTTP.
  • allowed_origin (in [api]) — the CORS allowed origin for browser calls.

Leaving secure_cookies true in production is what keeps the HttpOnly refresh cookie from being sent over plain HTTP.

Next

Auth wired up. Carry on through the admin guide.