Skip to main content

System Design

BreachResponse is a distributed system spanning five runtime environments: a Next.js frontend, a Python sentinel agent, Mantle Sepolia smart contracts, a Neon PostgreSQL database, and GenLayer StudioNet consensus validators.


System Topology

┌──────────────────┐
│ GenLayer Validators│
│ (Nondeterministic │
│ LLM Consensus) │
└────────┬─────────┘
│ readContract / writeContract

┌──────────────┐ HTTP/SSE ┌──────────────┐ RPC/WS ┌──────────────┐
│ │◄───────────────►│ │◄─────────────►│ │
│ Next.js │ │ Python │ │ Mantle │
│ Frontend │ POST /api/* │ Sentinel │ eth_getLogs │ Sepolia │
│ (Vercel) │ SSE /logs/ │ Agent │ eth_sendTx │ RPC Node │
│ │ stream │ (Railway) │ │ │
└──────┬───────┘ └──────────────┘ └──────────────┘

│ SQL (pg)

┌──────┴───────┐ ┌──────────────┐
│ │ │ │
│ Neon │ │ Upstash │
│ PostgreSQL │ │ Redis │
│ │ │ (Event Bus) │
└──────────────┘ └──────────────┘

Component Breakdown

1. Next.js Frontend (frontend/)

The Command Center is a Next.js 16 App Router application with:

LayerTechnologyFiles
RoutingNext.js App Routersrc/app/
API RoutesRoute Handlers (Node.js runtime)src/app/api/**/route.ts
StylingTailwind CSS 4Global styles
WalletRainbowKit + Wagmi + ViemWallet connection, Mantle Sepolia
Charts/UIFramer Motion + Lucide ReactDashboard widgets
Consensusgenlayer-js SDKGenLayer contract interaction

Key libraries from package.json:

{
"@rainbow-me/rainbowkit": "^2.2.11",
"ethers": "^6.16.0",
"framer-motion": "^12.40.0",
"genlayer-js": "^0.8.0",
"next": "^16.2.7",
"pg": "^8.21.0",
"viem": "^2.52.2",
"wagmi": "^2.19.5"
}

API Route Architecture

src/app/api/
├── audit/route.ts POST -- AI contract auditing
├── analyze/route.ts POST -- AI threat classification
├── compare/route.ts POST -- Groq vs Hunyuan parallel analysis
├── gas-estimate/route.ts POST -- Gas cost estimation
├── sentinels/route.ts GET/POST/PUT -- Sentinel node CRUD
├── nodes/heartbeat/route.ts POST -- Agent heartbeat
├── vault/status/route.ts GET -- TargetVault pause status
├── metrics/value-monitored/route.ts GET -- Total value monitored
└── logs/
├── ingest/route.ts POST -- Agent telemetry ingestion
└── stream/route.ts GET -- SSE telemetry stream

Each route runs in the nodejs runtime (not Edge) because it accesses process.env, PostgreSQL via pg, and the Node.js crypto module.


2. Python Sentinel Agent (agent/)

The agent is a single-process Python application with three core modules:

ModuleFilePurpose
Main Loopagent/main.pyBlock scanning, threat detection, response execution
Incident Analyzeragent/incident_analyzer.pyOpenAI-compatible LLM integration for exploit analysis
Reporteragent/reporter.pyHTTP bridge to frontend API for telemetry and heartbeats

Dependencies

web3 -- Ethereum/Mantle RPC interaction
requests -- (transitive, used by web3)
python-dotenv -- Environment variable loading
openai -- LLM API client (Groq, Hunyuan, OpenAI-compatible)

Entry Points

  • Railway deployment: railway.json specifies python main.py from the repo root, which shims to agent/main.py
  • Direct execution: cd agent && python main.py
  • With virtualenv: cd agent && source venv/bin/activate && python main.py

3. Smart Contracts (contracts/)

Four Solidity contracts deployed with Hardhat:

ContractFileNetworkAddress
SentinelRegistrycontracts/SentinelRegistry.solMantle Sepolia0xea3C039795B5b04105B795c8B0cB85e0a42Cc85C
TargetVaultcontracts/TargetVault.solMantle Sepolia0x9d9b602CFe69cfF9706EAc399808E84682ce94FB
Attackercontracts/Attacker.solMantle Sepolia(deployed per simulation)
TestProtocolcontracts/TestProtocol.solMantle Sepolia(deployed per test)

Hardhat configuration (contracts/hardhat.config.ts):

networks: {
mantle_sepolia: {
type: "http",
chainType: "op", // Optimism stack (Mantle is OP-derived)
url: "https://mantle-sepolia.drpc.org",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
timeout: 1000000,
}
}

GenLayer Consensus Guard (contracts/genlayer/IncidentConsensusGuard.py):

Deployed at 0x86369EC44fbB5EB682729368557176858aBe0c73 on GenLayer StudioNet. This is a Python smart contract (not Solidity) that uses GenLayer's gl.Contract base class and gl.vm.run_nondet_unsafe for LLM-based consensus.


4. Database Layer (frontend/src/lib/db.ts)

The database module implements a dual-mode storage system:

// Primary: Neon PostgreSQL (when DATABASE_URL is set)
const pool = new Pool({
connectionString: databaseUrl,
ssl: { rejectUnauthorized },
max: 3
});

// Fallback: In-memory store (development / no database)
const globalStore = globalThis as unknown as {
breachResponseStore?: Store;
};

Database schema (auto-created on first connection):

CREATE TABLE IF NOT EXISTS sentinel_nodes (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
address TEXT NOT NULL UNIQUE,
owner TEXT,
status TEXT NOT NULL CHECK (status IN ('ACTIVE', 'PAUSED', 'OFFLINE')),
latency TEXT NOT NULL DEFAULT '6.4ms',
events INTEGER NOT NULL DEFAULT 0,
last_heartbeat TIMESTAMPTZ NOT NULL DEFAULT NOW(),
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS telemetry_logs (
id TEXT PRIMARY KEY,
text TEXT,
level TEXT,
tx_hash TEXT,
protocol TEXT,
verification_type TEXT,
gas_saved TEXT,
status TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

The prisma export provides a Prisma-compatible API surface:

export const prisma = {
sentinelNode: {
findMany, create, findUnique, update, upsert, updateMany
},
alert: {
findMany, create
}
};

This is not actual Prisma -- it's a hand-written adapter that works with both PostgreSQL and in-memory storage. The naming convention matches Prisma so the code reads naturally.


5. Event Bus / SSE (frontend/src/lib/eventEmitter.ts)

A singleton Node.js EventEmitter provides in-process event delivery:

const globalForEvents = globalThis as unknown as {
sseEmitter: EventEmitter | undefined;
};

export const sseEmitter = globalForEvents.sseEmitter ?? new EventEmitter();

In production, this should be replaced with Redis Pub/Sub. The code already has Upstash Redis integration for telemetry persistence (frontend/src/lib/telemetry.ts):

if (hasRedisEnv()) {
await redisCommand(['LPUSH', telemetryKey, JSON.stringify(payload)]);
await redisCommand(['LTRIM', telemetryKey, 0, maxTelemetryEvents - 1]);
}

6. GenLayer Consensus Client (frontend/src/lib/genlayerConsensus.ts)

TypeScript client for interacting with the GenLayer consensus guard:

export class IncidentConsensusGuardClient {
async submitIncident(input: ConsensusIncidentInput) { ... }
async evaluateIncident(incidentId: string) { ... }
async listIncidents() { ... }
async getIncident(incidentId: string) { ... }
}

Uses genlayer-js SDK with the GenLayer simulator chain and StudioNet endpoint:

export function createGenLayerClient(account?: GenLayerAccount) {
return createClient({
chain: simulator,
endpoint: GENLAYER_STUDIO_URL, // https://studio.genlayer.com/api
...(account ? { account } : {}),
});
}

Security Model

Threat Surface

ComponentThreatMitigation
Agent private keyKey exposure -> unauthorized pause txMinimal wallet balance, manual mode default
RPC endpointMan-in-the-middle, data tamperingHTTPS, verify tx receipts on-chain
Frontend APIUnauthenticated telemetry injectionINGEST_TOKEN bearer auth, timingSafeEqual comparison
DatabaseSQL injection, data exfiltrationParameterized queries, SSL connections
AI classificationHallucination, adversarial prompt injectionDual-model comparison, human approval gate
GenLayerValidator collusionrun_nondet_unsafe enforces re-execution, consensus thresholds

Authentication Flow

The INGEST_TOKEN system secures agent-to-frontend communication:

// frontend/src/lib/ingestAuth.ts
export function isAuthorizedIngest(request: Request): boolean {
const expected = process.env.INGEST_TOKEN;
if (!expected) return true; // Open when not configured (dev-friendly)

const header = request.headers.get('authorization') ?? '';
const token = header.startsWith('Bearer ') ? header.slice('Bearer '.length).trim() : '';
if (!token) return false;

return timingSafeEqual(Buffer.from(token), Buffer.from(expected));
}

The agent sends the same token:

# agent/reporter.py
def ingest_headers() -> dict:
headers = {"Content-Type": "application/json"}
token = os.getenv("INGEST_TOKEN")
if token:
headers["Authorization"] = f"Bearer {token}"
return headers

Performance Characteristics

MetricValueNotes
Block scan interval~3 secondsConfigurable via time.sleep() in main loop
Block catch-up speed~50 blocks/secTesting on Mantle Sepolia (~1 block/2s)
AI classification latency200-800msGroq Llama 3.1-8B-Instant, depends on token count
Dual-model comparison300-1200msParallel Groq + Hunyuan
Transaction confirmation~2-5 secondsMantle Sepolia block time
SSE event delivery<50msIn-process EventEmitter
Database query5-50msNeon PostgreSQL, connection pooling (max 3)

Deployment Architecture

Refer to the Deployment section for detailed deployment guides. The recommended production topology is:

  • Vercel -- Next.js frontend (static export or serverless)
  • Railway -- Python agent (always-on worker)
  • Neon -- PostgreSQL (serverless, autoscaling)
  • Upstash -- Redis (optional, for cross-instance SSE)

Next Steps