surrealctl accepts two kinds of credential. This page explains what each one is good for, how to create and inspect them, and how to hand one to a CI job. It is for anyone setting up their own machine, and for engineers wiring surrealctl into a pipeline.
Two kinds of credential
The two credentials differ in what they can write, not in what they can reach.
| Credential | How you get it | Use it for | Cloud writes |
|---|---|---|---|
| Login session | surrealctl auth login | Everyday work at a terminal | Yes |
| Personal access token | surrealctl token create, or SurrealDB Studio | Scripts, CI jobs, shared runners | No |
A login session is the credential that can do everything. A personal access token reads the control plane and cannot change it, which is what makes it safe to leave in a CI secret store.
Login sessions
surrealctl auth login signs in through your identity provider and stores a refresh token plus one access token per audience. See Install for the three sign-in flows and when each is used.
Four commands inspect and maintain the session.
surrealctl auth status # which credential is in use, read from disk
surrealctl auth status --verify # the same, plus one request to check it
surrealctl auth refresh # renew now, rather than at the next request
surrealctl auth logout # remove this profile's stored credentialsauth status and auth scopes touch no network, so they work on a plane, in a container with no egress, and in a CI job that is about to fail for another reason. whoami is the command that asks the API who you are.
auth status also warns when this machine's clock is more than five minutes out of step with the API, because a wrong clock produces authentication failures that look like a rejected credential.
auth logout revokes the refresh token at the identity provider on a best-effort basis. A failed revocation prints a warning and still removes the credential from this machine. Logging out when nothing is signed in succeeds and still prints a document, so a teardown script does not fail on its second run.
auth scopes returns an empty list for a login session. Identity scopes are not the authorisation model for a session — your role in the organisation is. Run surrealctl org permissions to see what you may do.
Personal access tokens
A personal access token is a long-lived string beginning sdbp_. Create one with a login session in place, then hand it to whatever needs it.
surrealctl token scopes
surrealctl token create "ci-nightly-report" --scope read:cloud --expires-in 90 | tail -1Four things about token create are worth knowing before you run it.
The secret is returned once. It is the last line on stdout, so
| tail -1is the whole capture recipe. Nothing can recover it later.It refuses to print a secret to a terminal. Pipe the output, or pass
--revealif you meant to read it. The check happens before the token is minted, so a forgotten flag never costs you a credential nobody can use.It is the one command with no
--jsonform. The secret can neither go inside the document nor share stdout with it, so--jsonis a usage error here.A token with no
--scopeis permitted nothing. Grant what the job needs and no more.
List and revoke tokens by id or label.
surrealctl token list
surrealctl token delete ci-nightly-report Revocation is not instant. A revoked token can continue to work for several minutes while the platform's exchange cache expires. Rotate a leaked token and then confirm with surrealctl token list.
What a personal access token cannot do
It cannot manage personal access tokens. All four token verbs — list, create, delete, and scopes — refuse a personal access token. The refusal is local, costs no requests, and exits 4. A leaked token must not be able to mint its own replacements, nor revoke the one you would use to clean up after it.
Managing personal access tokens needs an interactive login session, so nothing was sent.
Sign in with: surrealctl auth loginIt cannot write to the control plane. The API refuses a personal access token on every mutating route, whatever scopes the token carries. instance create, instance delete, org update, and team invite all need a login session. Reads work normally.
It cannot exceed its scopes. A missing scope produces a 403 naming the scope, and exits 4.
Everything else works: org list, instance list, instance logs, auth status, auth scopes, and auth logout.
Choosing between them
At a terminal, sign in. A login session covers every command and renews itself.
In CI, use a token for anything that only reads: nightly usage reports, drift checks, dashboards, alerting on instance state.
For unattended writes, use a login session on a dedicated machine account, with the credential file mounted into the job. Scope the account's role in the organisation to what the pipeline needs.
Supplying a token
Two flags, each with an environment variable, and none of them writes the token to disk.
SURREALCTL_TOKEN is the usual choice in CI, because the environment is where a secret store puts things.
export SURREALCTL_TOKEN="$(cat /run/secrets/surrealctl)"
surrealctl instance list --json--token has no short form on purpose: -t reads as --type on the instance commands, and a credential in argv is visible through ps and lands in shell history.
A credential supplied this way lasts for the one invocation. The only command that stores a token is auth login --with-token, which reads it from stdin and remembers it for the profile.
surrealctl auth login --with-token --label laptop < ~/token.txtScopes
Five scopes exist: read:cloud, write:cloud-instances, write:cloud-organization, write:cloud-billing, and write:cloud-spectron.
Two commands report on them, and they answer different questions.
surrealctl token scopes— the catalogue of scopes a token can be granted. This one is the menu.surrealctl auth scopes— what the credential in your hand carries. This one is the receipt.
Profiles
A profile bundles a credential, an API base URL, and configuration under one name. Use profiles to keep a work account and a personal account apart, or production and staging.
surrealctl context use staging # switch the active profile
surrealctl --profile staging auth login # sign that profile in
surrealctl context list # every profile, and which is active
surrealctl context show # what this invocation resolved, and whyEach profile stores its own credential, so switching profiles switches identity. surrealctl org use works within a profile and remembers an organisation; neither command is expressible as the other.
Where credentials are stored
Credentials live in credentials.json, mode 0600, beside config.toml in the configuration directory described in Install. config.toml holds no secrets and is safe to commit.
surrealctl uses a file rather than the operating system keyring, deliberately. macOS keychain access is bound to the requesting binary's code signature, so a CLI re-prompts for permission after every upgrade — unanswerable inside a CI job. Headless Linux and containers have no secret service at all. There is no keyring backend.
Credential writes take an exclusive advisory lock and re-read under it, so two surrealctl processes racing to renew the same session cannot invalidate each other's tokens. Readers take no lock. If a run reports that another surrealctl is updating credentials, retry in a moment.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
Exit 3 on any command | No credential, or one that expired and could not be renewed | surrealctl auth login, or surrealctl auth refresh |
Exit 4 on a token command | A personal access token is in use | Sign in with a login session |
Exit 4 on a write | Insufficient scope, insufficient role, or a token where a session is needed | Check surrealctl org permissions; use a login session for writes |
auth status warns about clock skew | This machine's clock is more than five minutes out | Fix time synchronisation on the host |
Exit 30 | An interactive sign-in did not complete | Run surrealctl auth login again; add --flow device when there is no browser |
Exit 11 | The credential store could not be read or written | Check the permissions and free space on the configuration directory |
A refusal to read credentials.json | The file is readable by group or other | chmod 600 on the path in the message |
To see which requests are being made and which configuration layer won each value, add --debug. Credentials are logged as a digest, never in full.
Next steps
Instances — everyday instance work.
Scripting — exit codes,
--json, and unattended runs.Members and roles — what a role permits in an organisation.