IsItDown

[![Release](https://img.shields.io/github/v/release/DevManfre/isitdown?style=flat-square)](https://github.com/DevManfre/isitdown/releases) [![CI](https://img.shields.io/github/actions/workflow/status/DevManfre/isitdown/ci.yml?branch=main&style=flat-square&label=CI)](.github/workflows/ci.yml) [![Licenza](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE) [![Node](https://img.shields.io/badge/node-%E2%89%A5%2024-5FA04E?style=flat-square&logo=node.js&logoColor=white)](.nvmrc) [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6?style=flat-square&logo=typescript&logoColor=white)](tsconfig.json) [![Dashboard](https://img.shields.io/badge/dashboard-react-61DAFB?style=flat-square&logo=react&logoColor=black)](docs/development.it.md#92-stack-tecnologico) [![Dipendenze runtime](https://img.shields.io/badge/runtime%20deps-4-lightgrey?style=flat-square)](docs/development.it.md#92-stack-tecnologico) [![Docker](https://img.shields.io/badge/docker-light%20%7C%20ui-2496ED?style=flat-square&logo=docker&logoColor=white)](docs/docker.it.md#4-docker) [![i18n](https://img.shields.io/badge/i18n-en%20%7C%20de%20%7C%20es%20%7C%20fr%20%7C%20it%20%7C%20pt-orange?style=flat-square)](docs/theming.it.md#82-localizzazione) [English](README.md) · **Italiano** Monitoraggio self-hosted e containerizzato delle status page **degli altri**. Interroga le status page pubbliche dei provider da cui dipendi — GitHub, Cloudflare, Anthropic, npm, qualunque cosa giri su Atlassian Statuspage — e ti avvisa quando uno di loro cambia stato. Esiste perché un singolo sviluppatore o un piccolo team abbia un preavviso sui problemi upstream senza tenere aperte cinque dashboard di stato. Notifica sulle *transizioni*, mai a ogni poll: una settimana tranquilla è una settimana silenziosa. Due edizioni dallo stesso codice: **Light** (solo polling e notifiche, nessun server) e **UI** (lo stesso core più una dashboard locale, configurabile a runtime). ## Indice In questa pagina: [1. Cosa fa](#1-cosa-fa) · [2. Avvio rapido](#2-avvio-rapido) · [10. Client da terminale](#10-client-da-terminale) · [11. Roadmap](#11-roadmap) · [12. Layout dei branch e politica di merge](#12-layout-dei-branch-e-politica-di-merge) Il manuale, un file per sezione: - [3. Configurazione](docs/configuration.it.md) — `config.yml`, impostazioni a runtime, segreti, provider, canali, instradamento, politica di consegna - [4. Docker](docs/docker.it.md) — immagini, profili Compose, volumi e probe, Kubernetes, outpost - [5. Verificare un deployment](docs/verifying.it.md) — controlli rapidi, la dashboard, una notifica end-to-end, risoluzione dei problemi - [6. API HTTP](docs/api.it.md) — ogni route, le metriche, gli aggiornamenti live, i badge - [7. Come funziona](docs/how-it-works.it.md) — flusso dei dati, componenti, quando scatta una notifica, resilienza, consenso degli outpost - [8. Tema e localizzazione](docs/theming.it.md) — temi, le sei lingue della dashboard, accessibilità - [9. Sviluppo](docs/development.it.md) — struttura, stack tecnico, sviluppo live, test, rilasci - [10. Client da terminale](docs/cli.it.md) — `isitdown watch`, una vista di sola lettura della flotta per il terminale, `isitdown discover`, una prima flotta letta dai file di un progetto, e `isitdown import`, una flotta portata da Uptime Kuma, Gatus, Statping o StatusGator Ogni pagina ha accanto la sua gemella inglese (`docs/.md`). ## 1. Cosa fa Ogni pochi minuti IsItDown recupera la status page di ogni provider, normalizza la risposta, la confronta con quella precedente e invia un messaggio solo se è cambiato qualcosa davvero. ``` GitHub operational ████████████████████████████ 99.98% Cloudflare degraded ███████████████▁▁▁▁▁████████ 99.61% ← ti arriva un messaggio Anthropic operational ████████████████████████████ 99.93% ``` ### Principi di base - **Nessuna dipendenza esterna a runtime.** Nessun database server, nessun broker, nessun account cloud. A questa scala bastano un file JSON o un SQLite embedded. L'unica eccezione è opt-in: gli outpost ([Docker §4.7](docs/docker.it.md#47-outpost--un-secondo-punto-di-vista-per-le-sonde)) sono container che gestisci tu per una seconda lettura delle sonde, e senza nessuno configurato non se ne cerca nessuno. - **Guidato dalla configurazione.** Aggiungere un provider non significa mai toccare codice: una voce in `config.yml` (Light) o un dialog nella dashboard (UI). - **Notifiche idempotenti.** Notificano solo le *transizioni* di stato: operational → degraded, degraded → outage, outage → resolved. Un restart non notifica nulla. - **Indipendente dal provider.** La maggior parte gira su Atlassian Statuspage e non richiede codice; per tutto il resto serve un piccolo adapter. - **Segreti solo dall'ambiente.** Un token si legge da una variabile d'ambiente e non viene mai scritto in un file di configurazione, in un database, in una risposta API o in una riga di log. L'edizione UI può salvarne uno in un file `0600` accanto al database, sempre come variabile, così la dashboard imposta una credenziale senza riavvii. ### Le due edizioni | | Light | UI | |---|---|---| | Immagine | `ghcr.io/devmanfre/isitdown:light-latest` | `…:ui-latest` (costruita `FROM` light) | | Poller · Adapter · Diff Engine · Notifier | condivisi | condivisi | | Configurazione | `config.yml`, riletto a ogni ciclo | SQLite, modificata dalla dashboard | | State store | file JSON, scritture atomiche | SQLite (contiene anche la cronologia) | | Server HTTP | assente | Express sulla :3000 | | Cronologia uptime e grafici | — | viste 7/30/90 giorni, più un calendario annuale per provider | | Tema | — | chiaro / scuro / sistema, più varianti ad alto contrasto, per daltonici e OLED e un accento personalizzato | | Localizzazione | testo delle notifiche | testo delle notifiche **e** tutta la dashboard, in sei lingue (`en`, `de`, `es`, `fr`, `it`, `pt`) | | Impronta | immagine 264MB, nessuna porta in ascolto | 267MB: l'immagine Light più un layer | Entrambe le edizioni eseguono lo stesso core. Differiscono solo per ciò che viene iniettato: da dove arriva la configurazione e dove viene tenuto lo stato. ### La dashboard La dashboard dell'edizione UI è una single-page app React servita dallo stesso processo dell'API, su `http://localhost:3000`. Le sue viste, tutte raggiungibili dalla rail, dalla command palette (`⌘K` / `Ctrl+K`) e dalle scorciatoie da tastiera: | Vista | A cosa risponde | |---|---| | **Overview** | Qualcosa è giù adesso? Un titolo calmo, la lista dei provider e una griglia componibile di widget che puoi disporre e salvare come dashboard con nome. | | **Providers** · **Components** | La flotta, una riga per provider, e la stessa flotta vista componente per componente; ogni provider ha una pagina propria con uptime, calendario annuale, latenza, incidenti, una mappa e il suo runbook. | | **Incidents** | Ogni incidente come lista sotto una timeline Gantt, ciascuno con la propria pagina. | | **History** | Uptime su 7, 30 o 90 giorni, in al massimo cinque tab: trend, affidabilità, pattern, confronto e latenza. | | **Delivery log** | Cosa è stato inviato, a quale canale, e cosa diceva o perché è fallito. | | **Settings** | Provider, canali, instradamento, politica di consegna, aspetto e dati, applicati al ciclo successivo senza restart. | | **Wallboard** | Una vista a schermo intero, senza cornice, per uno schermo libero, con un QR code verso il provider nei guai peggiori. | È chiara, scura o di sistema, con varianti ad alto contrasto, per daltonici e OLED, e tradotta in sei lingue. Ogni route che ci sta dietro è documentata nell' [API HTTP](docs/api.it.md); il percorso guidato è in [Verificare un deployment](docs/verifying.it.md). --- ## 2. Avvio rapido ### 2.1 Con Docker **Edizione UI, senza clone** — un file, due comandi. Le immagini sono pubblicate su GHCR per `linux/amd64` e `linux/arm64`, quindi questo è anche il percorso per Raspberry Pi, Unraid, Portainer e Synology: ```bash curl -O https://raw.githubusercontent.com/DevManfre/isitdown/main/docker-compose.yml docker compose --profile ui up -d # poi apri http://localhost:3000 ``` Tutto ciò che serve all'edizione UI si configura nella dashboard, credenziali comprese: **Settings → un canale → Valore → Salva** ha effetto subito. Se preferisci che sia il container a possederle, mettile in un `.env` accanto al file compose: è opzionale, e viene letto se presente. ```bash printf 'TELEGRAM_BOT_TOKEN=...\nTELEGRAM_CHAT_ID=...\n' > .env docker compose --profile ui up -d # ricrea il container con i token ``` **Edizione Light** — polling e notifiche, niente in ascolto. Questa ha bisogno di un `config.yml` da montare, quindi si parte da un clone: ```bash git clone https://github.com/DevManfre/isitdown.git && cd isitdown cp .env.example .env # compila solo i canali che abiliterai cp config.example.yml config.yml # modifica: provider, intervallo, canali docker compose --profile light up -d docker logs -f isitdown-light ``` Possono girare entrambe insieme: usano volumi dati separati. Chi sviluppa dal sorgente aggiunge `--build`, che ignora il pull e costruisce l'immagine in locale: ```bash docker compose --profile ui up -d --build ``` ### 2.2 Senza Docker Richiede **Node 24** sia per la build sia per il runtime: `.nvmrc` lo fissa e `npm install` rifiuta versioni più vecchie, perché il driver SQLite integrato nel runtime e il suo supporto TypeScript nativo sono fondamentali anche in fase di build. `build:ui` in più esegue Vite per pacchettizzare la dashboard; `build:light` salta questo passaggio, dato che Light non distribuisce nessuna dashboard. ```bash nvm use # oppure: 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, poi apri :3000 ``` Variabili utili in locale: `CONFIG_PATH`, `DATA_PATH`, `DB_PATH`, `PORT`, `LOG_LEVEL` (vedi [3.3](docs/configuration.it.md#33-variabili-dambiente)). --- ## 11. Roadmap Consegnato: - **v1 — edizione Light**: polling, diff engine, notifiche Telegram e webhook generico, `config.yml` con segreti referenziati dall'ambiente, state store JSON con scritture atomiche, `ghcr.io/devmanfre/isitdown:light-latest`. - **v1.1 — prototipazione UI**: la dashboard esplorata in Claude Design e conservata in `design/claude-design-prototypes/`. L'opzione `3a`, la console navigabile, è il riferimento per l'implementazione; la palette scura e le etichette italiane più lunghe sono state validate lì invece di essere scoperte dopo. - **v1.2 — edizione UI**: quel design come dashboard Express (scritta all'inizio come moduli ES semplici, poi sostituita da quella React qui sotto) su SQLite, con la configurazione gestita a runtime e applicata al ciclo successivo senza restart. `ghcr.io/devmanfre/isitdown:ui-latest`, costruita `FROM` l'immagine Light. - **v1.3 — cronologia**: uptime e cronologia incidenti per provider con le barre giornaliere in stile status page e le viste 7/30/90 giorni, aggregate lato server e servite da `/history`. - **v1.4 — tema scuro e i18n**: tema chiaro/scuro/sistema basato su token con preferenza persistita, e dashboard localizzata (`en`, `it`) sopra i messaggi di notifica localizzati che entrambe le edizioni già condividevano. - **v1.5 — la dashboard React**: la dashboard vanilla portata su React 19, Vite, Tailwind v4 e shadcn/ui, compilata in `dist/ui/public` e servita dallo stesso server Express; il client legge gli aggregati del server tramite TanStack Query e non ricalcola mai una percentuale. Dopo la v1.5 (su `dev`): adapter per AWS, Google Cloud e Azure, richieste condizionali così che la maggior parte dei cicli sia un `304`, una cadenza di polling per provider, la vista del log invii, una rimozione di provider annullabile entro una finestra di ripristino, ntfy e Gotify accanto agli altri canali, un calendario annuale per provider, la readiness separata dalla liveness, una verifica di integrità con vacuum su richiesta e una dashboard Grafana committata, poi: - **Notifiche**: oltre trenta canali tenuti a un unico contratto — chat, push, strumenti di reperibilità e di incidenti, email, SMS e chiamate — con una politica di consegna (riepilogo dei guasti condivisi, accorpamento degli aggiornamenti, un periodo di silenzio per le interruzioni note, un report periodico, una rotazione di reperibilità e un test settimanale di consegna). - **Dashboard**: una Overview componibile costruita da un catalogo di widget, con dashboard con nome, un riepilogo «mentre eri via» e numeri che rimandano alle loro righe; un Gantt degli incidenti, percentili di latenza, confronto a cinque, cronologia con zoom, una vista Components, un ordine manuale della flotta, una command palette con ricerca testuale, scorciatoie da tastiera, filtri conservati nell'indirizzo, una procedura guidata al primo avvio con tour guidato e un passaggio di accessibilità (axe) su ogni frame. - **Wallboard e feed**: un codice QR che apre il provider nei guai peggiori, un impulso e un suono opzionale quando una tile cambia, e la flotta servita come PNG a 1 bit e SVG per cornici e-ink e chat. - **Qualità della vita per l'operatore**: un runbook Markdown per provider, mostrato nella sua pagina e citato in ogni avviso; tag accanto al gruppo, filtrabili e instradabili come `tag:`; silenziamenti pianificati, settimanali o una tantum; finestre di manutenzione dichiarate dall'operatore, che silenziano e vengono disegnate come quelle del provider; modifica in blocco di cadenza, gruppo, tag, silenziamento e interrogazione su una selezione; e Duplica su ogni provider ([3.25–3.29](docs/configuration.it.md#325-runbook--cosa-fare-se-è-giù)). - **Redesign calmo**: un compito per vista — una Overview che risponde a «c'è qualcosa di giù?» e ripiega il resto, una sola lista di provider invece di due, History divisa in cinque tab, Settings elencato per categoria, un'intestazione su una riga, spaziature e movimento su token di design, e gli effetti decorativi ritirati. Nulla è stato eliminato; le funzioni secondarie sono passate dietro tab, disclosure e menu. - **Rete di sicurezza della configurazione**: ogni scrittura di configurazione registrata in una cronologia da cui ripristinare un valore, snapshot con nome dell'intera configurazione, un'anteprima in diff prima di applicare l'import di un `config.yml`, e un provider letto da un form non salvato prima di salvarlo ([6. API HTTP](docs/api.it.md#6-api-http)). - **Hardening**: la dashboard ascolta di default solo sul loopback, rifiuta scritture cross-origin e con host rebound, limita le dimensioni delle risposte e rifiuta i target link-local. - **Catalogo e strumenti**: un catalogo di 205 provider, i comandi `isitdown discover` e `isitdown import` e una CI divisa in job paralleli. Ancora aperto: - Una revisione madrelingua delle stringhe italiane. La roadmap che ha portato il progetto fin qui è finita — ogni riga spedita. **Cos'è IsItDown.** Sia un aggregatore di status page sia un monitor di cose che non ne pubblicano nessuna — e ogni provider dichiara quale delle due risponde per lui. Un provider con status page è `declared`: fa fede la sua pagina, e una sonda che non concorda viene riportata come disaccordo, non come scavalcamento. Una sonda è `observed`: fa fede la nostra misura, e una pagina che la contraddice è un'opinione. Il default viene dall'adapter e non richiede configurazione; il campo `authority` esiste solo per i casi in cui l'operatore non è d'accordo con quel default. Questa domanda è rimasta aperta per due roadmap, ed è il motivo per cui "cosa significa 93% qui" aveva due risposte sulla stessa schermata. Non-obiettivi espliciti: autenticazione multi-utente (questa è una dashboard locale per un singolo operatore), status page dietro login, e un'app mobile pacchettizzata. --- ## 12. Layout dei branch e politica di merge Il tooling di Claude Code (`.claude/`, `CLAUDE.md`) e il filtro di merge stesso (`.mergeexclude`, `.githooks/`, `scripts/`) sono versionati **solo su `dev`**. Su `main` nessuno di quei percorsi esiste, né nel commit né nel working tree. Tutto il resto (sorgenti, documentazione, configurazione) passa normalmente da `dev` a `main`. Poiché il filtro non è leggibile da `main`, viene installato nella directory `.git` di questo clone, che tutti i branch condividono. ### Setup, una volta per clone ```bash git switch dev scripts/setup-hooks.sh ``` Copia: | Da (`dev`) | A (condiviso da ogni branch) | |---|---| | `scripts/git-merge-clean` | `$GIT_DIR/merge-clean` | | `.githooks/*` | `$GIT_DIR/hooks/*` | | `.mergeexclude` | `$GIT_DIR/merge-exclude` | e installa l'alias `git mergeclean`. Rieseguilo dopo aver modificato `.mergeexclude` o `scripts/git-merge-clean`. ### Merge verso `main` ```bash git switch main git mergeclean dev # non `git merge dev` ``` `git mergeclean` fa il merge del branch, elimina i percorsi elencati nella lista di esclusione, committa col formato di subject `🔀` del repository e rimuove quei percorsi dal working tree. Si rifiuta di partire su un albero sporco. I conflitti veri fuori dai percorsi esclusi fermano l'esecuzione così puoi risolverli e fare `git commit` come al solito. ### Cosa lo fa rispettare | Pezzo | Ruolo | |---|---| | `$GIT_DIR/merge-exclude` | la lista dei percorsi | | `$GIT_DIR/merge-clean` | il wrapper di merge (`--sync` elimina, `--guard` controlla) | | `$GIT_DIR/hooks/post-checkout` | elimina i percorsi esclusi dopo un cambio di branch | | `$GIT_DIR/hooks/pre-merge-commit`, `pre-commit` | interrompono qualunque commit che aggiungerebbe un percorso escluso a un branch che non lo traccia | Un `git merge dev` semplice su `main` viene rifiutato dagli hook di guardia: esegui `git merge --abort` e usa `git mergeclean`. `git commit --no-verify` aggira la guardia se ti serve davvero. ### Conseguenze del tenere il filtro fuori da `main` - **Il setup è per clone e non può essere automatico.** Git non esegue mai hook presi da un clone, e un clone che fa checkout solo di `main` non ha nulla da cui installare. Su una macchina nuova, fai checkout di `dev` ed esegui il setup prima di fare merge verso `main`. - **Finché il setup non viene eseguito, non è imposto nulla.** Un `git merge dev` semplice su un clone appena fatto porterà `.claude/` e `CLAUDE.md` dentro `main` appena risolvi i conflitti che solleva. ### Regole pratiche - Modifica `.claude/`, `CLAUDE.md`, `.mergeexclude`, `.githooks/` e `scripts/` solo stando su `dev`: su `main` non esistono. - L'eliminazione cancella solo i file che anche `dev` ha e che sono identici byte per byte, quindi i file locali della macchina (`.claude/settings.local.json`) e le modifiche locali non vengono mai toccati.