This page describes
ravi.bot@next. Command counts and defaults come from that branch.How it fits together
The checks on this page run on the machine, in the daemon or the CLI. The Console checks only the calls made to it, such as member roles, page access and connector approvals.- Entry. Instance policies and routes decide who reaches an agent.
- Ceiling. A runtime profile and a list of capabilities cap what an agent may do.
- Turn. Each turn gets its own authority, from the ceiling and from who sent the message.
- Commands. Every
ravicommand declares its risk. Commands with outside or irreversible effects wait for--execute. - Record. Denials are stored and published, and each command publishes an audit event.
Who can reach an agent
Entry policies decide who reaches an agent. Each instance has one policy for direct messages and one for groups, and a route can override them.- The policy comes from the matching route’s
--policyfirst, then the instance, thenopen. - An explicit route admits its group under every group policy,
closedincluded. Any pattern other than*is explicit. ravi instances pending approveon a chat creates that route. On a contact, it sets the contact toallowed.- A sender who gets in without a contact receives text replies only. The turn has no tools and no commands.
ravi agents listing <agent> on --execute), and reach it from another machine only after the operator opens it with ravi agents requests <agent> on --execute. Those turns run as the person who asked, with no capabilities. See Machines.
Channel details, such as how Slack differs from WhatsApp, are in Instances and routing.
What an agent may do
Each agent has a ceiling: a runtime profile plus an optional list of capabilities. Each turn gets its own authority under that ceiling.
The authority of a turn depends on who sent it:
A chat becomes governed as soon as one contact holds a grant scoped to it. Set a narrow ceiling, grant each person what they need in the chat, and check the result:
ravi pages share, ravi invites and ravi access request, act for the person who wrote. The Console resolves that person through their ravi link. A turn that cannot be attributed is refused and never falls back to your login.
Other Console writes made in a chat, such as ravi pages ship and ravi bases rows, are credited to the person who wrote when they have linked their account with ravi link and are an active member of your organization. The Console then acts with the lesser of their role and yours, and records both of you in the audit log. Without that link the write is refused, and Ravi starts the link for them: on WhatsApp the agent asks them to message it in private (it never starts the conversation), and Ravi answers that message with their approval link. Once they approve, their private chat reaches the agent even if it had no route. Reads still use your login.
See Permissions for capabilities, the bootstrap floor, governed chats and admin tags.
Every command declares its risk
Everyravi command declares a kind (read or mutate), a resource, an action and a risk. A command without that declaration is denied, whoever runs it. The counts below are from 18 October 2026 and grow as commands are added.
The kind, resource and action form the capability the command checks, such as
mutate:agents:permissions. The older execute:group:<group> form also works, except on a few commands that need an exact resource. To read any command’s declaration:
access block shows kind, resource, action, risk and requiresConfirmation.
Exit codes and the write brake
157 commands carry the write brake. Without
--execute they print the plan and exit 3. Some brake only when the change adds authority: ravi agents permissions applies a reduction at once.
python, node, perl, ruby and php are always blocked, under every profile. bun -e is not blocked. See The Bash check.
Approvals
When a turn lacks a capability, Ravi can ask a trusted person in the chat. Today only the Pi and Grok providers ask for capabilities.
On every provider, a
ravi command outside the ceiling is refused without asking.
- Who can approve: a person whose chat identity links to a contact or agent. Its global capabilities must include the requested one, or
admin:system:*. Plans and specs needadmin:system:*, and chat-scoped grants do not count. - Slack: the Approve and Reject buttons. Reactions and replies are ignored.
- WhatsApp and other channels: react with 👍 or ❤️ to approve. Any other reaction, or a reply to the request, rejects it.
- Timeout: after 5 minutes without a decision, the request is denied.
- Scope: an approval covers the current runtime context only. For lasting access, use
ravi permissions alloworravi permissions resolve.
- The turn has no chat, such as
ravi sessions sendfrom your terminal. - The sender has no contact. The request would go back to that same sender, who could approve it.
- The agent is
chat-onlyand the request is for a tool, a CLI group or an executable.
ravi gmail send --execute, follow a separate path. The account owner approves on a Console page, and the command runs again with --approval <id>. See Connect your accounts.
Credentials
The credential broker keeps provider secrets out of Ravi’s database. Ravi stores the connection and a reference, and the secret stays in macOS Keychain or in Vault.- Each
ravi credentialscommand checks its own capability, such asmutate:credentials.broker:exec. No profile belowfull-accessholds these by default. - Adding a connection, removing one and running the broker are CLI-only. A
ravicommand from an agent turn is refused for them. broker execis braked. With--executeit resolves the secret inside the process and never prints it.- Native Slack reads its tokens from the broker when its channel names a connection. Its secret holds
appTokenandbotToken, as JSON or as env lines.
policies explain prints the policy the broker declares for an action. Using a connection needs use:credential:<provider>:<connection> and execute:<provider>:<action>. Every action except a short list of reads, such as auth.check and users.list, is marked as needing approval.
Keys for apps
Apps, scripts and external CLIs call Ravi with a context key (rctx_...). A key is a lease of named capabilities, and it never holds more than the context that issued it.
Isolated CLIs never run locally
A process that carriesRAVI_CONTEXT_KEY sends every command to the daemon’s gateway. It uses RAVI_GATEWAY_URL when set, otherwise the daemon’s socket at ~/.ravi/cli-gateway.sock. The daemon that owns the key authorizes the command.
If no gateway is reachable, the command fails with GATEWAY_REQUIRED. It never falls back to running locally against another database. CLI-only commands are refused in this mode.
The HTTP gateway
- The gateway binds to
127.0.0.1by default. The daemon refuses to start on any other host unlessRAVI_GATEWAY_NETWORK_AUTHORIZED=1is set. - Superadmin commands are refused through the gateway unless
RAVI_SDK_GATEWAY_ALLOW_SUPERADMIN=1is set. - CLI-only commands are not exposed through the gateway at all.
Plug in your own authority
Theexternal-authority provider lets an outside issuer cap what agents may do. The issuer signs an assertion, and Ravi verifies it locally, with no network call. Any doubt is a denial.
How it combines. Ravi asks every provider in the chain, and any denial wins. With external-authority added, a request must pass the agent’s ceiling and match the assertion. The provider never grants a capability on its own.
What it covers. Checks that carry the agent’s identity: every ravi command an agent runs, and tool and shell requests on Pi and Grok. Your own terminal commands stay with operator-control, which is always in the chain.
What it does not cover yet. Outside governed chats, Claude Code and Codex check built-in tools and shell commands against the agent’s ceiling directly. The assertion does not apply to those checks.
1
Write and sign the assertion
sub is the subject, such as agent:<id>. iat and exp are seconds since the epoch, and exp is required. In a scope entry, * matches any value of that whole field.To sign, remove sig and serialize the rest as JSON with keys sorted at every level and no spaces. Sign those bytes with an Ed25519 private key and put the base64 signature in sig.A matching entry with "requiresApproval": true answers “needs approval” instead of “allow”. A ravi command has no way to ask, so it is refused.2
Point Ravi at the assertion and the public key
permissions. needs admin:system:*. You hold it at your terminal. Inside an agent turn, only an agent that holds it, such as a full-access agent, passes.3
Add the provider to the chain
4
Check the chain
authorization providers line must list external-authority. Ravi reads the setting and the assertion file on every check, so no restart is needed. To rotate, replace the assertion file.
To turn it off, remove the setting. The command is braked:
Audit
Denials are stored and published, and each command publishes an audit event. Apps can watch the audit topics live over the SDK.view:system:audit can stream every ravi.audit.> event from GET /api/v1/_stream/audit, or with audit() in the SDKs.
ravi.audit.>andravi._cli.cli.>are kept in theRAVI_EVENTSJetStream stream for 7 days, up to 512 MB. It is a replay buffer, not an archive.- Events on
ravi.<sessionKey>.cli.*topics are published live only. The denials table is the lasting record. - Ravi has no sync that sends the denials table or the audit topics to the Console. See What leaves your machines for what does reach it.
RAVI_SUPPRESS_AUDIT_EVENTS=1in a process’s environment, the daemon or the CLI, stops that process from publishing audit events. Denial rows are still written. Keep it out of~/.ravi/.env.