Skip to main content

System Overview

CALMGuard is built on Next.js 15 with a multi-agent AI architecture. This page covers the technical stack, system design, and key architectural decisions.

Tech Stack

LayerTechnologyNotes
FrameworkNext.js 15 (App Router)Server Components, API Routes, SSE streaming
LanguageTypeScript (strict mode)No any types. Zod for all runtime validation
LLM SDKVercel AI SDK v6generateObject with Zod schemas for structured output
Default LLMGoogle Gemini@ai-sdk/google. Multi-provider support
UIshadcn/ui + Tailwind CSS v4Dark theme, slate palette
GraphReact Flow (@xyflow/react)Architecture visualization
ChartsRechartsCompliance gauges, heat maps
StateZustandFlat state, SSE event updates
ValidationZodCALM parsing + agent output validation
Package ManagerpnpmWorkspace tooling

Architecture Diagram

SSE Streaming Flow

Analysis results stream from server to client in real-time using Server-Sent Events (SSE):

Directory Structure

src/
app/
api/
analyze/route.ts # POST /api/analyze — SSE streaming endpoint
calm/parse/route.ts # POST /api/calm/parse — validation endpoint
pipeline/route.ts # GET /api/pipeline — pipeline download
dashboard/
page.tsx # Dashboard with tabs
findings/page.tsx # Findings detail page
pipeline/page.tsx # Pipeline preview page
page.tsx # Landing page with architecture selector
layout.tsx # Root layout with providers

lib/
calm/
parser.ts # CALM JSON validation with Zod
extractor.ts # Convert CalmDocument -> AnalysisInput
types.ts # Zod schemas for CALM entities
agents/
orchestrator.ts # Phase 1 (parallel) + Phase 2 (sequential)
architecture-analyzer.ts
compliance-mapper.ts
pipeline-generator.ts
risk-scorer.ts
registry.ts # Agent YAML config loader
types.ts # AgentEvent, AgentResult schemas
ai/
providers.ts # LLM provider registry (Gemini, Anthropic, OpenAI)
streaming.ts # AgentEventEmitter (globalThis singleton)
api/
schemas.ts # Request body Zod schemas
compliance/
frameworks.ts # Framework definitions and control mapping
scoring.ts # Compliance score calculation
pipeline/
github-actions.ts # GitHub Actions YAML generation

components/
ui/ # shadcn/ui base components
dashboard/
ComplianceGauge.tsx # SVG gauge with animation
ArchitectureGraph.tsx # React Flow graph
RiskHeatMap.tsx # Recharts heat map
FindingsTable.tsx # Findings with severity
PipelinePreview.tsx # Shiki-highlighted YAML
AgentFeed.tsx # Live agent event stream
layout/
Sidebar.tsx # Navigation sidebar
DashboardLayout.tsx # Two-column layout

hooks/
use-agent-stream.ts # EventSource + SSE event handling
use-calm-parser.ts # CALM file parsing hook

store/
analysis-store.ts # Zustand store (flat state)

agents/ # YAML agent definitions (AOF-inspired)
architecture-analyzer.yaml
compliance-mapper.yaml
pipeline-generator.yaml
risk-scorer.yaml

skills/ # Compliance knowledge for LLM prompts
sox-compliance.md
pci-dss-compliance.md
nist-csf-compliance.md
finos-ccc-compliance.md

examples/ # Demo CALM architecture files
trading-platform.json
payment-gateway.json

Key Design Decisions

SSE over WebSockets

Server-Sent Events (SSE) were chosen over WebSockets for agent streaming because:

  • SSE works natively with Next.js API routes on Vercel serverless
  • One-way (server-to-client) data flow is sufficient for streaming agent events
  • No additional infrastructure required

Zustand over Redux

Zustand was selected for client-side state management because:

  • Minimal boilerplate for SSE event updates
  • Flat state structure avoids nested mutation issues
  • Works well with React 19 concurrent features

generateObject over generateText

All agents use Vercel AI SDK's generateObject with Zod schemas rather than generateText because:

  • Structured output enforced at the LLM call level
  • Type-safe agent results without string parsing
  • Zod validation catches LLM hallucinations in schema

globalThis Singleton for AgentEventEmitter

The AgentEventEmitter uses globalThis singleton pattern to survive Next.js hot module reloads in development. Without this, the emitter instance used in /api/analyze would differ from the one subscribed to by agents after a hot reload.

Edge Runtime Compatibility

Instead of Node.js EventEmitter, a simple listener-pattern emitter is used for SSE event broadcasting. This maintains compatibility with Vercel's Edge Runtime if needed, and avoids memory leaks from uncleaned Node.js listeners.