reef

Contained AI agents on your servers.

Scopes inside an agent

The Access policy on an agent’s hostname decides who gets in. What they can do once inside is OpenClaw’s, and it is three settings that compose rather than one. This page is what the enterprise roles ship, and how to give one named person more.

Give this to your agent:

Read https://reef.clawbits.ai/docs/enterprise/scopes.md before changing any
scope on a running agent. Tell me which of the three settings you are changing
and why. Never put operator.admin in deviceAutoApprove. Write config as the node
user, never as root.

The three settings

setting grants to lives in
gateway.auth.trustedProxy.deviceAutoApprove.scopes every operator device that passes SSO the role, seeded once
gateway.auth.identityScopes one named email config, editable live
gateway.roles a ceiling every session is filtered through config plus runtime state

They are not alternatives. The device grant is capped by deviceAutoApprove when an operator device first pairs, identityScopes is unioned on top at every Control UI connection, and the gateway.roles ceiling, if configured, filters the result last.

What the roles ship

"deviceAutoApprove": {
  "enabled": true,
  "scopes": ["operator.read", "operator.write", "operator.approvals", "operator.questions"]
}

That is the shared baseline, and it is byte-identical to OpenClaw’s own default; the roles state it explicitly so a reviewer can read it. enabled: true is a requirement rather than a convenience: a trusted-proxy Control UI session with no paired device is admitted and then stripped of every scope, and it cannot approve its own pairing.

operator.admin is deliberately absent. It is the one scope that short-circuits every check, and it alone unlocks the operator terminal, config mutation, the secret store, and session deletion. In deviceAutoApprove it would go to every operator device that passed SSO rather than to a person.

Note that deviceAutoApprove.scopes is validated as plain strings, not against the scope list, so a misspelled scope boots cleanly and silently narrows what your team can do.

Granting one person admin

identityScopes is the only per-person grant that lives in config. It is keyed by the email the proxy supplied, matched exactly and then case-insensitively for keys containing @, and it is unioned with the device’s scopes on every connection:

reef agent exec coding -- runuser -u node -- env HOME=/home/node \
  openclaw config set gateway.auth.identityScopes \
  '{"ana@example.com":["operator.admin"]}' --strict-json

Use the whole-map form. An email contains dots, so a dotted path would be parsed as path segments. The map replaces the grant list rather than adding to it, so read the current value first and keep every name in it; pass that value as --expect-current-json and a concurrent edit fails the write instead of losing it. runuser -u node is not optional: the config is node-owned 0600, and a write as root replaces it by rename and leaves it root-owned, which breaks the gateway’s own config writes from then on. The command reports whether a restart is needed; reef agent stop then reef agent start keeps the volume, so the config, the sessions and the user profiles all survive.

The grant applies on the person’s next connection, so they reload the tab. The gateway logs identity scope grant elevated connection when it happens.

The trap. When the Control UI says Administrator access required, it suggests running openclaw devices on the gateway. That cannot work here. The CLI is a gateway client, and under trusted-proxy a loopback peer is refused before any pairing logic runs, so an in-VM invocation returns a bare unauthorized. Use identityScopes.

Why the roles do not ship gateway.roles

gateway.roles looks like the answer to per-person permissions and is not. It carries named capability bundles and a default, with no email, group or pattern matching of any kind. Binding a person to a bundle is durable state inside the agent, written only by the users.setRole RPC, which requires operator.admin and has neither a CLI nor a Control UI.

Configuring it also changes failure from soft to hard: an identity with no resolvable profile gets an empty scope set rather than the default, and the device handshake is refused outright. A role file cannot seed the assignments that would make that safe, so the roles leave it unset and everyone shares the baseline until you name them individually.

What is still shared

Scopes decide what a session may call, not what it can see: one agent is one session list, one workspace, one credential pool and one cookie jar whatever anyone’s scopes are. When people should not share those, give them separate agents rather than separate scopes. See set up a team.

The other axis

Scopes govern the operator device. A terminal inside the VM is a different identity, a different audit trail, and unaffected by anything on this page.

Next: terminal access.