Skip to content

Architecture

Platform Overview

Rose is an AI-first inbound marketing platform built as a distributed system. The production Website Agent combines a browser widget, Cloudflare edge routing, FastAPI services on Cloud Run, LangGraph orchestration, retrieval infrastructure, and external model and enrichment providers.

Agent Architecture

Rose agents follow a common four-layer pattern:

flowchart TB UI[Frontend interface] --> API[API service] API --> Core[Agent core] Core --> Shared[Shared infrastructure]
Layer Purpose Website Agent example
Frontend interface User-facing surface React widget
API service Authentication, validation, and transport Search API
Agent core Business logic and orchestration ixchat LangGraph
Shared infrastructure Retrieval, models, storage, and configuration ixrag, ixllm, Redis, MongoDB, Neo4j

Active Agents

Agent Frontend Backend API Core package
Website Agent frontend/widget/ backend/apps/api/search/ ixchat

See Agents Overview for beta and roadmap agents.

Deployed Cloud Run services

Services are hosted in GCP project inboundx, in europe-west9 (Paris), except Factory relay in europe-west1 (Belgium). The table shows supported deployment names, not live deployment status; optional staging targets may be absent. — means no dedicated target. Uses production means the frontend calls the production function rather than an isolated copy.

Service Purpose Test Staging Production
Search API Website Agent chat, streaming and retrieval for widget/MCP search-test-test search-staging-staging search-production-production
Admin API Backoffice authorization, billing and private Knowledge API access admin-api-test admin-api-staging admin-api-production
Knowledge API Ingestion state, dispatch, recovery and status knowledge-api-test knowledge-api-staging knowledge-api-production
Content worker Discovery, scrape and clean tasks; GCS artifacts knowledge-content-worker-test knowledge-content-worker-staging knowledge-content-worker-production
IXVector API Vector.co identification webhooks and widget visitor-ID links ixvector-api-test ixvector-api-staging ixvector-api-production
R4A MCP Buyer tools: vendor questions, alternatives, offers, trials and purchases r4a-mcp-test — r4a-mcp-production
Rose MCP Claude/ChatGPT brand conversations, discovery and reply cards — rose-mcp-staging rose-mcp-production
Integrations API HubSpot OAuth, CRM webhooks, email-capture tasks and sync integrations-test-test integrations-staging-staging integrations-production-production
create-support-ticket Backoffice reports and visual feedback → Linear; Cloud Function Uses production — create-support-ticket
rose-factory-relay Legacy developer tooling (code now in Night Shift): Linear Agent webhooks → local coding workers, until its routes move to Night Shift assigned issues; outside application environments — — —

Deployment workflows are under .github/workflows/. develop generally deploys to test; Rose MCP is the exception and deploys to staging. Production releases run from main through release.yml, with components selected by changed paths. Pushes to the staging branch deploy Search, Admin and IXVector to staging. Knowledge API and content worker staging use manual workflow dispatch. Integrations staging uses its operator recipe; Factory relay is operator-deployed.

Search and Integrations retain <api>-<endpoint>-<environment> naming: search-production-production is one service, not two. Newer components use one environment suffix. Cloud Run's Container / Source / Function labels describe how a service was deployed, not whether it is obsolete.

Knowledge API and content worker require Google IAM/OIDC; Ingress: All does not bypass it. Other services are public at the Cloud Run boundary, with protection varying by route: API keys, user tokens, buyer OAuth or webhook checks. Rose MCP has no inbound authentication. IXVector's webhook uses a shared secret; its widget /link route is unauthenticated. ixvector handles Vector.co identification; chat vectors live in ixrag storage. The content worker produces artifacts; the document-loader job writes them to retrieval storage. See Knowledge ingestion control plane.

Low traffic or an old deployment date does not establish that a service is a leftover. Before retirement, check callers, external webhooks/MCP registrations and deployment workflows. In particular, Rose MCP and R4A MCP have different tool contracts, and rose-mcp-staging depends on the staging Search API.

System Components

  • Product surfaces: Astro marketing website, React widget, client backoffice, playground, and Chrome extension.
  • Cloudflare edge: Astro website Worker, the api.userose.ai unified proxy, Cloudflare Pages, GCS widget assets, and R2 GEO pages.
  • Cloud Run APIs shown in the diagram: Search, Admin, Knowledge, knowledge content worker, Integrations, IXVector, and Rose MCP.
  • Async execution: Cloud Tasks queues, Cloud Scheduler recovery, document loading, tagging, website mapping, PostHog batch processing, HubSpot sync, the fenced FAQ rollback job, and GEO publishing.
  • Data: Supabase Postgres, Redis, MongoDB, Neo4j, the rose-scraping GCS bucket, the widget CDN bucket, and Cloudflare R2.
  • Providers: model and embedding vendors, visitor enrichment providers, observability and analytics products, and CRM/tools.

Architecture Diagram

The diagram reads top-to-bottom through seven layers: users, product surfaces, Cloudflare edge, Cloud Run services, asynchronous orchestration, data, and external providers. Detailed arrows are limited to the real-time chat lane and the knowledge-ingestion control plane so the operational paths remain legible.

Rose system architecture

Source of truth: docs/diagrams/rose-system-architecture.dot. just dev and just build render the SVG into src/_generated/; do not edit or commit that generated asset. See the diagram inventory for ownership rules.

Component Details

Product Surfaces

Component Purpose Technology
Marketing website userose.ai Astro 6 with @astrojs/cloudflare
Widget Embeddable Website Agent React, TypeScript, UMD bundle
Client backoffice Analytics, configuration, content, and operations React, Vite, Cloudflare Pages
Playground Internal testing across clients and environments React, Vite, Firebase Hosting
Chrome extension Inject and test Rose on arbitrary sites WXT / Chrome Extension APIs

The standalone stats opt-out extension is a developer tool and is intentionally not shown in the hosted runtime diagram. See Frontend Overview.

Backend Packages

Package Responsibility
ixchat Website Agent LangGraph, skills, memory, and response routing
ixrag LightRAG retrieval and tenant-aware storage integration
ixllm Multi-provider model routing and fallback
ixknowledge Knowledge workflow contracts and domain logic
ixscraping Website discovery, scraping, and cleaning logic
ixconfig Typed unified configuration models and resolver
ixskills Global and per-client agent skill system
ixmongo / ixneo4j Tenant-aware MongoDB and Neo4j adapters
ixdata / ixinfra / ixweb Data access, shared runtime utilities, and web boundaries
ixgeo GEO content generation and publishing
ixtagging / ixevaluation Conversation classification and evaluation tooling

Async and Batch Execution

Execution unit Trigger Responsibility
Knowledge content worker Discovery, scrape, and clean Cloud Tasks queues Produce immutable artifacts and report fenced results to Knowledge API
Document loader Knowledge API / approved on-demand execution Write controlled snapshots to LightRAG, MongoDB, and Neo4j
Tagging Cloud Scheduler Classify and score conversations
Website mapping Scheduled Cloud Run Job Refresh website mapping data
PostHog batch processor Scheduled Cloud Run Job, production only Convert raw batch exports into analytics tables
HubSpot contacts sync Jobs API / on demand Synchronize CRM contacts and Rose data
Scheduled export Scheduled Cloud Run Job (daily), production only Post the previous day's or week's conversations and accounts exports to client webhooks
FAQ update Scheduled, production-only rollback path Legacy direct knowledge_faqs ingestion; fenced during controlled cutover
GEO pipeline On demand Generate geo.faqs publishing content and deploy static R2 pages

config.knowledge_faqs is chat retrieval knowledge. geo.faqs is generated publishing content and never enters the chat ingestion path.

Data Layer

  • Supabase Postgres: configuration, conversations, analytics, GEO content, knowledge workflow state, immutable snapshot metadata, and freshness state.
  • Redis: LangGraph checkpoints, conversation memory, locks, and caches.
  • MongoDB: LightRAG chunks, vectors, key-value state, and document status.
  • Neo4j: tenant-aware knowledge graph nodes and relationships, extracted by the LLM with LightRAG's default entity types (ENTITY_TYPES is not configured). Retrieval does not filter by type; the type only reaches the model as a label next to each entity. Node names are free text, so near-duplicates never merge: it is a search index, not a curated model of the client (production audit on IX-5554).
  • GCS rose-scraping: immutable source and processing artifacts for the knowledge control plane.
  • GCS inboundx-cdn and Cloudflare R2: widget assets and published GEO pages.

External Providers

Model selection is role-based rather than one global default:

Role Providers
Answer generation and fallbacks Azure OpenAI and OpenAI
Fast routing, signals, and suggestions Cerebras with configured fallbacks
Query rewriting Mistral
Scraping decisions OpenAI Europe (gpt-5.6-terra)
Selectable routes OpenRouter routes, including Anthropic, Gemini, and Groq
Embeddings Azure/OpenAI embeddings
Identity and enrichment Reveal, Snitcher, RB2B, Vector, and Enrich.so
Observability and analytics Langfuse, Sentry, and PostHog EU
CRM and tools HubSpot, n8n, Linear, Google Drive, and IndexNow
Payments Stripe (Billing, Checkout, Customer Portal, Tax) for self-serve plans

The authoritative per-task model routing lives in backend/packages/ixchat/ixchat/chat_routing.py; the diagram deliberately avoids model-version labels that change frequently.

Primary Data Flows

Chat Request

  1. The widget sends a chat request through api.userose.ai.
  2. The Search API validates the request and invokes ixchat.
  3. LangGraph starts retrieval, enrichment, and profiling work and routes the turn through the appropriate response path.
  4. ixrag reads MongoDB and Neo4j; ixllm selects the configured model route.
  5. Tokens stream back through the API proxy while checkpoints remain in Redis and conversation/profile data is persisted in Supabase.

The complete graph is generated from code on the IXChat package page.

Knowledge Ingestion

  1. Backoffice producers call the Admin API, which authorizes the user and calls the private Knowledge API with service-to-service OIDC.
  2. The Knowledge API records durable operation state in Supabase before dispatching discovery, scrape, or clean work to Cloud Tasks.
  3. The content worker writes immutable artifacts to GCS and reports fenced results to the Knowledge API.
  4. After snapshots converge, the Knowledge API admits the document-loader job, the only controlled writer to LightRAG stores.
  5. Cloud Scheduler invokes the same API dispatcher to recover pending or ambiguous work.

See Knowledge ingestion control plane for state transitions and operational details.

Analytics

  1. The widget sends rw_* interaction and conversion events to PostHog through the unified analytics proxy.
  2. $pageview and $pageleave use only the first-party path: the widget sends PostHog's canonical event shape to a narrow unified-Worker endpoint and suppresses its PostHog copy. The Worker durably enqueues it, and a private Queue consumer batches it into analytics.first_party_page_events_raw using a service-role-only RPC.
  3. PostHog exports the events on the batch export's include_events allow-list (edited in the PostHog UI) into posthog_events_raw in Supabase.
  4. The production-only PostHog batch processor updates visitor sessions, page views, session events, conversations, and analytics marts.
  5. Backoffice pages read those tables and RPCs directly through RLS-protected Supabase access.

The batch processor treats first-party storage as the sole page-event source; retained historical PostHog page-event copies are ignored. Operational deployment steps live in backend/cloudflare/README_clean.md; the phased cutover design is recorded in the PostHog cost and removal ADR.

Billing

  1. A self-serve client starts an upgrade from Plan & usage. The Admin API authorizes the user against every domain of the client and returns a Stripe-hosted Checkout URL; card entry, invoices, and cancellation stay in Stripe (Checkout and the Customer Portal).
  2. Stripe calls the Admin API webhook. The API verifies the signature, re-reads the subscription from Stripe, and mirrors it through a service-role-only RPC into billing.client_billing and config.self_serve_plan_entitlements.
  3. Rose only ever reads the entitlement. A client linked to billing without an active subscription is reported as stopped.

Billing belongs to the client, not the domain. Diagrams, states, and environment setup live in Self-serve billing.