Authentication and tokens
An access token lets a script, agent, or CI consumer act against neosource over the API, git-over-HTTPS, or the container registry without a browser session. Every token is fine-grained and expiring by default: it carries a set of scopes and an optional resource restriction, and it can only ever do less than the account that created it.
What a token is
Section titled “What a token is”A token is an attenuated bearer credential. Authorization for a token-backed request is the intersection of the two:
effective = account-permissions ∩ token-grantThe account’s normal access — workspace role, repo visibility, and per-repo grants — sets the ceiling; the token’s grant narrows it. A token can never widen its owner’s access, and a leaked token exposes only the slice of authority you granted it, only until it expires.
Tokens are stored hashed at rest. The plaintext you copy once at creation has
the shape prefix_payload_checksum (a CRC32 check segment, GitHub-style, so
secret scanners can validate a leaked candidate offline). The prefix tells you
where a token came from:
| prefix | kind |
|---|---|
nsp_ |
personal access token — owned by your account |
nso_ |
workspace / org access token — owned by a service account |
nsc_ |
CI job token — minted automatically per job, not by you |
Creating a personal token
Section titled “Creating a personal token”Create tokens in the web UI at /settings/developer, which offers a scope picker, a resource picker (workspaces / repos), and an expiry choice. The same page manages your SSH keys.
Creating or rotating a token is a sensitive action, so it is sudo-gated (see Rotating & revoking) — and a token can never mint another token.
Scopes
Section titled “Scopes”A scope is a colon-separated permission. Scopes do not nest, except through
an explicit *. A held scope matches a requested permission only when every
segment is equal and both have the same number of segments — or when the held
scope reaches a *, which matches everything below it.
The broad repo: scopes are the one deliberate exception to that rule:
The v1 grantable catalog. “Covers” means exactly what the scope authorizes — nothing more, and no parent/child inheritance between rows:
| scope | covers |
|---|---|
repo:read |
read repo metadata over the API, and git clone / git fetch |
repo:write |
write repo content over the API, and git push / git fetch. Not repo:read |
repo:admin |
repo settings, grants, webhooks, and git push / git fetch. Not read or write |
repo:* |
all of the above |
repo:registry:read |
pull images from a repo’s container registry namespace |
repo:registry:write |
push images to it. Not delete, and not git push |
repo:registry:delete |
delete manifests and tags. Not implied by repo:registry:write |
workspace:read |
list a workspace’s repos, read its settings and CI-secret names |
workspace:write |
create and modify repos and workspace content, set/delete CI secrets, register a self-hosted runner |
workspace:registry:read |
pull any of the workspace’s images |
workspace:registry:write |
push any of them. Again not delete, and not git push |
workspace:* |
full workspace administration |
team:read / team:write |
read / manage teams and their rosters |
user:read / user:write |
profile, SSH-key list/manage, notifications |
user:* |
all user-owned surfaces |
* |
everything the owner can do |
Grant the narrowest set that gets the job done. An agent that only pushes to
one repo wants repo:write restricted to that repo (see below) — not
repo:*.
site:admin also exists in the grammar, but it is deliberately absent from the
scope picker — the site-admin control plane lives on its own origin and is not
reachable with a token minted here.
Resource restriction
Section titled “Resource restriction”Leaving the resource picker empty makes the token unrestricted — it can reach any workspace or repo its owner can, within the granted scopes. Selecting specific workspaces or repos locks the token to them: a request against anything outside the list is denied.
Both gates apply, scope first and resource lock second. A repo-locked token is refused a repo outside its lock even when that repo is public, so the lock is a real boundary rather than a filter over what the account can already see.
Expiry
Section titled “Expiry”Every token expires. The choices are 30 / 90 / 180 / 365 days (default 90), with a hard cap of 400 days — there is deliberately no “never”. A short-lived credential is the whole point of handing one to an automated process.
Rotating & revoking
Section titled “Rotating & revoking”Rotate issues a new secret for the same token — the token id, scopes, resources, and label stay put, but the old secret dies immediately. Rotation cannot be performed by the token itself, so a leaked token can’t quietly extend its own life. Update the consumer with the new secret.
Rotation and creation are sensitive actions and normally require a fresh (sudo) session; an account that has no step-up credential set up yet (no password, enabled TOTP, or passkey) is allowed through on its first credential instead.
Revoke kills a token outright. Revocation and rotation take effect within seconds across all nodes.
Workspace (organization) tokens
Section titled “Workspace (organization) tokens”Organizations need keys that aren’t tied to any one person’s account — CI for an org repo, a shared deploy bot, an import job. A workspace token is owned by a service account: an identity that has no password and can never log in, holding a membership in the workspace at a role no higher than the creator’s and a token resource-locked to that workspace.
Create one under Organization settings → Tokens. The minted secret carries the
nso_ prefix. Because the service account is a normal workspace member under
the hood, the token flows through the same authz, membership, and audit
machinery as a human’s access — it just can’t exceed the workspace, its role,
or its scopes.
CI job tokens
Section titled “CI job tokens”Tokens with the nsc_ prefix are minted automatically for each workflow job
and scoped to that job’s repo for its lifetime. They are a separate mechanism
from personal and workspace tokens — you don’t create or manage them, and this
page’s create/rotate flows don’t apply to them.
See also
Section titled “See also”- Getting started — using a token to push over HTTPS, and adding an SSH key.
- Container registry —
docker login, and which registry scope a publish credential should hold. - The neosource access model (workspace role → repo visibility → per-repo grants) — how account permissions are resolved before the token intersection applies.