Keys and scopes

A key is a credential plus a list of scopes. There is no policy language, no role hierarchy, and no inheritance to reason about — what the key can do is what is written on it.

Scope syntax

text
repositories:<action>:<project>/<repository>

<action> is pull, push, or delete. push does not imply pull; list both if the job needs both.

The repository segment accepts * as a wildcard:

Scope Grants
repositories:pull:acme/api Pull that one repository
repositories:push:acme/api Push to that one repository, and nothing else
repositories:pull:acme/* Pull every repository in acme
repositories:pull:*/* Pull every repository you have access to

Management scopes cover the API rather than the registry, and are only available on user-owned keys:

text
projects:read   projects:write   keys:read   keys:write

Project keys and user keys

A project key is bound to one project. Its scopes cannot name any other project, so even a mistake in the form cannot widen it. This is what CI should use.

A user key acts as you, across every project you can reach. It can carry management scopes. Treat it like a password.

When in doubt, mint a project key.

Minting a key

In the panel, + → New API key, or on the project itself. Pick the repository, tick the actions, choose an expiry. The key is shown once and stored as a one-way hash — we cannot show it to you again, and neither can anyone who obtains our database.

Expiry

Every key can carry an expiry. Set one. A key with no expiry is a key you will still have in three years, on a laptop belonging to someone who left.

Expiry is enforced at token issue, so an expired key stops working on the next docker login and on the next token refresh during a long push.

Revoking

Revoke in the panel. Registry access tokens are short-lived, so a revoked key stops working within the token lifetime — seconds to a few minutes — rather than immediately mid-request. For an urgent leak, revoke the key and rotate anything it could have pushed.

Worked example: a CI key

The build job for acme/api needs to push that repository and nothing else:

text
repositories:push:acme/api

Attempting anything outside that fails at the registry, not in a review:

bash
sudo docker push lazer.sh/acme/api:$GIT_SHA
# 2f9a1c: Pushed

sudo docker pull lazer.sh/acme/billing:latest
# Error response from daemon: denied: insufficient scope