← Gallery ThreatWatch

MDLC build dossier · v0.3.3

Every gate failed it first.Then every gate passed.

ThreatWatch is a multi-tenant security operations platform: signed event ingest, detection and correlation on Temporal, alert triage, a live dashboard and threat map. MDLC built it end to end from a one-page blueprint. The reviewer, the security auditor, the design scorer and the web baseline each sent it back at least once. This page is the record of what they caught and how each failure became a pass.

17.3 h active 1 session, 29 subagents Claude Opus 5.5 10 features in 6 waves
https://localhost:9443/dashboardLIVE
The ThreatWatch dashboard: open critical and high alerts, event trend, threat map, top sources and recent alerts

Gate ledger

Twenty-three runs. Fifteen sent it back.

Each square is one run of one gate, in the order it happened. A red square sent the build back. The bar a gate measures against never moved: the build changed until it cleared. Select any square to see what that run found.

Gate · attempts in orderSent backPassed

What the gates caught

Recent alerts, from the build itself.

Every one of these was written, committed, and passing its own tests when a gate flagged it. None of them would have shown up by running the app and clicking around.

  • High Runtime images were built on stale bases with fixable critical and high OS CVEs.Trivy found 9 critical and 108 high in the API and worker images. Rebuilt on patched bases with the npm CLI stripped out; the unfixable upstream residue is reasoned in ADR-028. Security S1-3Resolved
  • Medium Outbound webhooks checked the destination IP, then connected to whatever DNS said next.A DNS-rebinding window let a customer webhook reach an internal address after passing the SSRF guard. Fixed by pinning the connection to the validated IP, then a reviewer insisted that pinned path get its own test. Security S1-1 · R2-4Resolved
  • Medium Sign-in, registration and account forms had no method="post".Before JavaScript loads, a submit would put the password in the URL, where it lands in history and proxy logs. A live probe confirmed the server accepted such URLs. Security S1-2Resolved
  • Fail The fix for duplicate requests broke duplicate requests.Round 1 caught concurrent requests with the same Idempotency-Key both running. The fix dropped the stored result when a client disconnected, so a retry ran the side effect again. Round 2 caught the regression; it was reverted and re-fixed. Reviewer R1-8 → R2-1Resolved
  • Fail A crafted pagination cursor returned a 500 on nine list endpoints.Now a 400 with a field error. The next round found five more lists that still ignored a bad cursor silently. Reviewer R1-2 · R2-3Resolved
  • Fail Scheduled-job tests checked what the job returned, not what it did.Five of seven Temporal job tests asserted returned counts. The invariant requires asserting the effect in the database. They were rewritten to drive each job per organization and check the rows. Reviewer R1-1 · INV-11Resolved
  • Fail The alert list promised live updates and polled every 20 seconds instead.The WebSocket hook existed, but only the dashboard used it. Reviewer R1-3Resolved
  • ✗ Design The integration-health tile rendered every integration: a page 14,708 px tall on a phone.Now the eight least healthy, with a link to the rest. The same pass caught IP addresses split mid-octet and table headers that had drifted from the rest of the app. Design score 1Resolved
  • Fail Twenty-six loading skeletons used an ARIA label on an element that cannot have one.Surfaced only when a baseline run happened to snapshot the dashboard mid-load. Fixed across all 26, and reviewed again in round 4. Web baseline 15:36 · RB-7Resolved

Held, not waived

The floor was 80. It stayed 80.

The dashboard's Lighthouse performance score has a floor of 80. Six measurements came in under it, several on a host loaded by unrelated work. None was accepted or explained away. Each was re-measured on unchanged code.

The reruns kept surfacing real defects in between: a hydration mismatch, a pulse animation that ignored reduced motion, the skeleton ARIA bug. Those were fixed at the root. The gate passed at 81, with accessibility 100 on both the landing page and the dashboard.

Dashboard Lighthouse performance across seven definitive runs: 78, 79, 77, 79, 77, 75, then 81. The floor is 80. 908070 floor 80
Definitive --full runs, 2026-10-05 UTC. Only runs that recorded a dashboard score are shown.

Governance

Anything touching auth waited for a human.

Every commit was classified by what it changed and written to an append-only ledger: 40 decision records over 23 changes. Eleven changes cleared automatically. Twelve were held for a person because they touched authentication, authorization, the security boundary or infrastructure. Nothing was blocked.

At the end, the build tried to commit the ledger itself. The guard refused it as audit tampering, so the last commit of that file is a human's to make.

12 held for human approval11 auto-approved0 blocked
{"id":"GD-0001","change":"F-1",
 "category":["api","application","authentication",
   … "security-boundary","tests","unknown"],
 "outcome":"HUMAN_APPROVAL",
 "reason":"minimum for project authentication+
   dependencies+infrastructure+security-boundary+unknown"}

{"id":"GD-0030","change":"F-1","supersedes":"GD-0001",
 "outcome":"AUTO_APPROVE","approver":"human",
 "reason":"approved by human"}

The product

Twenty-nine screens, measured at three widths in two themes.

Every screen below was captured from the running stack. Each one was checked at 375, 768 and 1280 px in light and dark: 145 cells for layout overflow, contrast, keyboard focus, axe, console errors and Core Web Vitals.

ThreatWatch

v0.3.3 · tag at b2597c7 · 2026-10-05
Server tests
403 / 403
Web component tests
15 / 15
End-to-end
42 / 42
Smoke, live stack
24 / 24
Coverage (floor 70%)
93.29%
Invariants
43
  machine-proven
42
  manual, verdict
1 PASS
Compliance
50✓ 1⚠ 0✗
Baseline cells
145 / 145
Lighthouse a11y
100 / 100
Screens
29
Public endpoints
78
ADRs
32
Commits
30
Active time
17.3 h
Assistant turns
6,289
Output tokens
4.1 M
Cache-read tokens
652.6 M
Measured by the build’s own pinned runners
ThreatWatch landing page: See every threat. Act on the right one.
Landing, 1280 px · performance 94, accessibility 100
ThreatWatch dashboard on a 375 px phone
Dashboard, 375 px

Honest posture

What this build does not claim.

It runs locally

The whole stack ships as Docker Compose behind Caddy TLS on localhost:9443. There is no public deployment of this build.

Payments are deferred

The blueprint asked for them; no workflow charges money, so ADR-006 defers them and keeps the money invariant in place for later.

Scale is not measured

Built for the small tier: one API, one worker, in-memory rate limits. Availability and horizontal-scale metrics are recorded as not measured.

Four low findings remain

Round 4 passed with four LOW advisories, none correctness-class, such as a search count label that screen readers skip. One compliance item is ⚠: volume encryption is left to the deploy target.

One drift line waits on a person

The drift gate reads 41 of 42 because the governance ledger is uncommitted, and committing it is a human action by design.

Evidence pack

Read it in the order it was written.

FileWhat it is
RESEARCH.mdThe blueprint: product vision, 12 key workflows, threat model, market research, GO decision.
ARCHITECTURE.md · diagramSystem design and the 43 invariants, each tied to the proof that enforces it.
SPEC.md29 screens, 78 endpoints, workflows W1 to W12 and the error contract.
DECISIONS.md32 ADRs, including every accepted residual and why.
Reviewer roundsFive independent review reports, verbatim, from round 1 FAIL to round 4 PASS.
Security passesThree audit passes: scanners, live probes and manual review of auth, ingest, SSRF and outboxes.
Design scoresThree design-rubric scorings of all 29 screens, from 7 ✗ to 0 ✗.
COMPLIANCE.mdControl mapping with evidence: 50 ✓, 1 ⚠, 0 ✗.
GOVERNANCE-DECISIONS.jsonlThe append-only decision ledger: 40 records, 12 human approvals.
REPORT.mdThe full build report: every gate, every run, smoke log and usage receipt.