Warning
This project is no longer actively maintained (October 2026).
Watchman is left in a stable, working state: CI passes, known HIGH and CRITICAL dependency advisories were resolved at that time, and the documentation matches the code. No new features are planned.
- Issues, pull requests, and security reports may not get a response.
- Dependency and security updates are not guaranteed. Advisories published after October 2026 will not be fixed unless someone picks the project up.
- Dependabot still opens update pull requests, and patch and minor updates merge automatically when CI passes. Nobody reviews them.
- If you run it, keep it on a trusted network as described below, and review the dependencies yourself before you deploy.
- You are welcome to fork it under the terms of the AGPL-3.0-only license.
Watchman is a full-stack dashboard for monitoring self-hosted services from one place. It combines a React + TypeScript frontend with a Node.js + Fastify backend and supports real-time status visibility across your stack.
Important
Monitoring-only by design
Watchman is intentionally read-only. It does not start/stop/restart/reconfigure services or write to devices (no GPIO writes). The only writes are one-time setup handshakes you start yourself: Hue bridge pairing (creates an app key) and Roon extension registration. See ADR-032.
If you run multiple services, Watchman gives you one dashboard instead of 10+ browser tabs.
| You need... | Watchman gives you... |
|---|---|
| Fast visibility across services | Unified health + stats endpoints and cards in a single UI |
| Support for multiple instances of a service | Multiple instances per service type, configured in the UI (e.g. several qBittorrent or Synology instances) |
| Live status updates | WebSocket-based real-time updates |
| A trusted-network security model | Origin allow-list (CORS + WebSocket), service secrets encrypted at rest, PII-redacted logs — no auth/CSRF/HTTP rate limiting by design (single-user) |
| Extensibility | Factory-driven service integration architecture |
- ✅ Unified dashboard for self-hosted service health and statistics
- ✅ Monitoring-only architecture (no service control plane)
- ✅ Multi-instance support for selected integrations
- ✅ Real-time updates via WebSockets
- ✅ OpenAPI 3.1 spec (
apps/backend/openapi.yaml) — no Swagger UI is served - ✅ Circuit breaker + caching patterns for resilient service polling
- ✅ Trusted-network security model — origin allow-list, encrypted secrets at rest, PII-redacted logging
Watchman supports 13 service kinds. You can enable only what you use.
| Category | Integrations |
|---|---|
| Network & Security | AdGuard Home, Tor, Router (ping, TCP, SNMP) |
| Cryptocurrency | Bitcoin, Alby Hub |
| Storage & Sharing | qBittorrent, IPFS, Synology |
| Smart Home & Media | Homebridge, Philips Hue, Roon |
| Infrastructure | Mac Mini, Raspberry Pi |
See full docs: docs/integrations/index.md
| Layer | Stack | Responsibility |
|---|---|---|
| Frontend | React 18, TypeScript, Vite, Tailwind CSS, TanStack Query | Dashboard UI, server state (React Query), live updates |
| Backend | Node.js, Fastify 5, WebSocket (ws), DuckDB, OpenAPI |
Service polling, REST + WebSocket API, config store |
| Monorepo | npm workspaces | Shared development workflow for frontend/backend |
Requires a Mac with Apple Silicon: the desktop build is arm64 only and does not run on
Intel Macs. The install.sh script sets up everything from scratch — Homebrew, Node.js, workspace dependencies, and a .app launcher:
git clone https://github.com/EraPartner/Watchman.git
cd Watchman
./install.shAfter installation, double-click the Launch Watchman.command shortcut or run:
npm run electron:prodThe desktop app needs no env file: it sets the backend port and data directory itself, and the backend generates a master key on first run. Services are configured in the UI.
Closing the window does not quit Watchman: it keeps running in the Dock with its backend. Click the Dock icon to reopen the window, and press ⌘Q to quit. See
docs/guides/running-the-desktop-app.md.
git clone https://github.com/EraPartner/Watchman.git
cd Watchman
npm run deps:ci:portable
cp apps/backend/.env.example apps/backend/.env.local # optional; see Backend environment
npm run build
npm run start| Service | URL |
|---|---|
| Frontend (preview) | http://localhost:4173 |
| Backend API | http://localhost:3001 |
git clone https://github.com/EraPartner/Watchman.git
cd Watchman
npm run deps:ci:portable
cp apps/backend/.env.example apps/backend/.env.local
npm run dev| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:3001 |
Set these in apps/backend/.env.local (gitignored). apps/backend/.env is read as a fallback,
and real environment variables take precedence over both. All of them are optional.
| Variable | Why it matters |
|---|---|
WATCHMAN_MASTER_KEY |
Encrypts stored service secrets at rest. Optional: if unset, a key file is generated in DATA_DIR. Back it up; without it, stored secrets are unrecoverable |
CORS_ALLOWED_ORIGINS |
Extra browser origins to allow (needed for non-loopback web access) |
BACKEND_V2_PORT / BACKEND_V2_HOST |
Bind address — optional (defaults 3001 / 0.0.0.0) |
Service connection details (hosts, tokens, etc.) are not env vars — they live
in the DuckDB config store, managed via the /config API or the UI. Full surface:
docs/reference/environment-variables.md.
# Development
npm run dev # frontend + backend in watch mode (concurrent)
npm run dev:frontend # frontend only
npm run dev:backend # backend only
npm run backend # alias of dev:backend
# Building
npm run build # production build (backend + frontend)
npm run build:frontend # frontend production build
npm run build:backend # backend production build
npm run build:dev # dev-mode frontend build
npm run preview # preview production frontend build
npm run dist # build backend + frontend, then package Electron .app
npm run clean # remove all node_modules / dist / out folders
# Production self-host
npm run start # backend + frontend preview (concurrent)
npm run start:backend # backend only
npm run start:frontend # frontend preview only
# Linting
npm run lint # frontend ESLint
npm run lint:frontend # frontend ESLint (explicit alias)
npm run lint:backend # backend ESLint
npm run lint:fix # autofix across workspaces
npm run format # Prettier across workspaces
npm run format:check # Prettier check across workspaces
npm run typecheck # tsc across backend + frontend
# Testing
npm run test # backend Vitest suite
npm run test:frontend # frontend Vitest suite
npm run test:all # backend + frontend (concurrent)
npm run test:coverage # frontend coverage report
npm run test:watch # backend watch mode
npm run test:e2e # Playwright e2e suite
npm run test:e2e:visual # Playwright with snapshot updates
# Electron (desktop app)
npm run electron:dev # desktop dev mode (NODE_ENV=development)
npm run electron:prod # desktop production mode (built backend + frontend)
npm run electron:clean # clean install, rebuild, launch
# Types
npm run generate:types # regenerate TypeScript types from openapi.yamlWatchman/
├── apps/
│ ├── frontend/ # React + TypeScript + Vite app (Playwright e2e in tests/)
│ ├── backend/ # Node.js + Fastify API + WebSocket
│ └── desktop/ # Electron desktop wrapper
├── docs/ # Knowledge base and project documentation
├── packaging/ # Electron release / packaging assets
├── scripts/ # Repo maintenance scripts (git hooks, …)
└── tools/ # Development and maintenance tooling
packages/*is a reserved npm-workspace glob for future shared packages; no shared packages exist yet.
- OpenAPI 3.1 spec:
apps/backend/openapi.yaml— documentation only; no Swagger UI is served. - Security model: single user on a trusted network — no authentication, CSRF, or HTTP rate limiting, by design (ADR-017 / ADR-025). The only limits are on the WebSocket: 30 handshakes per IP per minute and 10 open connections per IP (
apps/backend/src/transport/ws/). Anyone who can reach the port can read everything and change Watchman's service configuration (Watchman itself never changes the monitored services), so do not expose the backend beyond your trusted network. - Browser cross-origin access is gated by an origin allow-list shared by CORS and the WebSocket upgrade: desktop
watchman://, loopback, and anything inCORS_ALLOWED_ORIGINS. - Service secrets are encrypted at rest in the DuckDB config store (
WATCHMAN_MASTER_KEY); secrets and PII are never logged.
See SECURITY.md for the full threat model and how to report a vulnerability.
Read more:
| Topic | Link |
|---|---|
| Getting Started | docs/getting-started.md |
| Setup Guide | docs/guides/setup.md |
| Common Tasks | docs/common-tasks.md |
| Architecture | docs/architecture/index.md |
| Integrations | docs/integrations/index.md |
| Environment Variables | docs/reference/environment-variables.md |
| Troubleshooting | docs/troubleshooting.md |
| Contributing | docs/guides/contributing.md |
The project is not actively maintained, so pull requests and issues may stay unanswered. If you
want to continue development, forking is the most reliable route. The development workflow and
checks are still documented in CONTRIBUTING.md, and the security model
and reporting channel are in SECURITY.md.
This project is licensed under AGPL-3.0-only.
See LICENSE for details.