CacheGuard is an LSTM-powered cache-timing side-channel attack detector. The core concept is sound and the bugs are now fixed, but the project is a flat collection of scripts — not a deployable product. This plan lifts it to industry-grade across every dimension: architecture, ML rigour, API hardening, real-time dashboard, testing, DevOps, and observability.
Important
Q1 — Frontend stack: The plan proposes a React + Vite dashboard. Is that okay, or do you want plain HTML/JS?
Q2 — Deployment target: Is this meant to run locally, on a server/VM, or on a cloud platform (e.g. GCP/AWS)?
Q3 — Auth complexity: Should the WebSocket/API use simple API key auth, or full JWT with a login flow?
Q4 — Model improvement depth: Are you okay with replacing the single-layer LSTM with a deeper model + attention mechanism, which will require retraining? The saved .pth would need to be regenerated.
Reorganize the flat script layout into a professional Python package layout with proper config, logging, and dependency management.
cache-hack/
├── cacheguard/ ← main Python package
│ ├── __init__.py
│ ├── config.py ← Pydantic settings (all env-based config)
│ ├── logging.py ← structured loguru config
│ ├── model/
│ │ ├── __init__.py
│ │ ├── lstm_detector.py ← improved architecture
│ │ └── normalizer.py ← NEW: input z-score normalizer
│ ├── data/
│ │ ├── __init__.py
│ │ ├── generator.py
│ │ └── dataset.py
│ ├── api/
│ │ ├── __init__.py
│ │ ├── app.py ← FastAPI factory
│ │ ├── deps.py ← NEW: dependency injection (model, auth)
│ │ ├── middleware.py ← NEW: security headers, rate limiting
│ │ ├── routers/
│ │ │ ├── status.py ← GET /api/v1/status, /health, /metrics
│ │ │ └── telemetry.py ← WS /ws/v1/telemetry
│ │ └── schemas.py ← NEW: Pydantic request/response models
│ └── ml/
│ ├── train.py ← improved training with val split
│ └── evaluate.py ← proper held-out test evaluation
├── frontend/ ← NEW: React + Vite dashboard
├── tests/ ← NEW: full pytest suite
├── .github/workflows/ ← NEW: CI/CD pipeline
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
├── .env.example
├── pyproject.toml ← replaces requirements.txt
└── README.md
Central Pydantic BaseSettings class. All configurable values — model path, thresholds, CORS origins, API keys, log level — read from environment variables or .env file. No more hardcoded paths or magic numbers anywhere in the codebase.
loguru-based structured JSON logging. Log level, format, and output file controlled via config. Replaces all print() calls across the codebase.
Replaces requirements.txt. Uses [project.dependencies] with pinned versions. Includes separate [project.optional-dependencies] for dev (pytest, ruff, mypy) and ml (torch, torchvision).
Documents every environment variable with safe defaults. Never commit .env.
Upgrade the model architecture:
- Add dropout (
p=0.3) between LSTM and FC layer for regularization - Increase to 2 LSTM layers with
hidden_size=128 - Add Layer Normalization after LSTM output
- Keep the
Sigmoidoutput — binary classification is correct
class CacheGuardLSTM(nn.Module):
def __init__(self, input_size=1, hidden_size=128, num_layers=2, dropout=0.3):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers,
batch_first=True, dropout=dropout)
self.norm = nn.LayerNorm(hidden_size)
self.fc = nn.Linear(hidden_size, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
out, _ = self.lstm(x)
out = self.norm(out[:, -1, :])
return self.sigmoid(self.fc(out))Z-score normalizer fitted on training data, saved alongside the model as normalizer.pkl. Applied to all inputs at inference time so raw nanosecond values are properly scaled.
- 80/10/10 train/val/test split — deterministic with a fixed random seed
- Train on 10,000 samples (up from 1,000)
- Early stopping on val loss with patience of 5 epochs
- Learning rate scheduler (
ReduceLROnPlateau) - Save best model by val accuracy, not just latest epoch
- Save
normalizer.pklalongside model weights
- Load the held-out test set (never seen during training)
- Report: Accuracy, Precision, Recall, F1, ROC-AUC, Confusion Matrix
- Export metrics as JSON for CI assertion (
accuracy > 95%gate)
Pydantic models for every request and response — no raw dict anywhere:
class TelemetryPayload(BaseModel):
timing: list[float] = Field(..., min_length=50, max_length=50)
sensor_id: str
class ThreatResponse(BaseModel):
status: Literal["CLEAN", "CRITICAL_ALERT"]
attack_probability: float
sensor_id: str
timestamp: datetimeget_model()— singleton model loaded once at startup, shared via FastAPI dependency injectionverify_api_key()— readsX-API-Keyheader, validates against env configget_normalizer()— singleton normalizer
- Rate limiting: max 100 WebSocket messages/second per IP using
slowapi - Security headers:
X-Content-Type-Options,X-Frame-Options,Strict-Transport-Security - Request ID: UUID injected per request for log correlation
- API versioned under
/api/v1/ - CORS restricted to configured origins (no more
*) - Lifespan event to load model once on startup
- Exception handlers returning structured JSON errors
| Endpoint | Description |
|---|---|
GET /api/v1/status |
Latest threat data |
GET /health |
Liveness probe (returns 200 if model loaded) |
GET /metrics |
Prometheus-format counters: total requests, attack alerts, avg latency |
GET /api/v1/history?limit=100 |
Last N threat events from in-memory ring buffer |
- Validate
X-API-Keyon WebSocket upgrade - Use Pydantic
TelemetryPayloadfor JSON validation - Apply normalizer before inference
- Store each result in the history ring buffer
- Consistent response keys everywhere
A React + Vite dashboard that connects to the live API.
Animated radial gauge (0–100%) showing current attack probability. Color transitions: green → amber → red.
Recharts line chart — scrolling 60-second window of attack probability over time. Shaded red zone above 50%.
Live feed of last 20 threat events. Each row: timestamp, sensor ID, status badge (CLEAN / CRITICAL), confidence %.
Top bar: total packets analyzed, attack detections in last 60s, model confidence avg, uptime.
Custom React hook managing WebSocket connection to /ws/v1/telemetry with auto-reconnect logic.
- Model forward pass shape assertions
- Sigmoid output bounded [0, 1]
- Model loads from
.pthwithout error
- Normal sequence values always in [4, 150] range
- Attack sequences contain values in [200, 300] range
- Label types are correct
TelemetryPayloadrejects sequences shorter/longer than 50TelemetryPayloadrejects non-numeric values
GET /healthreturns200GET /api/v1/statusreturns validThreatResponsestructure- WebSocket rejects connection without API key
- WebSocket processes valid 50-element sequence and returns threat response
- End-to-end: generate data → train → evaluate → assert accuracy ≥ 95%
Multi-stage build:
builderstage: install Python depsruntimestage: copy only what's needed, run as non-root user
Services:
cacheguard-api— the FastAPI backendcacheguard-frontend— Nginx serving the built React appprometheus— scrapes/metricsgrafana— pre-configured dashboard for attack rate visualization
On every push/PR:
- Lint with
ruff - Type-check with
mypy - Run full
pytestsuite - Assert model accuracy ≥ 95% on held-out test set
- Build Docker image
- Project overview & architecture diagram (Mermaid)
- Quick-start (Docker Compose one-liner)
- Configuration reference
- API reference
- How to retrain the model
pytest tests/ -v— all unit + integration tests passpytest tests/ml/ -v— model accuracy ≥ 95%ruff check .— zero lint errorsdocker compose up— all services healthy
- Run
sensor.pyagainst live API, confirm real-time updates appear in dashboard - Trigger a simulated attack sequence, confirm gauge turns red and alert appears in feed
- Verify
/healthreturns 200 and/metricsexports Prometheus counters - Confirm WebSocket connection fails without valid
X-API-Key
| Phase | Focus | Est. Effort |
|---|---|---|
| 1 | Project restructure + config + logging | ~2 hrs |
| 2 | ML pipeline upgrade + retrain | ~2 hrs |
| 3 | API hardening (schemas, auth, middleware) | ~3 hrs |
| 4 | React dashboard | ~4 hrs |
| 5 | Full test suite | ~2 hrs |
| 6 | Docker + CI/CD + README | ~2 hrs |
| Total | ~15 hrs |