Documentatie
Hoe backup.noaber.ai werkt
Centrale monitoring voor backup-jobs over Noaber.ai-eigen systemen én klant-stacks. Doel: 's nachts schrijven alle scripts hun status hierheen, 's ochtends één Telegram-samenvatting. Verder via deze pagina kunnen we per klant een eigen dashboard tonen met alleen hun eigen scope.
Het idee in één plaatje
backup-job (Cloudflare cron, plex VPS, LaunchAgent, NUC LXC, …)
│
│ HTTPS POST → https://backup.noaber.ai/api/report
│ Header: X-Backup-Token: bh_xxxxxxxxxx
│ Body : { system_key, status, duration_ms, rows_count, size_bytes, … }
▼
backup.noaber.ai (Cloudflare Worker + D1 + KV)
│
├─ Live dashboard (passkey-gated): per systeem, status, history
├─ /api/events, /api/systems, /api/summary (JSON voor automatisering)
└─ Cron 06:00 → 1 Telegram-samenvatting (failures + ok-overzicht + missing)
Wat is een "systeem"?
Een systeem is één discreet backup-mechanisme. Voorbeelden in de Noaber-stack:
| system_key | Categorie | Locatie | Frequentie |
|---|---|---|---|
topview | cf-cron | Cloudflare Worker | elke 24u |
lurvink | cf-cron | Cloudflare Worker | elke 24u |
bubbelbrein | cf-cron | Cloudflare Worker | elke 24u |
padelclippy | cf-cron | Cloudflare Worker | elke 24u |
cashspot | cf-cron | Cloudflare Worker | elke 24u |
paperless-r2 | plex-cron | Plex VPS (LXC) | elke 24u |
topview-nuc | nuc-cron | NUC LXC 103 | elke 24u |
restic-mbp-mini | launchd | MBP → Mac mini SFTP | elke 24u |
restic-mbp-r2 | launchd | MBP → R2 (weekly) | elke 168u |
vaultwarden-r2 | launchd | MBP → R2 | elke 24u |
Het verwachte payload-formaat
POST naar /api/report met header X-Backup-Token: bh_xxxxxxxxxxxxxxxx (of Authorization: Bearer bh_...). Body:
{
"system_key": "bubbelbrein",
"status": "ok",
"category": "cf-cron",
"started_at": 1747094400000,
"completed_at": 1747094403127,
"duration_ms": 3127,
"rows_count": 22057,
"size_bytes": 7093500,
"details": { "tables": 21, "files_present": "1/1" },
"source": "cf-cron"
}
Status moet ok, failed, warning, of in_progress zijn. Andere velden zijn optioneel maar verhogen wel de waarde van het dashboard. details mag JSON-object of string zijn.
Onboarden van een nieuwe klant of systeem
1. Token uitgeven
Ga naar /admin/tokens → maak een token aan met scope (bv. noaber, bubbelbal, lurvink) en omschrijving. Token wordt eenmalig getoond → bewaar in Vaultwarden + in de script-secrets.
2. Backup-script aanpassen
Vervang bestaande Telegram-notify door een POST naar /api/report. Failures mogen ook nog direct naar Telegram (urgent), succes hoeft niet meer (komt 's ochtends in samenvatting).
Voorbeeld bash-snippet:
BACKUP_TOKEN="bh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
COLLECTOR="https://backup.noaber.ai/api/report"
curl -fsS -X POST "$COLLECTOR" \
-H "X-Backup-Token: $BACKUP_TOKEN" \
-H "Content-Type: application/json" \
--data "{
\"system_key\": \"paperless-r2\",
\"status\": \"ok\",
\"duration_ms\": $DURATION,
\"size_bytes\": $SIZE_BYTES,
\"details\": { \"file\": \"$BASENAME\" }
}" >/dev/null
Cloudflare Worker (cf-cron) snippet:
await fetch('https://backup.noaber.ai/api/report', {
method: 'POST',
headers: {
'X-Backup-Token': env.BACKUP_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({
system_key: 'bubbelbrein',
status: 'ok',
duration_ms: Date.now() - startedAt,
rows_count: totalRows,
size_bytes: totalBytes,
details: { tables: tableCount },
source: 'cf-cron',
}),
})
3. Systeem registreren (optioneel)
Voor het dashboard kun je een systeem vooraf registreren zodat het ook zichtbaar is voordat het eerste event binnenkomt. Eerste event registreert het systeem automatisch (met display_name = system_key); via POST /admin/systems kun je naam/beschrijving/frequentie opgeven.
Beveiliging
- Dashboard achter passkey-login (WebAuthn). Geen wachtwoorden. Sessies via opaque cookie + KV-store, 30 dagen TTL.
- Ingestion via shared secret per scope. Token wordt SHA-256 + pepper gehasht in D1; nooit plain opgeslagen. Per token een eigen scope — backup-script kan niet over scope-grens heen schrijven.
- Token revoken via /admin/tokens. Backup-script raakt onmiddellijk 401.
- Bootstrap-token voor eerste admin staat als secret in de Worker — gebruikt voor enrollment van de eerste passkey, daarna kan deze worden gerouleerd (
wrangler secret put BOOTSTRAP_TOKEN) of gewoon nooit meer gebruikt.
Daily summary
Cron-trigger: 0 4 * * * (04:00 UTC = 06:00 CEST). Output: 1 Telegram-bericht aan SpotBot (chat 467689725) met:
- Failures (events in 24h met status
failedofwarning) - Missing (systemen met verwachte frequentie ≤ 24u die geen event hadden)
- OK (laatste succes per systeem, met rows/size/duur)
- Link naar dit dashboard voor diepere details
Failures kunnen daarnaast direct doorkomen vanuit de scripts zelf (snellere reactie); het ochtend-rapport is voor totaal-overzicht.
LXC / backup-mechanisme definitie
Voor systemen die op een LXC of VPS draaien, hanteren we deze conventie zodat de scripts onderling consistent zijn:
- Source folder:
/mnt/docker/<app>/op de host (bv./mnt/docker/paperless/). - Backup folder:
/mnt/docker/backups/<app>/, retention 30 dagen lokaal. - Backup-scripts:
/mnt/docker/scripts/backup-<app>.sh(lokale tar),sync-<app>-nas.sh(Synology rsync),backup-<app>-r2.sh(offsite encrypted). - Encryption voor offsite: GPG symmetric AES256, passphrase in
/root/.config/<app>-r2-passphrase(chmod 600), mirror in Vaultwarden. - R2 buckets:
noaber-<app>-backups(WEUR), scoped S3-tokens met alleen Object Read & Write op die bucket. - Cron-schedule: lokaal 03:30 · NAS 03:45 · R2 04:15. Drie windows zodat eerst-fase moet slagen voor twee-fase begint.
- Reporting: na de R2-upload (laag 3) doet het script één
POST /api/reportmet succes-status + size + duur. Failure-pad: meteenPOSTmet statusfailed+ details + nog steeds Telegram-alert direct.
Klant-specifiek dashboard (later)
Het schema ondersteunt al per-scope filtering. Voor een klant-dashboard kan op subdomein backup.<klant>.nl dezelfde Worker draaien met een query-default ?scope=<klant> en alleen klant-tokens accepteren. Niet nu nodig, schema is klaar.