Skip to content

Access Control


In, users authenticate either using an SSH key, or using an auth token. When using the Nix client to interact with (running builds or using it as a Nix store), you can either use SSH keys or auth tokens for authentication. When using the's HTTP API you must use auth tokens.

Both SSH keys and auth tokens have their strengths and weaknesses, and you must yourself find out what fits your use case best. As a general advice, sticking with SSH keys is usually easier until you need the additional flexibility that auth tokens provide.

Using SSH Keys

SSH keys are the default authentication method in When you create an account, you must provide a public SSH key which is tied to your account. After account creation, you can add or remove SSH keys as you like, using the administration shell.

If you're a team, you can add each team member's personal SSH key to your account. That way, all members can use as a remote builder and, since they all share the same account, they can reuse build inputs and outputs.

On your Nix clients, you set up your SSH config to use the corresponding keys when connecting to This does not differ from using regular remote Nix builders, or using SSH in general. You can see step-by-step instructions in the Getting Started guide.

Using Auth Tokens

To be able to use an auth token for authentication, you first need to create one using the administration shell. When creating a token you must assign it an expiration date and a set of permissions. The permissions are decribed in more detail below. If you want to create an auth token that should be valid for 30 days and only be allowed to query, pull and push store paths from (not start any builds), you invoke the command like this:> tokens create --ttl-seconds 2592000 -p store:read -p store:write

Revoking Auth Tokens

Auth tokens should be treated as secrets and stored in a sensible way. If you ever need to revoke a token, you can run tokens revoke in the administration shell, with the token you want to revoke as its only argument:> tokens revoke EpYCCqsBCgxhY2NvdW50OnJlYWQKDWFjY291bnQ6d3JpdGUKCmJ1aWxkOnJlYWQKC2J1aWxkOndyaXRlCgpzdG9yZTpyZWFkCgtzdG9yZTp3cml0ZRgDIggKBggHEgIQBCImCiQIBBIgOh4KAxiACAoDGIEICgMYgggKAxiDCAoDGIQICgMYhQgyJgokCgIIGxIGCAUSAggFGhYKBAoCCAUKCAoGIJesxKkGCgQaAggAEiQIABIg_vub4yDKe3X_ZYwW1OPNcQhBzm9aCFq87JuDioh3Iz4aQF-D8EMnLkyTnuqCGc7MLK7HOLcPAGNViWZc5jeQPt83SbY1K9Hot5GHelpHwLVQAUmlJwmkIgy8mkCTYZLssA8atQEKSwoKb3BlcmF0aW9ucwoDb3BzGAMyNgo0CgIIGxIICIYIEgMIhwgaJAoFCgMIhwgKCQoHOgUKAxiBCAoEGgIIBQoEEgIIAQoEEgIIABIkCAASIO--7mSwDxqhhL4O6K6DtqYDfKJUH0JvYM2OErB0a2jhGkBWHj5nDRyU7w8oItWMd5A2c5SuJy-3tSqNXmPRJvuz9rfhu4BNF2YIJ9OkuMg6Ob3onzT5OlZLdYh94GiN_eIPIiIKIO6HoXcsjkDS8eE1nPsigbmq_YfTUykRL9JWqWHL9Ftj
The provided token has been revoked

When revoked, the token will no longer work, but existing builds or operations that have been started using the token will not be cancelled. You can also revoke attenuated tokens, in which case only the attenuated token will be revoked, not the original token. On the other hand, if you revoke a token, any attenuated tokens that originated from the revoked token will also be revoked automatically.

Accessing the HTTP API using an Auth Token

To access the HTTP API using an auth token, you must provide an Authorization header in your requests. The header value should be set to Bearer: <TOKEN>.

Using the Nix Client with an Auth Token

To make the Nix client use an auth token when performing builds and other store operations in, you need some specific SSH configuration:

  User authtoken
  PreferredAuthentications none
  SetEnv token=<TOKEN>
  ServerAliveInterval 60
  IPQoS throughput

The important settings above are User, PreferredAuthentications and SetEnv. Setting up your SSH client configuration like above bypasses the normal SSH authentication, and sends the auth token to in the environment passed along when the SSH connection is initiated. When gets such a connection request, it will not look at the public SSH key at all. Instead it will decode and verify the auth token provided in the token SSH environment variable. If the token is valid, will use the account embedded inside the token to perform authentication.


Once authentication is done, and has figured out which account a request belongs to, an authorization phase starts. Authorization in is centered around a few simple permissions, but there is also support for implementing more complex access policies, if auth tokens are used. We will demonstrate this below.

Authorization with SSH Keys

When an SSH key is used for authenticating to, the permissions that take effect are decided by the value of the default-permissions setting. This means you can lock down specific SSH keys to a specific set of permissions, but it is not possible to implement more complex access policies. In most cases, assigning permissions to SSH keys is perfectly adequate, though.

Authorization with Auth Tokens

Each auth token has a set of permissions built-in. Those permissions are assigned during token creation, as shown above.

In addition to building-in permissions, you can further weaken the permissions of tokens through an operation called attenuation. The key advantage of this operation is that it does not need involvement of at all. You would first create one or more root tokens with wide capabilities, through the administration shell. Then you could create much less capable sub-tokens by attenuation, completely offline from This functionality is possible since auth tokens are Biscuit tokens.

Token Attenuation

To perform attenuation, you need to use the biscuit-cli command line tool which provides the biscuit executable. This tool is available in nixpkgs. Lets walk through the complete process of creating and attenuating an auth token.

We create a new token, with all permissions enabled and an expiration time of 30 days:> tokens create --ttl-seconds 2592000 --all-permissions

The token will expire at:
  2023-10-19 11:42:14Z

The token has the following permissions:

We store the token in a file to make it easier to handle with the biscuit program:

$ echo >token EpYCCqsBCgxhY2NvdW50OnJlYWQKDWFjY291bnQ6d3JpdGUKCmJ1aWxkOnJlYWQKC2J1aWxkOndyaXRlCgpzdG9yZTpyZWFkCgtzdG9yZTp3cml0ZRgDIggKBggHEgIQBCImCiQIBBIgOh4KAxiACAoDGIEICgMYgggKAxiDCAoDGIQICgMYhQgyJgokCgIIGxIGCAUSAggFGhYKBAoCCAUKCAoGIJesxKkGCgQaAggAEiQIABIg_vub4yDKe3X_ZYwW1OPNcQhBzm9aCFq87JuDioh3Iz4aQF-D8EMnLkyTnuqCGc7MLK7HOLcPAGNViWZc5jeQPt83SbY1K9Hot5GHelpHwLVQAUmlJwmkIgy8mkCTYZLssA8iIgogTCydeLS2B7m8L6-e5tWH0PfdDimN6JXBvDN-WP0l20Q=

We can use biscuit inspect to take a look at what's inside the token:

$ biscuit inspect ./token

Authority block:
== Datalog ==
right(["account:read", "account:write", "build:read", "build:write", "store:read", "store:write"]);
check if time($time), $time < 2023-10-19T11:42:15Z;

== Revocation id ==


🙈 Public key check skipped 🔑
🙈 Datalog check skipped 🛡️

We can see that the token contains an owner account identifier, a set of permissions and an expiration check. The Authority block is using the Biscuit policy language, and that is also what you'll use to attenuate the token.

Lets attenuate the token now, and create a new token that can't be used to perform any changes to the account configuration using the administration shell:

$ biscuit attenuate ./token --block 'check if operations($ops), !($ops.contains(["account:write"]))'


If we use the resulting token when accessing the administration shell, then we can not run any operations that require write access:> tokens create --all-permissions
Authorization failed. You might lack one or more of these permissions:

An attenuated token can be further attenuated into new tokens, if one wishes to do so.

You may wonder where the operations() and time() constructs, that were used in the Biscuit policies above, came from. These are Biscuit facts and they are added by during the authorization phase. You can make use of these facts to define your own access policies. Below follows the complete list of facts that you can use when authoring access policies.

It is strongly recommended to read through the Biscuit documentation to gain a deeper understanding of how Biscuit tokens work and how they can be used.

Available Permissions

The permissions available in are the following:


Grants read-only access to the administration shell.


Grants read and write access to the administration shell. A user holding this permission can make any changes to the account configuration. This includes adding new public SSH keys and generating new auth tokens.


Grants read access to build information, including build logs.


Grants the ability to run builds on In practice, to be able to run Nix builds, you also need the build:read, store:read and store:write permissions since Nix performs multiple different store operations when running a build.


Grants read-only access to the Nix store. A user holding this permission can download build outputs or any other store paths from (as long as the paths belong to the account).


Grants the ability to upload paths to

Available Biscuit Facts

These are the facts available to your Biscuit policies. It is expected that this list of available facts will grow in the future, to make it possible to define a wide range of access policies.


Type: (integer)

This fact will be set for any request that concerns a specific build. It can be used to restrict a token to only access information about a specific build id.


Type: ([string])

The set of the permissions necessary to allow the request to be performed. This fact can be used to restrict the permissions of a token to only a subset of its initially assigned permissions.

Example attenuation block:

check if operations($ops), !($ops.contains(["account:write"]));


Type: (string, *)

This fact represents a subset of the settings that was in effect during the request.

The settings available in this fact are listed below.


Type: [string]

You can use this fact to restrict a token to only be allowed to trust a specific set of public signing keys. If such a token is used, there is no way for a user to use any store paths signed by other keys as build inputs, or fetch such store paths . Here is an example attenuation block that achieves this:

check if
  setting("trusted-public-keys", $tpks),
    [ ""
    , ""

Note that your build signing key and your upload signing key are implicitly included in the trusted-public-keys setting, and will also be part of the setting("trusted-public-keys", $tpks) fact. You may therefore need to include them in your policy.


Type: (date)

The current date and time, in RFC 3339 format. This fact can be used to create tokens with a shorter expiration time.

Example attenuation block:

check if time($time), $time <= 2022-03-30T20:00:00Z;