> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avocadostudio.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> How the three services, packages, and data flow fit together.

This page is for the developer or self-hoster who wants the map: which process does what, which package holds it, and how a change travels between them.

## System overview

Avocado Studio is a pnpm monorepo with three apps and fourteen packages — twelve published to npm, two private:

```mermaid theme={null}
graph TD
    subgraph Clients["Inbound channels — pick any (or all)"]
        direction TB
        Editor["<b>Editor (:4100)</b><br/>Vite + React<br/>Chat panel · live preview iframe"]
        MCP["<b>MCP Server</b><br/>stdio + streamable HTTP<br/>Claude Desktop · Claude Code · any MCP host"]
        Jira["<b>Jira integration</b><br/>webhook + polling<br/>standalone server only"]
    end

    subgraph Orchestrator["Orchestrator — in your Next.js app, or standalone on :4200"]
        O1["AI planning (LLM)"]
        O2["Operation validation"]
        O3["Session state + undo/redo"]
        O4["Publishing pipeline"]
    end

    subgraph Site["Site (:3000) — Next.js or Astro"]
        S1["Renders BlockInstance pages"]
        S2["Draft mode / Published mode"]
        S3["Block manifest API"]
    end

    Editor -->|"HTTP (REST + SSE)"| Orchestrator
    MCP -->|"HTTP (REST)"| Orchestrator
    Jira -->|"HTTP (REST)"| Orchestrator
    Editor <-->|"postMessage"| Site
    Orchestrator <-->|"HTTP"| Site
```

The orchestrator runs in one of two hosts. In **library mode**, `createOrchestrator()` mounts it inside your own Next.js app, usually at `/api/avocado`, and the site and the orchestrator are one process. The **standalone server** is a Fastify app on `:4200`, for Docker and other self-hosted deployments. Both serve the same routes from the same `@avocadostudio-ai/orchestrator-core` code.

It has **three parallel front doors**: the editor web app for humans, the MCP server for AI assistants in any MCP host, and the Jira integration for ticket-driven workflows. All three go through the same operation pipeline — Zod validation, undo history, version log, demo-mode gating — so anything you can do in the web editor, you can do from an MCP client or a Jira ticket, and the other way round. The Jira integration runs on the standalone server only. See [MCP server](/integration/mcp-server) and [Jira integration](/integration/jira) for setup.

That the vocabulary is the same at every door is the point, and it is also the safety boundary: all three doors speak [operations](/concepts), and an operation cannot express a change to your code.

## Data flow: from chat to preview

When a user sends a message in the editor, here's what happens:

```mermaid theme={null}
sequenceDiagram
    participant CS as Editor
    participant O as Orchestrator
    participant LLM
    participant S as Site

    CS->>O: POST /chat/start (message + page context)
    O-->>CS: streamId
    CS->>O: GET /chat/stream?streamId=… (SSE)
    O->>LLM: prompt with block schemas + page state
    LLM-->>O: structured edit plan (JSON)
    O->>O: validate every operation against its Zod schema
    O-->>CS: stream plan + per-op results with a new preview version (SSE)
    CS->>S: postMessage — draftUpdated
    S->>O: GET /draft/pages
    S->>S: re-render the changed blocks
```

The validation step is where a malformed plan dies. An operation naming a prop the block does not declare, or a value the schema rejects, never reaches your content — it comes back as a skipped op with a reason, not as a broken page.

## Packages

The monorepo includes these packages — the ones under `@avocadostudio-ai` are published to npm, the `@ai-site-editor` ones are monorepo-only:

| Package | Purpose |
| - | - |
| `@avocadostudio-ai/shared` | Zod schemas for PageDoc, BlockInstance, Operation, EditPlan. Block registry. Shared types across all apps. |
| `@avocadostudio-ai/blocks` | 20 built-in block renderers (Hero, CTA, FAQ, Gallery, etc.). Each block is a React component with a typed Zod schema. |
| `@avocadostudio-ai/preview-adapter` | PreviewBridge component that runs inside the site iframe. Handles postMessage communication with the editor, block selection overlays, and CSS highlights. |
| `@avocadostudio-ai/site-sdk` | SDK for integrating AI editing into any Next.js 15+ site. Provides route handlers, draft mode utilities, content resolution for the editor, the field-table lens packs for Storyblok, Sanity and Contentful, and two commands: `avocado-register` and `avocado qa`. |
| `@avocadostudio-ai/astro` | Astro integration (Astro 5+). Mounts the editor API routes, injects the preview bridge and resolves draft props into `Astro.locals`, so `.astro` components stay the renderer — no React, no islands. |
| `@ai-site-editor/editor-puck` | [Puck](https://puckeditor.com/)-based visual drag-and-drop editor, including the chat sidebar prototype. Generates the same ops as chat mode and publishes via the orchestrator. Monorepo-only — not published. |
| `create-avocado-site` | `npm create avocado-site`. Bootstraps a whole runnable demo project — the Avocado Hub, the orchestrator mounted in library mode, one `npm run dev` that starts the site and the editor together — or, run inside an existing Next.js app, writes the wiring for it (editor API routes, page routes, block manifest, request rewrite, optional CMS template). |
| `@avocadostudio-ai/migration-sdk` | Utilities for migrating existing content into the PageDoc / BlockInstance shape. |
| `@ai-site-editor/immersive-widget` | Embeddable widget used for immersive / full-bleed block experiences. Monorepo-only — not published. |
| `@avocadostudio-ai/orchestrator-core` | The orchestrator as an embeddable library — session state, AI planning, operations engine, publishing, and the CMS adapter interface. Powers `createOrchestrator()` in library mode. |
| `@avocadostudio-ai/mcp-server` | Model Context Protocol server exposing 49 page/block/discovery tools over stdio (`avocado-mcp`) and streamable HTTP (`avocado-mcp-http`), so any MCP host drives the same operation pipeline as the editor. |
| `@avocadostudio-ai/richtext` | The rich-text grammar and a ProseMirror-document pivot, with converters to and from Contentful rich text, Sanity Portable Text, Strapi Blocks, Storyblok rich text, and markdown. No runtime dependencies. |
| `@avocadostudio-ai/cli` | The `avocadostudio` self-host launcher — serves a prebuilt editor bundle and points it at any orchestrator and preview site you control. |
| `@avocadostudio-ai/skills` | The agent skills (`avocado`, `avocado-integrate`, `avocado-demo`, `avocado-blocks`, `avocado-cms`) and `npx @avocadostudio-ai/skills`, which installs them into an existing repository. `create-avocado-site` writes the same files into a new project. |

## Communication protocols

### Editor ↔ orchestrator: HTTP + SSE

The editor communicates with the orchestrator via REST API and Server-Sent Events:

* `POST /chat/start` — Start a streamed run, returns a `streamId`
* `GET /chat/stream?streamId=…` — Subscribe to the stream via SSE; a dropped connection can resubscribe
* `POST /chat/cancel` — Stop a running turn
* `POST /chat` — Non-streaming variant (one JSON response)
* `GET /draft/pages` — Fetch current draft page state
* `POST /ops` — Apply hand-authored operations (bypassing the planner)
* `POST /history/undo`, `POST /history/redo`, `GET /history/log`, `POST /history/restore` — Undo, redo and the version log
* `GET /publish/diff`, `POST /publish` — Review and publish the draft

### Editor ↔ site: postMessage

The editor embeds the site in an iframe. They communicate via the `site-editor/v1` postMessage protocol:

* **Editor → site**: Highlight or scroll to a block, navigate to a page, stream field values while a plan is written (`liveDraft`), refresh after a change lands (`draftUpdated`)
* **Site → editor**: The block a click selected, route changes, and acknowledgements of applied patches

### Site ↔ orchestrator: HTTP

The site fetches draft content from the orchestrator when in draft mode:

* `GET /draft/pages?session=…&slug=…` — One draft page by slug. Both parameters are required; an unknown slug is a 404.
* `GET /draft/slugs?session=…` — Every slug the session has, which is the route that lists pages
* `GET /draft/site-config?session=…&siteId=…` — The draft site configuration

In library mode the site's own draft reads go to the mounted orchestrator in-process when `ORCHESTRATOR_URL` is unset or names the mount, so a password-protected mount needs no extra token for them.

## Session state

The orchestrator maintains **per-session state** for each editing session:

* **Draft pages** — Current page content with all pending edits
* **Undo and redo** — Up to 50 entries per page in each direction
* **Version log** — Up to 100 entries, each with a restorable snapshot and who made it
* **Chat history** — The last six messages, so a follow-up has context
* **Site config** — Name, navigation, theme

Session state is scoped per session ID and site. Multiple users editing different sessions don't interfere with each other. State is persisted to a SQLite database (`.data/orchestrator.db`, via `better-sqlite3` + WAL). A request's writes are coalesced for about 30 ms and then written in one transaction, and a clean shutdown flushes any pending write before it exits.

Plans held for approval are kept in memory only. A restart drops them, and the person asks again.

**SQLite is the working copy, not the source of truth.** Your CMS / JSON file / custom store is the origin; SQLite holds drafts, undo stacks, and chat history scoped per session. On the first request for a fresh session, the orchestrator calls the configured [`CmsAdapter.getPages()`](/integration/cms-adapters) to seed SQLite. On publish, `onPublish(pages)` writes back. That separation is what lets the same chat UX work against any upstream store without per-integration handshakes.

<Note>
  The orchestrator runs as a single instance with a local SQLite file. Multi-replica horizontal scaling — which would require moving state to a network-accessible store (Postgres, Turso/libSQL, Redis, etc.) — is on the roadmap but not implemented today.
</Note>

## Publishing pipeline

Publishing promotes draft content to production:

```mermaid theme={null}
flowchart LR
    Draft["Draft (orchestrator)"] --> PT["PublishTarget"] --> Prod["Production (your site)"]
```

The `PublishTarget` interface is pluggable. Three built-in targets ship in the box:

* **`site-contract`** — POSTs pages + assets to the remote site's `/api/editor/publish` endpoint. Selected when `siteOrigin` is supplied. The receiving route is the site's, not the orchestrator's, and it fails closed twice: 401 when it runs under `NODE_ENV=production` with no publish secret, 409 when the payload would remove every page. Both carry a `reason` the editor shows.
* **`git`** — Serializes draft pages to JSON, commits, and pushes to a Git branch. A Vercel deploy hook wired to that branch auto-builds.
* **`deploy-hook`** — Calls a raw `VERCEL_DEPLOY_HOOK_URL` and polls the Vercel API for deployment status.

Register your own via `registerPublishTarget()` to integrate with any workflow — S3, GitLab Pages, Netlify, a CMS API, a custom CI/CD pipeline. See [How it works — Publishing](/how-it-works#publishing) for the full interface.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.