CI tokens

Organization-scoped, expiring credentials for CI pipelines and scripts. What a CI token can do, how to create, use, rotate and revoke one, and how it is kept safe.

Not verified yet

A CI token is a credential your organization gives to automation: a GitHub Actions workflow, a GitLab pipeline, a cron job on a server. It replaces the old pattern of storing a person's refresh token in CI.

  • It acts for the organization, not for the person who created it, so it keeps working when that person leaves.
  • It can do exactly what its scopes allow, nothing more, whatever role its creator has.
  • It always expires (1 hour to 366 days) and can be revoked at any time.
  • Its secret is shown once. Norcube stores only a hash.

Create a token

In the dashboard, open IAM › CI tokens and click Create token:

  1. Name it after where it runs (GitHub Actions deploy), so you know what breaks if you revoke it. Names are unique among an organization's active tokens.
  2. Pick scopes. Grant only what the job needs. You can grant a scope only if you hold every permission it covers across the whole organization; scopes you can't grant are shown disabled.
  3. Pick an expiry: 30 days, 90 days, 1 year, or a date.
  4. Copy the token (nrc_ci_…) and store it in your CI's secret store. It is not shown again.

Creating, listing and revoking tokens need the IAM permissions auth:CreateCIToken, auth:ListCITokens and auth:RevokeCIToken. Organization owners hold them; grant them to others with a policy.

Scopes

ScopeAllows
backup.readRead data sources, backup jobs, restore checks and policies.
backup.writeStart a backup of a data source, plus everything backup.read allows.
backup.verifyRun a restore check (into a throwaway database), plus everything backup.read allows.
domainradar.checkRun single and bulk domain checks (metered) and read results.
langsync.readRead namespaces, terms with translations, languages and the glossary.
langsync.syncRun nrc langsync sync (push source terms, pull translations), plus LangSync reads.
langsync.writeAdd and edit terms, write translations, translate missing values, plus LangSync reads.
billing.readRead billing status, usage, estimates, invoices, credits and the subscription.
cms.readRead CMS sites, content files and their history, schema and media.
cms.writeEdit and delete content files and upload or manage media, plus everything cms.read allows.

Anything a scope doesn't list is refused with 403 INSUFFICIENT_SCOPE, including every product's settings and administration: a CI token can't create namespaces, change backup policies, edit payment methods or manage members. Metered actions (checks, AI translation) count towards your plan like any other use.

Use a token

With the Norcube CLI

Set the token as NORCUBE_TOKEN and the CLI (0.18 and later) uses it instead of a sign-in:

# .github/workflows/translations.yml
- name: Sync translations
  run: nrc langsync sync
  env:
    NORCUBE_TOKEN: ${{ secrets.NORCUBE_TOKEN }}

Over HTTP

Exchange the token for a short-lived access token to one product, then call that product's API with it. The exchange follows OAuth 2.0 Token Exchange (RFC 8693):

curl -s https://auth.api.norcube.com/auth/ci/token \
  -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
  -d subject_token_type=urn:norcube:params:oauth:token-type:ci-token \
  -d subject_token="$NORCUBE_TOKEN" \
  -d audience=langsync-api

You can also send the CI token as Authorization: Bearer nrc_ci_… and leave out subject_token. The response:

{
  "access_token": "eyJ…",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 300,
  "scope": "langsync.sync",
  "audience": "langsync-api",
  "organization_id": "62658568-5050-49fc-b331-f61bbdf850ad",
  "organization_slug": "acme",
  "organization_name": "Acme",
  "ci_token_id": "0b7a4c8e-2d3f-4a1b-9c6d-5e4f3a2b1c0d",
  "ci_token_name": "GitHub Actions deploy",
  "ci_token_expires_at": "2027-01-27T09:00:00Z"
}
audienceProduct
snapdb-apiBackup
langsync-apiLangSync
domainradarDomainRadar
billingBilling
cms-apiCMS

The access token lives 5 minutes. Reuse it for that long instead of exchanging on every request: the exchange allows 600 requests a minute per IP address.

StatustypeMeaning
401INVALID_TOKENThe token is malformed, unknown, expired or revoked. These aren't told apart.
400INVALID_AUDIENCEaudience isn't one of the products above.
403INSUFFICIENT_SCOPEThe token has no scope for that product.
429TOO_MANY_REQUESTSToo many exchanges from your address; wait for Retry-After.

Rotate and revoke

  • Rotate before a token expires: create a new token, update the CI secret, then revoke the old one. The list shows when each token was last used and from which address, so you can see when the old one goes quiet.
  • Revoke from IAM › CI tokens. The token can't be exchanged any more at once; access tokens already issued from it stop working within 5 minutes.
  • Revoked and expired tokens stay listed for 30 days, then they're deleted. Deleting the organization ends all its tokens.

Who did it

A CI token is its own identity. Actions it takes are recorded under CI token · name (in audit logs, in the CMS commit history, as the author of synced terms), never under the person who created it. The creator is kept on the token for your records.

Security

  • Format. nrc_ci_ + 32 random characters + a 6-character checksum. The fixed prefix lets secret scanners (GitHub push protection, gitleaks) recognise a leaked token; the checksum catches typos and truncated copies.
  • Storage. Only a SHA-256 hash of the token is stored, plus its last 4 characters to tell tokens apart. Nobody at Norcube can read it back.
  • No escalation. A token can never do more than its creator could do across the whole organization when they created it, and its scopes can't be changed afterwards: create a new token instead.
  • Treat it like a password. Keep it in your CI's secret store, never in a repository or a log. If one leaks, revoke it.
  • Tokens: how access tokens, audiences and refresh work.
  • Members & IAM: the permissions behind scopes.
  • CLI: NORCUBE_TOKEN and the other environment variables.

On this page