Morgana

Morgana

A magical witch assistant equipped with an enchanted grimoire powered by AI, yet shaped by you: your agents, your prompts, your tools…woven into her spells

HANDBOOK
.NET 10 Akka.NET Microsoft.Agents.AI Model Context Protocol

Architecture Overview

Morgana is a conversational AI framework built on a multi-agent, intent-driven architecture. It is multi-channel by design: the backend intelligence (Morgana) is decoupled from the channels users actually reach it through, and ships with two reference channels out-of-the-boxCauldron (HTML / Blazor Server, rich UI over SignalR) and Rune (TTY / Spectre.Console, poor-but-honest CLI over webhook). Every channel advertises its capability budget at conversation start, Morgana adapts its outbound expressivity accordingly, and each channel authenticates with its own JWT issuer.

Morgana reimagines conversational AI through 4 foundational pillars that work in harmony to deliver an orchestration framework that is powerful yet remarkably simple to configure.

🎭

Actor System

Fault-tolerant orchestration via Akka.NET. Each conversation is a hierarchy of specialized actors (Manager, Supervisor, Guard, Classifier, Router) communicating through async message passing.

🤖

Agent System

Domain specialists that self-register through declarative attributes. Supports native tools (MorganaTool) and MCP server integration for runtime capability discovery.

📝

Prompting System

Prompts as versioned project artifacts with layered personality: a global Morgana character plus per-agent specializations. No hardcoded strings—iterate without redeployment.

💾

Context System

Isolated agent contexts with selective shared registry (first-write-wins). Encrypted SQLite persistence for conversation history and shared variables, with multi-agent timeline reconciliation and actor lifecycle resilience.

System Topology

ComponentRoleTechnologyDefault Port
MorganaBackend — SignalR Hub, Actors, AgentsASP.NET 10, Akka.NET, Microsoft.Agents.AI5001
CauldronReference channel — HTML rich UI (SignalR delivery, full capability budget)Blazor Server5002
RuneReference channel — TTY CLI (webhook delivery, poor-but-honest capability budget)Spectre.Console on Kestrel5003
LLMLanguage model providerAnthropic, Azure OpenAI, Ollama, OpenAI
MCP ServerExternal tool provider (optional)Model Context Protocol

Extension Points

Every behavioural concern in Morgana is independently overridable via Dependency Injection. The default implementations ship as sensible baselines—replace any of them without touching a single line of framework code.

InterfaceDefault ImplementationPurpose
ILLMServiceAnthropic / AzureOpenAI / Ollama / OpenAILLM provider abstraction
IAuthenticationServiceJWTAuthenticationServiceRequest authentication (JWT HMAC-SHA256)
IGuardRailServiceLLMGuardRailServiceContent moderation & policy enforcement
IClassifierServiceLLMClassifierServiceIntent classification
IPresenterServiceLLMPresenterServiceWelcome presentation & first engagement
IRateLimitServiceSQLiteRateLimitServicePer-conversation throttling
IConversationPersistenceServiceSQLiteConversationPersistenceServiceEncrypted conversation storage
IAgentConfigurationServiceJsonAgentConfigurationServiceAgent discovery
IPromptResolverServiceEmbeddedResourcePromptResolverServiceSystem & Domain prompt loading
ISignalRBridgeServiceSignalRBridgeServiceReal-time frontend communication
Plugin Architecture: Build your agents as standalone .NET libraries referencing the Morgana.AI NuGet package. Drop the compiled DLL into the plugins/ folder and Morgana discovers it at startup—zero configuration needed.

Security Model

Morgana implements a defence-in-depth strategy with security at every layer. The architecture is designed so that infrastructure-level concerns (TLS, vault, WAF) remain the deployer's responsibility, while the product guarantees application-level protection.

JWT Authentication (HMAC-SHA256)
Content Moderation (Guard)
Rate Limiting
Conversation Encryption (AES-256)
Secrets Externalized
Fail-Closed Auth

Authentication Flow

Channel (Cauldron iss=cauldron · Rune iss=rune — each self-issues JWTs signed with its own shared key)
Bearer token in HTTP & SignalR / Webhook
Morgana (peeks iss, looks up the matching Issuers[] entry, validates signature + audience + lifetime; unknown issuers rejected outright)

Morgana uses a per-issuer trust model: each channel onboards with its own { Name, SymmetricKey } entry in Morgana:Authentication:Issuers[], so leaking one channel's key does not compromise the others. The IAuthenticationService extension point also allows deployers to replace JWT with any strategy (API keys, mTLS, OAuth with external IdP) without modifying the application.

Configuration Reference

Morgana and its reference channels all follow the standard ASP.NET configuration hierarchy: appsettings.json → environment variables → User Secrets. In Docker, environment variables in docker-compose.yml override everything.

Morgana (Backend)

SettingDescription
Morgana:LLM:ProviderLLM backend: Anthropic, AzureOpenAI, Ollama, OpenAI
Morgana:LLM:{Provider}:*Provider-specific settings (ApiKey, Model, Endpoint, ...)
Morgana:Authentication:AudienceExpected JWT audience (default: morgana.ai)
Morgana:Authentication:Issuers[]Per-channel trust list: array of { Name, SymmetricKey } entries (e.g. cauldron, rune)
Morgana:ConversationPersistence:EncryptionKeyAES-256 key for SQLite encryption (base64)
Morgana:ConversationPersistence:StoragePathDirectory for SQLite database files
Morgana:ActorSystem:TimeoutSecondsActor/agent timeout (default: 180)
Morgana:ActorSystem:EnableGuardrailToggle content moderation guard
Morgana:AdaptiveMessaging:*Streaming toggle, rich-features threshold for ingress heuristic
Morgana:RateLimiting:*Per-minute, per-hour, per-day message limits
Morgana:OpenTelemetry:*Tracing: exporters, service name, endpoints

Cauldron (HTML channel)

SettingDescription
Cauldron:MorganaURLMorgana backend URL
Cauldron:Authentication:SymmetricKeyHMAC-SHA256 key matching the cauldron entry in Morgana's Issuers[]
Cauldron:StreamingResponse:*Typewriter effect speed (tick ms, chars per tick)
Cauldron:LandingMessagesArray of random loading messages

Rune (TTY channel)

SettingDescription
Rune:MorganaURLMorgana backend URL
Rune:CallbackURLAbsolute URL Morgana posts inbound messages to (webhook listener)
Rune:Authentication:SymmetricKeyHMAC-SHA256 key matching the rune entry in Morgana's Issuers[]
Rune:Authentication:IssuerToken issuer (default: rune)
Rune:Authentication:AudienceToken audience (default: morgana.ai)
Rune:MaxMessageLengthBudget capability declared for maximum message length (default: 500)

Docker Deployment

Morgana ships with a ready-to-use docker-compose.yml defining Morgana, Cauldron and Rune on a dedicated bridge network with persistent SQLite volume. docker compose up starts Morgana + Cauldron; Rune is a TUI that needs its own terminal, so it is launched interactively on demand in a separate shell.

# Quick Start (production images from Docker Hub) cp production.env.template .env nano .env # fill in secrets docker compose up # Morgana + Cauldron (HTML on :5002) docker compose run --rm --service-ports --use-aliases rune # Rune TUI (TTY on :5003) — `--use-aliases` registers `rune` as a network alias so Morgana's webhook callback resolves # Development (build from source) cp development.env.template .env nano .env dotnet build ./Morgana && dotnet build ./Channels/Cauldron && dotnet build ./Channels/Rune docker compose --env-file .env --env-file .env.versions build docker compose --env-file .env --env-file .env.versions up

Required Secrets (.env)

VariablePurposeGeneration
ENCRYPTIONKEYAES-256 encryption for SQLiteopenssl rand -base64 32
JWT_SYMMETRIC_KEYJWT signing key shared between each channel and its Issuers[] entry on Morganaopenssl rand -base64 32
LLM_PROVIDERActive LLM backendOne of: anthropic, azureopenai, ollama, openai
{PROVIDER}_APIKEYLLM provider API keyFrom your provider dashboard (if cloud-based)

Observability (OpenTelemetry)

Morgana provides end-to-end distributed tracing across the entire conversation pipeline. Traces are structured to be meaningful both to IT operators (latencies, TTFT, errors) and non-technical stakeholders (intent, agent name, response preview).

Span Architecture

SpanKey Attributes
morgana.turnconversation.id, turn.user_message
morgana.guardguard.compliant, guard.violation
morgana.classifierclassification.intent, classification.confidence
morgana.routerrouter.intent, router.agent_path
morgana.agentagent.name, agent.ttft_ms, agent.response_preview

Exporter Configuration

"OpenTelemetry": { "Enabled": true, "ServiceName": "Morgana", "Exporters": [ { "Name": "console", "Enabled": true }, { "Name": "otlp", "Enabled": true, "Endpoint": "http://localhost:4317" } ] }

Compatible with Jaeger, Grafana Tempo, Azure Monitor, Datadog, and any OTLP-compliant backend.

Building Your Agents

Morgana.AI is also distributed as a NuGet package. You build agents as standalone .NET libraries—no need to fork or modify the Morgana codebase.

Agent Development Workflow

[HandlesIntent("billing")] public class BillingAgent : MorganaAgent { ... } [ProvidesToolForIntent("billing")] public class BillingTool : MorganaTool { public async Task<string> GetInvoices(string userId, int count) { ... } } // Or acquire tools dynamically from an MCP server: [HandlesIntent("monkeys")] [UsesMCPServer("https://func-monkeymcp.azurewebsites.net/")] public class MonkeyAgent : MorganaAgent { ... }
Fail-fast validation: At startup Morgana validates bidirectional consistency between declared intents and classifier configuration. Mismatches are caught immediately, not in production.

Reference Channels

Morgana ships with two reference channels out-of-the-box, chosen to exercise opposite ends of the channel capability spectrum so the framework's multi-channel design is demonstrable without any extra work.

Cauldron — HTML / Blazor Server

Cauldron is a Blazor Server application providing a rich, real-time chat interface with streaming responses, quick replies, rich cards, and animated feedback. It communicates with Morgana over authenticated SignalR and REST (deliveryMode=signalr), with all capability flags on. It can be depicted as the official Morgana's home.

Presentation Chatting Rich Cards

Rune — TTY / Spectre.Console

Rune is a minimal command-line channel: a Kestrel-hosted console app with a Spectre.Console Live UI (sticky header, streaming-free rendering), exercising the webhook delivery mode (deliveryMode=webhook) with lowest capability profile (all rich features off, MaxMessageLength=500). It is the contract surface MorganaChannelAdapter is supposed to degrade toward — a working proof that the channel abstraction holds, not just a demo.

Rune is launched interactively (docker compose run --rm --service-ports --use-aliases rune, or dotnet run from Channels/Rune/) because the Spectre.Console Live UI needs to own the terminal. --use-aliases is mandatory: compose run skips network aliases by default, so without it Morgana's webhook callback to http://rune:5003 fails DNS resolution.

Deployment Responsibility Model

Morgana draws a clear line between product responsibilities and deployer responsibilities, making it ready for enterprise deployment without prescribing infrastructure choices.

ConcernOwnerNotes
Application authenticationProductJWT HMAC-SHA256 — replaceable via IAuthenticationService
Content moderationProductLLM-based guard — replaceable via IGuardRailService
Rate limitingProductSQLite-backed — replaceable via IRateLimitService
Conversation persistenceProductSQLite-backed — replaceable via IConversationPersistenceService
Secrets externalizationProductEnvironment variables / User Secrets
TLS terminationDeployerReverse proxy (nginx, Traefik, ALB, App Gateway...)
Secrets vaultDeployerAzure Key Vault, HashiCorp Vault, AWS Secrets Manager...
Network policies & WAFDeployerCloud/on-prem infrastructure
Key rotationDeployerOperational procedure for JWT_SYMMETRIC_KEY

Quick Reference

ResourceLocation
Source codegithub.com/mdesalvo/Morgana
NuGet packagenuget.org/packages/Morgana.AI
Docker Hub (Morgana)hub.docker.com/r/mdesalvo/morgana
Docker Hub (Cauldron)hub.docker.com/r/mdesalvo/cauldron
Docker Hub (Rune)hub.docker.com/r/mdesalvo/rune
ChangelogCHANGELOG.md in repository root
The Morgana Difference: Adding a new domain agent requires only configuration and attribute decoration—no changes to the core framework, no manual registration, no brittle coupling. It's conversational AI designed for domain-driven evolution.