CSE 110 · Spring 2026

Which deployment started the fire?

WatchTower is a lightweight observability dashboard for early-stage teams. It captures runtime errors, Core Web Vitals, and user feedback from any instrumented web app — then correlates them with the deploy events from your CI so you know exactly when the regression landed.

01One place for three signals

PostHog and Sentry are great when you've scaled. WatchTower is tightened down to what you actually need when your software first ships — and nothing more.

!

Errors

Uncaught exceptions and unhandled rejections with full stack traces, captured by a tiny SDK that hooks window.onerror.

Performance

Core Web Vitals (LCP, INP, CLS) and page-load timing, sampled via PerformanceObserver. No extra config.

Feedback

Drop-in rating + comment widget. Real signal from real users, alongside the technical data.

02Who it's for

WatchTower is built for the moment when "my software is in production now and I have no idea what it's doing."

The on-call dev at 2am

Open the dashboard. See errors spiking after a 1:47am deploy. Click into the trace, find the line, ship a fix. Go back to bed.

The solo founder

Ship from your laptop. WatchTower lives on Cloudflare Workers' free tier; one SDK include on your site is the whole integration.

The student team

Built by 13 CSE 110 students in five sprints. Vanilla JS, zero framework lock-in, every decision captured as an ADR you can read on your way in.

The future maintainer

22 ADRs, an architecture doc, a changelog, sprint retros, and a wiki — written so the next team can pick this up without a handoff meeting.

03Quickstart

Three steps. About five minutes total.

Register your project

Create an account on the dashboard. You'll get a projectId to identify your app.

Drop in the SDK

One <script> tag. No build step, no bundler, no npm install. Served from jsDelivr.

<script
  src="https://cdn.jsdelivr.net/gh/cse110-sp26-group7/Watchtower@main/client/watchtower.min.js"
  defer></script>

Initialize

Configure with your projectId and tell WatchTower which environment this is.

const wt = new Watchtower({
  projectId: "your_project_id",
  endpoint: "https://watchtower-ingest.cse110piedpiper7.workers.dev/ingest",
  environment: "prod",
});
wt.init();

Trigger a test error

Drop this button into your page to confirm the round-trip ends up in the dashboard.

<button id="trigger-error">Trigger Test Error</button>
<script>
  document.getElementById("trigger-error").addEventListener("click", () => {
    throw new Error("Manual test error triggered");
  });
</script>

Full setup instructions are in the repo README.

04How it's built

WatchTower is a five-layer pipeline: SDK → Ingest Worker → D1 → API Worker → Dashboard SPA. Every layer is one file or one folder you can read in an afternoon.

What's inside

  • Single-file vanilla-JS SDK, served from a CDN.
  • Two Cloudflare Workers (ingest, API) with isolated blast radius.
  • Cloudflare D1 (SQLite) with one polymorphic events table.
  • Vanilla-JS dashboard on Cloudflare Pages — no framework, no bundler.
  • Tag-triggered CI/CD via GitHub Actions; the same tag fires a deploy event back into WatchTower.

What's deliberately not

  • No queueing layer in front of D1. Bursts above the rate limit drop and we say so.
  • No staging environment — tag-gated production deploys instead.
  • No third-party auth (OAuth). Signed-cookie sessions.
  • No long-term retention beyond 30 days. The data window matches the use case.

See the architecture doc and the 22 architectural decision records for the reasoning.