# Authorization: per-tool access via OpenFGA

> ToolMesh enforces per-tool authorization via OpenFGA in a User→Plan→Tool model. Covers bypass/restrict modes, DADL access levels, and tuples.json.

Canonical: https://www.toolmesh.io/en/authorization/

ToolMesh uses [OpenFGA](https://openfga.dev/) for fine-grained authorization. The model follows a **User → Plan → Tool** relationship structure.

## Modes

| Mode | Config | Behavior |
|------|--------|----------|
| `bypass` | `OPENFGA_MODE=bypass` | No authorization checks (default) |
| `restrict` | `OPENFGA_MODE=restrict` | OpenFGA enforced on every tool call |

Start with `bypass` for development, switch to `restrict` for production.

## Authorization Model

```
User --member_of--> Plan --can_execute--> Tool
```

Each user (from OAuth login or API key) has an assigned plan. Plans grant access to specific tools. When a tool call arrives, ToolMesh checks:

```
Check(user, can_execute, tool)
```

If denied, the execution stops immediately with an unauthorized error.

## DADL Access Levels

DADL tools declare an `access` classification:

| Level | Meaning |
|-------|---------|
| `read` | Read-only operations |
| `write` | Create/update operations |
| `admin` | Administrative operations |
| `dangerous` | Destructive or irreversible operations |
| *custom* | Extendable with any string value |

Policy files bundle these access levels into roles, and OpenFGA assigns roles to users.

## Configuration

```bash
OPENFGA_API_URL=http://openfga:8080    # OpenFGA API endpoint (Docker service name)
OPENFGA_STORE_ID=your-store-id          # OpenFGA store ID
OPENFGA_MODE=restrict                   # Enable enforcement
```

## Setup

The authorization model and relationship tuples are defined as editable files in `config/openfga/`:

| File | Purpose |
|------|---------|
| `model.fga` | Authorization model in FGA DSL |
| `tuples.json` | User/plan/tool relationships |
| `setup.sh` | Shell script that uses the `fga` CLI to apply model and tuples |

The `setup.sh` script runs on the host and uses `docker run` with the `openfga/cli` image to apply the model and tuples (the CLI image is distroless and has no shell, so it cannot run as a Docker Compose service):

```bash
# 1. Uncomment the OpenFGA services in docker-compose.yml (+ mysqldata volume)
# 2. Start all services
docker compose up -d

# 3. Bootstrap the OpenFGA store
./config/openfga/setup.sh

# 4. Copy the OPENFGA_STORE_ID from the output into .env
# 5. Set OPENFGA_MODE=restrict in .env
# 6. Restart
docker compose down && docker compose up -d
```

### Demo Tuples

The default `tuples.json` demonstrates authorization with a deny case:

- **Free plan** (all users via wildcard): can execute `echo_echo` and `echo_time`, but **not** `echo_add`
- **Pro plan** (company:acme members): can execute all tools

This lets you verify that authorization works correctly out of the box — free-plan users are denied access to `echo_add`.

## Caller-Origin Integration

Authorization decisions can also consider the **CallerClass** (trusted, standard, untrusted). This allows policies like "untrusted clients cannot access admin tools" even if the user's plan would normally allow it.
