Skip to content

Changelog

Each release and what changed in it. The panel links here when it sees a newer version. All notable changes to Slipstream are recorded here. This project follows semantic versioning once it reaches 1.0.

  • PHP security fixes now install themselves on Ubuntu 24.04. PHP there comes from ppa:ondrej/php, and unattended-upgrades only applies Ubuntu’s own security fixes by default, so a critical PHP fix (php2shell, CVE-2026-109618 and CVE-2026-109619) never arrived without someone running apt upgrade. The installer now adds the PPA to unattended-upgrades, and switches unattended-upgrades on where nothing has configured it (an explicit “off” is left alone). On 26.04 PHP comes from Ubuntu and was already covered. scripts/e2e-verify.sh now fails when a PHP update is pending, when php-fpm is still running a replaced binary, or when automatic updates do not cover PHP. Servers installed earlier do not get this change. Patch them with apt update && apt install --only-upgrade 'php8.4*', then copy /etc/apt/apt.conf.d/51slipstream-php-ppa from installer/install.sh.
  • Requiring 2FA for admins now covers every route. With require_2fa_admin on, an admin with no second factor was refused on admin-only routes but could still use every site management route (deploy, restore, SQL console, file editor) and the server-wide views. They now reach only the 2FA enrolment routes and sign out, as the documentation already said.
  • Read-only accounts can no longer read secrets through the file manager. They could open wp-config.php (the database password and the connector’s site token) and download the database dump every backup leaves in logs/db-latest.sql, as well as exports in shared/exports/. Every quoted value in wp-config.php except a short list of harmless ones (database name and host, site URLs, paths) is now masked for them, as are .env values. Dumps, exports, archives (a migration upload or a backup plugin’s archive holds both), .git/, .htpasswd, .my.cnf and similar credential files, and private keys are refused. Admins and operators are unaffected.

Released 2026-10-15

  • The release binaries are now built with go1.26.9. The v0.2.3 binaries were built with go1.26.8, not go1.26.9 as its entry below says: the release workflow asked for “1.26” and took whichever patch release the build machine had, and the pin lived only in a local script. go1.26.8 has ten standard library vulnerabilities that Slipstream’s code reaches, fixed in go1.26.9, among them HTTP/2 server crashes and memory and CPU exhaustion (GO-2026-6617, GO-2026-6603, GO-2026-6611). If you run v0.2.3, update.
  • The Go version now comes from the toolchain line in go.mod, which CI, CodeQL, the release workflow and scripts/build-release.sh all read. A release now fails if govulncheck finds a reachable vulnerability, or if any binary was built with a different Go version.
  • The installer waits for apt instead of failing. On a server a few minutes old, unattended-upgrades holds the dpkg lock and the install stopped at step 2. It now waits up to ten minutes and says so (#74).
  • A failed PHP repository is reported as one. On Ubuntu 24.04, if ppa:ondrej/php could not be added, the install carried on and failed later with “Unable to locate package php8.4-fpm”. It now stops with a message pointing at the cause (#74).

Released 2026-10-15

  • Built with Go 1.26. Go 1.25 left security support when 1.27 shipped, so v0.2.2 and earlier were compiled with a toolchain that no longer gets fixes. Release builds use 1.26 (scripts/build-release.sh pins go1.26.9). Correction: the published binaries were built with go1.26.8. See 0.2.4.
  • golang.org/x/crypto 0.58.0, golang.org/x/sys 0.49.0.
  • modernc.org/sqlite 1.60.1.
  • The panel UI is built against React and React DOM 19.3.0.

Released 2026-09-04

  • The panel tells you when an update exists. A banner on the dashboard names the newer version, links to what changed, and offers an Update now button. Nothing installs by itself: the check reports, the operator decides. Before this there was no way to learn a new release existed, and the only control was a button labelled “Check & update” that checked nothing and updated immediately.
  • The changelog is published at slipstreampanel.com/changelog, generated from this file, so every release ships its own notes and the panel can link to them.
  • Two-factor authentication can be required on admin accounts (require_2fa_admin, off by default). An admin can do anything root can do on the machine, so a second factor should be enforceable rather than per-account opt-in. While it is on, an admin without one can reach only their own account and the enrolment routes, so turning it on cannot lock anyone out.
  • The panel can be restricted to known addresses. Its vhost now includes /etc/slipstream/panel-access.conf if present, so allow/deny rules survive the certificate operations that regenerate that file. A firewall rule cannot do this, because the sites share port 443.
  • The settings page could not save. GET /api/settings returns the read-only panel_domain alongside the editable keys, the page sent the whole object back, and the handler rejected it, so Save answered 400 panel_domain is set by the operation that owns it on a form nobody had edited. Every settings change through the UI has failed since that field became readable.
  • The update button had no source. It posted an override that was never saved (update_url is deliberately not writable: it is where root binaries come from) and got “no update URL configured”. It now defaults to the project’s own releases, and the field that could never work is gone.
  • Updates verify build provenance, and fail closed. The checksums are published in the same release as the binaries, so anyone able to write a release could replace both and reach root on every server that updates. GitHub’s signed attestation proves the bytes came from this project’s release workflow at a specific commit and cannot be forged by rewriting the release. A check that runs and fails aborts the update always; a host with no gh to check with is refused unless the caller explicitly accepts that, which is recorded in the audit log.

Released 2026-09-03

  • A deploy silently turned the object cache off. The drop-in is a symlink into shared/, written once at provisioning, and nothing re-linked it into a new release directory, so the first deploy after provisioning left the site with no object cache while the panel went on reporting object_cache: true. Found on a live server where a site had been in that state for two weeks; wp cache flush even reports success, because there is nothing to flush. A deploy now makes the release agree with the site’s recorded setting, in both directions, so installs that already lost their drop-in repair themselves on the next deploy.
  • Enabling the object cache started a Redis daemon that nothing used. The endpoint called ensureRedis() whenever it was asked to enable caching, but the request carries no backend and the agent only selects Redis when explicitly told to, so every call installed the APCu drop-in and the daemon sat there unused. On the 1 GB servers supported since 0.2.0 that is real memory spent on nothing, and the progress message described work that was not happening.
  • The benchmark suite behind docs/benchmarks.md is now in the repository rather than described by it: bench/wrk-suite.sh runs every published scenario, bench/cpu-parity.sh is the hardware check the method depends on, and bench/README.md documents the one-box A/B/A method actually used. The figures were re-measured against CloudPanel CE 2.5.4 on 3 September 2026 and now report the timeout count beside each percentile, because wrk drops timed-out requests from the latency distribution and a panel that fails to answer otherwise posts a better p99 than one that answered slowly.
  • The end-to-end suite is 55 checks, verified green on both Ubuntu 24.04 and 26.04. Its migration fixture had been writing a database dump into a directory the site user cannot write to, with stderr discarded, so three checks failed for one invisible reason.

Released 2026-09-03

  • panel-api recover-admin. Losing the panel password meant losing the panel: no reset, and the only password endpoint needed a session you could not get. Run from the host, it lists accounts, resets one, creates the first admin when none exists, and clears 2FA only when asked. The password is generated and printed once rather than accepted as an argument, a reset revokes that account’s sessions, and every use writes an audit event.
  • 1 GB servers are supported. Preflight refused anything under 1500 MB and the docs asked for 2 GB, which undersold the lighter stack the README compares against CloudPanel.
  • The object cache drop-in installed itself over a segment it could not reach. The guard at the top of object-cache.php was a bare return above the function and class declarations, and PHP hoists those, so it never prevented anything. Under apc.enable_cli=0 every wp-cli cache call returned false, wp cache flush died, and on a server with no APCu extension the first cache write fatalled the site. When APCu is unusable the drop-in now behaves like WordPress’s own request-scoped cache.
  • A flush from wp-cli did not reach PHP-FPM. The two hold separate APCu segments, so a theme activated from the command line kept serving the old one until PHP-FPM restarted. Keys now carry an epoch from a file both processes read, and a flush writes a new one.
  • A deploy dropped the cache connector, and drift could not see it. installConnector ran at provisioning and on import but never on DeployRelease, so a build artefact from CI promoted a release with no mu-plugin in it.
  • An imported site dropped the cache connector, for the same reason: the archive carries the source host’s wp-content, which has no connector in it.
  • Block theme edits served stale pages. Changing a header, footer, template, global style or navigation in the Site Editor left every interior page on the old version until its TTL expired, up to 24 hours on the Maximum profile. Those changes now purge the whole site.
  • Guard measured the box rather than the change, which undercut the promise a promotion is gated on. The site budget is now divided per site and migration is covered.
  • Password changes and the panel domain gave no feedback. The panel did the work and did not say so, so a saved domain looked like it had failed.
  • The panel silently fell back to a system font. Vite inlined a 2,028 byte JetBrains Mono subset as a data: URI, under its 4 kB limit, and the API’s own font-src 'self' rejected it.
  • CSS side-effect imports now typecheck, via Vite’s client types.
  • ShellCheck comes from the runner instead of apt. One CI run sat in apt-get install for over two hours on 19/08 and had to be cancelled by hand, blocking a dependency PR that was green.
  • Dependencies: React 19.2.8, react-router-dom 7, TypeScript 7, Vite 8, @vitejs/plugin-react 6, golang.org/x/crypto 0.55.0, modernc.org/sqlite 1.56.0.

Released 2026-07-26

  • Build provenance attestations. Every released binary now carries a GitHub-signed statement proving it was built by this repository’s release workflow at a specific commit. Verify with gh attestation verify panel-agent --repo Sulaiman-Dauda/slipstream; the installer checks it automatically when the GitHub CLI is present.
  • CodeQL analysis for Go and TypeScript.
  • ROADMAP.md, and a written review standard in .github/REVIEW_GUIDELINES.md.
  • All GitHub Actions pinned to immutable commit SHAs.
  • Every workflow declares explicit permissions.
  • Dependency updates grouped by minor/patch, with majors kept separate.
  • The documentation-site rebuild required a personal access token that was never configured, so it failed on every docs change.

Released 2026-07-26

  • Installer progress display: a spinner per phase, a tick with its measured duration, and a full log at /var/log/slipstream-install.log. Degrades to plain lines when not attached to a terminal.
  • https://get.slipstreampanel.com as the install URL, with /vX.Y.Z to pin.
  • Installer output no longer interleaves apt, systemctl and nginx messages with the progress display; a failure now prints the tail of the log.

Released 2026-07-26

First tagged release. Everything below was built and verified before this tag; the version number simply makes it installable with the one-line installer, which fetches binaries and their SHA-256 checksums from the release assets.

Pre-1.0. Slipstream is a release candidate: it works end to end on both supported Ubuntu releases, but has not yet carried production traffic and has had no external security audit.

  • Ubuntu 26.04 LTS support alongside 24.04, with PHP 8.5, nginx 1.28 and MariaDB 11.8. Both releases are verified on every change.
  • HTTP/3 (QUIC), enabled automatically where the OS ships an nginx built with ngx_http_v3_module and left off where it does not. Verified with a real QUIC client, not just by reading the config.
  • Log rotation for per-site nginx and PHP logs — daily, 14 days, compressed. Without it a busy site fills the disk and takes every site on the box down.
  • Performance tuning taken from a head-to-head benchmark: larger nginx worker connections and FD limits, kernel accept-queue tuning, open_file_cache, and FPM self-heal (emergency_restart), backlog and FD limits.
  • --version and --help on all three binaries.
  • Full documentation set under docs/.
  • The woocommerce site type never installed WooCommerce. It provisioned plain WordPress and set a WooCommerce-specific constant for a plugin that was not there, leaving /shop, /cart and /checkout returning 404.
  • A fresh install left nginx not listening on 443, so the setup URL the installer printed could not be reached.
  • Security headers were absent from every real response. nginx inherits add_header only when the inner block declares none, and both the PHP and static locations declared their own — so X-Content-Type-Options, X-Frame-Options and Referrer-Policy were silently dropped sitewide.
  • Stale object cache after out-of-band changes. Restores, updates and migrations left the live site serving pre-change options and rewrite rules, because an FPM reload does not clear APCu. The panel now flushes the affected site’s cache through its own pool.
  • /tmp removed from every pool’s open_basedir; it is shared between tenants and was not needed.
  • Requesting an uninstalled PHP version now fails immediately with the available versions, instead of failing deep in provisioning and leaving a half-created site.
  • Deleting a site now removes its log directory.
  • Cross-tenant database access (high). The SQL console ran as the MariaDB superuser with the site’s database only as the default schema, so an operator scoped to one site could read and write every other tenant’s database by qualifying the table name. Queries now run as a throwaway account granted on exactly one database.
  • Login source addresses were never recorded. Every login was logged as 127.0.0.1 because the API sits behind the panel’s own nginx, defeating the audit trail and collapsing rate limiting onto one shared bucket. Forwarded headers are now trusted from loopback only.
  • File-manager symlink race. A path was validated and then opened separately, as root, in directories the tenant owns. Reads and writes now use openat2(RESOLVE_NO_SYMLINKS) and ownership is applied through the descriptor.
  • TOTP replay. Codes were valid for the whole ±1 step window with no record of use; the accepted step is now recorded per user.
  • Licence is AGPL-3.0.
  • pm.max_children remains sized from available memory. Measured on a 2-core box, raising it makes uncacheable throughput worse (6 workers 5.54 req/s, 10 → 4.29, 16 → 4.24, 0% CPU idle throughout).
  • Kernel TLS was implemented, measured at a 28% loss on cached throughput, and removed. The reasoning and numbers are kept in installer/install.sh so it is not re-added.