> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getcliq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth

> Credentials, grants, and which Hub plane to call.

<div className="cliq-kicker">Credentials</div>

Use the smallest credential that fits the job. Privilege lives on the **token grant**, not on an `/admin` URL.

Hub Core accepts **exactly two** Bearer forms on `/v1`: **user PAT** (`cliq_tok_…`) and **realm token** (`cliq_dt_…`). There is no Hub session JWT and no `cliq_dk_…` prefix.

## Cheat sheet

| I am… | Use | Storage / how you get it |
| - | - | - |
| A person in the browser | **BFF session** (cookie `session_id`) | Console login → `POST /v1/session/create`; browser never holds the PAT |
| A person in the terminal | **Session-scoped user PAT** `cliq_tok_…` | `cliq login` → kept by cliqd as `hub.session` |
| CI / a script talking to Hub | **User PAT** `cliq_tok_…` | `cliq auth token <name>`; `cliq settings set cliq.api_key` logs a cliqd in with it |
| A daemon joining a realm | **Realm token** `cliq_dt_…` | `cliq auth token <name> --realm <realm_id>` → `CLIQ_DAEMON_TOKEN`; stored by cliqd as `hub.daemon_token` |
| Daemon verifying Hub dispatch | **Dispatch public PEM** | Loaded on register; rotate with `cliq auth rotate-dispatch --realm <realm_id>` (realm admin) |
| Hub calling a daemon | **Dispatch JWT** | Hub mints; daemon verifies with the realm PEM |
| A CLI or script calling a cliqd on another host | **Local token** `cliq_…`, or a user PAT in the realm | Minted on that machine with `POST /v1/tokens/create` → `CLIQ_DAEMON_TOKEN` — see [cliqd → Local tokens](/cliqd#local-tokens) |
| CliqHub BFF provisioning users/orgs | **Service grant** (internal) | Never public — not documented on this site |

All Hub `/v1` data-plane calls use:

```http theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
Authorization: Bearer <cliq_tok_… | cliq_dt_…>
```

Token types on Hub mint are only **`user` | `realm`**.

## API planes

| Plane | Where | Who calls it |
| - | - | - |
| **Hub API** (`/v1`) | Public product API | People, CI, daemons (with the right credential) |
| **BFF session** (`/v1/session/*`) | CliqHub BFF / gateway | Browser cookie session; CLI login (`X-Client: cli`) |
| **Internal API** (`/internal`) | Not on public ingress | **CliqHub BFF only** — authenticate, issue/revoke session PAT, signup, delete user/org. Spec is private (not published here). |
| **Daemon API** | Your machine (`localhost:4900`) | CLI and local tools; remote callers with a dispatch JWT, a realm member's PAT, or a local token |

There is no `/admin` route tree. The same resource paths serve everyone; the grant decides if the call is allowed.

Interactive specs on this site: **Hub API** and **Daemon API** only.

## Grants (domains + access)

Every user and realm token carries a grant under **`permissions`**:

```json theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "domains": {
    "orgs": [42],
    "realms": ["…"]
  },
  "access": {
    "teams": ["read", "write"],
    "scopes": ["read", "write"],
    "runs": ["read", "write"],
    "dispatch": ["write"],
    "users": ["read"]
  }
}
```

* **Domains** — *where* the credential may act (each key is optional):
  * `orgs` — list of org **ids** (integers), or `*`
  * `realms` — list of realm **ids**, or `*`
* **Access** — per **entity**, one or more of `read` | `write` | `admin`:
  * `read` — get / list (including run details and logs)
  * `write` — create / update / publish / dispatch / cancel / heartbeat
  * `admin` — irreversible tenancy ops (delete user/org, rotate org keys, …)

Package namespaces (`@acme/…`) are **not** a token domain. Which namespaces you can publish or install into comes from **scope membership** on the scopes resource (owned / org / member). Tokens only use `access.scopes` to manage that catalog.

Allowed `access` entity keys:

| Entity | Typical use |
| - | - |
| `users` | list / manage users |
| `orgs` | org settings and membership |
| `scopes` | create / update / delete package namespaces (catalog admin) |
| `teams` | publish / install / list teams |
| `drafts` | draft teams |
| `tokens` | mint / revoke / rotate tokens |
| `realms` | realm CRUD and membership |
| `daemons` | register / heartbeat / list |
| `runs` | run details, events, logs, artifacts |
| `dispatch` | enqueue / claim / cancel |
| `workspaces` | workspace list / bind |
| `agents` | agent catalog |
| `settings` | Hub settings |
| `secrets` | secrets |
| `notifications` | inbox / channels |
| `builder` | team builder |
| `hug` | Hug |
| `reports` | reports |

Team publish/list for a namespace requires **scope membership** plus `access.teams` (usually `write`).

Dispatch is a **write** (usually `runs` write or `dispatch` write). Run events, logs, phases, and artifacts share the `runs` entity — they are not separate domains.

Package namespaces live on the **[Scopes](/scopes)** resource (membership + catalog CRUD) — not under Auth token mint.

### Mint templates

**CI publisher** — put the machine user in the `acme` scope (scopes resource); grant team write via permissions:

```json theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "domains": { "orgs": [42] },
  "access": {
    "teams": ["read", "write"],
    "runs": ["read", "write"],
    "dispatch": ["write"]
  }
}
```

**Realm enroll token**

```json theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "domains": { "realms": ["<realm_id>"] },
  "access": {
    "daemons": ["write"],
    "dispatch": ["write"],
    "runs": ["read", "write"]
  }
}
```

**Platform / BFF (internal callers)**

```json theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "domains": { "orgs": "*", "realms": "*" },
  "access": {
    "users": ["admin"],
    "orgs": ["admin"],
    "scopes": ["admin"]
  }
}
```

## Session (people)

Interactive login goes through the **BFF session** resource — not Core `/v1/auth/login`.

| Endpoint | Behavior |
| - | - |
| `POST /v1/session/create` | Authenticate → persist session; browser gets `session_id` cookie; CLI (`X-Client: cli`) may receive `{ token }` |
| `POST /v1/session/get` | Identity for the current act-as subject |
| `POST /v1/session/update` | `{ act_as_user_id }` to act-as, or `null` to exit |
| `POST /v1/session/delete` | Revoke session PAT(s); destroy session; clear cookie |

The browser never holds Hub PATs. The BFF stores `user_token` and `target_token` server-side and always calls Core with `Authorization: Bearer <target_token>`. Act-as is a **session update**, not a Core RPC.

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
cliq login
cliq whoami
```

Headless:

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
cliq login --headless username:alice password:secret
```

`cliq login` hands the credentials to cliqd, which calls `POST /v1/session/create` and keeps the returned session-scoped PAT (`cliq_tok_…`) in its store as `hub.session`. The CLI stores nothing. Do **not** use that value as `cliq.api_key`. There is no JWT refresh path — `cliq logout` revokes the session PAT; cliqd keeps its daemon token and realm.

Accounts are **not** created via a public signup API. Provisioning goes through CliqHub BFF → private Hub `/internal` routes. Day-to-day login stays on the BFF session API.

## User token (automation)

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
cliq auth token ci-pipeline
# → cliq_tok_… shown once
echo '<token>' | cliq settings set cliq.api_key --stdin
```

Setting `cliq.api_key` logs cliqd in with that standing user PAT (no password needed — handy for CI and containers). Only durable user PATs (`cliq_tok_…`) are accepted; a session-scoped login PAT is refused.

Use that Bearer on **Hub API** the same way you’d use a session PAT, limited by the grant. Rotate or revoke from the console (**Account → Tokens**) or Hub API:

| Endpoint | Purpose |
| - | - |
| `POST /v1/auth/generate_token` | Mint (`type`: `user` \| `realm`) — permissions only; no package-namespace list |
| `POST /v1/auth/get_tokens` | List your tokens |
| `POST /v1/auth/rotate_token` | New plaintext secret |
| `POST /v1/auth/revoke_token` | Revoke one token |
| `POST /v1/auth/validate_token` | Introspect a plaintext token (daemon identity check) |

Create response (`POST /v1/auth/generate_token`):

```json theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "ok": true,
  "data": {
    "token": "cliq_tok_…",
    "name": "ci-pipeline",
    "id": 42,
    "realm_ids": []
  }
}
```

Plaintext `token` is returned **once**. `realm_ids` is non-empty for realm enroll tokens.

## Realm membership roles

People join a realm through an invitation in CliqHub. A realm admin sets each member’s role (CliqHub realm page, or `POST /v1/realms/add_member`). The role is that user’s access inside the realm — not a bulk add of everyone with a title. There is no `cliq realm` command.

| Role | View realm / members / daemons | Mint realm tokens | Manage members, revoke tokens, update·delete realm, rotate dispatch key |
| - | - | - | - |
| `admin` | yes | yes | yes |
| `operator` | yes | yes | no (default when adding a user) |
| `member` | yes | yes | no (same view gates as operator today) |

The realm’s creator is `admin`.

## Realm token (machines)

Mint on the realm page in Hub, or as any realm **member** (`cliq login` does this automatically when it enrolls a daemon):

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
cliq auth token laptop-enroll --realm <realm_id>
# → cliq_dt_… shown once
```

On the machine to enroll, start cliqd with the token:

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
export CLIQ_DAEMON_ID=edge-1        # optional: preferred daemon id
export CLIQ_DAEMON_TOKEN=cliq_dt_…
cliqd
```

cliqd stores the token and registers in that token’s realm. Successful register returns an enroll bundle (`realm_id`, `dispatch_public_key`, `acl`); cliqd holds the dispatch key in memory and refreshes it on heartbeat. Rotate it with `cliq auth rotate-dispatch --realm <realm_id>` when needed.

A daemon only syncs runs of teams on the realm’s team list. Runs of other teams wait on the machine until a realm admin adds the team — see [How teams get installed](/cliqd#how-teams-get-installed).

<Warning>
  Daemons reject user PATs for enroll / heartbeat / job pickup. If register fails with 401, you’re almost certainly using the wrong token type — use a realm token (`cliq_dt_…`).
</Warning>

## What each surface expects

| Surface / area | Credential | Grant |
| - | - | - |
| BFF session create/get/update/delete | Cookie (browser) or returned PAT (CLI) | — |
| Hub token APIs | User PAT | `tokens:*` |
| Orgs, scopes, teams (product) | User PAT | `orgs` / `scopes` / `teams` read·write·admin |
| Realms, mint realm tokens | User PAT | `realms` write |
| Daemons list | User PAT | `daemons` read |
| Daemons register / heartbeat | **Realm token** | `daemons` write |
| Dispatch install / enqueue / run / cancel | User PAT | `dispatch` / `runs` write |
| Dispatch claim | **Realm token** | `dispatch` write |
| Signup, delete user/org, set role | BFF → internal Hub routes (not public) | `users` / `orgs` / `scopes` **admin** |

## Related

* Mental model → [Concepts](/concepts)
* CLI login / tokens → [CLI](/cli)
* Daemon credentials and local tokens → [cliqd](/cliqd)
* Path checklist for implementers → `design/api-route-permissions.md` in the repo


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.