Built with Elixir/OTP

Architecture

Technical architecture documentation covering 13+ components of the Prismatic Platform.

0
Components
0
Categories
OTP
Foundation
0
Showing
Showing of components
Component
Action

No architecture components match your search.

Page of

Technical deep-dive into Prismatic Platform's deterministic, fault-tolerant architecture built on Elixir/OTP. The platform runs as 93 OTP applications on a single BEAM node, coordinated through supervision trees, PubSub event buses, and a pluggable storage adapter layer -- achieving microsecond inter-service latency without the operational complexity of distributed microservices.

Abstract

This section documents the architectural decisions, runtime characteristics, and design patterns that underpin the Prismatic Platform. The architecture is organized around three core principles: fault tolerance through OTP supervision (every stateful component lives in a supervised process tree with automatic restart), data consistency through event sourcing (system state is derived from an append-only event log, enabling full auditability and temporal queries), and cognitive reliability through the NABLA epistemic pipeline (all beliefs formed by the system pass a formal Trinity Gate before influencing decisions).

The platform rejects the microservice-over-HTTP pattern in favour of a monolithic deployment of 93 OTP applications sharing a single BEAM virtual machine. This design eliminates network serialization overhead between components while preserving modularity through Elixir's umbrella project structure and compile-time dependency enforcement.

Introduction

Why Elixir/OTP

The choice of Elixir on the BEAM VM is not incidental -- it directly addresses three requirements that disqualify most runtime environments:

  1. Soft real-time guarantees: The BEAM scheduler provides preemptive scheduling across lightweight processes with sub-millisecond context switching, ensuring that no single computation starves others. This matters when 434+ AI agents, web dashboard connections, and background crawl jobs share the same runtime.

  2. Fault isolation without containers: Each Erlang process has its own heap and garbage-collects independently. A crash in one process does not corrupt another's memory. OTP supervisors automatically restart failed processes according to configurable strategies, making the system self-healing at the process level.

  3. Hot code loading: The BEAM supports replacing module code in a running system without dropping connections or losing process state. While not used in production today, this capability enables zero-downtime deployments for future production scenarios.

Architectural Layers

The platform's architecture follows four distinct layers, each with clear responsibilities:

block-beta
    columns 1
    L4["Layer 4 — Web &amp; API<br/>Phoenix LiveView (4000) · REST API (4004) · WebSocket"]
    L3["Layer 3 — Domain Applications<br/>Perimeter · DD · Hawkeye · AI Drift · OSINT · Agents"]
    L2["Layer 2 — Platform Services<br/>Auth · Audit · Cache · PubSub · Compression · Search"]
    L1["Layer 1 — Core Infrastructure<br/>Storage Adapters · Kernel · 3NL · Core Types · Config"]
    L0["Layer 0 — BEAM Runtime<br/>Erlang VM · OTP · Schedulers · ETS · Distribution"]

    style L4 fill:#1e293b,stroke:#a855f7,color:#fff
    style L3 fill:#0f172a,stroke:#3b82f6,color:#fff
    style L2 fill:#1e293b,stroke:#10b981,color:#fff
    style L1 fill:#0f172a,stroke:#f59e0b,color:#fff
    style L0 fill:#1e293b,stroke:#dc2626,color:#fff

Dependencies flow strictly downward. Layer 4 may call Layer 3 and below; Layer 3 may call Layer 2 and below. Circular dependencies are rejected at compile time.

Core Architecture Components

This section provides an overview of the 12 architectural subsystems documented in detail on their individual pages.

OTP Supervision Trees

Every stateful application defines a supervision tree rooted in its Application.start/2 callback. The platform uses three OTP restart strategies depending on process coupling:

StrategyCountPattern
:one_for_one67 appsIndependent workers -- restart only the failed child
:rest_for_one14 appsSequential dependencies -- restart failed child and all started after it
:one_for_all12 appsTightly coupled -- restart all children when any fails

The top-level supervisor for the entire platform uses :one_for_one, meaning a failure in the Perimeter application does not restart the Web application. This isolation is critical for operational stability.

# Platform-level supervision structure (simplified)
defmodule Prismatic.Application do
  use Application

  @impl true
  def start(_type, _args) do
    children = [
      Prismatic.Config,
      {Phoenix.PubSub, name: Prismatic.PubSub},
      Prismatic.Repo,
      PrismaticWeb.Endpoint
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

Deep dive: Supervision Trees

Umbrella Application Structure

The 93 OTP applications are organized as an Elixir umbrella project. Each application resides in apps/<name>/ with its own mix.exs, lib/, test/, and priv/ directories. Inter-application dependencies are declared explicitly:

# apps/prismatic_perimeter/mix.exs
defp deps do
  [
    {:prismatic_core, in_umbrella: true},
    {:prismatic_storage_core, in_umbrella: true},
    {:prismatic_osint_core, in_umbrella: true}
  ]
end

The umbrella structure provides microservice-like modularity (independent compilation, isolated testing) without microservice operational overhead (no service discovery, no network serialization, no distributed tracing).

Deep dive: Umbrella Applications

Storage Adapter Architecture

The storage layer implements a behaviour-based adapter pattern. All storage operations route through the PrismaticStorage.Adapter behaviour, allowing the platform to swap backends without changing domain code:

AdapterBackendLatencyUse Case
ETSErlang Term Storage~1 μsCaches, sessions, hot data
EctoPostgreSQL~2-10 msPersistent records, transactions
MeilisearchMeilisearch~5-20 msFull-text search, faceted queries
KuzuDBKuzuDB~3-15 msGraph queries, relationship traversal
RedisRedis~1-5 msDistributed cache, rate limiting

The adapter protocol enforces a uniform API across all backends:

@callback store(key, value, opts) :: {:ok, term()} | {:error, term()}
@callback fetch(key, opts) :: {:ok, term()} | {:error, :not_found}
@callback delete(key, opts) :: :ok | {:error, term()}
@callback list(prefix, opts) :: {:ok, [term()]} | {:error, term()}

Deep dive: Storage Adapters

Event Sourcing

Critical subsystems use event sourcing for auditability and temporal queries. Rather than storing current state directly, the system persists an ordered sequence of domain events. Current state is derived by replaying events through a projection function:

flowchart LR
    E1["UserCreated"]
    E2["RoleAssigned"]
    E3["PermissionGranted"]
    E4["..."]
    P1["%User{id: 1}"]
    P2["%User{role: :admin}"]
    P3["%User{perms: [:read, :write]}"]

    E1 --> E2 --> E3 --> E4
    E1 -.->|project| P1
    E2 -.->|project| P2
    E3 -.->|project| P3

    classDef event fill:#1e293b,stroke:#3b82f6,color:#fff
    classDef projection fill:#0f172a,stroke:#10b981,color:#e2e8f0
    class E1,E2,E3,E4 event
    class P1,P2,P3 projection

This pattern provides complete audit trails, enables temporal queries ("what was the state at timestamp T?"), and supports retroactive bug fixes by re-projecting events through corrected logic.

Deep dive: Event Sourcing

Phoenix LiveView

Web interfaces are built with Phoenix LiveView, delivering real-time server-rendered UI without client-side JavaScript frameworks. Each LiveView process maintains a WebSocket connection and pushes DOM diffs to the browser:

DashboardRouteFunction
Main Dashboard/Platform overview, metrics, navigation
DD Cases/dd/casesDue diligence case management
Graph Explorer/dd/graphEntity relationship visualization
Perimeter EASM/perimeterSecurity ratings, asset discovery
AI Drift Monitor/driftAI decision drift detection
Jobs Dashboard/jobsOban background job monitoring

LiveView's process model means each connected user gets a dedicated Erlang process with its own state, crash isolation, and garbage collection -- no shared mutable state between sessions.

Deep dive: Phoenix LiveView

PubSub Event Bus

Inter-application communication for event-driven workflows uses Phoenix.PubSub backed by the Erlang pg module for local distribution:

# Publisher (any application)
Phoenix.PubSub.broadcast(Prismatic.PubSub, "perimeter:scan_complete", {:scan, result})

# Subscriber (any application)
Phoenix.PubSub.subscribe(Prismatic.PubSub, "perimeter:scan_complete")

# handle_info in subscriber GenServer
def handle_info({:scan, result}, state) do
  # React to scan completion
  {:noreply, update_state(state, result)}
end

PubSub events are fire-and-forget with at-most-once delivery within the local BEAM node. For guaranteed delivery, the platform uses Oban jobs.

Deep dive: PubSub

Telemetry & Observability

The platform uses Erlang's :telemetry library for metrics emission. Every significant operation emits telemetry events that are consumed by handlers for logging, metrics aggregation, and alerting:

:telemetry.execute(
  [:prismatic, :perimeter, :scan],
  %{duration: duration_ms, assets_found: count},
  %{domain: domain, scan_type: :full}
)

Key telemetry dimensions: request latency, query duration, cache hit rates, agent response times, queue depths, error rates.

Deep dive: Telemetry

NABLA Epistemic Pipeline

The NABLA Infinity (∇∞) framework implements a 16-level epistemic pipeline ensuring that beliefs formed by the platform's AI agents meet formal consistency requirements before influencing decisions:

flowchart TD
    L0["L0 — Raw Signal"]
    L1["L1 — Validated Signal"]
    L2["L2 — Correlated Evidence"]
    L3["L3 — Synthesized Belief"]
    L13["L13 — Decision-Ready Intelligence"]
    Meta["Meta Level<br/>Pipeline Self-Assessment"]
    Cons["Consciousness Level<br/>System Self-Awareness"]

    L0 --> L1 --> L2 --> L3
    L3 -. "L4 – L12" .-> L13
    L13 --> Meta --> Cons

    classDef raw fill:#0f172a,stroke:#475569,color:#e2e8f0
    classDef synth fill:#1e293b,stroke:#3b82f6,color:#fff
    classDef ready fill:#1e293b,stroke:#10b981,color:#fff
    classDef meta fill:#1e293b,stroke:#a855f7,color:#fff
    class L0,L1,L2 raw
    class L3 synth
    class L13 ready
    class Meta,Cons meta

Every belief must pass the Trinity Gate -- three independent consistency checks that must all succeed:

  1. Structural Consistency: Graph-theoretic validation of the belief's logical structure
  2. Logical Consistency: Rule-based evaluation against known axioms
  3. Formal Necessity: Modal logic verification with optional Lean4 proof

Deep dive: NABLA Framework

PostgreSQL & KuzuDB

The platform uses PostgreSQL as its primary relational store (via Ecto) and KuzuDB as an embedded graph database for relationship-heavy queries:

  • PostgreSQL: Entity records, audit logs, configuration, Oban job queues
  • KuzuDB: Entity relationship graphs, influence networks, ownership chains

The dual-database architecture avoids forcing graph queries into SQL joins or flattening relational data into graph structures.

Deep dive: PostgreSQL & KuzuDB

Ollama Local AI

Local AI inference via Ollama provides <3 second response times for code generation, content analysis, and decision support without external API dependencies:

ModelParametersResponse TimeUse Case
qwen3-coder7B<3sCode generation, refactoring
gpt-oss:20b20B<5sComplex reasoning, analysis
deepseek-coder6.7B<3sCode review, documentation

The architecture includes automatic fallback to cloud APIs when local models are unavailable or when tasks exceed local model capabilities.

Deep dive: Ollama Integration

Meilisearch

Full-text search across platform data uses Meilisearch, an embedded search engine providing typo-tolerant, faceted search with sub-50ms query times:

  • Indexed content: Agents, commands, OSINT sources, applications, documentation
  • Features: Typo tolerance, faceted filtering, ranking rules, synonym support
  • Deployment: Embedded in the platform, no external service dependency

Deep dive: Meilisearch

GraphQL

The platform exposes a GraphQL API alongside REST for complex, nested data queries where clients need control over response shape:

  • Schema: Auto-generated from Ecto schemas and domain types
  • Subscriptions: Real-time updates via WebSocket for dashboard widgets
  • DataLoader: N+1 query prevention through batched data loading

Deep dive: GraphQL

Cross-Cutting Concerns

3NL (Three Nested Levels) Framework

All architectural components are organized according to the 3NL framework:

  • Level 1 (Strategic): Public APIs, facade modules, external interfaces
  • Level 2 (Tactical): Inter-application coordination, pipeline orchestration, agent messaging
  • Level 3 (Operational): OTP process internals, ETS operations, BEAM optimizations

The 3NL framework ensures that consumers at each level interact only with appropriate abstractions, preventing implementation details from leaking across boundaries.

Error Handling Philosophy

The platform follows Erlang's "let it crash" philosophy, augmented with structured error tuples at API boundaries:

  • Internal processes: Let supervisors handle crashes and restarts
  • API boundaries: Return {:ok, result} / {:error, reason} tuples
  • Web interfaces: Convert error tuples to appropriate HTTP status codes and user-facing messages

This hybrid approach provides the self-healing benefits of OTP supervision while maintaining predictable error handling at system edges.

Configuration Architecture

Configuration follows a layered precedence model:

flowchart TD
    Env["Runtime Environment<br/><i>highest precedence</i>"]
    Runtime["config/runtime.exs"]
    EnvCfg["config/{env}.exs<br/><i>dev.exs · prod.exs · test.exs</i>"]
    Cfg["config/config.exs"]
    App["Application defaults<br/><i>lowest precedence</i>"]

    Env --> Runtime --> EnvCfg --> Cfg --> App

    classDef high fill:#1e293b,stroke:#dc2626,color:#fff
    classDef mid fill:#0f172a,stroke:#3b82f6,color:#fff
    classDef low fill:#0f172a,stroke:#475569,color:#e2e8f0
    class Env high
    class Runtime,EnvCfg,Cfg mid
    class App low

Sensitive configuration (database credentials, API keys) is loaded exclusively from environment variables at runtime, never committed to source control.

Performance Characteristics

Runtime Metrics

MetricValueContext
BEAM Schedulers10Matches physical CPU cores
Process Count (idle)~2,500Supervised processes at startup
Memory (idle)~350 MBAll 93 apps loaded
Boot Time~8sCold start to all apps ready
ETS Tables~180Active tables at runtime
GC Pauses<1msPer-process garbage collection

Compilation Performance

OperationDurationParallelism
Full compile (clean)~45sUp to 10 parallel compilers
Incremental compile~2-5sSingle module recompilation
Full test suite~90sConcurrent test processes
Dialyzer analysis~120sIncremental PLT updates
Credo analysis~15sParallel file checking

Security Architecture

Process-Level Isolation

The BEAM VM provides natural security boundaries:

  • Memory isolation: Processes cannot access each other's heap
  • Message-based communication: All inter-process data is copied, not shared
  • Scheduler fairness: No process can monopolize CPU time (reduction counting)
  • Port isolation: External program interaction through controlled ports

Network Security

  • TLS everywhere: All external connections use TLS 1.3
  • API authentication: JWT tokens with configurable TTL and refresh
  • Rate limiting: Per-endpoint and per-client rate limiting via Token Bucket
  • CORS: Strict origin whitelisting for browser clients
  • CSP: Content Security Policy headers on all HTML responses

Data Protection

  • Encryption at rest: PostgreSQL TDE for sensitive tables
  • Encryption in transit: TLS for all inter-service and client-facing connections
  • PII suppression: Automated PII detection and removal in logs and exports
  • Audit trails: Immutable, append-only audit log for all state-changing operations

Conclusion

The Prismatic Platform's architecture demonstrates that a monolithic Elixir/OTP deployment can scale to 93 applications and 2.8 million lines of code while maintaining the modularity, testability, and fault-tolerance properties typically associated with distributed microservice architectures. The BEAM VM's process model, combined with OTP supervision trees and Elixir's umbrella project structure, provides a foundation that is simultaneously scalable, maintainable, and operationally simple.

The architectural components documented in this section -- supervision trees, storage adapters, event sourcing, PubSub, telemetry, the NABLA epistemic pipeline, and the various data stores -- compose into a coherent whole through the 3NL framework's layered abstraction model and the platform's strict dependency graph.

References

Architecture Pages

Platform Documentation

External References


Architecture documentation reflects the platform state as of 2026-02-06. Component counts and performance metrics are measured on a 10-core development machine running macOS.


Connect & Contribute

Created by Tomas Korcak (korczis) | Open Source under GHL