# API tokens

Sign in normally, then open **Profile → Settings → API tokens**. Choose a
connected source, a name, read or write access, and an expiry. Copy the token
once and keep it in your tool's secret or environment configuration.

Tokens work with both GitHub and SQLite sources. Each token grants exactly one
source, including its Git branch and board folder. It cannot discover other
sources, change source settings or issue more tokens. The account can hold up
to 20 active tokens; an expiry must be between 1 and 90 days.

| Access | Allowed | Limits |
| --- | --- | --- |
| Read | Board, cards, health and MCP read tools | No edits |
| Read and write | Read, create and edit cards through HTTP, CLI or MCP | Current source permissions still apply; a read-only store stays read-only |

Use **Revoke** in Settings to disable a token immediately, including any signed
session derived from it. Signing out of the browser ends that browser session;
tool tokens stay valid until revoked or expired. API tokens are stored as
SHA-256 hashes. Their plaintext is returned only at creation and is never saved
in browser storage.

## Keep authorization on persistent storage

The account backend stores token metadata and encrypted GitHub credentials in
`FOLIO_BOARD_AUTH_DB`. If it is unset, the file is
`<workdir>/.auth/security.sqlite3`. Put it on persistent storage alongside the
SQLite boards. Restrict this directory to the service account and preserve
`FOLIO_BOARD_SESSION_KEY` across deployments.

Provider credentials use AES-256-GCM with a key derived from the signing key.
Git origins contain no credential. Fetch, clone and push receive a transient
authentication header through Git’s process environment, with credential helpers
disabled; the header is never written to the clone’s configuration.
Back up that key separately from the databases. Starting with a different key
fails instead of silently losing access to the encrypted credentials.

GitHub tokens use the signed-in person's access, never the deployment owner's
credential. Repository membership is checked on every request. An expiring
GitHub user token is refreshed when the App supplies a refresh token; tool and
browser handles share the refreshed credential. Without refresh support, a
GitHub API token cannot outlive that user credential. Reconnect and issue a new
token if GitHub revokes the connection or its refresh credential expires.

SQLite tokens retain the stable signed-in identity and recheck the database's
reader/writer policy. GitHub token expiration does not disable a SQLite token.

## Use the remote CLI

Install the network binary from the same checkout as the backend:

```bash
cargo install --path crates/folio-board-server --bin folio-board --locked
```

Set `FOLIO_BOARD_API` to the API origin and `FOLIO_BOARD_TOKEN` through your
shell or secret manager. The CLI does not accept a secret in an argument or URL.

```bash
export FOLIO_BOARD_API=https://board.example.com
folio-board health
folio-board board
folio-board card improve-search
folio-board move improve-search in-progress
folio-board update improve-search --set priority=high
folio-board comment improve-search "The new layout is ready."
folio-board trail improve-search "Implemented the layout" --ref abc1234
folio-board description improve-search --file description.md
folio-board create improve-search --file card.md
```

`move`, `update` and `description` read the current card hash, then send one
guarded write. A concurrent change returns a conflict; the CLI does not retry
by overwriting it. Append operations use the service's append contract.
`folio-board apply --file request.json` sends a complete
[operation request](/docs/reference/api#operation-requests), including an
explicit hash or `dry_run` flag.

`folio-agents board` remains the filesystem CLI. Board skills continue to edit
Markdown/Git through that CLI; they do not open SQLite databases directly.
The network CLI and [MCP endpoint](/docs/deployment/mcp) use the same API and
domain service as browser edits.
