Skip to content

Latest commit

 

History

530 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

IsItDown

Release CI License Node TypeScript Dashboard

Runtime dependencies Docker i18n

English · Italiano

Self-hosted, containerized monitoring for other people's status pages. It polls the public status pages of the providers you depend on — GitHub, Cloudflare, Anthropic, npm, anything running Atlassian Statuspage — and messages you when one of them changes state.

It exists so a single developer or a small team gets early warning about upstream trouble without keeping five status dashboards open. It notifies on transitions, never on every poll, so a quiet week is a silent week.

Two editions from one codebase: Light (polling and notifications only, no server) and UI (the same engine plus a local dashboard, configured at runtime).

Contents

On this page: 1. What it does · 2. Quick start · 10. Roadmap · 11. Branch layout and merge policy

The manual, one file per section:

Each page has an Italian twin beside it (docs/<name>.it.md).

1. What it does

Every few minutes IsItDown fetches each provider's status page, normalises the answer, compares it with what it saw last time, and sends a message only if something actually changed.

GitHub          operational    ████████████████████████████  99.98%
Cloudflare      degraded       ███████████████▁▁▁▁▁████████  99.61%   ← you get a message
Anthropic       operational    ████████████████████████████  99.93%

Core principles

  • No external dependencies at runtime. No database server, no message broker, no cloud account. A JSON file or an embedded SQLite file is enough at this scale.
  • Config-driven. Adding a provider never means touching code — an entry in config.yml (Light) or a dialog in the dashboard (UI).
  • Idempotent notifications. Only state transitions notify: operational → degraded, degraded → outage, outage → resolved. A restart notifies nothing.
  • Provider-agnostic. Most providers run Atlassian Statuspage and need no code at all; anything else gets a small adapter.
  • Secrets from the environment only. A token is read from an environment variable, never written to a config file, a database, an API response or a log line. The UI edition can save one to a 0600 file beside its database, still as a variable, so the dashboard can set a credential without a restart.

The two editions

Light UI
Image ghcr.io/devmanfre/isitdown:light-latest …:ui-latest (built FROM light)
Poller · Adapters · Diff Engine · Notifiers shared shared
Configuration config.yml, re-read every cycle SQLite, edited in the dashboard
State store JSON file, atomic writes SQLite (also carries history)
HTTP server none Express on :3000
Uptime history and charts 7/30/90-day views, plus a year heat calendar per provider
Theme light / dark / system
Localisation notification text notification text and the whole dashboard
Footprint 264MB image, no listening socket 267MB — the Light image plus one layer

Both editions run the same core engine. They differ only in what gets injected into it: where configuration comes from, and where state is kept.


2. Quick start

2.1 With Docker

UI edition, without a clone — one file, two commands. The images are published to GHCR for linux/amd64 and linux/arm64, so this is also the Raspberry Pi, Unraid, Portainer and Synology path:

curl -O https://raw.githubusercontent.com/DevManfre/isitdown/main/docker-compose.yml
docker compose --profile ui up -d
# then visit http://localhost:3000

Everything the UI edition needs is configured in the dashboard, credentials included: Settings → a channel → Value → Salva applies at once. Put them in a .env next to the compose file instead if you would rather the container own them — it is optional, and read if present:

printf 'TELEGRAM_BOT_TOKEN=...\nTELEGRAM_CHAT_ID=...\n' > .env
docker compose --profile ui up -d      # recreates the container with the tokens

Light edition — polling and notifications, nothing listening. This one needs a config.yml to mount, so start from a clone:

git clone https://github.com/DevManfre/isitdown.git && cd isitdown
cp .env.example .env                # only fill in the channels you will enable
cp config.example.yml config.yml    # edit: providers, interval, channels
docker compose --profile light up -d
docker logs -f isitdown-light

Both can run at once; they use separate data volumes.

Contributors building from the source tree add --build, which overrides the pull and builds the image locally instead:

docker compose --profile ui up -d --build

2.2 Without Docker

Requires Node 24 for the build as well as the runtime — .nvmrc pins it and npm install refuses anything older, because the runtime's own SQLite driver and native TypeScript support are load-bearing at build time too. build:ui additionally runs Vite to bundle the dashboard; build:light skips that step, since Light ships no dashboard.

nvm use                             # or: nvm install 24
npm install
cp config.example.yml config.yml
cp .env.example .env

npm run build:light && node dist/light/index.js     # Light
npm run build:ui    && node dist/ui/server.js       # UI, then open :3000

Useful overrides when running locally: CONFIG_PATH, DATA_PATH, DB_PATH, PORT, LOG_LEVEL (see 3.3).


10. Roadmap

Delivered:

  • v1 — Light edition: polling, the diff engine, Telegram and generic-webhook notifications, config.yml with environment-referenced secrets, a JSON state store with atomic writes, ghcr.io/devmanfre/isitdown:light-latest.
  • v1.1 — UI prototyping: the dashboard explored in Claude Design and kept in design/claude-design-prototypes/. Option 3a, the navigable console, is the implementation reference; the dark palette and the longer Italian label lengths were validated there rather than discovered later.
  • v1.2 — UI edition: that design as an Express + vanilla-ES-module dashboard over SQLite, with configuration managed at runtime and applied on the next cycle without a restart. ghcr.io/devmanfre/isitdown:ui-latest, built FROM the Light image.
  • v1.3 — history: per-provider uptime and incident history with status-page daily bars and 7/30/90-day views, aggregated server-side and served from /history.
  • v1.4 — dark mode and i18n: token-based light/dark/system theming with a persisted preference, and a localised dashboard (en, it) on top of the localised notification messages both editions already share.

Since v1.4 (on dev): adapters for AWS, Google Cloud and Azure, conditional requests so most cycles are a 304, a per-provider poll cadence, the delivery log view, a provider removal that can be undone inside a restore window, ntfy and Gotify alongside the other channels, a year heat calendar per provider, readiness split from liveness, an integrity check and vacuum on demand, and a committed Grafana dashboard.

Still open:

  • A native review of the Italian strings.

The roadmap that carried the project this far is finished — every row of it shipped — and is archived at docs/roadmap/ROADMAP.md. What comes next is being weighed in ROADMAP_2.md and ROADMAP_3.md, neither of which commits to anything.

What IsItDown is. Both an aggregator of status pages and a monitor of things that publish none — and every provider says which of the two answers for it. A status page provider is declared: its own page is the record, and a probe that disagrees is reported as a disagreement rather than as an overrule. A probe is observed: our reading is the record, and a page that contradicts it is an opinion. The default comes from the adapter and needs no configuration; the authority field is only there for the cases where the operator disagrees with that default. This was left open for two roadmaps, and leaving it open is why "what does 93% mean here" had two answers on one screen.

Explicit non-goals: multi-user auth (this is a local, single-operator dashboard), status pages behind a login, and a packaged mobile app.


11. Branch layout and merge policy

The Claude Code tooling (.claude/, CLAUDE.md) and the merge filter itself (.mergeexclude, .githooks/, scripts/) are tracked on dev only. On main none of those paths exist — neither in the commit nor in the working tree. Everything else (source, docs, config) flows normally from dev to main.

Because the filter is not readable from main, it is installed into this clone's .git directory, which every branch shares.

Setup, once per clone

git switch dev
scripts/setup-hooks.sh

It copies:

From (dev) To (shared by every branch)
scripts/git-merge-clean $GIT_DIR/merge-clean
.githooks/* $GIT_DIR/hooks/*
.mergeexclude $GIT_DIR/merge-exclude

and installs the git mergeclean alias. Re-run it after changing .mergeexclude or scripts/git-merge-clean.

Merging into main

git switch main
git mergeclean dev        # not `git merge dev`

git mergeclean merges the branch, drops the paths listed in the exclude list, commits with the repo's 🔀 subject format, and removes those paths from the working tree. It refuses to run on a dirty tree. Genuine conflicts outside the excluded paths stop the run so you can resolve them and git commit as usual.

What enforces it

Piece Role
$GIT_DIR/merge-exclude the path list
$GIT_DIR/merge-clean the merge wrapper (--sync purges, --guard checks)
$GIT_DIR/hooks/post-checkout purges the excluded paths after a branch switch
$GIT_DIR/hooks/pre-merge-commit, pre-commit abort any commit that would add an excluded path to a branch that does not track it

A plain git merge dev on main is refused by the guard hooks — run git merge --abort and use git mergeclean instead. git commit --no-verify bypasses the guard if you ever genuinely need to.

Consequences of keeping the filter off main

  • The setup is per clone and cannot be automatic. Git never runs hooks taken from a clone, and a clone that only ever checks out main has nothing to install from. On a new machine, check out dev and run the setup before merging anything into main.
  • Until the setup is run, nothing is enforced. A plain git merge dev on a fresh clone will pull .claude/ and CLAUDE.md into main as soon as you resolve the conflicts it raises.

Rules of thumb

  • Edit .claude/, CLAUDE.md, .mergeexclude, .githooks/ and scripts/ only while on dev — on main they do not exist.
  • The purge only deletes files that dev also has and that are byte-identical to it, so machine-local files (.claude/settings.local.json) and local edits are never touched.

About

Self-hosted status-page watcher: polls GitHub, Anthropic, Cloudflare & 100+ Statuspage providers, alerts on Telegram. Docker, one file config.

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages