---
title: Architecture
---
# Architecture Overview

## System at a Glance

```mermaid
graph TB
    subgraph "External"
        USER[WhatsApp User]
        WA[WhatsApp Cloud API]
        SHOPIFY[Shopify Store]
        LLM[LLM Provider]
    end

    subgraph "Gateway (FastAPI)"
        WEBHOOK[Webhook Handler<br/>Thin, non-blocking]
    end

    subgraph "Rivet Actor Cluster"
        RIVET[Rivet Runtime]
        A1["Actor: User A<br/>(AgentOS + Pi Agent)"]
        A2["Actor: User B<br/>(AgentOS + Pi Agent)"]
        AN["Actor: User N<br/>(AgentOS + Pi Agent)"]
    end

    subgraph "Storage"
        DURABLE[(Rivet Durable State)]
    end

    USER -->|message| WA
    WA -->|webhook| WEBHOOK
    WEBHOOK -->|get_actor| RIVET
    RIVET --> A1
    RIVET --> A2
    RIVET --> AN

    A1 -->|Shopify MCP| SHOPIFY
    A1 -->|LLM calls| LLM
    A1 -->|send reply| WA
    A1 -->|persist| DURABLE

    WA -->|deliver| USER

    style RIVET fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px
    style A1 fill:#c8e6c9,stroke:#388e3c
    style A2 fill:#c8e6c9,stroke:#388e3c
    style AN fill:#c8e6c9,stroke:#388e3c
```

## Component Responsibilities

| Layer | Component | Responsibility |
|-------|-----------|---------------|
| Messaging | WhatsApp Cloud API | Receive/send messages, media, interactive messages |
| Gateway | FastAPI | Thin webhook handler, immediately returns 200, routes to Rivet |
| Orchestration | **Rivet** | Durable actor lifecycle, routing, distribution, sleep/wake |
| Sandbox | **AgentOS** | Per-actor V8 isolate with POSIX-compatible OS |
| Agent Runtime | **Pi Agent Core** | LLM orchestration, tool execution, streaming, events |
| Extensions | Custom | MCP client, WhatsApp formatter, state manager, analytics |
| E-commerce | **@shopify/dev-mcp** | Product search, cart, checkout, orders, policies |
| State | Rivet Durable State | Persistent conversation + user state |

## The Thin Gateway Pattern

FastAPI is intentionally dumb. Its only jobs:

1. Receive webhook POST from WhatsApp
2. Verify signature
3. Extract `user_id` (phone number) and `text`
4. Call `rivet.get_actor(ShoppingAgentActor, user_id)` — this creates the actor if it's new
5. Send the message to the actor via `actor.on_message(text)`
6. Return `200 OK` to WhatsApp immediately (so WhatsApp doesn't retry)

**Everything else happens inside the actor.** No agent logic, no message queuing, no state management lives in the gateway.

```python
@app.post("/webhook")
async def webhook(request: Request, background_tasks: BackgroundTasks):
    body = await request.json()
    for msg in extract_whatsapp_messages(body):
        user_id = msg["sender"]
        actor = await rivet.get_actor(ShoppingAgentActor, user_id)
        background_tasks.add_task(actor.on_message, msg["text"])
    return {"status": "ok"}
```

That's it. That's the entire gateway.

## The Actor Pattern

Each user gets one durable actor. The actor:

- Is created automatically on first webhook hit
- Contains an AgentOS V8 isolate
- Runs a Pi Agent Core instance
- Has Shopify MCP tools registered
- Handles its own rate limiting, injection detection, state persistence
- Sleeps after 5 minutes of inactivity
- Wakes up when a new message arrives
- Terminates after 1 hour idle
- State persists across sleep/wake/restarts

```python
@rivet.actor
class ShoppingAgentActor(Actor):
    async def on_init(self):
        # Create Pi Agent, register tools

    async def on_message(self, text) -> str:
        # Rate limit → inject check → process → save state → return

    async def on_wake(self):
        # Reconnect resources

    async def on_terminate(self):
        # Final cleanup
```

## Data Flow — Message Lifecycle

```mermaid
sequenceDiagram
    actor U as User
    participant W as WhatsApp
    participant G as FastAPI Gateway
    participant R as Rivet
    participant A as Actor (per user)
    participant P as Pi Agent
    participant L as LLM
    participant M as @shopify/dev-mcp
    participant S as Shopify

    U->>W: Message
    W->>G: Webhook POST
    G-->>W: 200 OK
    G->>R: get_actor(user_id)

    alt New user
        R->>A: Create + on_init()
    else Existing user
        R->>A: Wake if sleeping
    end

    R-->>G: actor
    G->>A: on_message(text)
    A->>P: agent.prompt(text)
    P->>L: Stream
    L-->>P: Tool call
    P->>M: Execute Shopify tool
    M->>S: GraphQL
    S-->>M: Data
    M-->>P: Result
    P->>L: Continue
    L-->>P: Response
    P-->>A: Result
    A->>A: Save state (durable)
    A-->>G: Response
    G->>W: Send reply
    W-->>U: Reply

    Note over A: Sleeps after 5min idle
```

## Key Design Principles

1. **Actor-per-user** — One durable Rivet actor per WhatsApp user
2. **Thin gateway** — FastAPI routes only, no agent logic
3. **Automatic creation** — Actors created on first webhook hit, no registration
4. **Tool-first** — Agents execute Shopify MCP tools, not just text generation
5. **Stateful** — Durable state survives sleep/wake/restarts
6. **E-commerce native** — Official `@shopify/dev-mcp`, Storefront-scoped
7. **Defense in depth** — Rate limits, injection detection, sanitization at every layer
8. **Horizontally scalable** — Rivet distributes actors across cluster

## Documents

| Document | Scope |
|----------|-------|
| [Architecture](/architecture) | This overview |
| [Agent Architecture](/agent-architecture) | Agent stack + layers |
| [Rivet Actor Model](/rivet-actor-model) | Actor implementation + code |
| [Agent Lifecycle](/agent-lifecycle) | Message → actor flow |
| [AgentOS Configuration](/agentos-configuration) | Per-actor sandbox |
| [Pi Agent Setup](/pi-agent-setup) | Pi Agent Core install |
| [Pi Agent Core API Reference](/pi-agent-core-api-reference) | Python API |
| [Building Pi Extensions](/building-pi-extensions) | MCP, formatter, state, analytics |
| [Concurrency & Security](/concurrency-security) | Rate limiting, injection |
| [Shopify Integration Research](/shopify-integration-research) | MCP decision |
| [WhatsApp Business Cloud API](/whatsapp-business-cloud-api) | WhatsApp integration |
| [Integration Guide](/integration-guide) | End-to-end wiring |
