# Service accounts and tokens

A script, a continuous integration pipeline or an internal tool calls Bastide with a token. This page explains which token to choose, a personal token or a service account token, how to mint, rotate and revoke them, and how to deploy from continuous integration.

## Two kinds of token

| | Personal token | Service account token |
| --- | --- | --- |
| Prefix | `pat_` | `tok_` |
| Who acts | You: the audit log names you. | The token itself: it is the subject of the call, and a named person answers for it. |
| What it opens | Everything your rank opens, within its scopes. | An explicit list of the organisation's projects, 1 to 20, with a rank on each, within its scopes. |
| What it does | Everything you do, except actions reserved to a console session. | Reads on the named projects; among writes, deploying and cancelling a deployment, nothing else. |
| Lifetime | 1 to 365 days. | 1 to 90 days. |
| Cap | 50 active tokens per account. | 50 active tokens per organisation. |
| Where to mint it | **Compte** (Account), section "Vos jetons" (your tokens). | **Membres** (Members), card "Comptes de service" (service accounts). |

Prefer a service account for continuous integration: it outlives a member leaving as long as the person answering for it stays active, it opens only the named projects, and it cannot change or delete anything beyond deployments.

Both are shown once, at creation: Bastide keeps only a hash. Their scopes and expiry are fixed at creation; a token is not edited, it is replaced.

## Personal tokens

### Minting one

1. Open **Compte**, section "Vos jetons", then **Frapper un jeton** (mint a token).
2. Say what it is for: that name shows in the list.
3. Choose its lifetime: 7, 30, 90, 180 or 365 days. The console suggests 90 days.
4. Tick its scopes. The console offers only the scopes your session carries; a token never grants what it does not have.
5. Copy the token shown: it will not be shown again.

`bastide login` also mints a personal token, valid for 90 days with every scope, stored in the machine's keychain. See [The command line](cli.md).

### Its life cycle

- It expires on the chosen date, at most a year after creation.
- Unused for 90 days, it is disabled. You are warned seven days before. A disabled token is reactivated from **Compte**; reactivation requires a console session.
- **Révoquer** (Revoke) refuses it from the next call.
- The list shows, for each token, its scopes, expiry, last use and the address of that use.

## Service accounts

### Minting one

You need the Administrateur (Administrator) rank on the organisation, and the `org:admin` scope.

1. Open **Membres**, card "Comptes de service", then **+ Frapper un compte de service** (mint a service account).
2. Name its purpose, choose its lifetime (1 to 90 days) and its scopes.
3. Add the projects it opens, each with a rank: Lecteur, Développeur, Mainteneur, Administrateur or Propriétaire (reader to owner). You cannot give it a higher rank than yours on the project, nor a scope your session does not carry.
4. **Frapper le jeton** (mint the token), then copy it. The card offers a check command that reads its first project.

You answer for it: the form names you. If your account is suspended or deleted, the token moves to the `orphaned` state and opens nothing.

Through the API, the operation is `service_token.create`:

```json
{
  "name": "Deployment from continuous integration",
  "scopes": ["project:read", "deploy:write"],
  "expiresInDays": 90,
  "projects": [{ "projectId": "prj_01JQ8ZK4M7NP2RSTVWXYZ34567", "role": "developer" }]
}
```

### What it can do

A service account has no person behind it. The platform therefore closes to it everything that assumes a person:

- It reads, on its projects, what its rank and scopes allow: projects, environments, deployments, logs, the audit log.
- Among writes, it runs only `deployment.create` and `deployment.cancel`. Any other write is refused with `service_action_forbidden`. Each operation's reference page says, on its "Compte de service" line, what a service account does with it.
- It never validates an irreversible operation (`validation_humaine_requise`).
- It reaches nothing above its projects: neither the organisation nor billing.

If the organisation requires a second factor, a service account can be exempted by name, with a reason, for up to 92 days: the organisation's **Réglages** (Settings), section "Second facteur".

### Review

The "Comptes de service" card is the review: every token, open or closed, with its scopes, its projects and their ranks, the person answering for it, its expiry and its last use. Go through it every quarter and revoke what is no longer used. Revocation is final and is confirmed in a dialog that names the token.

## Rotating a token

No token can be extended. To replace one without interruption:

1. Mint a new token with the same scopes and projects.
2. Swap it into your continuous integration secret or your tool.
3. Check one call with the new token.
4. Revoke the old one.

A Bastide token found in a repository during a build is revoked automatically, and the person answering for it is notified: mint another one and keep it out of version control.

## Deploying from continuous integration

The command line reads the token from `BASTIDE_TOKEN` and then consults nothing else. A service account with the Développeur (developer) rank and the scopes `project:read` and `deploy:write` is enough for `bastide deploy`.

Example for GitHub Actions, with the token stored in the repository secret `BASTIDE_TOKEN`:

```yaml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - run: npx --yes bastide deploy --project-id prj_01JQ8ZK4M7NP2RSTVWXYZ34567 --ref "$GITHUB_SHA"
        env:
          BASTIDE_TOKEN: ${{ secrets.BASTIDE_TOKEN }}
```

`bastide deploy` exits with code 0 if the deployment is serving, and 13 if it failed or was not put live: the step fails with it. The other codes are in [The command line](cli.md).

Without the command line, call the API directly: `POST /v1/projects/{projectId}/deployments` with an idempotency key, then follow the task with `task.read`. See [The HTTP API](api.md).

> **Tip.** For a deployment triggered by simply calling an address, with no token, a deploy hook is enough: create it on the project's **Webhooks** screen. See [Webhooks](webhooks.md).

## Common errors

| Code | Cause | Fix |
| --- | --- | --- |
| `unauthenticated` | Token expired, revoked, disabled, or its responsible person is inactive. | Mint another one. |
| `service_action_forbidden` | The write is not open to service accounts. | Use a personal token, or do it from the console. |
| `token_scope_missing` | The token lacks the scope. | Mint a token with the scope. |
| `insufficient_rank` | The rank given on the project is too low. | Mint a token with a sufficient rank. |
| `not_found` | The project is not in the token's list. | Mint a token that names it. |
| `quota_de_jetons_de_service_atteint` | 50 active tokens in the organisation. | Revoke the ones no longer used. |
| `quota_de_jetons_personnels_atteint` | 50 active tokens on your account. | Revoke the ones no longer used. |

## See also

- [The HTTP API](api.md)
- [The command line](cli.md)
- [What an agent can do](agent-safety.md)
- [Organisations and members](organizations.md)
