Skip to main content
This page gives you a working mental model of a Ravi install: which processes run, who starts each one, how a Slack or WhatsApp message reaches an agent and how the reply gets back, and where state lives. Use it when something does not answer and you need to know where to look.

The picture

Processes

Ravi runs as PM2 processes. Each one is started by a different command. ravi daemon start starts only ravi. If you use Slack, you also run ravi channels start once. If you skip Omni because you only use Slack, you run your own nats-server under PM2 instead of omni-nats (see Install). Restarting the daemon does not restart the runner, and the other way around. ravi update restarts only the processes that were online before it ran. When native channels are enabled and ravi-channels was not running, it warns, and you start the runner with ravi channels start.

NATS JetStream: the bus

Every Ravi process talks through one NATS server with JetStream, at NATS_URL (default nats://127.0.0.1:4222). Ravi does not ship a NATS server. It uses the one Omni installs, or one you run yourself. Ravi creates these streams itself: the daemon creates SESSION_PROMPTS and RAVI_EVENTS at start, and the channel runner creates CHANNEL_OUTBOUND. Omni owns the MESSAGE, INSTANCE and REACTION streams, which the daemon reads. See Events for every topic.

How a message flows

Inbound. Slack sends the event to ravi-channels over Socket Mode. The runner resolves the channel account, applies the instance policies and routes, picks the agent and session, saves any attachment into the agent’s working directory, and publishes a prompt to ravi.session.<name>.prompt. The daemon picks it up from SESSION_PROMPTS and runs the turn.Outbound. When the agent replies, the daemon’s delivery layer sees that the target channel is Slack and writes an outbound job to CHANNEL_OUTBOUND. The runner consumes the job and posts it with the Slack Web API, in the thread the routing policy chose. While the turn runs, the runner shows a “working” status in the thread.Slack tokens are read by the runner from the credential broker. See Slack.
Routing is the same for both: an instance maps a channel account to an agent, routes override that per chat pattern, and the DM scope decides which messages share a session. See Routing.

Sessions and the runtime

A session is one conversation thread with an agent, with its own history and provider state. Its key encodes where it came from, for example agent:main:dm:<peer> or agent:main:slack:<account>:group:<id>. See Sessions. When a prompt arrives, the daemon runs the turn with the session’s provider, in the agent’s working directory (~/ravi/<agent>), which holds the agent’s AGENTS.md: A session uses its own override if it has one (ravi sessions set-provider), otherwise the provider it last used, then the agent’s preset or provider, then the runtime.defaultProvider setting, then codex; ravi agents set moves sessions that have no override to the agent’s new provider. Set your agent explicitly:
Messages that arrive while a turn runs are queued and delivered in order. Agents use the ravi CLI as their tools, checked against their permissions (see Permissions). Provider CLIs that run in a sandbox reach ravi through a local socket, ~/.ravi/cli-gateway.sock.

Background runners

Inside the daemon, runners handle work that is not a reply to a message: cron jobs, heartbeats, event triggers, hooks, background jobs, task checkpoints and the Console inbox poller. If you run more than one daemon against the same NATS server, they elect a leader through a NATS key-value lease (30 seconds, renewed every 10). Only the leader runs cron, heartbeat and the other scheduled runners, so a job never fires twice. Every daemon runs triggers, hooks and the Console poller.

Storage

All of it is local SQLite and files. The full list is in Configuration.

HTTP gateway and SDK

Set RAVI_HTTP_PORT and the daemon serves an HTTP API on 127.0.0.1 (change it with RAVI_HTTP_HOST). Under /api/v1/ it exposes the CLI’s commands (a few long-running ones stay CLI-only), authorized with Authorization: Bearer rctx_... runtime context keys. The same server receives webhooks. Binding a non-loopback address requires RAVI_GATEWAY_NETWORK_AUTHORIZED=1. The gateway builds its routes from the CLI’s command registry, and the TypeScript, Dart and Swift SDKs and the OpenAPI spec are generated from it; the pre-push hook and CI fail if they drift. See SDKs and HTTP gateway.

The Ravi Console

The Console (https://console.ravi.bot) is optional. ravi login links this install to your Console organization and stores the login in ~/.ravi/cloud-auth/. After that:
  • Inbox. The daemon polls the Console agent inbox every 15 seconds and publishes each item locally on ravi.console.inbox.item, where triggers can react to it. Without a login the poller stays paused.
  • Pages. ravi pages ship publishes HTML to your project host, <org>-<project>.ravi.page. See Pages.
  • Bases. ravi bases manages typed tables in a Console project. ravi bases subscribe <base> sends row changes to the inbox, so they arrive as events. See Bases.
See Log in to the Console.