Documentation

Log in and push with a scoped token

In this tutorial you turn a hosted OCI index into one that validates docker login and hands out repository-scoped tokens. You give the index a token that may push under team/*, log in with it, push an image the scope covers, and watch a push outside the scope get refused. It takes about ten minutes and builds on getting started.

Configure the realm

The realm needs a signing key and an index with a scoped credential. Save this as peryx.toml:

# peryx.toml
host = "127.0.0.1"
port = 4433
data_dir = "peryx-data"

[auth]
signing_key = "change-me-to-a-long-random-string"

[[index]]
name = "team"
route = "team"
ecosystem = "oci"
hosted = true

[[index.access_token]]
name = "ci"
secret = "ci-secret"
projects = ["team/*"]
actions = ["read", "write"]

The [auth] signing_key turns the token realm on. The [[index.access_token]] names one credential, ci, that may read and write any repository matching team/*. In production keep the key and the secret in files with signing_key_file and secret_file; see keep a secret out of the config file.

Start the server:

$ peryx serve --config peryx.toml

Log in

Point docker login at the registry. The username is ignored; the password is the token secret.

$ docker login localhost:4433 --username ci --password ci-secret
Login Succeeded

Behind that one command, docker probes GET /v2/, reads the WWW-Authenticate: Bearer challenge, requests a token from /v2/token with your credentials, and retries the probe with the token. A wrong password stops at the token request with a 401, so the login fails instead of succeeding against nothing. Try it:

$ docker login localhost:4433 --username ci --password wrong
Error response from daemon: login attempt ... failed with status: 401 Unauthorized

Push inside the scope

Tag an image under a team/* repository and push it. Docker requests a token scoped to that push, and peryx grants it because team/* covers team/app.

$ docker pull alpine:3.20
$ docker tag alpine:3.20 localhost:4433/team/app:1.0
$ docker push localhost:4433/team/app:1.0

The push succeeds. Pull it back to confirm the round trip:

$ docker pull localhost:4433/team/app:1.0

Watch a push outside the scope get refused

Now tag the same image under a repository the glob does not cover and push it.

$ docker tag alpine:3.20 localhost:4433/other/app:1.0
$ docker push localhost:4433/other/app:1.0
...
denied: token does not grant this action

The token endpoint mints a token whose access to other/app is empty, and the resource route refuses it with 401 insufficient_scope. The credential is valid, so docker does not retry; it reports the denial. To push there, widen the token's projects or add a second token.

What you built

One hosted index that validates logins and scopes a token to a set of repositories. To make its reads private too, set anonymous_read = false on the index and give the token a read grant; see make an OCI index private. For the wire details, read token authentication.

On this page