Both are one shape: something the box already knows, drawn as its opposite.
R-259 — A DISK WE FAILED TO READ WAS DRAWN AS A HEALTHY EMPTY DISK. readDiskUsage
(internal/system/info_linux.go) logged a statfs failure at DEBUG and returned, leaving the caller's
TotalGB/UsedGB/AvailGB/Percent at zero — and usageColor(0) is "nominal". The dashboard's
most-looked-at meter therefore rendered "0.0 GB / 0.0 GB (0%)" with a 0%-wide bar in the healthy
colour. "We could not look" and "there is plenty of room" were the same picture.
readDiskUsage now returns whether the measurement succeeded; SystemInfo gains DiskKnown and
HDDKnown (HDDConfigured is not a substitute: it says a path was configured, not that reading it
worked); and the template draws NO figure, NO percentage and NO meter fill when unknown, saying
"A tarhely merete most nem olvashato ki." instead. A healthy box is byte-identical, colour band
included.
This session rules the convention (felhom.eu CONTEXT.md S-39): an explicit `...Known bool` companion
beside the figures, checked in the template — the shape Offbox.StatsKnown already uses, whose own
comment says "a 0%-wide bar over an unread store is a picture of emptiness, and a picture is a
claim". Pointers and separate error fields are both legitimate Go, but a codebase with three
dialects cannot be gated (ROADMAP G-3 was blocked on exactly this). Existing call sites NOT
converted.
R-258 — THE PER-APP BACKUP TICK WAS GREEN ON PRESENCE, AND RED ONLY ON A GLOBAL CONDITION.
buildAppBackupRows set Tier1LastStatus from status.LastDBDump.Success, which is the box's single
most recent dump RUN, whichever app it belonged to. An app whose own dump failed showed a tick as
long as some other app dumped successfully afterwards; an app with no database took the nil branch
and went green on the mere existence of a restore point.
appDumpVerdict now reads THIS app's own entries in DBDumpStatus.Results (matched on
DumpResult.DB.StackName, failure = non-nil Error). Three states: any failing database -> error; all
clean -> ok; no result recorded -> NO verdict and no icon, titled "Errol a mentesrol nincs
eredmenyunk." The recovery unit carries no per-run outcome of its own, so green cannot honestly be
derived from presence. The global tier1DBStatus label is untouched — it is correct as a global.
RECENCY IS DELIBERATELY NOT ADDED. A tick over a three-week-old restore point is a real weakness,
but an age threshold means inventing a number and the time is already printed beside the icon.
Recorded as an observation, not changed.
AN EXISTING TEST WAS ASSERTING THE DEFECT AND WAS CORRECTED, NOT DELETED:
TestBuildAppBackupRows_Tier1FromRestorePoints expected "ok" for a status with no LastDBDump at all —
green from nothing but a file's existence. It now expects no verdict; its real subject, the
Tier1LastRun time, is unchanged.
The dashboard test EXTRACTS the meter block from the shipped template rather than copying it: a
copied block drifts, and a drifted copy passes while the page it claims to cover has changed — the
fixture-is-not-the-wire mistake this project has now hit twice.
Six red-proofs across both parts, each with the mutation asserted applied.
No new tag on any declared wire — report/builder.go maps into its own types and is untouched;
wire_contract_gate.py confirmed green.
go build / go vet / go test ./... green (28 packages), controller_gates --fast all OK, both run
separately from this commit.
The cgroup-only approach was a no-op on the demo: the controller container's OWN
cgroup is unlimited (the 2GB cap is on the LXC ancestor, hidden), and /proc has no
lxcfs, so it kept reporting the host's 16GB. The Docker daemon runs IN the LXC, so
'docker info' MemTotal reports the guest's real cap (2048MB) — now the authoritative
source (cgroup limit preferred when present, e.g. non-nested). The deploy memory
guard now uses the controller's committed-app memory (sum of running mem requests)
for 'used' — accurate and cheap — instead of host /proc RSS (unobservable per guest;
would make the guard never/always fire). /api/system/info reports the guest cap as
total and committed memory as used. Tests: cgroup-limit path, docker-info fallback
(nested case), GuestMemTotalMB fallback (dockerMemTotalFn stub).
/api/system/info reported the Proxmox host's 16GB (the controller container
reads host /proc/meminfo with no lxcfs), defeating the deploy memory-headroom
hard-block (deploy.go uses GetMemoryMB). readMemInfo now prefers the cgroup
memory limit (v2 memory.max / v1 memory.limit_in_bytes; sentinels = unlimited)
when finite and below the host total; used = memory.current/usage_in_bytes.
Test info_cgroup_test.go (cgroup v2 cap wins, v2 max sentinel, v1 unlimited,
v1 finite) — fails on pre-fix code.
Add detailed [DEBUG] logging to every controller module when
logging.level is set to "debug". Each module with stateful debug
uses SetDebug(bool) wired from main.go. Covers stacks, backup,
cloudflare, integrations, system, monitor, settings, scheduler,
web handlers, storage, metrics, API, selfupdate, and assets.
Also includes the app export/import (.fab bundles) feature from
v0.32.0 and its debug page integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deploy page, pre-start check, and deploy validation now use actual
/proc/meminfo usage instead of declared mem_request sums. New
GetMemoryMB() helper for lightweight real-time memory reads. Monitoring
page gains a stacked memory distribution bar showing per-container
usage, OS overhead, and free memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2 (Monitoring & Health):
- Central job scheduler replacing ad-hoc goroutines (internal/scheduler)
- CPU usage collector via /proc/stat background sampling (internal/system/cpu_linux.go)
- Temperature reading from /sys/class/thermal + /host/sys (Docker mount)
- Load average from /proc/loadavg
- Healthchecks.io-compatible HTTP pinger (internal/monitor/pinger.go)
- System health checks: disk, memory, CPU, temp, Docker, protected containers (internal/monitor/healthcheck.go)
Phase 3 (Backups):
- Database auto-discovery via docker ps + docker inspect (internal/backup/dbdump.go)
- Database dumping via docker exec (pg_dump / mariadb-dump) with atomic writes
- Restic backup integration with auto-password generation (internal/backup/restic.go)
- Backup orchestrator: DB dumps + restic snapshots + weekly prune (internal/backup/backup.go)
- Manual backup trigger via dashboard button and POST /api/backup/run
Dashboard UI:
- CPU usage bar with load average display
- Temperature with colored indicator dot
- Backup status card with last run time, DB count, repo stats
- "Mentés most" button for manual backup trigger
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>