Auth, tokens & the admin API
Two separate doors: a per-deployment sign-in gate on the data plane, and a two-tier gate on the admin API. App-tokens open both, each within their scope.
The sign-in gate
Any deployment can put a gate in front of everything it serves. It runs in the proxy, before a backend is chosen, so the application behind it needs to know nothing about it:
"auth": {
"provider": ["google", "app-token"],
"client_id": "…apps.googleusercontent.com",
"client_secret": { "secret": "google", "key": "client_secret" },
"allowed_domains": ["example.com"],
"public_paths": ["/healthz", "/api/webhook"],
"forward_identity": true
}
- Providers are alternatives.
googleadmits a person with a session (domain matching uses Google'shdclaim, so a personal Gmail with a lookalike address does not pass);app-tokenadmits a program presentingAuthorization: Bearer applb_…. A gate listing both admits either. The default is Google-only — a gate that should accept programs must say"app-token"explicitly. public_pathsare prefix-matched and skip the gate entirely — health checks and webhooks.forward_identityaddsx-auth-request-email/-userupstream for signed-in people, and app-lb strips those header names from inbound requests so nobody can forge them. A token is not a person: token-admitted requests carry no identity headers.
401 {"error":"authentication required"} from app-lb itself — the
request never reaches the deployment and never wakes it. If a scale-from-zero deployment
is "not waking up" for programmatic callers, check the gate's provider list
and public_paths first.App-tokens
The credential a program carries. Minted through the admin API (or
serverctl token mint), scoped, optionally expiring, revocable instantly.
The shape is applb_<id>_<secret> — greppable on purpose, and only
the secret's SHA-256 is stored, so the token file is not a fleet compromise and there is
no "reveal" endpoint.
| Scope axis | Values | Governs |
|---|---|---|
admin | none / view / admin |
What the token may do on the admin API. none still passes data-plane
gates — the shape for a token handed to an application. |
deployments | ids, or ["*"] |
Which deployments it may touch — both the per-deployment admin routes
(exec, shell, scale, delete) and data-plane gates. |
namespace | one namespace | A wall around everything: see Namespaces. |
# a token for CI to reach one deployment's data plane, expiring in a week
serverctl token mint ci-secrets -d secret-service --expires-in 168 -q
# a fleet-wide operator token
serverctl token mint ops --admin admin --all-deployments
serverctl token set re-scopes a token without changing its secret;
serverctl token revoke takes effect on the next request.
The admin API's two tiers
Unset, the admin listener is open (it binds loopback). Setting
APP_LB_DASHBOARD_PASSWORD gates the view tier — the directory,
/metrics, the dashboard, /security, /feeds. Setting
APP_LB_ADMIN_AUTH=1 extends the gate to the CRUD tier — everything
that changes state or reads specs.
Scoped tokens are narrowed rather than refused where that makes sense: a token scoped
to one deployment sees a directory, /metrics and /security
filtered to that deployment, and is refused fleet-wide routes (creating deployments,
reading secrets, minting tokens) outright — a token cannot widen itself.
Block rules and detection
Operators can arm block rules (by client IP, host, path, method, user-agent) that the
proxy enforces with an anonymous 403 — the refusal deliberately does not say which
property matched. A built-in analyzer (on unless APP_LB_SIEM=0) watches
access and auth failures, folds repeats, and raises findings on
GET /security with suggested rules.