npm package: @ravi-hq/openclaw-plugin
Description: OpenClaw plugin for Ravi — identity provider for AI agents. Email & SMS channels + full agent toolbox.
Latest version: 0.1.0

# @ravi-hq/openclaw-plugin

[![npm version](https://img.shields.io/npm/v/@ravi-hq/openclaw-plugin)](https://www.npmjs.com/package/@ravi-hq/openclaw-plugin)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

OpenClaw plugin for [Ravi](https://ravi.app) -- identity provider for AI agents.
Gives your agent a real email address, phone number, password manager, and
encrypted vault through a single plugin.

## Features

- **2 messaging channels** -- real-time email and SMS via Server-Sent Events
- **17 agent tools** -- manage identities, inboxes, passwords, vault secrets, and more
- **E2E encryption** -- PIN-based Argon2id + NaCl SealedBox, compatible with Ravi CLI
- **DM policy controls** -- allowlist or open policy per channel account
- **Lazy crypto** -- encryption keys derived on first use, not at startup

## Installation

```bash
openclaw plugins install @ravi-hq/openclaw-plugin
```

## Quick Start

### 1. Install and authenticate

```bash
openclaw plugins install @ravi-hq/openclaw-plugin
openclaw ravi login
```

This opens a browser for device-code authentication. Copy the access token from the output.

### 2. Add your token and PIN to the plugin config

```yaml
# In your OpenClaw config
plugins:
  ravi:
    token: "eyJhbGciOiJIUzI1NiIs..."   # From 'openclaw ravi login'
    pin: "123456"                        # Your 6-digit E2E encryption PIN
```

### 3. Run setup to configure channels

```bash
openclaw ravi setup
```

This lists your identities and generates a channel configuration snippet you
can paste into your OpenClaw config.

## Configuration

```yaml
plugins:
  ravi:
    # Ravi API base URL (default: https://ravi.app)
    apiUrl: "https://ravi.app"

    # JWT access token (required)
    token: "your-access-token"

    # 6-digit E2E encryption PIN (required)
    pin: "123456"

    # Channel accounts (from 'openclaw ravi setup')
    channels:
      ravi-email:
        accounts:
          "identity-uuid-here":
            identityUuid: "identity-uuid-here"
            identityName: "My Agent"
            email: "agent@in.ravi.app"
            dm:
              policy: "allowlist"           # "allowlist" or "open"
              allowFrom:
                - "you@gmail.com"

      ravi-sms:
        accounts:
          "identity-uuid-here":
            identityUuid: "identity-uuid-here"
            identityName: "My Agent"
            phone: "+15551234567"
            dm:
              policy: "allowlist"           # "allowlist" or "open"
              allowFrom:
                - "+15559876543"
```

## Channels

### Ravi Email

Real-time email channel powered by SSE events from the Ravi backend.

- **Threading model:** Each email thread maps 1:1 to an OpenClaw session. A new
  `thread_id` creates a new session; subsequent messages in that thread resume it.
- **Session key format:** `agent:{agentId}:ravi-email:{accountId}:thread:{threadId}`
- **DM policy:** Set `policy: "allowlist"` to restrict incoming email to specific
  sender addresses, or `"open"` to accept all senders.
- **Outbound:** Replies are sent via the Ravi API and threaded correctly in the
  original email conversation.

### Ravi SMS

Real-time SMS channel powered by the same SSE stream.

- **Threading model:** All messages from one phone number form a single session.
  A new `from_number` creates a new session; subsequent messages from that number
  resume it.
- **Session key format:** `agent:{agentId}:ravi-sms:{accountId}:dm:{phoneNumber}`
- **DM policy:** Set `policy: "allowlist"` to restrict incoming SMS to specific
  phone numbers (E.164 format), or `"open"` to accept all senders.
- **Outbound:** Replies are sent via the Ravi API as plain-text SMS.

## Agent Tools

The plugin registers 17 tools that agents can invoke.

### Identity

| Tool | Description | Parameters |
|------|-------------|------------|
| `ravi_identity_list` | List all identities for the authenticated user | -- |
| `ravi_identity_create` | Create a new identity (provisions email + phone + vault) | `name` (string, required) |
| `ravi_get_info` | Get the active identity's email and phone number | -- |

### Email Inbox

| Tool | Description | Parameters |
|------|-------------|------------|
| `ravi_inbox_email` | List email threads, optionally unread only | `unread` (boolean) |
| `ravi_read_email` | Read all messages in an email thread | `thread_id` (string, required) |
| `ravi_email_compose` | Compose and send a new email | `to`, `subject`, `body` (all required) |
| `ravi_email_reply` | Reply to an existing email | `message_id` (number, required), `body` (required), `reply_all` |

### SMS Inbox

| Tool | Description | Parameters |
|------|-------------|------------|
| `ravi_inbox_sms` | List SMS conversations, optionally unread only | `unread` (boolean) |
| `ravi_read_sms` | Read all messages in an SMS conversation | `conversation_id` (string, required) |

### Passwords

| Tool | Description | Parameters |
|------|-------------|------------|
| `ravi_passwords_list` | List all saved passwords (passwords hidden in list view) | -- |
| `ravi_passwords_get` | Get a specific password entry with decrypted password | `uuid` (string, required) |
| `ravi_passwords_create` | Create a new password (encrypted) | `domain` (required), `username`, `password`, `notes` |
| `ravi_passwords_delete` | Delete a password entry | `uuid` (string, required) |

### Vault

| Tool | Description | Parameters |
|------|-------------|------------|
| `ravi_vault_list` | List all secret keys in the vault | -- |
| `ravi_vault_get` | Get a secret by key (auto-decrypted) | `key` (string, required) |
| `ravi_vault_set` | Store a secret (auto-encrypted) | `key` (string, required), `value` (string, required) |

### Feedback

| Tool | Description | Parameters |
|------|-------------|------------|
| `ravi_feedback` | Submit feedback about Ravi | `message` (required), `category` (bug/feature/help/other) |

## CLI Commands

Run these via `openclaw ravi <command>`:

| Command | Description |
|---------|-------------|
| `login` | Authenticate via device-code flow (opens browser) |
| `status` | Show auth status and list available identities |
| `setup` | Generate channel configuration from your identities |

## E2E Encryption

All sensitive data (passwords, vault secrets, email content at rest) is encrypted
end-to-end using a 6-digit PIN.

- **Key derivation:** PIN + server-stored salt -> Argon2id (time=3, mem=64MB, threads=1) -> 32-byte seed
- **Encryption:** NaCl SealedBox (ephemeral X25519 keypair + Poly1305 MAC)
- **Ciphertext format:** `e2e::<base64>` -- same format used by the Ravi CLI
- **PIN verification:** On first use, the plugin derives the keypair and verifies
  it against the server's stored verifier before proceeding

The crypto layer is lazy-initialized: keys are only derived when a tool actually
needs encryption or decryption.

## Architecture

```text
Agent (OpenClaw runtime)
   |
   +-- Channels (ravi-email, ravi-sms)
   |     '-- SSE stream --> GET /api/events/stream/
   |           Events arrive in plaintext (server decrypts before dispatch)
   |
   +-- Tools (17 agent tools)
   |     '-- REST calls --> GET/POST/DELETE /api/...
   |           Identity-scoped via X-Ravi-Identity header
   |
   '-- Crypto layer (lazy-initialized)
         PIN + salt --> Argon2id --> NaCl SealedBox
         Decrypts historical data from REST ("e2e::<base64>" fields)
         Encrypts vault/password writes before sending to server
```

Key points:

- **SSE events are plaintext** -- the server decrypts before pushing to the stream.
  No client-side decryption needed for real-time channel messages.
- **REST data may be encrypted** -- historical email content, passwords, and vault
  secrets can contain `"e2e::<base64>"` ciphertext that the plugin decrypts
  client-side using the PIN-derived keypair.
- **Authentication** uses unbound JWTs. The `X-Ravi-Identity` header scopes each
  API request to a specific identity.

## Development

### Prerequisites

- Node.js 18+
- npm

### Setup

```bash
npm install
```

### Scripts

```bash
npm run build        # Compile TypeScript to dist/
npm test             # Run tests (vitest, single run)
npm run test:watch   # Run tests in watch mode
npm run lint         # Type-check without emitting (tsc --noEmit)
```

### Project structure

```text
src/
  index.ts            # Plugin entry point and registration
  types.ts            # TypeScript interfaces for API responses and SSE events
  client.ts           # RaviClient -- typed HTTP client for all REST endpoints
  sse.ts              # RaviSSEClient -- SSE with auto-reconnect and heartbeat
  crypto.ts           # E2E encryption (Argon2id + NaCl SealedBox)
  service.ts          # Background listener service (manages SSE connections)
  cli.ts              # CLI commands (login, status, setup)
  channels/
    email.ts          # ravi-email channel definition
    sms.ts            # ravi-sms channel definition
  tools/
    identity.ts       # Identity management tools
    inbox.ts          # Email and SMS inbox tools
    email-send.ts     # Email compose and reply tools
    passwords.ts      # Password manager tools
    vault.ts          # Vault secret tools
    feedback.ts       # Feedback tool
  __tests__/          # Test files (*.test.ts)
```

## License

MIT