Documentation

CI workload identity

Let a GitHub Actions or GitLab CI job use its own short-lived OIDC token instead of a person's session — trusted per repository, limited to approved profiles, off by default.

Off by default

CI workload identity is implemented and tested but switched off in every deployment until the DataNivra operator enables it (DATANIVRA_WORKLOAD_IDENTITY_ENABLED). While it is off, POST /v1/auth/workload/token answers 404 WORKLOAD_IDENTITY_DISABLED and no workload session is accepted. The token issuers a deployment accepts are an operator allow-list (by default https://token.actions.githubusercontent.com and https://gitlab.com).

What a workload session can do

A workload session holds a fixed, minimal permission set, whatever roles its service user has:

  • test_data.request — list the profiles that name it, plan, request, read and release its own test-data requests;
  • dataset.read and job.read — read metadata (never rows).

It cannot create raw dataset requests or leases, provision, revoke, approve anything, author or approve profiles, manage users or change settings. It is short (60–900 seconds, 600 by default) and cannot be refreshed: the job exchanges a fresh CI token when it needs one. Each trust owns a non-interactive service user that no human sign-in flow accepts.

Trust one repository (tenant administrators)

datanivra workload-identities create --name "claims CI" --provider GITHUB_ACTIONS \
  --repository acme/claims-service --audience datanivra:acme \
  --ref refs/heads/main --ref 'refs/pull/*'
OptionClaim checkedNotes
--providerissuerGITHUB_ACTIONS or GITLAB_CI; --issuer names a self-managed GitLab that the operator allow-listed
--audienceaudconfigure the same value in the pipeline
--repositoryGitHub repository / GitLab project_pathrequired, compared case-insensitively
--ref (repeatable)refexact, or a trailing *; GitLab branch and tag names are normalised to refs/heads/… and refs/tags/…
--environment (repeatable)environmentdeployment environment of the job
--event (repeatable)GitHub event_name / GitLab pipeline_sourcepull_request_target is never accepted: it runs fork code with the base repository's credentials
--protected-refs-onlyGitLab ref_protectedrequire a protected branch or tag
--token-ttl—workload session lifetime, 1m–15m

A trust grants nothing on its own: a profile's automation envelope must name it before any of its requests can be considered, and requests outside every envelope are denied.

In the pipeline

GitHub Actions — grant permissions: id-token: write, then:

datanivra login --workload-identity "$DATANIVRA_WORKLOAD_IDENTITY_ID" --tenant acme --audience datanivra:acme

GitLab CI — declare the token and let the CLI read it from DATANIVRA_ID_TOKEN:

id_tokens:
  DATANIVRA_ID_TOKEN:
    aud: datanivra:acme

Any other system can pass the token on standard input: datanivra login --workload-identity <ID> --tenant acme --id-token-stdin. Full pipelines are in CI recipes.

What the exchange checks

The issuer must be allow-listed and equal the trust's issuer; the signature must verify against the issuer's published keys with an asymmetric algorithm; aud, expiry, not-before and issued-at are checked, and the token must be at most 10 minutes old; every token is accepted once (a replay is refused). The repository, ref, environment, event and protected-ref conditions must all hold. Every refusal is a generic 401 INVALID_CREDENTIALS; the specific reason (for example WORKLOAD_REPOSITORY_MISMATCH, WORKLOAD_TOKEN_REPLAYED) is recorded, value-free, in your audit trail as auth.workload.denied.

Emergency disable

datanivra workload-identities disable <ID> --reason-code CREDENTIAL_COMPROMISE

Disabling is final. Every outstanding session of that trust is refused on its next request — there is no waiting for expiry — and new exchanges are refused. Create a new trust to start again. POST /v1/auth/logout with a workload session revokes that session immediately.

← All documentation