Tokens & capabilities
One credential authenticates the API, git over HTTPS, and MCP. What it may do is decided when it is minted, and a request outside that comes back naming what it wanted.
Minting one
From Settings → Tokens, or from the terminal:
$ rezee token create ci --expires 90
$ rezee token list
$ rezee token revoke <id>A token looks like reze_pat_..., is printed exactly once, and is stored as a hash - there is no way to read it back, here or anywhere else. Pipe it somewhere useful rather than reading it off the screen:
$ rezee token create ci --json | jq -r .tokenLifetimes go up to 365 days. The cap is less a security boundary than a forcing function: a credential nobody has looked at since last summer is one nobody will notice the theft of. A token minted with no lifetime never expires.
Two kinds of token
Unrestricted. A token minted with no capabilities can do whatever its owner can. This is what the tokens page gives you by default, and what the commands that change access need.
Scoped. A token carrying capabilities can do only those, and only ever a subset of what its owner could do anyway - a scope narrows, it never widens. rezee auth login mints one of these: the approval page lists every capability and you tick what this machine gets.
A request outside a token's capabilities comes back 403 naming the capability it wanted, so a client can ask to be widened rather than report a failure it cannot explain.
Capabilities
| Capability | What it reaches |
|---|---|
account:read | who you are, and your inbox |
workspaces:read | which workspaces you are in |
code:read | branches, trees, blobs, commits, blame - and git fetch |
code:write | git push, and re-running a pipeline |
repos:write | creating a repository |
issues:read / issues:write | issues, labels, projects and cycles |
pulls:read / pulls:write | pull requests, reviews, threads, merging |
docs:read / docs:write | documents, canvases, components and assets |
chat:read / chat:write | channels, messages and threads |
runs:read | pipeline runs, jobs, steps and logs |
secrets:write | listing, setting and rotating CI secrets |
releases:write | cutting a release and its tag |
tasks:read | reading delegated agent tasks |
tasks:run | an external agent runtime driving its own task, and nothing else's |
There is no secrets:read. The list returns names and update times and never a value, and the token that may rotate a secret is exactly the one that needs to see which ones exist.
releases:write is separate from code:write on purpose, so the token that publishes a build cannot also push to the branch it was built from.
Narrowing to a workspace or a repository
Two resource scopes sit alongside the capabilities and bind whatever else the token holds:
ws:acme # only this workspace
repo:acme/api # only this repositoryA repository path names its workspace in the same breath, so ws:acme narrows the code surfaces too - "this token is for acme" means acme's repositories as well as acme's issues.
Search is the one endpoint no single capability describes. It intersects the surfaces it would search with what the token actually holds, so a token carrying only issues:read gets issues back and never learns a document title. The resource scope still binds, and rezee search prints which surfaces it was allowed to read.
What no scoped token can reach
Some surfaces are refused to every scoped token, whatever it carries. They are the ones where a grant could be used to manufacture a wider grant:
- Members, invitations and workspace creation. A token that could add members could add itself colleagues.
- SSO and SCIM. A token that could point the workspace at an identity provider could provision itself colleagues.
- Runners. A runner token is handed the decrypted secrets of every repository at every claim - minting one would be minting a credential strictly wider than the token that asked.
- Branch protection. A token has no business reading or writing the rules its own pushes are judged against.
- Collaborators and webhooks. Repository configuration, not a working surface.
- Agents, their tokens, and schedules. A token that could create a schedule would have given itself work every morning forever, through the one door with no person at it.
- The audit log, and your own profile and password.
This is why rezee member, rezee invite, rezee runner, rezee protect, rezee webhook, rezee token and rezee ssh-key need an unrestricted token rather than the one a browser login mints.
Using one
# the API
$ curl -H "Authorization: Bearer reze_pat_..." \
https://api.rezee.app/api/workspaces
# git over HTTPS - username is ignored, password is the token
$ git clone https://git.rezee.app/acme/api.git
# MCP
Authorization: Bearer reze_pat_...The browser's session cookie authenticates the same endpoints, which is what the web app uses, and reads as unrestricted.
The other credentials in the system
Personal access tokens are the one you mint. The rest exist so you know what you are looking at when you see one:
| Prefix | What it is |
|---|---|
reze_pat_ | a personal, agent, or task token |
reze_dev_ | a device-login code - redeemed for a token rather than presented as one |
reze_oat_ / reze_ort_ | an OAuth connector's access and refresh token |
reze_runner_ | a CI runner's registration token |
reze_inv_ | a workspace invitation |
A pipeline's clone credential is separate again: minted per run, valid for that repository only, and read-only. Every build step sees it, and a pipeline that could write with it is a pipeline that could rewrite the branch it was asked to test.
Revoking
rezee token revoke, or the tokens page. Revocation is immediate - the row is deleted and the next request carrying it is a 401.
rezee auth logout does not revoke anything. It forgets the token locally, and the credential stays live until somebody deletes it. If the machine is not yours, revoke it as well.
Connected apps are revoked from their own list in account settings, which disconnects every token that client holds at once.
