feat: Give API keys their own page and group the sidebar

The token management card sat on /settings, which is owner|admin
throughout, so it hid a capability every member already had: the API has
never required a role to mint or revoke your own key. It is now the
/api-keys page, reachable at every role, with the instance-wide lifetime
cap left behind on /settings because that is policy rather than one
person's credentials — and that split is what lets the page be ungated.

The sidebar gains groups: Fleet, Access, Automation, Instance, each with a
small-caps heading and a rule above it. Grouping is by what the operator
is doing rather than by which service answers, so SSH keys, secrets and
API keys sit together as credentials. A group whose every item is
admin-only disappears whole for a member; a labelled section with nothing
under it reads as a failure rather than a restriction.

The UI says keys while the collection, prefix and routes still say tokens.
Renaming a published endpoint to match a nav label would break every
script already written against it.
This commit is contained in:
2026-08-13 08:54:42 +00:00
parent 63984af250
commit 99f5e799a4
+5 -3
View File
@@ -9,8 +9,10 @@ needs something it can hold onto instead — an API token.
## Creating one
Settings → **Access** → API tokens. Give it a name, a role (owner, admin or
member) and one or more scopes, and optionally an expiry. The value is shown
**API Keys**, in the Access group of the sidebar. The page is reachable at
every role: any member may create and revoke their own keys, and owner and
admin additionally see every key in the instance. Give it a name, a role
(owner, admin or member) and one or more scopes, and optionally an expiry. The value is shown
once, in full, immediately after creation:
```
@@ -54,7 +56,7 @@ independent of its owner.
## Expiry
An expiry is optional on a token you create. An instance can set a
**maximum token lifetime** (Settings → Access) that caps how far out a new
**maximum key lifetime** (Settings → Integrations) that caps how far out a new
token's expiry may be set; when that cap is in place, a token with no expiry
at all is refused, so there is no way to route around the policy by leaving
the field blank.