---
title: "API Keys and Service Accounts"
description: "Machine authentication with API keys, and how service accounts for backend workers are set up."
source: "Guardian Platform"
---
API keys and service accounts support machine-to-machine access. They should be used only when an interactive user session is not appropriate.

## API Keys

API keys are tied to platform access and should be treated as secrets. An API key acts on behalf of the user who created it, limited to the scopes chosen at creation, so it is sometimes called a user API key. Use them for integrations that act with a defined platform identity.

- **Empty scopes** keep legacy unrestricted behavior, in which the key impersonates the user. They do **not** enroll gateway agents.
- **Named scopes** are RBAC permission names the creator can grant (for example `register_agents`, `view_project_data`), plus `did:read` when the caller is not a pure agent operator. A scoped key is deny-by-default outside that list. `revoke_agents` and `restore_agents` are never accepted as scopes, because a key cannot act as a platform admin, and neither is `certify_agents`, which belongs to the governance worker service account alone.
- **`*`** is explicit full access, available to organization owners only.
- **`projectId`** binds **project-level** permissions to one project. Organization-level actions such as `register_agents` still apply across the organization, except `view_agent_certifications`, `submit_agent_assessments` and `review_agent_assessments`, which a project-bound key cannot carry. Omit `projectId` (**Organization**) for every project the holder can already access.
- API keys cannot call any `/api/v1/api-keys` route, so a key cannot create, list, read, or revoke keys. Manage keys with a user session.
- **`allowedIps`** limits the key to the listed IP addresses or CIDR ranges, matched against the address Auth Service sees the request come from.
- **Name, description, and expiry are required** at create time. `expiresAt` must be in the future and no more than 365 days out; a key with a passed expiry reports status `expired` even though it is still stored as active.

A project binding must be paired with named scopes. Sending `projectId` with empty scopes, or with `*`, is rejected at create time: neither one names the organization-level actions the binding cannot take away, so the binding would advertise a narrower reach than the key actually has. Naming those scopes explicitly is still accepted — `register_agents` alongside a `projectId` is a deliberate choice, not an accident. The exceptions are `view_agent_certifications`, `submit_agent_assessments` and `review_agent_assessments`: each is refused alongside a `projectId`, because agents' certifications and assessments are served only to a key bound to no project.

For enrollment, a key with `register_agents` or `*` may call the gateway with `Authorization: Bearer ak_…`. Both the key scope and the owner's RBAC grant are required, and empty-scope keys cannot enroll. Create the key from Guardian (**Settings** → **API Keys**) or the Auth Service API using a user session.

In Guardian, the create dialog requires at least one named scope and no longer offers **Full access** (`*`). An organization owner who needs full access can still send `"scopes": ["*"]` to the Auth Service API. Choosing **Project** access hides the scopes a binding cannot narrow — organization-level actions and `did:read` — so switch to **Organization** to include them.

Recommended practices:

- Store keys in a secrets manager.
- Prefer a short expiry and one key per agent or pipeline.
- Rotate keys when ownership or system responsibility changes.
- Revoke unused keys. Revoking a key does not unwind agents already enrolled with it, and an API key cannot revoke or restore an agent regardless of its scopes — that action requires a signed-in organization owner.
- Avoid sharing a single key across unrelated integrations.

### Integrity Service and the SDK

The Integrity Service accepts `Authorization: Bearer ak_…` on every route, so the Python SDK (`eqty_sdk`) and other integrations can sign with an auth-service signer and register lineage with one key and no user session. Each route is attributed to a project and checked against the key's scopes and binding through Auth Service RBAC.

- Registering a graph or statement under a project needs `create_declarations` on that project, and `view_project_data` to read it back. Bind the key to the project when the integration only ever writes to one. Registering a new project root needs `create_projects`, which is organization-level.
- Storing a blob needs `create_declarations` or `create_comments` in any project the key reaches, and reading one needs `view_project_data` in any project.
- A key writing outside its binding, or without the permission, gets `403 Forbidden` naming the permission it lacks. An unregistered graph ID is also a `403` on writes, so a key cannot probe for graph IDs.
- `GET /graph/v1` lists only the project trees the key can view. A key with no `view_project_data` anywhere gets an empty list rather than an error.
- `PUT /dynamic-config/v1` (`manage_org_settings`) stays user-session only.
- If Auth Service is unreachable while a check runs, the request is refused with `503 Service Unavailable`; retry it. A `403` is a policy decision and will not change on retry.

A typical SDK key for a pipeline is `create_declarations` plus `view_project_data`, bound to the project, with a short expiry. `GET /api/v1/rbac/permission/{permission}/projects` on the Auth Service reports the projects where a key or user holds a permission, after scopes and binding are applied, which is useful for checking a key before handing it to a pipeline. Send the key as a bearer token:

```bash
curl "https://guardian.example.com/authService/api/v1/rbac/permission/create_declarations/projects" \
  -H "Authorization: Bearer $GUARDIAN_API_KEY"
```

```json
{
  "permission": "create_declarations",
  "projects": [{ "id": 15, "uuid": "fd47f68b-8760-4d0d-94fa-7658924acb25" }]
}
```

An empty `projects` list means the key cannot use that permission anywhere.

### Governance Service

The Governance Service checks a key's scopes and binding through Auth Service RBAC, as it checks a person's permissions. The routes that act on who a person is to a program or an assessment accept a key bound to no project that has empty scopes or `*`, or the scope for that standing:

- `submit_agent_assessments`: start an assessment, and pin, declare, claim controls not applicable in and submit one the key's holder started.
- `review_agent_assessments`: review a control of an assessment of a program the holder reviews, ask again for a certification their review rests on, and revoke one they authorized.
- `manage_programs`: assign and remove a program's reviewers, revoke any certification, and ask again for any certification except one of an assessment the holder started or a submission they made.

A scope counts only while the holder still holds that permission in the organization, and their standing still decides, as it does for their sign-in. Assigning a reviewer looks the member up in Auth Service with the key, which only a key with empty scopes or `*` may do. Signing a certification with the platform key takes a key whose scopes cover every permission of a role that may sign.

### Control Plane

The Control Plane accepts `Authorization: Bearer ak_…` on the routes that ask Auth Service RBAC for a permission. Unlike the Integrity Service, it does not accept keys everywhere: most routes gated on identity alone still require a JWT. [Control Plane Authentication and RBAC](../control-plane/authentication-and-rbac.mdx) covers the permission each route checks.

- Minting an agent registration token needs `register_agents`, so a headless Viper enrollment can use a key instead of a Guardian sign-in.
- The project-scoped agent, trust, and membership routes are checked against the key's scopes and binding for that project.
- `GET /v1/agents` requires `project_uuid` when it is called with a key, so the same `view_project_data` check applies and the results are limited to that project. Filtering it with `has_profile=true` also needs a key that could read those profiles: one bound to no project, with empty scopes, `*` or `submit_agent_assessments`. Any other key gets `403 Forbidden`.
- A key never acts as a platform admin, even when its owner is one. Admin-only routes — creating or revoking agents, managing plugin secrets, and listing users — return `403 Forbidden` for any key.
- Per-agent routes under `/v1/agents/{id}` accept a key bound to no project that has empty scopes or `*`, which acts as its holder there, or the scope for what the route does, for the agents its holder is bound to:
  - `submit_agent_assessments` reads the agent: its detail, configurations, state history and the files a state references, definitions, tools and operating profile.
  - `view_agent_certifications` reads `GET /v1/agents/{id}/certifications`, so deploy automation can pull an agent's certifications.
  - `manage_programs` or `review_agent_assessments` attach a certification, as the Governance Service does for whoever asked for it.

  A scope counts only while the holder still holds that permission in the key's organization: Auth Service validation reports those as `keyHeldScopes`, beside the `keyScopes` the key was created with, and the Control Plane acts on nothing else. A state's raw snapshot and findings, audit exchanges, plugin events and saving an agent's profile take empty scopes or `*`. A project-bound key, or one without the route's scope, gets `403 Forbidden`.

- Other routes gated on identity alone (`/v1/me`, plugin catalog and configuration reads) refuse keys with `403` until each is mapped to a permission. Use a JWT for those.

## Service Accounts

Service accounts are for trusted platform services, such as backend workers that call another platform service, and for approved backend integrations. Operators configure service accounts as part of deployment and operations, not as an ad hoc user workflow.

Do not use service accounts for normal human user access. Human users should sign in through the identity provider and receive project roles.

### Setting Up Service Accounts

Auth Service creates one service account, `governance-worker`, at startup from the provider's client credentials:

- **Auth0:** `AUTH0_GOVERNANCE_WORKER_CLIENT_ID` and `AUTH0_GOVERNANCE_WORKER_CLIENT_SECRET`, plus `AUTH0_GOVERNANCE_WORKER_AUDIENCE` (defaults to `IDP_AUTH0_API_IDENTIFIER`).
- **Keycloak:** `KEYCLOAK_GOVERNANCE_WORKER_CLIENT_ID` and `KEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRET`. Without a secret, Auth Service creates the client in Keycloak.
- **Microsoft Entra:** `ENTRA_GOVERNANCE_WORKER_CLIENT_ID` and `ENTRA_GOVERNANCE_WORKER_CLIENT_SECRET`, plus `ENTRA_GOVERNANCE_WORKER_SCOPE`.

The chart sets these from `global.secrets.governanceWorker` while `auth-service.config.serviceAccounts.governanceWorker.enabled` is `true` (the default). Missing credentials or a failed creation is only logged, and Auth Service still starts, so check its startup logs. A platform service gets the account's token from `GET /api/v1/service-accounts/governance-worker/token`, authenticated with `API_SECRET` as a bearer token.

After setup, confirm the service account can perform only the workflow it was created for. If a service account fails, check its configured secret, service name, role expectations, and Auth Service health.

Store service-account credentials in the deployment secret system. Do not reuse service-account secrets across unrelated integrations, and rotate credentials after suspected exposure. To rotate the governance worker's secret, issue a new one in the identity provider, update `platform-governance-worker`, and restart Auth Service, which stores the configured secret at startup.

## Choosing Between Them

Use a user API key when the integration should be accountable to a user or customer-managed integration identity. Use a service account when the integration is a platform service that must operate independently of a human user.

## Related

- [API Keys](../user-guide/api-keys.mdx) — generating, scoping, and revoking keys in Guardian
- [RBAC and Membership](./rbac-and-membership.mdx) — the role permissions a key's scopes are drawn from
- [Register Agents](../user-guide/register-agents.mdx) — enrolling a gateway agent with a key
- [Token Flows](./token-flows.mdx) — how keys compare with the other credential flows
- [Authentication and Authorization Guidelines](./guidelines.mdx) — storage and rotation practices for keys and service-account secrets