> For the complete documentation index, see [llms.txt](https://mercure-technologies.gitbook.io/xprem/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mercure-technologies.gitbook.io/xprem/security/token-access.md).

# Token access

{% hint style="warning" %}
Token access rules are an Enterprise feature and require [control-plane mode](/xprem/installation-guide/overview.md). Editing a token's access requires an active Enterprise license. (See [open-core-and-licensing](/xprem/references/open-core-and-licensing.md))
{% endhint %}

Every `eoas` command authenticates with an API token. By default a token can act on every branch of its app from anywhere: fine for a small team, too broad once tokens live in CI systems and contractor machines. Token access scopes each token down to the branches it may touch, the actions it may perform there, and the networks it may be used from. Everything on this page is edited in one dashboard panel and enforced on every CLI request.

## Branch rules

A token carries a list of rules, each one granting a set of actions on the branches matching a pattern:

* Rules are an **allow-list**: a request goes through when at least one rule grants it. There are no deny rules, so no precedence to reason about.
* **A token with no rules has full access.** That is the default state of a new token, and the only state in the Community edition.
* A **pattern** is either an exact branch name (`production`) or a glob where `*` stands for any run of characters (`pr-*`). Branch names cannot contain `*`, so a pattern is never ambiguous with a literal name. Each pattern may appear in only one rule, and a token holds at most 50 rules.

The **actions** a rule can grant:

| Action     | Allows                                                                                         |
| ---------- | ---------------------------------------------------------------------------------------------- |
| `read`     | Listing branches, runtime versions and updates                                                 |
| `publish`  | Publishing updates, including to a branch that does not exist yet if its name matches the rule |
| `rollback` | Rolling back and republishing previous updates                                                 |

`publish` and `rollback` both imply `read`, since the CLI reads a branch before writing to it. **`publish` does not imply `rollback`**: reverting production to an older update is a more powerful capability than shipping a new one, so it is granted separately.

Rules are enforced on every CLI route, before the request reaches any handler. A denied request gets a `403` naming both the action and the branch:

```
this API key is not allowed to publish on branch "production"
```

## IP allowlist

The same panel can tie the token to a set of source addresses, so it only works when the request comes from a network you trust: your CI provider, your office, a bastion. Each entry is a single IP address or a CIDR range, and both IPv4 and IPv6 are supported (for example `203.0.113.7`, `203.0.113.0/24` or `2001:db8::/32`). An empty list means the token has no IP restriction and works from anywhere.

On every request that presents the token, the server compares the caller's source address against the list before even looking at the branch rules, and rejects anything that falls outside it.

### Behind a proxy or load balancer

By default the server uses the address of the network connection it received, which is the correct source IP when clients reach the server directly. When xprem runs behind a proxy or load balancer, that address is the proxy's, so you need to tell the server to trust the forwarded address instead.

Set `TRUST_PROXY_HEADERS=true` and the server reads the client address from the `X-Forwarded-For` header instead of the connection. That header is a comma-separated list to which each proxy appends the address it received the connection from, so the entries a client can forge always sit to the left of the one your outermost trusted proxy added.

Use `TRUST_PROXY_DEPTH` to tell the server how many trusted proxies sit in front of it. The client address is then read that many entries from the right of the list: a single ingress or load balancer uses `1` (the default), a CDN in front of a load balancer uses `2`, and so on. Set it to the exact number of proxies you control, because a value that is too low reads an intermediate proxy address, while one that is too high would trust an entry the client can supply.

Only enable `TRUST_PROXY_HEADERS` when the server is reachable exclusively through your trusted proxies, since a client that can reach the server directly could otherwise forge the header.

## When nothing is enforced

Token access lives in the control-plane database and is an Enterprise feature. In stateless mode there is nothing to enforce against, and without an active license the server enforces nothing: every token behaves as if it had no rules and no allowlist. Access already saved is kept, and enforcement resumes as soon as a valid license is present. Editing a token's access always requires an active license.

## Set it up

{% stepper %}
{% step %}
**Open the token's access panel**

In the dashboard, go to **API tokens** and open **Token access** for the token you want to scope.
{% endstep %}

{% step %}
**Restrict the token**

Switch the token from full access to scoped and add one rule per branch pattern with the actions it grants. To pin the token to your networks, add one IP address or CIDR range per line in the allowlist.
{% endstep %}

{% step %}
**Save and verify**

Save, then run an `eoas` command the token should no longer be allowed to perform: it must fail with a `403` naming the action and the branch, or refusing the source IP.
{% endstep %}
{% endstepper %}

## Common setups

| Token                  | Access                                       | Effect                                             |
| ---------------------- | -------------------------------------------- | -------------------------------------------------- |
| CI previews            | `pr-*` with `publish`, CI provider IP ranges | Publishes preview branches, only from CI           |
| Release pipeline       | `production` with `publish`                  | Ships to production, cannot roll it back           |
| On-call operator       | `production` with `rollback`                 | Rolls production back, cannot publish new updates  |
| Read-only dashboarding | `*` with `read`, office network only         | Sees everything, changes nothing, from one network |

## Auditing

Every change to a token's access is recorded in the [audit log](/xprem/security/audit-log.md): who changed it, which token, and the full rule list and IP allowlist after the change.

## Troubleshooting

* `403 this API key is not allowed to <action> on branch "..."`: the token is scoped and no rule grants that action on that branch. Check the pattern, and remember that `publish` does not grant `rollback`.
* `403 this API key cannot be used from this IP address`: the request came from outside the token's allowlist. The error includes the client IP the server resolved; if it shows your proxy's address instead of yours, review the `TRUST_PROXY_HEADERS` and `TRUST_PROXY_DEPTH` configuration above.
* `api key access rules require an active enterprise license` when saving: the license is missing or expired.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mercure-technologies.gitbook.io/xprem/security/token-access.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
