Skip to content

StackedTEN/Containment-Command-Lab

Repository files navigation

Containment Command Lab

Machine-speed containment reasoning. Human-approved action.

A public, portfolio-grade demonstration of human-approved agentic AI containment during a cyber incident β€” built around the Incident Commander.

Simulated containment actions only. No production systems are modified.

Containment Command Lab demonstrates how agentic AI can support Incident Commanders during one of the hardest moments in cyber response: deciding what to contain, when to contain it, who must approve it, what evidence may be lost, and what business operations may be disrupted.

It runs entirely on mock data with a deterministic, key-free AI core, so it works the moment you npm install β€” no API keys, no external services, no execution surface. On top of the workflow sits a bounded eight-agent reasoning layer that triages, maps blast radius, weighs business impact and proposes a containment strategy β€” and still cannot move without a recorded human approval chain. The agents advise; people approve; a ledger attests.

This project is part of the Cyber Resilience in the Age of AI body of work by RedCon1 Response.


Why this matters

In a live incident, containment is rarely a purely technical decision. The Incident Commander is making a high-stakes call under time pressure and incomplete information, balancing five questions at once:

  • What do we contain? Isolate the wrong asset and you blind the investigation; too little and the threat keeps moving.
  • When do we act? Every minute of deliberation can be spread β€” but acting on a bad read is worse.
  • Who must approve? A targeted action and a business-halting one demand very different authority.
  • What evidence is lost? Containing too early can destroy volatile memory, sessions and logs β€” and the attribution that lives in them.
  • What business breaks? Containment that stops payroll, production or a board-critical wire has a cost the security team cannot own alone.

AI is genuinely useful here β€” but only if it compresses the reasoning, never the accountability. That thesis is what this lab makes tangible.


What this demonstrates

Capability What it shows
AI-assisted triage Facts, assumptions and unknowns separated and scored β€” deterministically, no API keys.
Bounded agentic layer Eight cooperating agents reason over the incident and propose a strategy. They advise; humans approve.
Blast-radius mapping Interactive dependency graph of identities, assets and services, with suspected lateral movement flagged.
Containment options Minimal / Targeted / Aggressive / Staged, each with explicit security, business, evidence and recovery trade-offs.
Human-approved routing Approval chains from IR Lead to Executive; a code-level guardrail blocks even simulated execution until approvals exist.
Audit-ready ledger Every drafted, approved and simulated action appended to a hash-chained containment ledger.
Five seed scenarios Endpoint, cloud identity, ransomware, OAuth-consent and BEC β€” each a full, switchable mock incident.

Screenshots

Shipped as design-accurate SVG placeholders generated from the app's own design tokens (scripts/gen-screenshots.py). To capture real PNGs locally, run npm run screenshots (see How to run all validation checks).

Overview (landing) Incident Commander
Overview Commander
Agentic Analysis Blast Radius Map
Agents Blast Radius
Containment Ledger
Ledger

Demo walkthrough

  1. Pick a scenario on the landing page (or via the top-bar switcher) β€” e.g. Finance endpoint compromise, racing a payroll deadline.
  2. Incident Commander (/commander) gives the single operating picture: severity, blast radius, the business clock, decisions in flight. Click Run Agentic Analysis.
  3. Agentic Analysis (/agents) shows all eight agents' findings, recommendations, warnings, confidence and human-review flags β€” advisory only.
  4. Triage (/triage) separates known facts, assumptions and unknowns, and lists the evidence to preserve before any disruptive action.
  5. Blast Radius (/blast-radius) maps how the threat could propagate, with suspected paths dashed.
  6. Containment (/containment) presents four scored options plus the agent-proposed strategy and the decision authority required.
  7. Approvals (/approvals) routes a chosen option through its approval chain. The guardrail refuses simulated execution until every required approval is recorded.
  8. Brief (/brief) generates a leadership-ready situation report from live state.
  9. Ledger (/ledger) shows the append-only, hash-chained record of everything that happened.

Action lifecycle: Recommended β†’ Drafted β†’ Awaiting Approval β†’ Approved β†’ Simulated Executed, with Rejected and Rolled Back as terminal branches.


Architecture

A fully static Next.js app with no backend, no connectors and no execution surface. All logic is client-side and deterministic.

flowchart TB
  subgraph Client["Browser β€” static Next.js 15 app (no backend)"]
    UI["UI screens<br/>Commander Β· Triage Β· Blast Radius Β· Containment Β· Approvals Β· Brief Β· Ledger"]
    Store["Zustand store (persisted)<br/>workflow Β· ledger Β· agent runs Β· selected scenario"]
    subgraph Core["Deterministic core β€” no network, no API keys"]
      Data["Incident data<br/>5 mock scenarios"]
      Scoring["Scoring<br/>blast radius Β· recommendation"]
      Agents["Agentic layer<br/>8 bounded agents"]
      Guard["Execution guardrail<br/>canSimulateExecute()"]
    end
  end
  UI --> Store
  Store --> Data
  Store --> Agents
  Agents --> Scoring
  Agents --> Data
  Store --> Guard
  Guard -. blocks until approved .-> Sim["Simulated execution<br/>(local state + ledger only)"]
  Boundary["NOT PRESENT BY DESIGN:<br/>no EDR / SIEM / cloud / identity connectors Β· no execution surface Β· no exploit logic"]
Loading
src/
β”œβ”€β”€ app/                      # Next.js App Router β€” one folder per screen
β”‚   β”œβ”€β”€ layout.tsx            # Shell, self-hosted fonts, metadata
β”‚   β”œβ”€β”€ page.tsx              # Overview / landing (+ scenario picker)
β”‚   β”œβ”€β”€ error.tsx             # Route error boundary (on-brand)
β”‚   β”œβ”€β”€ not-found.tsx         # 404 (on-brand)
β”‚   β”œβ”€β”€ intake/ commander/ agents/ triage/ blast-radius/
β”‚   β”œβ”€β”€ containment/ approvals/ brief/ ledger/ about/
β”‚   └── globals.css
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ ui/                   # Primitives (button, card, badge, stat)
β”‚   β”œβ”€β”€ layout/               # Sidebar, top bar (+ scenario switcher), safety banner, shell
β”‚   └── domain/               # Containment cards, approval chain, ledger table, blast map,
β”‚                             #   agent output/run panels, scenario picker, badges
└── lib/
    β”œβ”€β”€ types.ts              # Domain models + Zod intake schema
    β”œβ”€β”€ mock-data.ts          # Default incident + INCIDENTS registry + getIncident()
    β”œβ”€β”€ seed-incidents.ts     # The four additional scenarios
    β”œβ”€β”€ scoring.ts            # Blast-radius / recommendation / exposure scoring
    β”œβ”€β”€ status.ts             # Severity / status / tier / volatility tokens
    β”œβ”€β”€ ai.ts                 # AiProvider interface + deterministic mock provider
    β”œβ”€β”€ store.ts              # Zustand store (persisted), keyed by scenario
    β”œβ”€β”€ use-hydrated.ts       # SSR-safe hydration gate
    β”œβ”€β”€ utils.ts              # cn(), demo hash chain, time helpers
    └── agents/               # Bounded agentic reasoning layer
        β”œβ”€β”€ types.ts          # AgentInput/Output/Finding/Recommendation/Run
        β”œβ”€β”€ helpers.ts        # Deterministic builders + advisory-safety guard
        β”œβ”€β”€ provider.ts       # LLM provider abstraction (disabled by default)
        β”œβ”€β”€ guardrail.ts      # canSimulateExecute() β€” the execution gate
        β”œβ”€β”€ *-agent.ts        # The eight agents
        β”œβ”€β”€ index.ts          # Registry, runAgents() orchestrator, selectors
        └── __tests__/        # Vitest unit tests (shape, determinism, safety)

Agent workflow

Eight agents run in order, each consuming the outputs of those before it. The chain ends at a guardrail that defers to a human approval chain β€” nothing executes (even in simulation) without it.

flowchart LR
  T[Triage] --> B[Blast Radius] --> E[Evidence] --> C[Containment Strategy]
  C --> BI[Business Impact] --> DA[Decision Authority] --> BR[Briefing] --> G[Execution Guardrail]
  G --> H{"Human approval chain<br/>IR Lead β†’ Incident Commander β†’ CISO β†’<br/>Business Owner β†’ Legal β†’ Executive"}
  H -- all approvals recorded --> S["Simulated execution β†’ hash-chained ledger"]
  H -- approvals missing --> X["Blocked + logged by guardrail"]
Loading

Every agent returns the same shape: summary, findings[], recommendations[], warnings[], confidence, requiredHumanReview, generatedAt. Findings are explicitly typed fact / assumption / unknown / observation so an assumption never poses as a confirmed fact. runAgents() is deterministic for a fixed clock; a deterministic timer can be injected in tests while the UI keeps real per-agent timings.

Data model

All scenarios are typed Incident objects. The registry in src/lib/mock-data.ts exposes INCIDENTS and getIncident(id); the four non-default scenarios live in src/lib/seed-incidents.ts.

interface Incident {
  id: string;
  scenario: string;            // picker key, e.g. "cloud-identity"
  scenarioLabel: string;       // human label for cards/menus
  title: string;
  severity: "low" | "medium" | "high" | "critical";
  detectedAt: string;          // ISO
  businessClock: {             // the time-critical milestone the incident races
    label: string;             // e.g. "Payroll run", "Board meeting"
    at: string;                // ISO deadline (drives the live countdown)
    note: string;
  };
  summary: string;
  triage: { knownFacts: string[]; assumptions: string[]; unknowns: string[] };
  identities: Identity[];
  assets: Asset[];
  services: BusinessService[];
  evidence: EvidenceItem[];    // each flagged preserve-before-containment or not
  options: ContainmentOption[];// minimal / targeted / aggressive / staged
  graph: { nodes: GraphNode[]; edges: GraphEdge[] }; // blast-radius graph
}

Each ContainmentOption is fully specified (security / business / evidence / recovery impact, required approvals, rollback plan, confidence + riskOfDelay scores, and the simulatedSteps appended to the ledger). Workflow state (ContainmentDecision, ApprovalStep, LedgerEntry) lives in the Zustand store, persisted and keyed by the selected scenario.


Public lab vs. RecoverIQ product boundary

Containment Command Lab (this repo) RecoverIQ Containment Command (proprietary)
Data Mock incidents only Real telemetry via governed connectors
Integrations None Read-only EDR / SIEM / identity, policy-guardrailed
Execution Simulated only (local state) Governed, reversible playbooks with attestation
AI Deterministic, key-free Pluggable LLM behind the same interfaces
Purpose Show the thinking, in the open Operate it safely in production

The lab is where the ideas are demonstrated working; RecoverIQ is the separate, commercial direction they point toward. Nothing proprietary ships in this repo.


Security and safety posture

This is a public demonstration lab, not a production IR platform, and not production IR advice. The safety properties are structural, not promises:

  • No execution surface. There is no integration layer and no outbound action capability. The "containment engine" only appends descriptive, simulated steps to local state.
  • Human-in-the-loop, enforced in code. A single guardrail (canSimulateExecute) is consulted by both the UI and the store; even a simulated execution is refused until every required approval is recorded, and bypass attempts are written to the ledger.
  • No offensive capability or exploit logic anywhere in the codebase.
  • Agents are advisory. enforceAdvisorySafety forces approval on every containment/guardrail recommendation and constrains recommendations to a fixed, benign set of kinds. Verified by tests.
  • Facts vs. assumptions vs. unknowns are kept explicitly separate.
  • Illustrative ledger hash. The hash chain is a non-cryptographic demonstration of tamper-evidence, clearly labelled as such β€” not a real integrity control.
  • No secrets, no network at runtime. Fonts are self-hosted; the deterministic AI needs no keys.

The full reasoning lives on the in-app About & Safety Model screen.

Dependency posture

  • Next.js 15.5.x (security line) β€” clears the high/critical advisories present on older 14.x.
  • postcss pinned to the patched line via an overrides entry (covers Next's nested copy).
  • Vitest 4 β€” clears the dev-only esbuild/vite advisory.
  • npm audit and npm audit --omit=dev both report 0 vulnerabilities at time of writing.

How to run all validation checks

Requires Node 18.18+ (Node 20+ recommended).

npm ci                       # clean, reproducible install from package-lock.json

npm run typecheck            # tsc --noEmit
npm run lint                 # next lint (no warnings or errors)
npm run test                 # vitest run (agent shape, determinism, safety)
npm run build                # production build β€” 14 static routes, no hang
npm run validate             # all four of the above in sequence

npm audit --omit=dev         # expect: found 0 vulnerabilities

npm run dev                  # http://localhost:3000

# Optional β€” capture real screenshots locally (needs the Playwright browser):
npm run build && npm run start &
npx playwright install chromium
npm run screenshots          # writes public/screenshots/*.png

The previous no-page-custom-font lint warning is gone: fonts are now self-hosted via @fontsource instead of a runtime Google Fonts <link>, which also removes a build-time network dependency. React Flow is loaded with ssr: false, so static generation never blocks on it.

Expected, harmless notices (no code warnings or errors): next lint prints a notice that it is deprecated and will be removed in Next.js 16, and npm ci notes that eslint@8 is no longer supported. Both are unavoidable while pinning the matching eslint-config-next@15 (which targets ESLint 8); they do not affect the build, tests, or the zero-vulnerability audit, and the linter still reports no warnings or errors.


Roadmap

v0.1 β€” this lab

  • Deterministic mock AI triage, blast-radius mapping and option scoring
  • Bounded eight-agent reasoning layer (advisory, human-approved)
  • Five seed scenarios spanning endpoint, identity, ransomware, OAuth and BEC
  • Simulated approval routing, execution guardrail and a hash-chained ledger

Next β€” depth

  • Pluggable LLM provider behind the existing provider interfaces
  • Configurable approval policies and role-based views
  • Richer scenario authoring and shareable incident snapshots

RecoverIQ Containment Command β€” proprietary

  • Read-only connectors for EDR / SIEM / identity behind strict policy guardrails
  • Governed, reversible containment playbooks with real approvals and full attestation
  • Resilience metrics, post-incident learning loops and executive reporting

Tech stack

Next.js 15 (App Router) Β· React 18 Β· TypeScript (strict) Β· Tailwind CSS (custom design system) Β· Zustand (persisted) Β· @xyflow/react (React Flow, dynamically loaded) Β· Zod Β· lucide-react Β· self-hosted @fontsource (Sora / IBM Plex Sans / IBM Plex Mono) Β· Vitest.

License

Source-available for review and demonstration only. Β© RedCon1 Response. You may read, run and learn from this code; it is not licensed for production use, redistribution as a product, or commercial deployment, and it carries no warranty. The "RecoverIQ Containment Command" product direction is proprietary and not part of this grant. See LICENSE.

Disclaimer

Containment Command Lab is a public demonstration by RedCon1 Response. It uses mock data only, performs no real security actions, contains no offensive or exploit logic, and is not production incident-response advice. RecoverIQ Containment Command is a separate, proprietary product direction.

About

Agentic AI containment decision support for Incident Commanders. Machine-speed containment reasoning. Human-approved action.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors