Add access that signs in with a private key, use its key file from the CLI, scripts and your own code, and rotate or revoke it
A script, CI job or agent acts in a workspace as its own principal. It holds the role editor or
viewer and signs in with a private key instead of a login. The key stays on the system that holds the
key file and never travels.
You need the CLI installed and signed in and the workspace's admin role.
Add access that signs in with a private key
Add a short description so the access list shows what the key is for:
nevia access add --private-key --description "deploys from CI"The key file is saved as ~/.config/nevia/keys/<key>.json, next to the CLI's own login, readable only by
you. To save it elsewhere, pass a folder or file to --out, or pass --out - to print it:
nevia access add --private-key --description "deploys from CI" --out ./secretsNevia keeps no copy of the private key, so this file is the only one. Store it where your automation reads
secrets, such as a CI secret, and delete the local copy. If you lose it, issue a new key with keys rotate.
A private key defaults to editor, which can create and manage computers and databases. A login added without
a role starts as a viewer. Pass --role viewer for read-only access. A private key is never an admin; see
Workspace permissions.
The console manages the same access. Open the workspace's Settings, where one Access table lists logins and private keys. Add access creates a private key and downloads its key file.
Use it from the CLI
Point NEVIA_KEY_FILE at the file, and every command runs as that principal:
export NEVIA_KEY_FILE=~/.config/nevia/keys/<key>.json
nevia whoami
nevia lswhoami shows the principal, (private key), and the one workspace it belongs to. The CLI signs in with the key
and holds a short-lived access token in memory for the process. It signs in again when the token expires. Nothing
is written to disk.
A key file outranks a saved nevia login. Unset NEVIA_KEY_FILE to act as yourself again. To use the key file
without setting the variable each time, save its path:
nevia auth use-key-file ~/.config/nevia/keys/<key>.json --local # this project: writes key_file into .nevia.toml
nevia auth use-key-file ~/.config/nevia/keys/<key>.json # everywhere: writes it into your config
nevia auth use-key-file --unset # back to your own loginOnly the path is saved. The key stays in its file. nevia whoami and nevia config show which identity is in
effect, and NEVIA_KEY_FILE still wins in the current shell.
Use it from scripts and your own code
For curl or another HTTP client, have the CLI produce the token:
export NEVIA_KEY_FILE=~/.config/nevia/keys/<key>.json
TOKEN=$(nevia auth access-token)
curl https://api.nevia.cloud/v1/me -H "Authorization: Bearer $TOKEN"The token lasts 15 minutes. Run the command again for a new one. Routes are in the API reference.
Without the CLI, your code repeats what the CLI does: sign a short JWT with the key file's private key and post it
to the file's token_url for an access token. The file holds everything this takes: subject, key_id,
audience, token_url and private_key, plus principal, the ID that nevia access uses for the key's
principal. In Node, using only the standard library:
import { readFileSync } from "node:fs";
import { randomUUID, sign } from "node:crypto";
const key = JSON.parse(readFileSync(process.env.NEVIA_KEY_FILE, "utf8"));
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
const now = Math.floor(Date.now() / 1000);
const header = encode({ alg: "RS256", typ: "JWT", kid: key.key_id });
const payload = encode({
iss: key.subject,
sub: key.subject,
aud: key.audience,
iat: now,
exp: now + 60,
jti: randomUUID(),
});
const input = `${header}.${payload}`;
const signature = sign("RSA-SHA256", Buffer.from(input), key.private_key).toString("base64url");
const response = await fetch(key.token_url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ assertion: `${input}.${signature}` }),
});
const { access_token } = await response.json();Send access_token as the Bearer token on every call. Any JWT library can do the signing in a few lines. Sign
in again when a request returns 401.
Reuse the token for its 15 minutes instead of exchanging before every call. The token route allows 30 exchanges
a minute per client address and returns 429 with a Retry-After header beyond that. A refused exchange is a
plain 401 with no reason. Either the key file is wrong, or its key was revoked, expired or replaced.
List, change and remove access
Logins and private keys share one list, which also shows how each signs in:
nevia access ls
nevia access set <principal> --role viewer
nevia access rm <principal>Removing private-key access deletes the principal and its key, and its access tokens stop working at once. A role change applies from the principal's next request.
Rotate the key
A principal has one key at a time, addressed through the principal, so there is no key id to type. Rotating issues the next key and revokes the current one in the same step, so there is never a moment with two keys or with none:
nevia access keys rotate <principal>Keys expire 90 days after creation unless you pass --expires-in, which accepts 15m to 90d. nevia access ls
shows each principal's key state in the STATUS column, active, expired or revoked, and
nevia access get <principal> adds the expiry.
Rotating or revoking takes effect at once. The old key can no longer sign in, and every access token the principal
holds stops working on its next request. With the new key file, the CLI signs in again on its own, and your own
code should sign in again on a 401. To cut off access without replacing the key, revoke it. The principal keeps
its role but cannot sign in until the next keys rotate:
nevia access keys rm <principal>Bring your own key
If keys must be generated on your own systems, pass the public half and keep the private key:
openssl genrsa -out ci.pem 2048 && openssl rsa -in ci.pem -pubout -out ci.pub
nevia access add --private-key --own-key ci.pub # the .pub, never ci.pem: the CLI and the API refuse a private key hereThe resulting key file has no private key. Add yours as private_key before pointing NEVIA_KEY_FILE at it.
Nevia accepts RSA keys of 2048 bits or more.