We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
Mycelial Network Architecture: Bio-Inspired System Design
Bio-inspired mycelial network topology for cross-domain signal propagation, nutrient routing, pattern extraction, and adaptive load distribution in a distributed platform.
Tomas Korcak (korczis)
Prismatic Platform
Mycelial networks in nature solve a hard problem: distributing nutrients and signals across vast, decentralized organisms with no central coordinator. The Prismatic Platform borrows this metaphor for cross-domain coordination. When an OSINT discovery affects a DD case, when a security scan reveals a risk that impacts an investigation, or when a pattern in one domain matches anomalies in another, the mycelial network propagates these signals to the right consumers.
The Biological Metaphor
In forest ecosystems, mycorrhizal networks connect trees through underground fungal filaments. Trees share nutrients, warn each other of threats, and allocate resources to where they are needed most. The platform's mycelial network mirrors these properties:
|-------------------|-----------------|----------------|
Network Topology
The mycelial network is organized in three layers: the substrate layer (raw PubSub), the routing layer (domain-scoped signal processors), and the intelligence layer (pattern extraction and correlation):
defmodule PrismaticMycelium.Network do
@moduledoc """
Core mycelial network coordinator.
Manages the three-layer signal propagation topology:
substrate (PubSub), routing (domain processors),
and intelligence (pattern extraction).
"""
use GenServer
require Logger
@type signal :: %{
id: String.t(),
source_domain: atom(),
target_domains: [atom()] | :broadcast,
type: atom(),
payload: map(),
priority: :low | :normal | :high | :critical,
timestamp: DateTime.t(),
ttl: non_neg_integer()
}
@domains ~w(osint dd security investigation perimeter academy)a
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@spec propagate(signal()) :: :ok
def propagate(signal) do
GenServer.cast(__MODULE__, {:propagate, signal})
end
@spec subscribe(atom()) :: :ok
def subscribe(domain) when domain in @domains do
Phoenix.PubSub.subscribe(PrismaticWeb.PubSub, "mycelium:#{domain}")
end
@impl GenServer
def init(_opts) do
state = %{
signal_count: 0,
domain_stats: Map.new(@domains, fn d -> {d, %{sent: 0, received: 0}} end),
active_patterns: []
}
{:ok, state}
end
@impl GenServer
def handle_cast({:propagate, signal}, state) do
targets = resolve_targets(signal)
enriched = enrich_signal(signal, state)
for domain <- targets do
Phoenix.PubSub.broadcast(
PrismaticWeb.PubSub,
"mycelium:#{domain}",
{:mycelial_signal, enriched}
)
end
new_state = update_stats(state, signal.source_domain, targets)
{:noreply, new_state}
end
defp resolve_targets(%{target_domains: :broadcast}), do: @domains
defp resolve_targets(%{target_domains: targets}), do: targets
defp enrich_signal(signal, state) do
Map.merge(signal, %{
hop_count: 0,
network_load: state.signal_count,
propagation_id: generate_propagation_id()
})
end
defp generate_propagation_id do
Base.encode16(:crypto.strong_rand_bytes(8), case: :lower)
end
end
Domain-Scoped Signal Processing
Each domain has a signal processor that filters, transforms, and acts on incoming mycelial signals. The processor implements domain-specific logic for how signals are consumed:
defmodule PrismaticMycelium.Processors.DDProcessor do
@moduledoc """
DD domain signal processor.
Processes mycelial signals relevant to due diligence:
entity risk changes from OSINT, security findings,
investigation updates, and perimeter alerts.
"""
use GenServer
require Logger
alias PrismaticMycelium.Network
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl GenServer
def init(_opts) do
Network.subscribe(:dd)
{:ok, %{processed: 0, actions_taken: 0}}
end
@impl GenServer
def handle_info({:mycelial_signal, signal}, state) do
case process_signal(signal) do
{:action, action} ->
execute_action(action)
{:noreply, %{state | processed: state.processed + 1, actions_taken: state.actions_taken + 1}}
:ignored ->
{:noreply, %{state | processed: state.processed + 1}}
end
end
defp process_signal(%{type: :entity_risk_change, payload: payload}) do
case PrismaticDD.Cases.find_by_entity(payload.entity_id) do
[] -> :ignored
cases ->
{:action, %{
type: :update_case_risk,
cases: cases,
new_risk: payload.new_risk_score,
source: payload.source
}}
end
end
defp process_signal(%{type: :security_finding, priority: priority})
when priority in [:high, :critical] do
{:action, %{type: :escalate_to_analyst, priority: priority}}
end
defp process_signal(_signal), do: :ignored
defp execute_action(%{type: :update_case_risk} = action) do
for dd_case <- action.cases do
PrismaticDD.Cases.update_risk_from_signal(dd_case, action.new_risk, action.source)
Logger.info("DD case #{dd_case.id} risk updated via mycelial signal")
end
end
end
Nutrient Routing
The nutrient routing system implements demand-based resource allocation. Domains that need more processing capacity signal their demand, and the network redistributes resources:
defmodule PrismaticMycelium.NutrientRouter do
@moduledoc """
Demand-based resource allocation across domains.
Monitors domain processing queues and redistributes
worker capacity based on real-time demand signals.
Implements exponential decay for demand smoothing.
"""
use GenServer
require Logger
@check_interval :timer.seconds(10)
@decay_factor 0.9
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl GenServer
def init(_opts) do
schedule_check()
{:ok, %{demand: %{}, allocations: %{}, history: []}}
end
@impl GenServer
def handle_info(:check_demand, state) do
current_demand = measure_demand()
smoothed = smooth_demand(state.demand, current_demand)
new_allocations = compute_allocations(smoothed)
apply_allocations(new_allocations, state.allocations)
schedule_check()
{:noreply, %{state | demand: smoothed, allocations: new_allocations}}
end
defp measure_demand do
for domain <- PrismaticMycelium.Network.domains(), into: %{} do
queue_depth = PrismaticMycelium.Processors.queue_depth(domain)
processing_rate = PrismaticMycelium.Processors.processing_rate(domain)
{domain, %{queue: queue_depth, rate: processing_rate}}
end
end
defp smooth_demand(previous, current) do
Map.merge(current, previous, fn _key, curr, prev ->
%{
queue: curr.queue (1 - @decay_factor) + prev.queue @decay_factor,
rate: curr.rate (1 - @decay_factor) + prev.rate @decay_factor
}
end)
end
defp compute_allocations(demand) do
total_demand = demand |> Map.values() |> Enum.map(& &1.queue) |> Enum.sum()
if total_demand == 0 do
Map.new(demand, fn {domain, _} -> {domain, 1.0 / map_size(demand)} end)
else
Map.new(demand, fn {domain, %{queue: q}} -> {domain, q / total_demand} end)
end
end
defp schedule_check, do: Process.send_after(self(), :check_demand, @check_interval)
end
Pattern Extraction
The intelligence layer detects recurring signal patterns across the network. When the same type of signal appears from multiple domains within a time window, it may indicate a systemic issue or a cross-domain correlation:
defmodule PrismaticMycelium.PatternExtractor do
@moduledoc """
Detects recurring patterns in mycelial signal flows.
Uses sliding time windows to identify signal clusters,
cross-domain correlations, and anomalous propagation
patterns that may indicate systemic issues.
"""
use GenServer
require Logger
@window_size :timer.minutes(5)
@min_cluster_size 3
@type pattern :: %{
id: String.t(),
signal_type: atom(),
domains: [atom()],
frequency: non_neg_integer(),
first_seen: DateTime.t(),
last_seen: DateTime.t(),
confidence: float()
}
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@spec detected_patterns() :: [pattern()]
def detected_patterns do
GenServer.call(__MODULE__, :get_patterns)
end
@impl GenServer
def init(_opts) do
for domain <- PrismaticMycelium.Network.domains() do
PrismaticMycelium.Network.subscribe(domain)
end
schedule_analysis()
{:ok, %{signals: [], patterns: []}}
end
@impl GenServer
def handle_info({:mycelial_signal, signal}, state) do
entry = %{
type: signal.type,
source: signal.source_domain,
timestamp: signal.timestamp,
payload_hash: :erlang.phash2(signal.payload)
}
pruned = prune_window(state.signals ++ [entry])
{:noreply, %{state | signals: pruned}}
end
@impl GenServer
def handle_info(:analyze, state) do
patterns = extract_patterns(state.signals)
for pattern <- patterns, pattern.confidence > 0.7 do
Logger.info("Mycelial pattern detected: #{pattern.signal_type} across #{inspect(pattern.domains)}")
end
schedule_analysis()
{:noreply, %{state | patterns: patterns}}
end
defp extract_patterns(signals) do
signals
|> Enum.group_by(& &1.type)
|> Enum.filter(fn {_type, group} -> length(group) >= @min_cluster_size end)
|> Enum.map(fn {type, group} ->
domains = group |> Enum.map(& &1.source) |> Enum.uniq()
%{
id: Base.encode16(:crypto.strong_rand_bytes(4), case: :lower),
signal_type: type,
domains: domains,
frequency: length(group),
first_seen: group |> Enum.map(& &1.timestamp) |> Enum.min(DateTime),
last_seen: group |> Enum.map(& &1.timestamp) |> Enum.max(DateTime),
confidence: length(domains) / length(PrismaticMycelium.Network.domains())
}
end)
end
defp prune_window(signals) do
cutoff = DateTime.add(DateTime.utc_now(), -@window_size, :millisecond)
Enum.filter(signals, fn s -> DateTime.compare(s.timestamp, cutoff) == :gt end)
end
defp schedule_analysis, do: Process.send_after(self(), :analyze, :timer.seconds(30))
end
|--------|-------|-------------|
The mycelial network provides a coordination layer that would be difficult to achieve with direct module-to-module dependencies. Domains remain loosely coupled while still responding to events across the platform. The biological metaphor guides design decisions toward resilient, self-organizing patterns that scale naturally with platform growth.