Skip to content

Repository files navigation

 ██████╗██╗  ██╗ █████╗  ██████╗ ███████╗
██╔════╝██║  ██║██╔══██╗██╔═══██╗██╔════╝
██║     ███████║███████║██║   ██║███████╗
██║     ██╔══██║██╔══██║██║   ██║╚════██║
╚██████╗██║  ██║██║  ██║╚██████╔╝███████║
 ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝ ╚═════╝ ╚══════╝
 ██████╗ ██████╗ ███╗   ██╗████████╗██████╗  ██████╗ ██╗     ██╗     ███████╗██████╗ 
██╔════╝██╔═══██╗████╗  ██║╚══██╔══╝██╔══██╗██╔═══██╗██║     ██║     ██╔════╝██╔══██╗
██║     ██║   ██║██╔██╗ ██║   ██║   ██████╔╝██║   ██║██║     ██║     █████╗  ██████╔╝
██║     ██║   ██║██║╚██╗██║   ██║   ██╔══██╗██║   ██║██║     ██║     ██╔══╝  ██╔══██╗
╚██████╗╚██████╔╝██║ ╚████║   ██║   ██║  ██║╚██████╔╝███████╗███████╗███████╗██║  ██║
 ╚═════╝ ╚═════╝ ╚═╝  ╚═══╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝╚═╝  ╚═╝

🎯 A two-sided chaos engineering console. Break a distributed system from the Red Team panel, then watch the Blue Team's circuit breakers, token buckets and autoscalers fight back. Every failure is simulated in your browser — no cluster, no cloud bill, no blast radius.

Status: work in progress License: MIT Failures are simulated only React 19.2 TypeScript 5.9 strict Vite 7.2 Tailwind 4.1 Backend: Encore.ts Node 22+

🪫 Reliability is a skill you only get to practise during an outage — the worst possible time to be learning it. Chaos Controller hands you the outage on demand: kill the primary, partition the network, flood the ingress, then watch what the error budget does about it. Nothing real breaks, so you can break it as often as you like.

⚠️ Project status — early work-in-progress. This is an actively developed portfolio / learning project, not a finished product. Working today: the client-side simulation core — timed failure state machines, a real token-bucket rate limiter, circuit breaker, autoscaler, and SLO / cost model — plus an optional Encore.ts metrics backend. Not done yet: there are no frontend tests, several panels are staged with placeholder data, and a few controls are inert. The full, honest breakdown is in Status & limitations — please read it before judging completeness. But do explore — WIP is not off-limits. Try the client-only simulator in 60 seconds ↓


🎪 Try it in 60 seconds (client-only)

No backend, no cloud, no config — the simulator runs entirely in your browser. This is the fastest way to see where the project is headed.

git clone https://github.com/AkashVarma007/Chaos-Controller.git
cd Chaos-Controller && pnpm install && pnpm dev

Open http://localhost:5173, then click "Full Chaos Dashboard" in the header. That view is the fully client-side simulator and needs no server. (The default Blue Team view expects the optional backend and shows a metrics error until you start one — that is expected, not a bug.)

Then poke at it:

  • Fire a red attack — Traffic Flood or Kill Database. Latency and queue charts move within a second; the event log appends a timestamped line.
  • Flip a blue mitigation — open the circuit breaker or tighten the rate limiter, and the next tick visibly reacts to it.
  • Run a scenario — Traffic Flood + Autoscale plays a scripted incident end to end, then auto-stops.

That is the high-level direction. The toolkit breaks down every action, and Status is honest about what is real versus staged today.


🗺️ Repository map

Chaos-Controller/
├── index.html                  Vite entry document
├── src/                        React frontend — 96 TS/TSX files, ~4.5k lines
│   ├── app/                    entry point, root component, global stylesheet
│   ├── components/
│   │   ├── red/                17 attack-side components
│   │   ├── blue/               35 defence-side components (incl. charts/)
│   │   ├── common/             3 shared metric/chart wrappers
│   │   └── ui/                 6 presentational primitives
│   ├── core/                   ports + adapters, and framework re-exports
│   ├── store/                  Zustand store, failure logic, scenarios (1,075 lines)
│   └── lib/                    small pure helpers
├── server/                     Encore.ts backend — 6 TS files, ~300 lines
│   ├── metrics/                in-memory aggregation + order ingestion
│   └── hello/                  starter smoke-test endpoint
├── docs/                       backend requirements + implementation checklist
└── .github/                    CI workflow, PR and issue templates
Path What it is Tracked?
src/ The console itself. All chaos logic lives here. ✅
server/ Optional Encore.ts backend. Feeds the Blue Team view real request metrics. Started separately — the root pnpm dev does not launch it. ✅
docs/ Design docs for the backend. The checklist is a partly stale work log, not a spec — see Status. ✅
node_modules/, server/node_modules/ Dependencies. ❌ ignored
dist/ Vite production output. ❌ ignored
server/.encore/, server/encore.gen/ Generated by the Encore CLI. ❌ ignored
.env Local config. Copy from the tracked .env.example. ❌ ignored

🧰 The chaos toolkit

There are two views, toggled from the header, and they run on completely different data. This is the most important thing to understand about the project:

View Renders Data source Cadence
Blue Team Dashboard (default) A single live system-overview card Real HTTP metrics from the Encore backend polls /metrics/blue/snapshot every 5 s
Full Chaos Dashboard Everything else — every red and blue panel below Client-side simulator (Zustand) ticks every 1 s

The simulator loop starts only while the Full Chaos Dashboard is mounted; switching back tears the interval down (src/app/App.tsx). The Blue Team view is deliberately thin — it is the slice that talks to a real server, and it shows a fetch error until you start one.

🔴 Red Team — ten failure modes

Each is a logic* function in src/store/simulationLogic.ts, dispatched through the store as a handle* action. They are timed state machines, not one-shot toggles — most inject damage, hold it, then decay:

Action What it does, concretely
Flush cache +120 ms latency now, +80 ms more at 2 s, cache rebuilt at 5 s
Terminate node Marks the first active node dead, +40 ms latency; a replacement is scheduled at 4 s
Kill database FAILING_OVER + DEGRADED; every 800 ms adds +30 ms latency and +8 queue depth; recovers at 6 s
Region outage OFFLINE now, region flip at 2 s, healthy at 6 s
Traffic flood Ramps every 300 ms for 4 s (queue → 500, latency → 600 ms), then drains for 5 s. Refuses to start unless the system is HEALTHY
Latency inject +200 / +120 / +60 ms by severity; only ~70 % is reversed at 4 s, so damage lingers
Network partition Drops 300 / 180 / 90 per 500 ms tick for 5 s
Error inject +10 / +5 / +2 % error rate, reversed at 6 s
Disk saturation +80 / +40 / +20 queue depth every 600 ms for 6 s, latency capped at 800 ms
Queue jam +200 / +120 / +60 queue depth, 80 % reversed at 5 s

Attacks are first-class records rather than fire-and-forget buttons: each carries a status (RUNNING / PAUSED / COMPLETED / ABORTED), a rolling 120-point series of sent / allowed / dropped, a peak-RPS marker, a geo heat map, and a cost breakdown across compute, network and other. They can be paused, resumed, aborted, snapshotted, and replayed into a fresh attack.

🔵 Blue Team — the mitigations

These controls write to the same store the simulator reads, so a mitigation genuinely changes what the charts do on the next tick.

Control Behaviour
Circuit breaker CLOSED / OPEN / HALF_OPEN. Open suppresses the error rate to zero; half-open lets it climb back at +2 %/tick
Rate limiter A real token bucket — refills perSecond / 2 per tick, capped at burst, drops the overflow. Adaptive mode trims the limit 5 % on drops and raises it 5 % when the queue sits below half its target
Autoscaling desired vs actual replicas converging one step per tick against a target queue depth, with a scale-event log
Cache TTL clamped to 30–3600 s, plus a warm-up that adds 10 points of hit ratio
Queue / DLQ Pause consumers, change concurrency, poison messages in, reprocess, or purge
Region & DNS Force a failover between us-east-1 and us-west-2; cycle routing policy latency → geo → weighted
Database Flip the endpoint between read-write and read-only
Brownout Four graded levels for shedding non-essential work
Feature flags Percentage rollouts, persisted to localStorage
Deployments Canary or blue-green rollout

Everything is scored against an SLO (99.9 % availability by default) with a depleting error budget, and a cost model that recomputes estimated hourly spend from replica count, queue depth and latency every tick.

🎬 Scenario engine — seven scripted incidents

Steps fire at an atMs offset against the store; the run auto-stops at durationMs.

Scenario Duration Shape
Traffic Flood + Autoscale 15 s Flood at t=0, autoscaler responds
DB Failover 12 s Kill the primary at t=0
Region Outage 12 s Full region loss at t=0
Cache Stampede 8 s Cold cache at t=0
Circuit Breaker + Brownout 14 s Open → brownout L2 at 5 s → half-open at 9 s → closed at 12 s → brownout off at 13 s
Rate Limiting + DLQ 16 s Clamp to 50/s at t=0 → 25 poison messages at 2 s → reprocess 15 at 10 s
Canary Rollout 18 s Start canary → 10 % at 4 s → 50 % at 8 s → 100 % at 12 s → finish at 16 s

Presets can be duplicated, edited and deleted at runtime — built-ins are delete-protected — so you can compose your own game day. Custom scenarios live in memory only and do not survive a reload.

📋 Sessions, roles and audit

Three roles (viewer / operator / admin) gate destructive actions through a hasPermission map: killing the database and triggering a region outage require admin, terminating a node needs operator. Attack actions append to a 500-entry audit log with user, params, outcome and timestamp, and a session can be exported as JSON with its full metric history.

Be precise about what this is: the permission check is enforced in the manual chaos grid only. The same actions remain reachable unchecked via the keyboard shortcuts, the detailed attack configurator, and scenario presets. It is an incident-response rehearsal surface, not access control — see Safety model.


🏛️ Architecture

┌──────────────────────────── Browser ─────────────────────────────┐
│                                                                   │
│   Header ──▶ view toggle                                          │
│                 │                                                 │
│      ┌──────────┴───────────┐                                     │
│      ▼                      ▼                                     │
│  BlueTeamDashboard    MonitoringDashboard                         │
│  └─ SystemOverviewLite  (Full Chaos — every other panel)          │
│      │  the only component      │                                 │
│      │  that calls the server   ▼                                 │
│      │                ┌─────────────────────┐                     │
│      │                │  Zustand store      │◀── red/ panels      │
│      │                │  simulationStore.ts │    dispatch handle* │
│      │                │                     │                     │
│      │                │  1 s tick:          │──▶ blue/ panels     │
│      │                │   token bucket      │    render 90-point  │
│      │                │   autoscaling       │    histories        │
│      │                │   error rate        │                     │
│      │                │   cost model        │                     │
│      │                └─────────┬───────────┘                     │
│      │                          │                                 │
│      │                          ▼                                 │
│      │                simulationLogic.ts ◀── scenarios.ts steps   │
│      │                (timed failure state machines)              │
│      │                                                            │
│      │  useBlueSnapshot() — 5 s poll                              │
│      ▼                                                            │
│  blueMetricsClient.ts ───────┐                                    │
└──────────────────────────────┼────────────────────────────────────┘
                               │ HTTP
                               ▼
┌──────────────────── Encore.ts server (localhost:4000) ────────────┐
│                                                                    │
│  POST /ecom/orders ──┐                                             │
│  GET  /ecom/health ──┴──▶ recordRequest()                          │
│                              │                                     │
│                              ▼                                     │
│                    in-memory ring buffers                          │
│                    1000 events · 300 history points                │
│                              │                                     │
│                    5 s aggregation over a 60 s window              │
│                              │                                     │
│                              ▼                                     │
│  GET /metrics/blue/snapshot ──▶ status · rps · error % · latency   │
│  GET /metrics/blue/history  ──▶ time series  (no client yet)       │
└────────────────────────────────────────────────────────────────────┘

Ports and adapters. src/core/ is organised as a seam: each concern is a directory with a port.ts type, an adapters/ directory, and an index.ts that picks the active implementation, so swapping one means changing a single re-export.

Honestly scoped: four of these are load-bearing — state, time, storage and rng are consumed throughout the store and components. The other five (cache, config, flags, http, observability) are implemented but have no call sites anywhere in the app. They are scaffolding for a composition root that was never wired up. Notably, the one real HTTP call in the project (blueMetricsClient.ts) uses fetch directly rather than the http port, and event logging goes through the store's own addLog rather than the observability port.


🚀 Quick Start

Requirements: Node.js 22+ and pnpm 10+. The backend is optional.

git clone https://github.com/AkashVarma007/Chaos-Controller.git
cd Chaos-Controller
pnpm install
pnpm dev

✅ Verify — Vite prints ready in … and serves on http://localhost:5173.

The app opens on the Blue Team Dashboard, which will show a metrics error until you start the backend — that is expected. Click Full Chaos Dashboard to reach the simulator, which needs no server at all.

✅ Verify — on the Full Chaos Dashboard, trigger any red action. The event log should append a timestamped line immediately, and the latency and queue charts should move within a second.

Optional: live backend metrics

Needs the Encore CLI — Encore's runtime library cannot be started by plain node.

cp .env.example .env          # VITE_METRICS_API_BASE=http://localhost:4000
cd server
npm install
encore run

✅ Verify — the endpoint answers:

curl http://localhost:4000/ecom/health
# {"status":"ok"}

Now generate traffic and watch the Blue Team Dashboard follow it:

for i in $(seq 1 20); do
  curl -s -X POST http://localhost:4000/ecom/orders \
    -H 'Content-Type: application/json' \
    -d '{"userId":"u1","items":[{"productId":"p1","quantity":1}]}' > /dev/null
done
curl http://localhost:4000/metrics/blue/snapshot

✅ Verify — totalRequests has climbed, and within one 5 s aggregation tick currentRps and avgLatencyMs become non-zero. Send a deliberately invalid body (omit items) a few times to push the error rate past 5 % and watch systemStatus fall to DEGRADED.

Full endpoint reference: server/README.md.


🧑‍💻 Working with it

Command What it does
pnpm dev Vite dev server with HMR on port 5173
pnpm typecheck tsc --noEmit against strict TypeScript
pnpm build Typecheck, then production bundle into dist/
pnpm preview Serve the built bundle
cd server && npm run typecheck Typecheck the backend
cd server && encore test Backend tests — requires the Encore CLI

pnpm typecheck and pnpm build are the entire automated safety net, and CI runs exactly those plus the backend typecheck. CONTRIBUTING.md has the conventions and a walkthrough of adding a new failure mode.


🔒 Safety model

Read this before drawing any conclusion from the word "attack".

  • Nothing is actually attacked. Every red action mutates local Zustand state in your browser tab. No packets are sent, no processes spawned, no infrastructure touched. There is no load-generation code in this repository.
  • The RBAC is a rehearsal prop. It is enforced in one panel and bypassable from three others, and the role lives in browser memory where devtools can change it in a line. Do not copy the pattern anywhere it guards something real.
  • The backend has no authentication. All five endpoints are exposed with no auth handler, and /metrics/blue/* will hand aggregate traffic data to any caller that reaches it. It is a localhost development tool.
  • VITE_-prefixed variables ship to the browser in plaintext, inlined at build time. Never put a credential in one.

The full model — local-storage handling, input validation, and how to report a vulnerability privately — is in SECURITY.md.


🧭 Status & limitations

Verified on the current commit:

  • pnpm typecheck — clean, 0 errors under strict.
  • pnpm build — succeeds. 2,868 modules, 718 kB JS / 25.6 kB CSS, ~4 s.
  • cd server && npm run typecheck — clean.
  • Zero TODO, FIXME, or @ts-ignore markers anywhere in src/.

This is an actively work-in-progress portfolio and learning project. The honest state: the simulation core is solid, while parts of the surrounding UI are staged and there is no automated frontend test coverage yet. Known gaps, in rough order of how much they'd matter to you:

Testing and tooling

  • No frontend tests and no linter. Typecheck and build are the only automated checks. This is the largest gap in the project.
  • Backend coverage is one assertion on the starter hello endpoint; metrics and ecommerce have none. encore test also could not be executed while this README was written — the Encore CLI was not installed on that machine — so the suite is unverified, not "passing".
  • The bundle is one 718 kB chunk. No code splitting; Vite warns every build.

Wiring gaps

  • GET /metrics/blue/history has no client. The endpoint works, but blueMetricsClient.ts implements only fetchBlueSnapshot, so no chart consumes real history yet.
  • BlueSnapshotLite is declared twice — in server/metrics/metrics.ts and src/core/blueMetricsClient.ts — and hand-synchronised. Change one, the other drifts silently.
  • Five of nine core/ ports have no consumers (cache, config, flags, http, observability), as described in Architecture.
  • Some state is never populated. tracingSpans stays empty, so the tracing timeline and quick-look always render blank; dependencyEdges and bulkheads are set once and never mutated; compliance and configDriftDetected are read by nothing.

Staged UI

  • Several panels display placeholder maths rather than real statistics: the p95/p99 lines are derived as fixed multiples of the mean, the query-latency histogram is a hardcoded array, and per-node CPU sparklines are random jitter regenerated each render. Panels that do this generally say "simulated" on their own labels.
  • BlueAIEventNarrator is not AI. Despite the name it is rule-based string matching over the audit log — no model, no API call.
  • Some controls are inert: the replay transport buttons have no handlers, the breaker's "trip threshold" input is never compared against anything, the Real / Demo-only mode selector is read by no logic, and "View trace" is permanently disabled because traceId is never set.
  • Six components are unreachable — three are never imported (ChaosControls, the non-Pro CircuitBreakerPanel, AutoMasonry) and three are imported but never rendered (the non-Pro RateLimitPanel, BattlegroundCard, AttackConfigurator), each superseded by a "Pro" or detailed variant.
  • Two empty directories (src/components/hud/, src/components/timeline/).

Build and docs

  • framer-motion is pinned via an override. 12.23.24 breaks against current motion-dom releases ("activeAnimations" is not exported), so motion-dom and motion-utils are pinned in package.json. Removing the override breaks the build.
  • The backend persists nothing. Orders are validated, acknowledged and dropped; metrics live in module memory and reset on restart.
  • docs/ecommerce-metrics-implementation-checklist.md is stale — steps are unchecked despite being partly built, and it specifies a useBlueMetrics hook that shipped as useBlueSnapshot without history support.
  • No CI badge above, because the workflow has not run yet. Add one once the first run is green.

📄 License

Released under the MIT License © 2026 Akash Varma.

The server/ tree began as the Encore TypeScript starter template; its hello service is what remains of it.

🤝 Contributing CONTRIBUTING.md
🔐 Security SECURITY.md
📜 Code of Conduct CODE_OF_CONDUCT.md
🖥️ Backend docs server/README.md

Break it here, so it doesn't break there.

About

Red Team / Blue Team chaos engineering console. Inject failures — node kills, region outages, cache flushes, traffic floods — into a simulated distributed system and watch circuit breakers, rate limiters, autoscaling and SLO budgets react. React 19 + TypeScript + Zustand, with an Encore.ts metrics backend.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages