Skip to content

MediaRaq — Architecture Addendum

Companion to: Engineering Summary · Updated: 2026-10-28 · Describes: master at 5d1094f (2026-10-26)

This addendum describes how MediaRaq is structured, how a persona agent handles a conversation, how content moves from plan to publication, how the platform is deployed, and why the key decisions were made. Diagrams are Mermaid.

Contents

  1. System context
  2. Logical components
  3. Persona agent architecture
  4. Memory and retrieval
  5. Agent actions and human approval
  6. Operator Copilot and the MCP tool surface
  7. Model routing
  8. Content production pipeline
  9. Events and reliability patterns
  10. Identity, tenancy and security
  11. Deployment topology
  12. Observability
  13. Delivery and release gates
  14. Architecture decision log

1. System context

flowchart LR
  operator([Business operator])
  visitor([Visitor / customer])
  aiclient([Claude Desktop · ChatGPT])
  subgraph MR["MediaRaq"]
    studio[Studio web app]
    agents[Persona agents · Copilot]
    production[Media production]
  end
  llm[(LLM providers<br/>Anthropic · OpenAI · Azure OpenAI · Gemini)]
  media[(Media providers<br/>fal.ai · HeyGen · ElevenLabs · …)]
  social[(Social platforms<br/>Instagram · YouTube · TikTok · X)]
  web[(WordPress)]
  crm[(CRMs<br/>EspoCRM · HighLevel)]
  msg[(Channels<br/>public chat · WhatsApp · email)]

  operator --> studio
  aiclient -- MCP --> agents
  visitor <--> msg <--> agents
  studio --> agents
  studio --> production
  agents --> llm
  production --> media
  production --> social
  production --> web
  agents -- approved actions --> crm
Actor What they do
Operator Creates and tunes personas, plans and approves content, approves agent actions, reviews analytics
Visitor Talks to a persona on its public page or a messaging channel; may become a lead or book an appointment
AI client Uses MediaRaq's MCP server to read data and create or configure personas from chat

2. Logical components

flowchart TB
  cf[Cloudflare] --> ngx[nginx · per-route configs]
  studio[Studio · React/Vite on Cloudflare] --> facade
  ngx --> par & oaw & facade
  subgraph agentic[Agent tier]
    par[persona-agent-runtime]
    conv[conversations-service]
    oaw[operator-agent-worker · Copilot]
    mcp[mcp-server · 69 tools]
  end
  subgraph prod[Production tier]
    facade[media-api-facade]
    workers[script · image · video · qc · publishing<br/>persona-evolution · affiliate · voice-intake]
  end
  subgraph data[Data tier]
    pg[(PostgreSQL + pgvector)]
    kafka[[Kafka · 25 topics]]
    redis[(Redis)]
    minio[(MinIO)]
  end
  conv --> par
  oaw --> mcp --> facade
  par --> pg & redis
  facade --> pg & kafka & minio
  kafka <--> workers
  workers --> pg & minio
Component Responsibility
persona-agent-runtime Runs persona agents: decision tools, memory retrieval, generation, action proposals, embedding worker
conversations-service Channels and messaging providers, persona agent factory, memory backfills
operator-agent-worker The operator Copilot: planning, MCP tool use, confirm-before-write, checkpoints
mcp-server First-party MCP server: tenant-scoped tools for personas, content, analytics, offers, productions
media-api-facade Orchestration boundary for media jobs, assets, persona creation, Reels storyboards, billing, SSE snapshots
image-service Image Platform: canonical image request contract, Image Prompt Engineer agent, provider routing
video-service, script-service, qc-service Video generation, script variants with persona continuity, quality approval
publishing-orchestrator Scheduled and on-demand publishing per destination, with retries and readiness checks
persona-evolution-service Persona believability and change over time
affiliate-service, voice-intake Affiliate offers; signed audio intake for voice cloning

3. Persona agent architecture

Personas are data, compiled into agents at runtime:

flowchart LR
  persona[(Persona record<br/>canon · profile · visual identity)] --> kernel[Compressed persona kernel]
  policy[(Launch profile<br/>tool grants · model policy · safety)] --> factory
  kernel --> factory[Persona agent factory]
  factory --> def[Agent definition<br/>instructions · ModelRoutingPolicy · SafetyPolicy · ToolGrants · MemoryScope]
  def --> runtime[persona-agent-runtime]

One conversation turn:

sequenceDiagram
  participant V as Visitor
  participant R as Agent runtime
  participant T as Decision tools
  participant M as Memory (hybrid RAG)
  participant L as LLM (routed)
  V->>R: message
  R->>R: preflight (message size, token budget)
  R->>T: run granted decision tools
  T->>M: persona.memory.retrieve / persona.context.lookup
  M-->>T: ranked items with source, revision, consent
  T-->>R: tool results (incl. lead or appointment proposal)
  R->>R: revalidate sources · build prompt within context budget
  R->>L: generate (prompt cache, per-turn cost ceiling)
  L-->>R: response
  R-->>V: reply (streamed)
  R->>R: record proposals, write memories, queue embeddings
  • No code per persona. Tool grants, model choice and safety rules are configuration on the persona's launch profile.
  • Decision tools run before generation, so the response is grounded in what the tools found, and the tool results are kept with the turn.
  • Cost is bounded per turn by a hard ceiling, and system prompts use prompt caching.

4. Memory and retrieval

Memory type Holds Retrieved by
Working The current conversation, a rolling summary and the history policy Context builder
Episodic What a specific person said before, per channel (public page, WhatsApp and others) Hybrid search scoped to that person, with consent checks
Semantic Business knowledge chunks and persona memories Hybrid lexical + pgvector search scoped to tenant and persona
Procedural How this persona handles situations Procedure lookup, revalidated before use
  • Hybrid retrieval. Lexical and vector candidates are merged and ranked. The query is embedded once per turn and reused by every lookup in that turn. If vector search fails, retrieval falls back to lexical and logs it.
  • Grounding checks. Every item carries its source ID and revision. Before an item reaches the prompt, the runtime confirms that the source is still current and the person's consent still holds.
  • Continuous embedding. A drain worker embeds new memories with a fresh one-tenant grant per job and tracks spend per persona.
  • Evaluation and gates. A frozen, signed, budget-capped evaluation scores each retrieval mode against release gates (see the Engineering Summary).

5. Agent actions and human approval

flowchart TB
  agent[Persona agent] -->|"lead.capture.propose · appointment.propose · ghl.contact.create"| prop[(Action proposal<br/>idempotency key · required-field check)]
  prop --> review{Operator approves?}
  review -- yes --> exec[Provider adapter]
  review -- no --> rejected[Rejected and recorded]
  exec --> espo[EspoCRM Lead<br/>signed API · correlation ID]
  exec --> ghl[HighLevel via MCP<br/>Microsoft Agent Framework client]
  espo & ghl --> result[(Sanitized execution result<br/>stored on the proposal)]
  • Proposals are provider-neutral. Only the approved execution step is provider-specific, so adding a CRM means adding an adapter, not changing the agent.
  • Idempotency. Repeating an approval can't create duplicate records: the adapter looks the record up by correlation ID first.
  • Routing is data. A persona can promote several clients, and HighLevel sites are first-class inventory assigned to personas (ADR 0008, ADR 0009).

6. Operator Copilot and the MCP tool surface

  • MediaRaq's own MCP server exposes 69 tenant-scoped tools to the Copilot and to outside AI clients (Claude Desktop, ChatGPT). The tools cover personas, content, analytics, affiliate offers, article lanes and agent runs, the image workbench, and the import of externally produced storyboards.
  • The Copilot discovers tools at runtime and starts each session from an operator handbook served by a tool. It classifies tools by metadata, tags write-intent tools, and shows a diff and asks for confirmation before any write.
  • Identity is propagated end to end: a service credential for the worker, plus the operator's own token and tenant, operator and session IDs for scoping and audit.

7. Model routing

Layer Options Default and mechanism
Persona agents Anthropic, OpenAI, Azure OpenAI, Gemini Claude Haiku 4.5; provider registry plus per-persona ModelRoutingPolicy
Image Prompt Engineer Gemini, Claude, OpenAI Gemini; selected at runtime
Copilot planner Anthropic, OpenAI Composite planner
Image generation fal.ai model family (FLUX, Seedream, Nano Banana, Kontext and others) Image Platform "effective model" from DB-backed presets; per-panel overrides
Video and voice fal.ai video models, HeyGen, Kling, Seedance, ElevenLabs Presets and quality lanes; visible fallback

The Image Prompt Engineer is itself a tool-using agent. It looks up model-family rules and endpoint constraints, retrieves cited prompting guidance from a curated knowledge pack, and composes provider-specific prompts. It was adopted only after running in shadow (see the Engineering Summary).


8. Content production pipeline

flowchart LR
  plan[content.plan.created] --> script[script-service]
  script --> sgc[script.generation.completed]
  sgc --> img[image-service] --> igen[image.asset.generated]
  sgc --> vid[video-service] --> vgen[video.asset.generated]
  igen & vgen --> qc[qc-service] --> appr[qc.*.approved]
  appr --> pub[publishing-orchestrator] --> dest[Instagram · YouTube · TikTok · X · WordPress]
  • Reels are storyboard-driven. A storyboard specification is decomposed into scenes. Keyframes are generated with persona-consistent references. Per-scene video and narration are produced asynchronously, with live progress over SSE, then composed.
  • Publishing treats each destination as its own publication with its own state, attempts and results (ADR-0021). PostgreSQL is the source of truth, and Kafka carries the work.

9. Events and reliability patterns

Pattern Where
Transactional outbox with a durable relay (PostgreSQL LISTEN/NOTIFY as a hint, FOR UPDATE SKIP LOCKED claims, acknowledged Kafka sends) Checkout requests, image terminal events, other cross-service events
Dead-letter topics and classified poison messages before offset commit Intake and checkout consumers
Idempotency keys Payment sessions, CRM writes, publication requests
Terminal-event durability Image results are either acknowledged by Kafka or written to an outbox before the source message is committed
No polling Operator-facing progress uses server-sent events; a guard script blocks reintroducing polling

10. Identity, tenancy and security

Layer Control
Identity Clerk for operators; workspaces map to internal tenants
Authorization Tenant memberships with roles (owner, admin, creator, editor, analyst, viewer), enforced in every API and worker from validated tokens
Isolation Retrieval, embedding and publishing are bound to tenant and persona; a second-tenant isolation proof was run before multi-tenant launch
Service identity Service-to-service credentials; scoped MCP service principal for the Copilot
Secrets Environment files on the server, plus an encrypted vault for per-tenant integration credentials; agents only ever see secret names
Webhooks Provider signatures verified; each webhook on its own nginx route
Agent safety Per-persona safety policy, message-size and token-budget preflight, approval-gated external actions, cost ceilings
Data Visitor memories require consent; retention limits on agent sessions

11. Deployment topology

flowchart TB
  users([Users]) --> cf[Cloudflare]
  cf --> pages[Studio<br/>Cloudflare]
  cf --> vm
  subgraph vm[Cloud VM · Docker Compose]
    ngx[nginx] --> svc[Backend services and agents]
    svc --> pgb[PgBouncer] --> pg[(PostgreSQL + pgvector<br/>pinned image digest)]
    svc --> k[[Kafka]]
    svc --> r[(Redis)]
    svc --> m[(MinIO)]
    obs[OTel Collector · Loki · Tempo · Grafana · GlitchTip]
  end
  svc -.-> obs
Choice Why Next step when it saturates
One VM running Docker Compose Lowest cost and operational overhead at current load; every service is still containerized Managed PostgreSQL and Kafka, then a container platform
PgBouncer in front of PostgreSQL Many services, bounded connections Read replica for analytics
Studio on Cloudflare Global static delivery, separate from backend deploys —
Self-hosted observability Full traces and logs without per-GB SaaS cost Managed backend if retention needs grow

12. Observability

  • OpenTelemetry traces and logs into Tempo and Loki, with Grafana as the engineering console and Prometheus metrics.
  • GlitchTip (Sentry-compatible) for exceptions, following a written exception and logging standard: structured errors, correlation IDs, sanitized messages.
  • Agent turns record their tool results, retrieval mode, cost and fallbacks. Generated images record which prompt was used and why.
  • Post-deploy verification includes a synthetic log query through the observability stack.

13. Delivery and release gates

flowchart LR
  h[Dated handoff] --> chk{Handoff checker}
  chk --> wt[Agent works in its own git worktree]
  wt --> val[Dated validation record<br/>incl. what was NOT verified]
  val --> rev{Architect review<br/>against code and live system}
  rev --> merge[Merge to master<br/>archive tag for the branch]
  merge --> base[Critical baseline]
  base --> deploy[Guarded VM deploy]
  deploy --> cap[Capability checks<br/>before and after activation]
  cap --> obs[Observability verification]
  • The deploy script refuses a dirty checkout or a non-fast-forward update. A database-image release gate blocks unreviewed images, and a verified backup is taken before risky releases.
  • The Studio deploys separately to Cloudflare.

14. Architecture decision log

# Decision Alternatives considered Rationale
1 Personas compiled into agents from data Code per persona; one shared prompt New agents without code; consistent safety, memory and tools
2 Hybrid lexical + pgvector retrieval in PostgreSQL Separate vector database; vector-only search One system of record; tenant filters in SQL; measurably better recall
3 Approval-first, provider-neutral agent actions Direct CRM writes from the agent No unreviewed writes to customer systems; adapters per CRM
4 First-party MCP server as the tool surface Bespoke Copilot API One tool surface for the Copilot and external AI clients
5 Shadow before adoption for new agents Feature-flag switch-over Evidence on real traffic before it affects output
6 Provider portfolio behind a media facade Single vendor Routing by quality, cost and availability; visible fallback
7 Kafka plus transactional outbox Synchronous calls; fire-and-forget No lost events; retries and replay
8 Server-sent events for operator feedback Polling Live feedback, lower load
9 Clerk identity, internal tenants as authority Clerk organizations as the only authority Backend owns authorization and isolation
10 Media analysis split into separate services (extractor, transcription, vision, synthesis) One analysis service Independent scaling and failure (ADRs 0001–0005)
11 Storyboard specification with parallel scene decomposition Linear generation Parallel scene work; per-scene regeneration (ADRs 0011–0021)
12 Per-destination publication records One post, many platforms Independent outcomes and retries per platform (ADR-0021)
13 Single VM with Compose at current stage Kubernetes now Cost and simplicity; containerized for a later move