# Project export: Riposte - Break the model. Prove it. Patch it.

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: Riposte fuzzes AI agents with a black box optimizer, verifies real MITRE ATT&CK techniques in a live browser, scores risk with ARiES, and opens a human reviewed PR to patch what it finds.
- Devpost: https://devpost.com/software/riposte-break-the-model-prove-it-patch-it
- GitHub: https://github.com/zaydabash/Riposte
- Demo: https://riposte-six.vercel.app/
- Team: 4 GitHub contributor(s) — yajat009 (20 commits), Zayd (11 commits), Cursor (2 commits), Atharva Rao (1 commits)

## Devpost submission (written by the team)

### Inspiration

Anthropic's Frontier Red Team published "Mapping AI-enabled cyber threats: Insights from the LLM ATT&CK Navigator", an analysis of 832 real accounts weaponizing AI across all 14 MITRE ATT&CK tactics. It gives you the taxonomy of what AI-enabled attacks look like. It doesn't tell you whether your deployment is actually vulnerable to any of them. That gap is Riposte: turn a fixed threat taxonomy into a runnable, evidence-based verification suite you can point at your own agent.

### What it does

Point Riposte at a target endpoint, a source repo, and a few lines of canary data (a private corpus + a benign baseline), and it runs a closed loop: Plan - selects MITRE ATT&CK techniques and generates adversarial fuzz seeds. Verify - drives a real headless browser (Browserbase + Stagehand) against the live target and runs each technique's scenario, capturing the DOM before/after and the network log as forensic evidence, not just the chat transcript. Evaluate - scores every response with ARiES, a calibrated composite metric: 0.35·M + 0.35·L + 0.20·A + 0.10·J, anomaly (PCA + Mahalanobis distance against a benign baseline), leakage (cosine + entity + token overlap against the private corpus), control failure (evidence-based, not text-based), and an ensemble LLM judge. Repair - on a critical finding (ARiES ≥ 75 or a confirmed control failure), drafts a defensive patch and opens a human-reviewed pull request. Nothing merges without a human. Global ARiES is the maximum score across every attack in the run, not the average — one critical failure shouldn't get to hide behind ninety-nine successful defenses.

### How we built it

Backend: Python/FastAPI, strictly layered (Routers → Services → Repositories), an asynchronous producer–consumer pipeline with no global singletons, four phases (plan/verify/evaluate/repair) wired through asyncio.Queues. Frontend: Next.js + React, ports-and-adapters architecture, polling a typed AuditService interface so the transport can be swapped without touching components. Browserbase + Stagehand drive the live verification scenarios. Redis Stack (RediSearch) runs HNSW vector search so leakage detection against the private corpus is O(log N) instead of a brute-force scan. MiniMax powers the ensemble judge and drafts the remediation patch. GitHub API opens the actual HITL pull request. Sentry instruments the pipeline, prompts and PII are never logged.

### Challenges we ran into

The fuzzer is black-box by necessity. We don't have gradient access to the target, so instead of backpropagating to find adversarial tokens, we run simulated annealing: swap one token in the suffix, score the response against a cross-entropy loss over two fixed prototypes (compliant-leak vs. refusal), and accept worse mutations with Metropolis probability so the search doesn't get stuck in the first local trap it finds. A scrolling bug that took real debugging to find. Our dashboard panels kept growing instead of scrolling as findings accumulated. The actual root cause was two layers deep: a shared GlassPanel component's inner wrapper was a plain <div> with no flex context, silently breaking flex-1 / overflow-y-auto for every panel that used it, not just the one we first noticed. Calibrating ARiES itself. Early on, pure in-subspace Mahalanobis distance scored leaked secrets identically to benign text, the anomaly signal lived in the residual subspace, not the principal components. Fixed by combining T² with the reconstruction residual (SPE) and max-pooling over sentences so a single leaked sentence buried in an otherwise-normal response still gets caught.

### What's next

Expanding the registered MITRE ATT&CK technique library, adding an SSE/live transport behind the same AuditService port, and persistent regression storage so repeat audits can flag re-introduced vulnerabilities.

## README (from the GitHub repository)

<div align="center">

# RIPOSTE

**Break the model. Prove it. Patch it.**

An autonomous security pipeline for LLM agents. Fuzz your models, verify attacks against real MITRE ATT&CK scenarios, evaluate vulnerabilities mathematically with ARiES, and automatically generate patches to fix them.

[![Next.js](https://img.shields.io/badge/Next.js-16-black?logo=next.js)](https://nextjs.org)
[![FastAPI](https://img.shields.io/badge/FastAPI-async-009688?logo=fastapi)](https://fastapi.tiangolo.com)
[![Redis Stack](https://img.shields.io/badge/Redis-Stack-DC382D?logo=redis)](https://redis.io)
[![Browserbase](https://img.shields.io/badge/Browserbase-Stagehand-orange)](https://www.browserbase.com)
[![MITRE ATT&CK](https://img.shields.io/badge/MITRE-ATT%26CK-555)](https://attack.mitre.org)

</div>

![Riposte landing page](docs/assets/landing-hero.png)

---

## Overview: What this is

Riposte is a continuous verification-and-repair loop for AI agents and AI-assisted software. Point it at a target endpoint and a source repository, give it a few lines of canary data (private corpus) and a few lines of normal behavior (benign baseline), and it will:

1. **Plan** — generate adversarial fuzz seeds and select MITRE ATT&CK techniques to test.
2. **Verify** — drive a real headless browser (Browserbase + Stagehand) against the live target and run each technique's scenario.
3. **Evaluate** — score every response with **ARiES**, a calibrated composite metric, not a single LLM judge's gut feeling.
4. **Repair** — on a critical finding, open a human-reviewed pull request with a proposed fix. Nothing merges without a human.

Nothing here is a mock. The fuzzer runs a real black-box optimization loop, the browser sessions are real Browserbase sessions, the leakage check runs real vector search in Redis, and the repair PRs are real GitHub pull requests.

## Inspired by Anthropic's Frontier Red Team

Riposte's threat model is built directly on top of Anthropic's own research:

> **["Mapping AI-enabled cyber threats: Insights from the LLM ATT&CK Navigator"](https://www.anthropic.com/research/attack-navigator)**
> — Kyla Guru, Alex Moix, and Jacob Klein, Anthropic Frontier Red Team

That report mapped observed AI-enabled cyber misuse across **all 14 MITRE ATT&CK tactics**, and found that the risk frontier is shifting from technical sophistication toward *agentic orchestration* — autonomous, multi-step attack execution with no human in the loop. The Navigator gives you the taxonomy of what's possible. It doesn't tell you whether *your* deployment is actually vulnerable to any of it.

Riposte is built to close that gap: it takes ATT&CK technique IDs and turns each one into a runnable, browser-executed verification scenario against a real target, with a control-pass/control-fail rubric instead of a vibes-based read of the transcript.

## How it works

<div align="center">

```
Plan ──▶ Verify ──▶ Evaluate ──▶ Repair
 │          │            │           │
 │          │            │           └─ MiniMax drafts a fix,
 │          │            │              opens a PR (awaiting human merge);
 │          │            │              post-merge re-verify via REPAIR_VALIDATION audit
 │          │            └─ ARiES = 0.35·M + 0.35·L + 0.20·A + 0.10·J
 │          └─ Browserbase + Stagehand run the live scenario,
 │             capture DOM before/after + network log
 └─ Adversarial fuzzer (simulated annealing) +
    ATT&CK technique selection
```

</div>

The landing page has a full interactive breakdown of every piece below — click any card to expand the formal math:

![How It Works section, expanded](docs/assets/how-it-works.png)

### Core Mechanics

<details>
<summary><strong>The fuzzer can't see inside the model — so it experiments instead</strong></summary>

<br>

A white-box attacker backpropagates through the target's weights to compute the exact tokens that maximize error. Riposte never has that access — it only sees what the target says back. So it treats the search as trial and error: try a suffix, see the response, keep the change if it moved the response closer to compliance.

It embeds the target's response and scores it against two fixed prototypes — one written as a compliant leak (*"Sure, here is the confidential info..."*), one as a refusal (*"I cannot share..."*). A softmax over their cosine similarities yields `P(objective)`.

```text
Loss = -log(P(objective)) + Penalty_refusal
P(accept) = e^(-ΔLoss / T)
```

Each step swaps one token in the adversarial suffix; a *worse* mutation is still accepted with Metropolis probability `P(accept)`, and `T` cools every step — broad exploration early, a tight freeze near the end. This is **simulated annealing**, the black-box stand-in for gradient descent when you don't have the weights.

</details>

<details>
<summary><strong>ARiES blends four signals because no single one is reliable alone</strong></summary>

<br>

Raw perplexity and a single LLM judge both break against fluent, well-written attacks. ARiES instead combines four independent checks into one calibrated score:

```
ARiES = 0.35·M + 0.35·L + 0.20·A + 0.10·J      (each component 0–100)
```

| | Signal | What it measures | Why this math |
|---|---|---|---|
| **M** | Anomaly | Uses Hotelling's T² + SPE residual to catch out-of-distribution hallucinations | Mahalanobis, not Euclidean — the benign "cloud" of normal answers is an elliptical shape, not a sphere, so distance has to account for the data's own spread. Adding SPE ensures we catch completely out-of-distribution hallucinations that standard Mahalanobis distance would miss. |
| **L** | Leakage | Uses the Overlap Coefficient for strict lexical grounding, preventing false positives | Cosine similarity alone hallucinates resemblance between sentences that just *sound* alike; entity and token overlap force strict lexical grounding |
| **A** | Control failure | Uses logarithmic scaling to penalize data dumps heavily while capping the score | Did a verification control actually fail? We check the post-attack DOM and network log instead of trusting the model's own account. Logarithmic scaling penalizes large leaks but prevents the score from blowing up to infinity. Refusals get a score of 10.0 because they leak the existence of a secret. |
| **J** | Judge | Ensemble of independent LLM judges scoring threat / vulnerability / impact | No single judge is trusted alone — independent judges that agree are far more reliable than any one of them |

A finding with `control_failed = true` or `ARiES ≥ 75` is **critical** and triggers a HITL repair PR. The dashboard shows **awaiting human merge** until the PR is merged and the target redeploys; a `repair_validation` audit then re-runs the same ATT&CK scenario against the live endpoint.

</details>

<details>
<summary><strong>Redis isn't just a cache here — it's the vector lookup that makes leakage detection fast</strong></summary>

<br>

Most people know Redis as a simple key-value cache for session IDs. Riposte runs **Redis Stack** with the RediSearch module, turning it into a vector database that can instantly check a response against an entire private corpus.

This is **HNSW** (Hierarchical Navigable Small World): document embeddings sit in a multi-layer graph, and a query vector descends layer by layer toward its nearest neighbors — a sparse top layer of long-distance shortcuts funneling down to a dense bottom layer of local connections. That turns a brute-force comparison against every private document (`O(N)`) into a graph traversal (`O(log N)`). Riposte issues this via `FT.SEARCH` with a `KNN` clause, retrieving the closest private documents in milliseconds.

</details>

<details>
<summary><strong>Riposte doesn't just read the reply — it reads the evidence</strong></summary>

<br>

Browserbase hosts the real headless browser session each verification scenario runs in. After each scenario, Riposte pulls a forensic dump — the DOM before the attack, the DOM after, and the full network

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 118 recognized source files, 471 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Anthropic (technology) — claimed on Devpost, not found in the code
- Docker (technology) — claimed on Devpost, not found in the code
- Redis (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository
- AI coding agent: Codex — evidence: config files committed to the repository
- AI coding agent: Cursor — evidence: config files committed to the repository; commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 134)

```
.agents/skills/improve-codebase-architecture/HTML-REPORT.md
.agents/skills/improve-codebase-architecture/SKILL.md
.agents/skills/parallel-debugging/references/hypothesis-testing.md
.agents/skills/parallel-debugging/SKILL.md
.cursor/rules/fastapi-architecture.mdc
.cursor/rules/no-fixtures.mdc
.cursor/rules/redis-async.mdc
.gitignore
backend/.env.example
backend/.gitignore
backend/Dockerfile
backend/pyproject.toml
backend/README.md
backend/src/__init__.py
backend/src/api/__init__.py
backend/src/api/deps.py
backend/src/api/routers.py
backend/src/api/sessions_router.py
backend/src/config.py
backend/src/core/__init__.py
backend/src/core/audit_defaults.py
backend/src/core/baseline.py
backend/src/core/embeddings.py
backend/src/core/fuzz_seeds.py
backend/src/core/models.py
backend/src/core/simulator.py
backend/src/core/telemetry.py
backend/src/core/text_analysis.py
backend/src/core/truncate.py
backend/src/demos/__init__.py
backend/src/main.py
backend/src/repositories/__init__.py
backend/src/repositories/vector_repo.py
backend/src/scenarios/__init__.py
backend/src/scenarios/artifacts.py
backend/src/scenarios/base.py
backend/src/scenarios/browser_capture.py
backend/src/scenarios/registry.py
backend/src/scenarios/techniques.py
backend/src/services/__init__.py
backend/src/services/browserbase_client.py
backend/src/services/eval_service.py
backend/src/services/fuzzer_service.py
backend/src/services/github_client.py
backend/src/services/minimax_client.py
backend/src/services/orchestrator.py
backend/src/services/remediation_engine.py
backend/src/services/repair_validation.py
backend/src/services/scenario_mutation.py
backend/src/services/verification_service.py
backend/src/workers/__init__.py
backend/src/workers/eval_worker.py
backend/src/workers/fuzz_worker.py
backend/src/workers/offensive_worker.py
backend/src/workers/patch_worker.py
backend/src/workers/scenario_plan_worker.py
backend/src/workers/verification_worker.py
backend/tests/__init__.py
backend/tests/conftest.py
backend/tests/sample_corpora.py
backend/tests/test_api.py
backend/tests/test_audit_defaults.py
backend/tests/test_baseline.py
backend/tests/test_browser_capture.py
backend/tests/test_embeddings.py
backend/tests/test_eval_service.py
backend/tests/test_fuzz_seeds.py
backend/tests/test_fuzzer_service.py
backend/tests/test_minimax_client.py
backend/tests/test_orchestrator.py
backend/tests/test_remediation_runner.py
backend/tests/test_scenarios.py
backend/tests/test_vector_repo.py
backend/tests/test_verification_service.py
backend/tests/test_worker_resilience.py
backend/uv.lock
docker-compose.yml
docs/verification-ci.md
frontend/.env.example
frontend/.gitignore
frontend/adapters/network-audit-adapter.ts
frontend/AGENTS.md
frontend/app/dashboard/page.tsx
frontend/app/globals.css
frontend/app/layout.tsx
frontend/app/page.tsx
frontend/CLAUDE.md
frontend/components/backgrounds/ArchitectureDitherBackground.tsx
frontend/components/backgrounds/DottedSurface.tsx
frontend/components/backgrounds/FallingPattern.tsx
frontend/components/backgrounds/HeroShader.tsx
frontend/components/backgrounds/PixelBackground.tsx
frontend/components/dashboard/aries-breakdown.tsx
frontend/components/dashboard/aries-score-badge.tsx
frontend/components/dashboard/control-plane.tsx
frontend/components/dashboard/dashboard-layout.tsx
frontend/components/dashboard/finding-card.tsx
frontend/components/dashboard/findings-view.tsx
frontend/components/dashboard/intelligence-layer.tsx
frontend/components/dashboard/network-timeline.tsx
frontend/components/dashboard/session-replay-player.tsx
frontend/components/dashboard/severity-badge.tsx
frontend/components/dashboard/verification-console.tsx
frontend/components/landing/architecture-section.tsx
frontend/components/landing/hero-section.tsx
frontend/components/landing/how-it-works.tsx
frontend/components/ui/expandable-tabs.tsx
frontend/components/ui/glass-filter.tsx
frontend/components/ui/glass-panel.tsx
frontend/components/ui/liquid-button.tsx
frontend/components/ui/math-formula.tsx
frontend/components/ui/scroll-reveal.tsx
frontend/components/ui/status-badge.tsx
frontend/eslint.config.mjs
frontend/hooks/use-audit.ts
frontend/lib/audit-selectors.ts
frontend/lib/backend-types.ts
frontend/lib/corpus-text.ts
frontend/lib/format.ts
frontend/lib/riposte-config.ts
[14 more files omitted for size]
```

### Dependencies

- backend/pyproject.toml: en_core_web_md@@ https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.8.0/en_core_web_md-3.8.0-py3-none-any.whl, fastapi@>=0.115, httpx@>=0.27, numpy@>=1.26, openai@>=1.54, pydantic@>=2.9, pydantic-settings@>=2.5, pytest@>=8.3, pytest-asyncio@>=0.24, pytest-cov@>=5.0, redis[hiredis]@>=5.2, respx@>=0.21, scipy@>=1.13, sentry-sdk@>=2.18, spacy@>=3.7, stagehand@>=0.4, tenacity@>=9.0, uvicorn[standard]@>=0.32
- frontend/package.json: @paper-design/shaders-react@^0.0.76, @radix-ui/react-slot@^1.3.0, @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, @types/three@^0.184.1, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9, eslint-config-next@16.2.9, framer-motion@^12.40.0, hls.js@^1.6.16, lucide-react@^1.21.0, next@16.2.9, react@19.2.4, react-dom@19.2.4, tailwind-merge@^3.6.0, tailwindcss@^4, three@^0.184.0, typescript@^5, usehooks-ts@^3.1.1, vitest@^4.1.9

### Recent commits (newest first)

- faster fuzz:
- final
- final fixers
- fixed queuing and global aries
- ir summary
- final few changes
- hierarchy of landing page workflow explanation
- small fixes
- pr request pivot + landing page fixes
- Merge branch 'main' of https://github.com/zaydabash/Riposte
- landing page fixes
- Add dither background to How It Works section.
- mitre attack stuff
- .
- changes
- Align repair pipeline docs with post-merge re-verification.
- fix:
- Update landing hero screenshot in docs assets.
- docs: README glow-up with screenshots and Anthropic Frontier Red Team citation
- feat: add How It Works section explaining the math in plain English

## Key source files (fetched from GitHub, selected and truncated for size)

### frontend/CLAUDE.md

```markdown
@AGENTS.md

```

### frontend/AGENTS.md

```markdown
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->

```

### docker-compose.yml

```yaml
# Riposte — local stack. The backend runs independently of the frontend; bring up
# Redis Stack for vector memory and the FastAPI backend.
services:
  redis:
    image: redis/redis-stack-server:latest
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  backend:
    build:
      context: ./backend
    ports:
      - "8000:8000"
    env_file:
      - ./backend/.env
    environment:
      # Point at the local Redis Stack service by default.
      REDIS_URL: redis://redis:6379/0
    depends_on:
      redis:
        condition: service_healthy

volumes:
  redis-data:

```

### backend/Dockerfile

```
# syntax=docker/dockerfile:1
FROM python:3.11-slim AS base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1

WORKDIR /app

# uv for fast, reproducible installs.
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

# Install dependencies first for better layer caching.
COPY pyproject.toml README.md ./
COPY src ./src
RUN uv pip install --system --no-cache .

EXPOSE 8000

# Non-root runtime user.
RUN useradd --create-home --uid 10001 riposte
USER riposte

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "test": "vitest run",
    "test:watch": "vitest"
  },
  "dependencies": {
    "@paper-design/shaders-react": "^0.0.76",
    "@radix-ui/react-slot": "^1.3.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "framer-motion": "^12.40.0",
    "hls.js": "^1.6.16",
    "lucide-react": "^1.21.0",
    "next": "16.2.9",
    "react": "19.2.4",
    "react-dom": "19.2.4",
    "tailwind-merge": "^3.6.0",
    "three": "^0.184.0",
    "usehooks-ts": "^3.1.1"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@types/three": "^0.184.1",
    "eslint": "^9",
    "eslint-config-next": "16.2.9",
    "tailwindcss": "^4",
    "typescript": "^5",
    "vitest": "^4.1.9"
  }
}

```

### backend/pyproject.toml

```
[project]
name = "riposte-backend"
version = "1.0.0"
description = "Riposte — autonomous continuous red-team + remediation pipeline for LLM agents"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.115",
    "uvicorn[standard]>=0.32",
    "pydantic>=2.9",
    "pydantic-settings>=2.5",
    "httpx>=0.27",
    "numpy>=1.26",
    "scipy>=1.13",
    "tenacity>=9.0",
    "openai>=1.54",
    "redis[hiredis]>=5.2",
    # --- sponsor integrations (optional at runtime; pipeline degrades gracefully) ---
    "stagehand>=0.4",
    "sentry-sdk>=2.18",
    "spacy>=3.7",
    # Real 300-d GloVe vectors for the ARiES anomaly + leakage math (CPU, no torch).
    "en_core_web_md @ https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.8.0/en_core_web_md-3.8.0-py3-none-any.whl",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.3",
    "pytest-asyncio>=0.24",
    "pytest-cov>=5.0",
    "respx>=0.21",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.metadata]
# Allow the spaCy model wheel to be referenced by direct URL.
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src"]

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
addopts = "-q"

[tool.coverage.run]
source = ["src"]
omit = ["src/workers/offensive_worker.py", "src/core/telemetry.py"]

```

### frontend/app/page.tsx

```typescript
import { HeroSection } from "@/components/landing/hero-section";
import { ArchitectureSection } from "@/components/landing/architecture-section";
import { HowItWorksSection } from "@/components/landing/how-it-works";

export default function HomePage() {
  return (
    <main className="landing-theme">
      <HeroSection />
      <ArchitectureSection />
      <HowItWorksSection />
    </main>
  );
}

```

### frontend/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { IBM_Plex_Mono, IBM_Plex_Sans } from "next/font/google";
import "./globals.css";

const plexSans = IBM_Plex_Sans({
  variable: "--font-plex-sans",
  subsets: ["latin"],
  weight: ["400", "500"],
});

const plexMono = IBM_Plex_Mono({
  variable: "--font-plex-mono",
  subsets: ["latin"],
  weight: ["400"],
});

export const metadata: Metadata = {
  title: "Riposte",
  description:
    "An autonomous security pipeline for LLM agents. Fuzz your models, verify attacks against real MITRE ATT&CK scenarios, evaluate vulnerabilities mathematically with ARiES, and automatically generate patches.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className={`${plexSans.variable} ${plexMono.variable} h-full antialiased`}
    >
      <body
        suppressHydrationWarning
        className="min-h-full flex flex-col bg-background text-foreground font-sans"
      >
        {children}
      </body>
    </html>
  );
}

```

### backend/src/main.py

```python
"""Riposte FastAPI application entry point.

Telemetry is initialized and the async orchestrator's worker pools are started
inside the lifespan context, and gracefully shut down on exit.
"""

from __future__ import annotations

import logging
from contextlib import asynccontextmanager

from fastapi import Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware

from src.api.deps import get_orchestrator
from src.api.routers import router as audit_router, techniques_router
from src.api.sessions_router import router as sessions_router
from src.config import get_settings
from src.core.telemetry import init_telemetry
from src.services.orchestrator import Orchestrator

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
logger = logging.getLogger("riposte")


@asynccontextmanager
async def lifespan(app: FastAPI):
    settings = get_settings()
    init_telemetry(settings)
    orchestrator = Orchestrator(settings)
    await orchestrator.start()
    app.state.orchestrator = orchestrator
    logger.info("Riposte backend ready: %s", orchestrator.telemetry_status)
    try:
        yield
    finally:
        await orchestrator.stop()


def create_app() -> FastAPI:
    settings = get_settings()
    app = FastAPI(
        title="Riposte — Continuous Verification & Repair Plane",
        description=(
            "Continuous verification and repair for AI agents and AI-assisted "
            "software, mapped to MITRE ATT&CK browser-testable controls."
        ),
        version="2.0.0",
        lifespan=lifespan,
    )
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.cors_origins,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    app.include_router(audit_router)
    app.include_router(techniques_router)
    app.include_router(sessions_router)

    @app.get("/health", tags=["Health"])
    async def health(orchestrator: Orchestrator = Depends(get_orchestrator)) -> dict:
        return {"status": "ok", "integrations": await orchestrator.integration_status()}

    return app


app = create_app()

```

### frontend/app/dashboard/page.tsx

```typescript
"use client";

import { useEffect, useState } from "react";
import Link from "next/link";
import { PixelBackground } from "@/components/backgrounds/PixelBackground";
import { DashboardLayout } from "@/components/dashboard/dashboard-layout";
import { useAudit } from "@/hooks/use-audit";
import type { AuditConfig } from "@/ports/audit-service";

/** Initial form values (user input state). The hook validates on Start. */
function initialConfig(): AuditConfig {
  return {
    targetEndpoint: "",
    sourceRepository: "",
    privateCorpusText: "",
    benignBaselineText: "",
  };
}

export default function DashboardPage() {
  const [config, setConfig] = useState<AuditConfig>(initialConfig);
  const {
    state,
    phase,
    alerts,
    health,
    error,
    lastSyncedAt,
    isSyncing,
    initializeAudit,
    reset,
    refreshHealth,
  } = useAudit();

  useEffect(() => {
    refreshHealth();
  }, [refreshHealth]);

  const handleStart = () => {
    refreshHealth();
    initializeAudit(config);
  };

  const handleReset = () => {
    reset();
    setConfig(initialConfig());
  };

  return (
    <div className="dashboard-theme relative flex h-[100dvh] flex-col overflow-hidden bg-background text-foreground">
      <PixelBackground />

      <header className="relative z-20 shrink-0 px-6 pt-5 md:px-10">
        <div className="mx-auto w-full max-w-[1480px]">
          <div className="flex items-center gap-6">
            <Link
              href="/"
              className="font-mono text-sm tracking-[0.35em] text-foreground/90"
            >
              RIPOSTE
            </Link>
          </div>
        </div>
      </header>

      <main className="relative z-10 flex min-h-0 flex-1 flex-col pt-8">
        <DashboardLayout
          config={config}
          onConfigChange={setConfig}
          onStart={handleStart}
          onReset={handleReset}
          state={state}
          phase={phase}
          alerts={alerts}
          health={health}
          error={error}
          lastSyncedAt={lastSyncedAt}
          isSyncing={isSyncing}
        />
      </main>
    </div>
  );
}

```

[100 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]