App-tokens are app-lb's own bearer credentials (applb_…), scoped to an admin tier and to particular deployments or one namespace, and these endpoints mint, list, re-scope and revoke them.
Back to the API reference. How tokens are used against deployment gates is in app-lb-auth.
All routes are CRUD tier. app-lb stores only sha256(secret). The secret appears once, in the mint response, and no endpoint reads it back.
Scope
A token carries two independent limits:
adminis its tier on this API:none,vieworadmin. Anonetoken can pass a deployment's app-token gate but can only call/whoamihere.- Reach:
deployments: ["*"]with no namespace covers the whole fleet. A list of ids covers only those.namespace: "<ns>"confines the token to one namespace, where an emptydeploymentslist means every deployment in it, now and in future.
A token scoped to specific deployments is refused the fleet-wide routes, including minting, so it cannot widen itself.
Mint a token
POST /tokens
Crate: Client::mint_token(&NewToken) -> MintedToken · Raw::mint_token(&NewToken)
| Field | Default | NewToken |
Meaning |
|---|---|---|---|
name |
required | NewToken::new(name) |
What it is for. The crate refuses a blank name before sending. |
admin |
none |
.admin(AdminScope::Admin) |
none, view or admin. |
deployments |
[] |
.for_deployments(ids), .fleet_wide() |
Ids, or ["*"]. |
namespace |
unset | .in_namespace(ns) |
Confine to one namespace. |
expires_in_secs |
never | .expires_in(duration) |
Lifetime. |
fleet |
false |
.on_all_servers() |
Control plane only: a token mirrored to every gateway. 409 on an app-lb with no gateways. |
The crate's defaults are the safe ones: a NewToken with nothing set can do nothing.
use hws::{AdminScope, NewToken};
use std::time::Duration;
let minted = lb.mint_token(&NewToken::new("ci")
.admin(AdminScope::Admin)
.in_namespace("team-a")
.expires_in(Duration::from_secs(86400))).await?;
store_somewhere(&minted.token); // the only time you will see it
Answers 201:
{"id": "7f3a9c2b1e4d", "name": "ci", "admin": "admin", "namespace": "team-a", "deployments": [],
"created_at": 1722400000, "expires_at": 1722486400, "minted_by": "token:b2c3d4e5f6a1",
"token": "applb_7f3a9c2b1e4d_…"}
MintedToken is the summary below plus token.
The token summary
TokenSummary, returned by list, get and patch. The mint response without token.
| Field | Meaning |
|---|---|
id, name |
|
admin |
none, view or admin. |
namespace, deployments |
Reach, as above. |
created_at, expires_at |
Unix seconds. expires_at absent means never. |
minted_by |
Who minted it, for tokens minted by a confined caller. |
last_used_at |
Flushed opportunistically, not per request, so a busy token can still read as unused. |
fleet, mirrored_from |
A fleet token, and the control plane it is mirrored from. A mirrored token cannot be changed here: PATCH and DELETE answer 409 naming the authority that owns it. |
List, get, re-scope and revoke
| Method and path | Crate | Does |
|---|---|---|
GET /tokens |
Client::tokens() -> Vec<TokenSummary> · Raw::tokens() |
Every token the caller can see. Expired tokens are swept first. |
GET /tokens/:id |
Client::token(id) · Raw::token(id) |
One summary. |
PATCH /tokens/:id |
Client::patch_token(id, &patch) · Raw::patch_token |
Re-scope, rename or change expiry without changing the secret, so narrowing a credential does not mean redistributing it. |
DELETE /tokens/:id |
Client::revoke_token(id) -> () |
Revoke. Answers 204 and takes effect on the next request: verification is a store lookup, not a signature check. |
Every PATCH field is optional: name, admin, namespace (null lifts the wall), deployments, and expires_at (absolute Unix seconds, null for never).
Confined callers
A confined caller may use these routes only if it administers a whole namespace: a namespace token with admin scope and no deployments list, or a federated namespace:<ns>:admin grant. app-lb then enforces:
- Mint:
namespaceis required and must be one you administer (403otherwise).expires_in_secsdefaults to, and may not exceed,APP_LB_TENANT_TOKEN_MAX_TTL_SECS(90 days by default;400past it). - List, get, revoke: only tokens confined to your namespaces. Any other id is
404 no token "<id>", the same as one that does not exist. - Patch: cannot move a token to another namespace, lift its wall or clear its expiry (
403), or push its expiry past the cap (400).
A deployment-scoped token or a view caller cannot mint.
Errors
| Status | hws::Error |
When |
|---|---|---|
400 |
Api |
Invalid body, or an expiry past the tenant cap. |
403 |
Forbidden |
Out of scope, per the rules above. |
404 |
NotFound |
No such token, or one you cannot see. |
409 |
Conflict |
A mirrored fleet token, or fleet: true on an app-lb with no gateways. |
500 |
Api |
The token file could not be written. |