github shukiv/jabali-panel release-db6d0f4da
Release db6d0f4da

latest release: release-0281aaf4b
3 hours ago

Auto-built release for commit db6d0f4.

2026-11-01 — monthly cut: db6d0f4da

stable moved from 51fd7fc25 to db6d0f4da: 302 changes.

Migrations 000309–000341 (33). Changing rows: 000311, 000317, 000318, 000332, 000335, 000340.

  • 9755a6aed fix(mail): DMARC reports reach every domain; postmaster@ is the server admin's (#1940)
  • 35826ef14 feat(php): per-domain log_errors, file_uploads and short_open_tag, pinned so they cannot leak between domains — GH #1701 (#1951)
  • bfa676212 feat(php): pin every per-domain PHP value on every PHP vhost, and show the value the pool really runs as the default — GH #1701 (#1969)
  • 393a37efa feat(domains): a tenant domain goes live only after its owner proves the name — GH #1816 (#1923)
  • 1b51eba08 chore(db): drop the retired users.webmail_enabled column — GH #1817 (#1970)
  • ad60c7788 fix(malware): "Update signatures" refreshes the signature-base YARA pack, not the removed PMF unit (#1971)
  • 8d121ac67 docs(ftp): isolated accounts re-mount on the first reconcile tick after a reboot; there is no boot unit (#1972)
  • a05912544 feat(php): per-domain open_basedir and allow_url_fopen, pinned on every PHP vhost — GH #1701 Slice 3 (#1974)
  • 654a15a30 fix(php): app installs in a subfolder get the domain's PHP values — GH #1701 (#1975)
  • 29b19cebb feat(php): PHP Settings shows each setting's state live and keeps unsaved changes safe — GH #1701 (#1976)
  • f71a5eb91 fix(ui): sticky elements in tenant pages stay in view while the page scrolls (#1977)
  • 0dc8eaf3d fix(backups): retention keeps or forgets whole backups, so no restore point is left partial — GH #1169 (#1978)
  • e2d94d29f fix(backups): retention works on rotated and default Local destinations and alerts when all fail; backup retention verify finds unrestorable backups — GH #1169 (#1979)
  • 2057dd0d0 fix(backups): retention keeps backups that lost only their manifest, and deletes rows with no snapshot left — GH #1169 (#1980)
  • c7212ee34 fix(backups): retention alerts when a schedule's destinations cannot be loaded (#1981)
  • b30e288ea fix(backups): retention deletes the rows of old failed and cancelled backups (#1982)
  • 071da216b fix(install): install systemd-timesyncd when it is missing, so the clock is synced (#1983)
  • 60c0f3b91 fix(crowdsec): the firewall bouncer no longer stays silently down (#1984)
  • cfdb7e1c6 fix(backup): a timed-out restic command no longer leaves its lock behind (#1985)
  • ae798731a fix(backups): retention deletes the rows of backups whose manifest is gone — GH #1169 (#1986)
  • 0542d0314 fix(cron): System jobs shows the last run of a disabled job, and a disabled job as not scheduled — GH #1686 (#1987)
  • 616e48126 fix(update): switching to the stable channel never moves a box backwards (#1988)
  • 7feffc412 fix(backups): the retention sweep runs after the panel self-update, not alongside it (#1989)
  • 623f697e8 fix(php): a package that allows PHP exec functions really allows them, and picks disabled functions one by one — GH #1701 (#1990)
  • 1d50e7c8a feat(mail): the catch-all is set on the mail domain's Settings tab — GH #1916 (#1991)
  • f3e633ba8 chore: land the work stale branches still held — a serve wiring test and three blueprints (#1994)
  • ef64db270 feat(files): the File Manager sorts by Name, Size or Modified when you click a header — GH #1184 (#1995)
  • 1f45e3c60 fix(backup): account-restore --apply=false really leaves the live system alone (#1996)
  • 71bcef49c feat(mail): mail apps find calendars and contacts on their own, so the per-mailbox URL modal is gone — GH #1917 (#1998)
  • 012038bb6 feat(dns): one zone list — the name opens its records, and DNSSEC lives in the row's ⋯ menu — GH #1918 (#2000)
  • 189db834e fix(php): a function this PHP-FPM build does not have is no longer shown as Allowed — GH #1701 (#2002)
  • ed392219f feat(nginx): a Front controller rule sets where requests that match no file go — GH #1999 (#2005)
  • 15642a648 fix(php): PHP Settings says when AppArmor lets exec functions start only the shell and cat — GH #2001 (#2006)
  • fc5d78938 fix(php): say when an allowed exec function can start any program because PHP's AppArmor profile is not enforcing — GH #2001 (#2007)
  • 45806c1e6 fix(security): phpMyAdmin and Adminer can no longer start programs — their PHP pool disables the exec functions (#2008)
  • 89058a2f3 chore(apps): new ITFlow installs get v26.10 — GH #1973 (#2009)
  • cda7a8307 fix(domains): deleting a domain on a server without mail finishes its teardown — GH #357 (#2010)
  • 807638655 fix(php): a failed PHP-FPM reload or restart says why, instead of "%!w()" — GH #1820 (#2011)
  • 340b42ac8 fix(limits): a pool or Python app that cannot start because the account is at its Max tasks limit says so — GH #1820 (#2012)
  • c28a1ce95 fix(python-apps): a first build no longer dies at the panel's 120s deadline — pip runs in the background — GH #357 (#2013)
  • fa848b0d0 ux(dns): DNS Zones no longer shows the "provisioned automatically" callout — GH #1919 (#2014)
  • 56cf38a80 fix(dns): DNS records are managed only under DNS > Zones — GH #1920 (#2015)
  • a596617fa fix(services): restarting nginx from the panel no longer cuts off its own request — GH #1992 (#2016)
  • c4a9b08f4 fix(backups): restoring an account backup into an account made on this server keeps its domains — GH #1993 (#2018)
  • 0c1e7196b fix(security): restoring an uploaded backup can't write outside the target account or restore admin-level settings — GH #1993 (#2019)
  • 1d55a8bff GH #1993: uploaded account backups stay on the server, listed in Backups with a retention choice (#2020)
  • d4f50d572 GH #1993: a restore attaches mail, grants and apps to the account's own domain, mailbox or database of the same name (#2022)
  • 0938c607a fix(update): re-render the AppSec config after the migrations, not before (#2024)
  • 700e1d728 fix(backups): a restore reports an app install or cron job that is already there as skipped, not "Error 1062" — GH #1993 (#2027)
  • d5dd5db1d fix(security): restoring an uploaded backup no longer imports the sign-in identity it carries — GH #1993 (#2026)
  • 6452c698e fix(backups): a domain restored onto another server gets a new SSL certificate instead of staying on plain HTTP — GH #1993 (#2028)
  • d49ccfcd7 feat(backups): a restore from an uploaded backup shows its progress by step — GH #1993 (#2021)
  • aad13631a fix(ssl): tenants no longer see Renew and Retry buttons that always fail with 403 (#2030)
  • f464bdf5b Creating an account that reuses an existing sign-in identity sets that identity's password (#2031)
  • 630a244cb fix(backups): a restore recreates each database user's MariaDB account, grants and password — GH #1993 (#2023)
  • 4cb0f6643 GH #1993: a restore brings back the domains' custom DNS records (#2025)
  • 572cee827 fix(security): restores and migrations re-own a tenant's files without following a link swapped in mid-walk (#2033)
  • 13904d8bb fix(backups): a restore from an uploaded backup follows no symlink outside a home or an app's data — GH #1993 (#2035)
  • 44cf076f3 GH #1993: a restore into an existing account keeps its home directory's owner and mode, so its sites stay up (#2034)
  • a1910b390 feat(backups): a restore from an uploaded backup adds only what the account is missing, unless "Overwrite existing items with the backup" is checked — GH #1993 (#2036)
  • 2d5799d8e fix(security): a restored domain's PHP limits reach its web server config only when the PHP settings page would accept them — GH #1993 (#2037)
  • 355b5b762 fix(security): a PHP pool restored from an uploaded backup keeps to the limits of the PHP pool page and the account's package — GH #1993 (#2039)
  • 04bc4f22e fix(security): an uploaded backup's database users get access only to a database that holds nothing but the backup's data — GH #1993 (#2040)
  • bca3e4dcc feat(backups): with Overwrite on, a restore from an uploaded backup gives the account's existing mailboxes and database users the backup's settings — GH #1993 (#2042)
  • ef762e730 fix(security): a domain restored from an uploaded backup takes a PHP limit only when the account's package lets a tenant set it — GH #1993 (#2043)
  • b18cc13de GH #1993: with Overwrite, a restore updates the account's own PHP pools (#2044)
  • 90058cb48 fix(installer): a failed download is tried once more over IPv4 — GH #2041 (#2045)
  • 7201968fb GH #1993: with Overwrite, a restore updates the account's own domains (#2046)
  • c09134876 fix(mail): a mail domain's Forwarders and Shared Folders tabs list that domain only — GH #1997 (#2047)
  • ee9ae6e19 GH #1993: a restore recreates each PostgreSQL database user's role and grants (#2048)
  • 2967fe00c GH #1993: a backup job restored into a recreated account rebuilds its panel rows (#2049)
  • fc6b75727 GH #1993: a restore from an uploaded backup checks the backup against this server first (#2050)
  • 7ddb95a19 GH #1993: restored PostgreSQL objects belong to the account's database user, not to postgres (#2051)
  • 0de6deb92 test: the pdnsutil and certbot test stubs reuse one directory per user (#2052)
  • 86dc6b7c6 GH #1993: a restore from an uploaded backup brings FTP passwords back into an account it created (#2053)
  • 287eb77b6 GH #1993: a restore from an uploaded backup brings SSH keys back only into an account it created (#2054)
  • 101abcfdc fix(ftp): an account restored under the same username gets its FTP subaccounts on the next pass (#2055)
  • 5eb4bd0b8 GH #1993: an account restore brings back each domain's nginx rules, redirects and web settings (#2057)
  • 11748d69a GH #1993: an account restore can bring the backup's SSL certificates back (#2060)
  • 3446c131d chore(docker-apps): n8n 2.42.2, zitadel 4.19.4, and fresh openemr and peertube digests (#2061)
  • f4a82c47b GH #1956: Docker app Update never crosses a major the app can't take in place (Gitea 28, Nextcloud 35, Odoo 20) (#2062)
  • 925d799be GH #1993: a restore brings back a domain's PHP settings, mail settings and web domain aliases (#2063)
  • 069f565f5 fix(mta-sts): deleting a domain removes its MTA-STS vhost, so nginx -t keeps passing (#2064)
  • b3f3b9ad7 fix(security): a box without CrowdSec no longer gets an agent call and a warning on every admin request (#2065)
  • 05a53bea4 GH #2001: a failed AppArmor mode switch says why, and apparmor-utils gets installed (#2066)
  • 25c662737 GH #2003: a tenant can flush its own Redis keys (panel-run, scoped to its prefix) (#2067)
  • 2b340336b jabali-cache 1.2.0: flushes bump a generation counter; jabali update syncs the bundled plugin (#2068)
  • f7b043912 WP cache: the panel cleans up old cache generations; disabling or deleting a site removes its Redis user (#2069)
  • ca85d4cb1 WP cache: a site's Redis user no longer gets SCAN (least privilege) (#2070)
  • 83cac19de GH #2001: jabali-sendmail gets its own AppArmor profile file; an update keeps each profile's mode (#2071)
  • 0b12ddea7 GH #2056: the Modules card shows why a module install failed, and Mail waits for DNS (#2074)
  • 3e18489b9 GH #2056: website mail can go out through a smarthost (#2075)
  • d094b7726 GH #2004: the update hands objects earlier restores left to postgres to the database's user (#2076)
  • b69ff5f3f docs: a mailbox's password is changed in the panel, not in webmail (GH #2072) (#2080)
  • 3eb1dbe8f Mail: app passwords and API keys follow the panel's password change, disable and suspend (#2081)
  • 4939ccd7c Mail: the spam filter thresholds are set in the panel, and an update keeps them (GH #2017) (#2083)
  • 1ddf4c5a9 Mail: a mailbox can trust senders, so their mail stays out of Junk (GH #2017) (#2084)
  • 1f7c8be9f Agent tests no longer depend on the free disk space of the machine (#2085)
  • eb9fb043c Disk usage matches df: blocks reserved for root are not counted as used (GH #2029) (#2086)
  • ba855d772 UserPHPPerformanceCard test: no toast can render after the test environment is gone (#2087)
  • ab6619fab GH #2032: a tenant can use their own Cloudflare token for DNS-01 (#2088)
  • 44b690612 GH #2032: users save their own Cloudflare token on the SSL page (#2090)
  • f63af486c GH #1249: admin Dashboard takes the community-mix layout (Option 7) (#2092)
  • 632823b1a GH #2082: mail. gets its Let's Encrypt certificate when the site and DNS are elsewhere (#2094)
  • 3462ba9fb GH #2082: mail. keeps answering Let's Encrypt with webmail off (#2095)
  • 731e9a9d0 GH #2082: mail apps keep autoconfig, calendars and contacts with webmail off (#2096)
  • 98ee194de GH #2093: jabali says it must run as root, and names each cron job's account (#2097)
  • 1bfd30b45 GH #2093 follow-up: deleting or disabling a cron job stops its timer (#2098)
  • f8dddcbb4 GH #1993: a restore brings back every PHP pool of the account, and an account with no email (#2099)
  • d38e9d45c Deleting an account no longer fails when its PHP pool is started again mid-delete (#2100)
  • 48b541c53 GH #2101: the Modules card says when it couldn't read the settings (#2102)
  • 1bcd29be7 docs: OPcache and JIT are set on the account's OPcache & JIT tab (GH #2091) (#2103)
  • d5aca63d6 GH #2058: Reset password also sets the SSH/SFTP password and the stored hash (#2104)
  • bb315e035 GH #2077: a destination test answers instead of the connection closing (#2105)
  • a6aadc5fc GH #2079: AIDE rebuild works, checks and rebuilds run in the background, and big reports aren't read as clean (#2107)
  • ca1c2c23e GH #2059: the Redis socket works from the SSH shell and cron jobs (#2108)
  • d85d79c03 GH #2056, #2101: turning DNS on no longer runs jabali update and breaks PowerDNS (#2109)
  • bd735ff96 Rotated install logs are deleted after 8 weeks (#2110)
  • 2d628745c GH #2089: backup downloads are named for the account and time, and the list names the account and destination (#2111)
  • 8bf71c268 GH #1993: a restore from an uploaded backup converts an admin's nginx directives to rules (#2112)
  • 4ac415c4f GH #2101: turning DNS on works when an earlier update masked PowerDNS (#2113)
  • ee7f8fbf8 GH #2077: a failed connection test keeps its reason behind Cloudflare (#2114)
  • c382c464f Errors that say why keep their reason behind Cloudflare (GH #2077 follow-up) (#2115)
  • 3d0ef7efa An agent failure keeps its message behind Cloudflare (GH #2077 follow-up) (#2116)
  • 829eb326b GH #2077: an SFTP-only account can be a backup destination (#2117)
  • 6cf080a33 Backup key repair keeps a repository path one argument on the backup host (#2118)
  • 4f5ed1c51 GH #2101: the Modules card follows an install to its end (#2119)
  • 38b3b5a41 GH #2078: acknowledge an AppArmor profile kept in complain mode (#2121)
  • a6f8a6833 CLI: a declined confirmation fails instead of exiting 0 (#2123)
  • f05b25d81 GH #2122: Run now leaves a long job running instead of killing it (#2124)
  • 5fc0c9853 Root cron jobs run, and Run now, the log and Last run reach them (#2125)
  • 20825883d Discussion #1032: Nextcloud one-click PHP app (#2127)
  • addd6fa7f One-click installers: pass credentials on stdin (#2128)
  • 1e251239b AppArmor: tenant PHP gets no system D-Bus (#2129)
  • 2013ea77a Python apps: no system D-Bus (#2130)
  • df5f979ab One-click installs refuse admin credentials the app would reject (#2132)
  • cf569dd9a panel-ui tests: toasts unmount with the test; DNS-template wait (#2133)
  • 953a16886 App snippets pass PHP to the domain's pool, not the default one (#2134)
  • 596efc187 Deleting a PHP pool removes that pool on the host, not the default one (#2135)
  • 9ad8cf8dd Server Settings → Nginx reaches nginx on every panel start, not only on a save (#2136)
  • 8c40f7cf5 "Show nginx version: off" reaches nginx when nginx.conf has it commented out (#2137)
  • d31440e09 A Moodle install's course files are the account's alone (#2138)
  • 3974bef45 Existing Moodle installs: the web server's group loses its way into the course files (#2139)
  • bb322d97a jabali appsec exclusion list says which rows an app install manages (#2140)
  • cb2bed281 GH #2126: the panel hostname gets no per-domain certificate (#2141)
  • 3e0b0cc74 GH #2131: secondary DNS servers (AXFR + NOTIFY), and NOTIFY that is actually sent (#2142)
  • f9c37f252 GH #2120: a tenant cron job's output shows on a volatile journal (#2143)
  • aec341cb9 Fix logo display for light and dark modes (#2106)
  • 93ffecf28 GH #2120: a tenant cron job's last line shows when it has no newline (#2144)
  • 5b30d15c2 Login: a session with no panel account offers to sign out (#2145)
  • 34a787f20 Opt-in "Ask Jabali" AI support chat for the admin and client panels (#2146)
  • c2bc8d4da Updates page: cards stack in two columns, no hole under the shorter card (#2147)
  • 91a471878 Support chat: load the one hosted Ask Jabali script tag (#2148)
  • cfe03cbde WP cache: stage the bundled plugin by file descriptor, inside the account's home (#2149)
  • 1b51e0088 jabali settings set: support_chat_admin and support_chat_clients (#2150)
  • 3bce4e8df docs: fix four claims that contradict the code (#2151)
  • 3831057c2 Docker-app domains get their Let's Encrypt certificate from the shared ACME root (#2153)
  • e2e440445 A docker-app domain's vhost and certificate go with its domain row (#2154)
  • ba71ca39e Remove a Docker app vhost left without its domain row (#2155)
  • 3e90c4b22 Docker apps: never give out a host port that is already in use (#2157)
  • 063f8469a Docker apps: status and domain stay right after a CLI start/stop or an edit (#2158)
  • 3cdfc3008 Python Apps: add sic (GH #2156) (#2160)
  • a7f151d07 Docker apps: a refused domain edit keeps the app's domain; the CLI checks the name (GH #2158) (#2161)
  • a1533c313 Docker apps: a removed Docker app domain takes its mail with it (#2162)
  • d79a84ac0 Docker apps: a deleted app's recreated containers leave no anonymous volumes (#2163)
  • df82ed080 API tokens follow act-as; Redis access refuses admin accounts (GH #2159) (#2167)
  • b37c15f86 One panel clock: server timezone and admin date format for every time shown (GH #2164) (#2168)
  • 45bb8674c Sign-in lands on the dashboard again, not a blank shell (#2169)
  • 7ade464eb Tenants can list and delete their own Redis keys (GH #2165) (#2170)
  • 22b04e8cb Panel SSL: the public DNS check no longer reads /etc/hosts (GH #2166) (#2171)
  • 2956895bb Panel hostname change: /etc/hosts, config.toml and all three doors (#2172)
  • 1084a2fe7 GH #2165: Redis keys per call is a hosting package setting (#2174)
  • 23cc21879 GH #2166: with Let's Encrypt off, Panel SSL regenerates the self-signed cert (#2175)
  • ac3aa612f GH #2166: Panel SSL tab in SSL Manager (#2176)
  • 63cbf323a GH #2166: the mail certificate follows the Let's Encrypt toggle (#2177)
  • 7094b76a6 AppArmor flip-mature: the soak gate reads the audit log and counts complain-mode ALLOWED (#2178)
  • c9904c151 GH #2166: no mail certificate, and no Stalwart start, without the mail module (#2181)
  • 9bf0c9c65 Installer: Docker, Docker apps and PostgreSQL install when chosen; mail needs DNS (#2183)
  • 3281958a4 Fresh install: the panel's mail repair waits for the installer (#2185)
  • 3f30d18b1 Fresh install: start the cache-doctor timer after the panel's first boot (#2186)
  • 510d752e8 GH #2166: with Let's Encrypt off, Panel SSL shows the certificate in use (#2187)
  • e860de823 Docs: install.sh --uninstall exists; say what it removes and what it keeps (#2188)
  • cf535c3ce Nginx: switch to allow proxy_pass to local / private addresses (#2189)
  • feec1052e Database users: one engine account, one panel row, in creates, imports and restores (#2201)
  • dcb988815 HestiaCP import: a PostgreSQL dump is not restored into MariaDB (#2202)
  • 770585630 Bandwidth suspension leaves manual disables alone; docs for bandwidth limits and the CrowdSec blocklist (#2191)
  • 80c41429f Domain statistics: daily traffic and visitors by country (#2192)
  • 38e590dd7 Bot blocking: per-domain "Block AI crawlers", a server-wide blocked user-agent list, admin rate limits (#2193)
  • bbb70bada Usernames with "-": the account can be deleted, and has database users and phpMyAdmin/Adminer (#2197)
  • 0b5d00f3a Docker apps: baseline hardening for admin installs; icc off on the default bridge (#2195)
  • ed11af263 CrowdSec: optional FireHOL blocklists and the admin's own lists on top of the community list (#2194)
  • c54ada336 Docker apps: a generated password never starts with "-" (#2196)
  • 81c63fe5d Docker apps: 16 catalog apps that could never start now install healthy (#2199)
  • e9e51a4a7 Rocket.Chat: MongoDB 8.0, with a one-time copy of an older install's 6.0 database (#2203)
  • 9764b7f04 Kratos: its MariaDB session runs in UTC (#2204)
  • 016396201 Docker apps: a catalog entry can be retired; Bigcapital is (#2205)
  • 50a41cb09 Extra blocklists: whole networks in the agent's own nftables table, plus IPsum level 3 and Spamhaus DROP for IPv6 (#2206)
  • a121fcddc CI: secret scan of each PR's commits (gitleaks) (#2207)
  • 438d1974b CI: fail a PR that adds a vulnerable dependency (OSV-Scanner) (#2208)
  • b103f56a6 CI: static analysis with gosec and Semgrep, advisory (#2209)
  • a706a649e PHP security releases reach the box, and FPM then runs the patched PHP (#2210)
  • 6709a5426 AppArmor: every profile includes its /etc/apparmor.d/local file (#2211)
  • dc495aa84 nginx: refuse a Host header that names another host than the request line (#2213)
  • 7acf9bc97 AppSec exclusions can name a CrowdSec rule (crowdsecurity/vpatch-…) (#2214)
  • ad510d89f WAF exclusions by path never follow a ".." segment out of their prefix (#2215)
  • 9750ada24 Vendor security updates install by themselves, and exposed services run them (#2216)
  • 6324fdad2 Hotfix releases: stable servers get a fix as a prebuilt release (#2217)
  • 17aceeb5e Security advisories reach every server within the hour (#2218)
  • 6760caebe Merge stable 51fd7fc into main's history (#2219)
  • 1367a2c9a Security advisories apply themselves, even with self-update off (#2220)
  • 1242b55d6 PHP errors go to a log file, not to nginx: no 502 on noisy pages (#2222)
  • 46f73d214 Logs page: PHP Errors, a live view of a domain's PHP error log (#2223)
  • ddb038f8b Egress: an app's loopback port opens only for the account that owns it (#2225)
  • 05b8a50d2 Egress: only a Python app's own process answers on its port (#2226)
  • 252d6fc55 ADR-0175: Node.js apps on the Application Manager (accepted) (#2227)
  • 5a97e4851 Agent: an app command changes into its directory as the account (#2228)
  • df2b574c1 Agent: a Python app's build runs in the owner's slice (#2229)
  • e87d8f994 Docs: a Python app's build runs in the account's slice (#2230)
  • e2620e5a8 Python apps: the env file is the same on every apply, so an app with several variables doesn't restart every minute (#2231)
  • a0132ed6f SSL: a domain waiting for proof of ownership says so, instead of offering a retry that can't help (#2233)
  • 35d7d468d Updates: install only releases signed with the release key (ADR-0162) (#2234)
  • 1305de327 Updates: Automatic Updates saves each switch when you change it (GH #2221) (#2235)
  • a4a91a69d Egress: learning mode counts each new connection and lists it as allowed (GH #2224) (#2236)
  • ae219af25 Repair Center: Delete orphans deletes them and lists any site left (GH #2237) (#2239)
  • faa070d4b Updates: the development channel streams main without the release signature (GH #2240) (#2241)
  • 113801c60 View as: the sidebar and the File Manager selection bar stay below the banner (GH #2243) (#2247)
  • 21b8d2661 API docs: pick a section from a list, search every endpoint, and jump works in the admin panel (GH #2244) (#2249)
  • 34e4923d5 Alerts: explanations fold to their title and stay as you left them (GH #2242) (#2248)
  • 2def5d2ce Alerts: the rest of the panel's explanations fold too (GH #2242) (#2250)
  • b3343c452 Alerts: an arrow at the end of the title folds and opens them (GH #2242) (#2252)
  • 672330205 API docs: the Personal API Tokens page is one card laid out like Notifications > Events (GH #2244) (#2253)
  • 39a001441 Backups: Cancel stops the backup, and a backup that failed stops showing as running (GH #2245) (#2254)
  • 62505cab2 Backups: an SFTP destination that is down fails in 30s, stall bounds follow the agent's limits, clearer 401 for the wrong token (GH #2245) (#2255)
  • dba7be0b9 Backups: an SFTP destination stored as a bare URL gets the ssh timeouts too (GH #2245) (#2256)
  • b783b3c26 Backups: an account's own schedule opens in Schedules with its type and interval, and saving it keeps them (GH #2246) (#2257)
  • b7a309301 Backups: admins add an account's own schedule, admin schedules run hourly (GH #2246) (#2258)
  • f763c95a4 API tokens: pin admin backups out of the backups scope (GH #2245) (#2259)
  • af72ba1a5 Backups: every account schedule in the list says what it backs up, Full account too (GH #2246) (#2260)
  • dc62527cb Backups: Cancel answers once the backup has stopped, tenants can cancel their own (GH #2245) (#2263)
  • cda44c76a Pages: Logs, Cron Jobs, Databases and Migrations are laid out like Security and Backups (GH #2251) (#2264)
  • ff571a3f2 Cache: the hourly check and the cache cleanup idle on a server with no cached WordPress site (GH #2261) (#2265)
  • 11c46a1b1 Pages: PHP Settings keeps its tabs in one card; Disk Usage, Python Apps, Mail, Notifications and Cache get page headings (GH #2251) (#2268)
  • f8d64391f Repair Center: Auto-Repair fixes docroot permissions, and a row it leaves says why (GH #2266) (#2269)
  • 5a24ed6cb PHP Defense: disabling a rule sticks, Recent incidents pages and counts right, shows the PHP version, and can be cleared (GH #2262) (#2270)
  • 7bdc3198e Firewall: a disabled firewall shows as disabled, so it can be enabled again from the page (GH #2271) (#2272)
  • dd17d4f0a Mail: a Can send as grant is kept only once the mail server takes it (GH #2267) (#2274)
  • b48a6a7a3 Free hostname: a claim retried from the same server gets its label back (#2275)
  • 79e96322e Free hostname: claim over IPv4, keep a claim on Back, check in once set up (#2278)
  • 72e62eaf5 Mail: the send-as list reaches the mail server after any mailbox change (GH #2267) (#2279)
  • eab3178c9 PHP Defense: incidents from websites show the visitor's address, not 0.0.0.0 (GH #2262) (#2281)
  • 8a1360b6c Free hostname: preview URLs get a real certificate (#2280)
  • 51e842968 SSL: a failed certbot run is reported as failed, not as the old certificate (#2282)
  • 6ffa1186a Drupal installer: Composer and drush run on the domain's PHP (#2283)
  • 17b2320fd Composer runs: users run Composer for a domain from the panel API (GH #2276) (#2284)
  • 7d06cd82c Composer runs: a Composer tab on each domain, the Composer package switch, and the ssh scope for tokens (GH #2276) (#2285)
  • 9bab34d72 CrowdSec: count SSH login failures only from the SSH server itself (#2287)
  • e4d13410e SSH sandbox: an admin can let PHP Defense blocks from shell and cron PHP reach Incidents (ADR-0177) (#2286)
  • eb1894cdf Git deploy: connect a domain to a Git repository and list its branches through the API (GH #2277) (#2291)
  • 64e345dd1 Outbound address checks refuse more non-public ranges (#2292)
  • d6cc3c95f Migrations: the source host check refuses the same ranges as the panel's other outbound checks (#2293)
  • e88d40058 Git deploy: deploy a domain's branch into releases, with deploy commands and rollback (GH #2277) (#2294)
  • aba73bf56 Composer runs: a domain can name its project folder, shared by several domains (GH #2276) (#2295)
  • f6f3d6f35 Git deploy: a push to the domain's branch deploys it, through a signed webhook (GH #2277) (#2296)
  • 8e2ce2fbd Python apps: a deleted app's unit no longer keeps running on its port (GH #2289) (#2297)
  • 87104e89d Python apps: deleting a domain deletes its Python apps (GH #2289) (#2298)
  • a1e1e7d0c Backups: the schedule drawer tests wait for the drawer's values (#2299)
  • 6ac1eba8e Git deploy: a Git tab on the domain's page (GH #2277) (#2301)
  • b111cbfff Domains: changing a domain's owner deletes its Python apps (GH #2289) (#2302)
  • e1f1f944d Domains: changing a domain's owner unlinks another account's Docker app (GH #2289) (#2304)
  • 4892cd960 Backups: an account's schedules keep their times of day, like cron (GH #2300) (#2305)
  • 27d4c910b Translations: Russian, Romanian and Portuguese strings from Weblate (#2306)
  • 29b395a05 Cron: an account whose plan includes SSH can schedule its own shell scripts (GH #2290) (#2307)
  • 167d61242 Backups: restore any domain's files and see every domain's DNS (GH #2303) (#2309)
  • e9f41eef3 Docs: no internal codes in the published pages; password-reset-confirm slug (#2308)
  • 420d20137 Translations: Russian covers every string in the panel (#2310)
  • e3b3fdac3 Git deploy: a web directory the repository doesn't have is created (GH #2277) (#2312)
  • e48333b0f Translations: Russian strings pass Weblate's checks (#2313)
  • 24802add3 Cron: the command help is a section you open, grouped by command type (GH #2290) (#2311)
  • be4279f2c i18n: Russian, Ukrainian and Arabic counts use their own plural forms (#2314)
  • 1a90a037a i18n: Russian "from N IPs" in the AppSec card inflects with the count (#2316)
  • 98b35e5ec Docs: Directory Privacy pages use a dash where it was lost (#2315)
  • d41b882ab Tests: the restore drawer test clicks a domain until antd takes it (#2317)
  • 5b66762cf PHP: session upload progress is off in every PHP pool (CVE-2026-115560) (#2321)
  • ea4d121be Docs: Ubuntu 24.04 is a supported install target (#2322)
  • 335373a13 Tests: the server_names_hash update test reads each function before matching (#2323)
  • 2e1e40822 Timezones: the lists use current zone names, so India, Ukraine and 16 more can be saved (GH #2320) (#2324)
  • ac170e9d4 Mail: an update keeps a newer Stalwart instead of downgrading it (GH #2318) (#2325)
  • be10d3064 Backups: deleting or auto-removing an account's backup removes it from its own destination (#2326)
  • bf0694a6f Backups: a schedule at the plan's limit removes its own oldest backup instead of stopping (GH #2300) (#2327)
  • 13846c712 README: link the documentation, and list every CLI command (#2330)
  • f43cb4a14 AppSec: rule exclusions by name match paths the way rule-id exclusions do (#2332)
  • 5f2a7c48a Free hostname: the verification code mail carries a Date and a Message-ID (GH #2319) (#2333)
  • 1408badbc Free hostname: the verification code mail says who asked for it (GH #2319) (#2334)
  • 46076c5bc Free hostname: the verification code mail names no domain in its text (GH #2319) (#2335)
  • 565b1d1dd Changelog: one entry per move of stable, from the October cut on (#2336)
  • 8e92d7cb7 Free hostname: the retired 6to4 relay block gets no hostname (#2337)
  • ceb56962e deps: monthly dependency bumps, November 2026 (GH #2338) (#2340)
  • 58d5ed806 deps: panel-ui on React 19 (GH #2338) (#2341)
  • 52b250eea Root terminal: connects on hosts in AppArmor enforce mode, and shows an error when it can't (#2343)
  • 6334cfc8a deps: Go 1.26.10 (GH #2338) (#2344)
  • fd601a7b2 chore(docker-apps): monthly catalog version bumps, November 2026 (#2345)
  • db6d0f4da deps: Bulwark webmail 1.19.1 (GH #2338) (#2346)

#2210 and #2211 are listed because #2219 joined the hotfix line to main's history. They shipped to stable in the Oct 21 hotfix, not in this cut.

Stable releases are signed from this cut on. Servers on 51fd7fc25 take this one without checking a signature, because their build predates the key. After it, every move of stable needs a signature.

What servers do on the first update with it:

  • Migration 000332 turns security auto-apply on, on every server. Hourly signed advisory checks start, and servers apply listed fixes and run jabali update on their own.
  • Migration 000335 deletes duplicate PHP Defense incidents and keeps the first of each. A down migration does not bring them back.
  • Migrations 000311, 000317, 000318 and 000340 fill new columns on existing rows: domains and aliases marked ownership verified, packages website_sends_email = 1, mailboxes password_changed_at, composer runs work_dir.
  • Vendor security updates cover more sources, and nginx, pdns, pdns-recursor and webmail restart once wherever they still run a replaced library.
  • PHP Defense is rebuilt once and every jabali-fpm@ reloads. session.upload_progress goes off in every PHP pool.
  • Webmail downloads Bulwark 1.19.1 (60 MB), swaps it in and restarts once.
  • AIDE tamper alerts may fire after the update.
  • Servers whose wildcard renewal was failing without saying so start logging "acme shared certs: issue failed".
  • Extra blocklists move to the agent's own nftables table. Old CrowdSec decisions from those lists expire within 48 hours.
  • Rocket.Chat installs need an admin to press Update once.

The same entry is in CHANGELOG.md.

Don't miss a new jabali-panel release

NewReleases is sending notifications on new releases.