[0.22.0] - 2026-11-14
Important
Upgrade notes
- Everyone signs in again once
- Take a backup if you may downgrade: older versions can't read the new tier records
- Without auth, and for
/metricsscraped without a bearer token, list the name you reach Kula by inweb.security.allowed_hostsunless it is local (reverse proxies that forwardHost, Tailscale, internal DNS) - Startup now stops on an unknown config setting, a
-configpath that cannot be read or created, anallowed_originsentry other than a plainscheme://host[:port], or a zerosession_timeout, WebSocket limit orargon2time or threads - PgBouncer in front of PostgreSQL needs
ignore_startup_parameters - Docker: mount
/run/utmpor/run/systemd/sessionsto keep the logged-in user count - Hosts with dozens of containers, disks or interfaces may need a higher
MemoryMaxin a systemd drop-in
Added
- Rearrange gauges and chart cards in the Customization menu (#51)
- Prometheus: current Apache2 rates
kula_apache2_accesses_per_secondandkula_apache2_served_bytes_per_second - MySQL over TLS with
sslmodeandsslrootcert install_v3.shinstaller- kula-scan reads the password from
KULA_SCAN_PASSWORD
Changed
- History queries read only their time range (TUI too), and restarts are faster: each tier keeps a small
tier_N.idxseek index - Rolling views refresh only their newest buckets instead of the whole range
- History queries, compressed responses and container lists use far less memory
- Stored records carry a checksum, and backups no longer pause collection or the dashboard
- Tier 1 and 2 rollups align to wall-clock minutes, so rolling and past views agree
/api/historyaccepts an optionaltierhint, and API and WebSocket errors are always JSON- History requests take turns; dashboards retry one the server was too busy for and reconnect at staggered times after a restart
- Disk temperatures and power supplies are read every 10 s, set with
collection.disk_temp_intervalandcollection.power_supply_interval - PostgreSQL table health, container details, GPU detection and System Info sensors are read far less often
- Only containers with a full runtime ID are monitored, up to a limit per sample
- Charts are built once on page load, and Graph Bounds apply without reloading them
- Cards keep one consistent order everywhere, and split charts expand and collapse like every other chart
- Apache2 charts and the AI assistant show current request and byte rates
- The login form tells a wrong password apart from a rate limit or a busy server
- Unknown settings and ones that would break login or WebSockets stop startup, and
allowed_originsis normalized kula_system_logged_in_usersis omitted when the host has neither utmp nor logind sessions- systemd: the unit caps Kula's memory and no longer sets
ProtectClock, which kept it from reading the clock - A setting the sandbox cannot grant is left out with a warning instead of disabling the whole sandbox
- Failed WebSocket writes are logged only at debug level, and the TUI prints its log lines when it exits
- kula-scan: a default run signs in once and logs out; account enumeration needs
-aggressive
Deprecated
- Prometheus
kula_apache2_requests_per_secondandkula_apache2_bytes_per_second(usekula_apache2_accesses_per_secondandkula_apache2_served_bytes_per_second)
Fixed
- Disk utilization is measured again (read 0% since 0.7.0)
- TCP retransmissions are measured and stored in history, and the first errors and resets after a quiet boot are counted
- Clock sync with a local-time RTC, mounts with a space in their path, and used memory and swap are measured correctly
web.graphs.gpu_temptakes effect- Logged-in user count works under the sandbox and on arm64, with a systemd-logind fallback
- Containers: rootless Podman is found for the user Kula runs as, hosts with hundreds of containers keep their metrics, and a socket that refuses connections is no longer reported as missing
- PostgreSQL and MySQL monitoring no longer piles up server connections; MySQL passwords with
@,/or:and IPv6 hosts work - PostgreSQL: polls on a frozen server are cut off, settings are quoted, and a standby of a quiet primary no longer shows a growing replication lag
- nginx, Apache2, PostgreSQL, MySQL and container rates no longer spike after a restart or failed poll
- A hung network mount or a slow nginx or Apache2 status page no longer stalls collection
- nginx and Apache2 failures are logged when they change, and redirects are not followed
https://targets, an Ollama URL without a port and PostgreSQLverify-ca/verify-fullwork under the sandbox- History no longer goes missing after a tier wraps, a crash, a restart or a clock step back, and long views keep 1-minute detail right after a restart
- Damaged tier files, records from a newer version, infinite, NaN or out-of-range values and over-long names or lists no longer hang or break history
- Custom metric values beyond the 32-bit float range are refused and logged once
- Shutdown waits for the sample being collected before closing storage
- Tier 1 and 2 rollups no longer carry needless statistics, so they retain more history
inspect_tier.pywalks damaged tiers the way Kula does and answers--help- Battery/UPS, PostgreSQL replication and application charts no longer end up in the wrong place
- Focus mode no longer hides split temperature charts or an application-only selection, or pins the crosshair of charts being selected
- Split charts no longer merge devices whose names differ only in punctuation
- A malformed aggregation or Focus mode preference no longer blanks the dashboard
- An expired session returns to the login screen instead of reconnecting forever, and AI panel actions no longer run twice after signing in again
- Alerts, application and battery charts, card subtitles, device names and login, AI panel and disk messages are translated; split charts and the sampling tooltip follow a language change
- Rolling views no longer stay marked "partial" after a late refresh or announce every refresh to screen readers
- Live views follow the server's clock when the browser's is off, and a background tab no longer piles up chart memory
- AI assistant: long answers no longer freeze the tab, long conversations are no longer refused, and the metrics tool keeps to the history range limits
- Bearer tokens work with auth status and logout
/metricswrites each metric family once, with valid UTF-8 label values, sopromtoolaccepts itkula hash-passwordrefuses an empty password, answers--helpand keeps spaces in a piped password- runit: logging no longer blocks Kula
Security
- DNS rebinding protection: with auth disabled, and on
/metricswithout a token, only local host names andweb.security.allowed_hostsare answered - Sessions are tied to the account password, and login is refused while auth is disabled
- Concurrent logins and history requests can no longer exhaust CPU or memory, and a history request can no longer read the whole raw tier
- Login rate limits count an IPv6 client by its /64 and a NAT64 one by its IPv4 address; behind
trust_proxythey use the proxy's ownX-Forwarded-Forentry, and only a valid address - Startup warnings when
allowed_originswithoutorigin_validationexposes the live stream, or aunix_socketwithouttrust_proxygives every client one login limit - A
unix_socketwhose mode cannot be set is not served, and the custom metrics socket limits its connections - Content-Security-Policy no longer allows inline styles, pins
base-uriandform-action, and lets only the game page reach the score URL - Every dashboard module loads with an integrity check
- Dashboard pages and API responses are never cached, and signing in clears the password field
- AI proxy: connects only to loopback, follows no redirects, forwards only user and assistant turns on a listed model, limits tool calls and concurrent chats, and keeps backend errors from the client
- Logs, the TUI and kula-scan escape text from requests, monitored servers and scan targets; logs hide credentials in status URLs, and kula-scan bounds what a target streams
- The sandbox confines Unix sockets, UDP and signals on newer kernels, opens storage under it, and covers
kula tui - Local users can no longer plant fake containers, and a root Kula no longer uses another user's Podman socket
- A fake database server can no longer crash Kula, and crafted tier files can no longer hang or exhaust it, the TUI or
inspect_tier.py - Storage, backup, session and GPU log files are never opened through a link or FIFO
.deband snap configs are no longer world-readable or writable by the service, upgrades leave a linked config alone, and release tarballs no longer hand/opt/kulato the builder's uid- Docker: the container drops all capabilities and runs read-only, and root owns the binary and config
- The documented installer check runs only an installer that passed it
- The Chart.js lockfile must match the registry, CI rebuilds the bundle from it, and
go_modules_updates.pyholds back freshly released updates - Vulnerabilities are reported privately
Full Changelog: 0.21.0...0.22.0