Changelog

What's new

Every feature, fix, and improvement shipped in sh0. 29 phases, 2 security audits, 90+ engineering sessions -- built from the ground up in Rust.

v1.7.1 Stable

v1.7.1: Dated Licences, Automatic Renewal

September 9, 2026

A licence now carries an expiry date matched to the subscription that paid for it, and a daily pass fetches and installs the renewed key on its own. Plus DNS-01 for servers behind a CDN, and four routing and lifecycle defects found while deploying real services.

230+

API endpoints

103

MCP tools

700+

Tests passing

180

Deploy templates

Development timeline

From first commit to production-ready -- the complete build history

v1.7.1: Dated Licences, Automatic Renewal

September 9, 2026 | v1.7.1
  • A licence now carries an expiry date matched to the subscription that paid for it. Monthly subscriptions were emitting perpetual licences: one month paid granted lifetime access.
  • A daily pass fetches the renewed key, verifies its signature and replaces the stored one -- nothing to do when a subscription renews. A cancelled subscription revokes the key and the plan drops at the next pass.
  • DNS-01 challenge per domain: a server behind a CDN that terminates TLS (Cloudflare orange cloud) now gets a certificate without disabling protection, instead of burning its Let’s Encrypt budget in a loop.
  • Routing and lifecycle: a restart that changes the published port now updates the Caddy route; an app listening on a port other than the configured one is no longer reported healthy while returning 502; a redeploy no longer leaves the old container running beside the new one.
  • Resource limits follow the hardware rather than the licence on self-hosted installs, and SSL status refreshers can now demote a certificate instead of only promoting it.
  • v1.7.1 is required for any licence purchased after September 9, 2026. Earlier versions cannot fetch a renewal and stop at the end of the first period.

v1.7.0: A Licence Is a Signed Document

September 7, 2026 | v1.7.0
  • A licence key is now an Ed25519-signed document that carries its own plan, not a string whose prefix was trusted. Any unrecognised string used to activate Pro.
  • Verification is offline: the public key ships inside the binary, so an air-gapped server validates its licence without calling anything.

v1.6.29: Dogfooding Fixes -- .dockerignore Parity, Crash Loops Named, Worker Apps Without a Port

September 7, 2026 | v1.6.29 · v1.6.30
  • The build context matched a .dockerignore pattern without a slash at every depth (gitignore semantics); Docker anchors every pattern at the root and only **/ traverses. A project ignoring *.md and importing src/lib/resources/x.md built on Easypanel and failed on sh0. sh0 now applies Docker's rules; its own generated list spells **/ out.
  • The health loop only read State.Running. A container exiting at startup under unless-stopped is restarted within a second, so it looked running, the TCP probe failed for 300 s and the log said timeout. The loop now reads RestartCount and Restarting, fails within seconds with "crashed N times (exit code X)", stops the loop and names an OOM-kill. v1.6.30, the same day: the exit code and OOM flag are read from a stopped snapshot, since Docker resets both the moment it restarts the container -- an OOM-kill was being reported as "exit code 0".
  • Image builds used rm=true, which removes intermediate containers only on success; every failed build leaked an Exited (1) container under a random name that no sweep could see. forcerm=true removes it in both cases.
  • Apps have a kind: web (unchanged) or worker. A worker resolves no port, publishes none, gets no Caddy route and no preview domain, and is healthy once it stays running without a restart for 15 s. Celery, BullMQ and cron-style processes deploy without being killed by a probe nothing could answer. The dashboard forms expose the choice.

Hotfix v1.6.28: the API Froze After Two Redeploys, and Sinatra Answered 403

September 6, 2026 | v1.6.28
  • The preview proxy for *.sh0.app addresses kept a DashMap read guard alive across the proxied request -- hours for a WebSocket. The cache purge added at redeploy then blocked a runtime thread, a waiting writer blocked new readers, and two redeploys under traffic froze the API with hundreds of connections queued. The cached entry is now copied out before anything is awaited. Do not install v1.6.27 in production.
  • The Ruby template never set RACK_ENV, so Puma started in development and Sinatra 4's host authorization returned 403 to every public host while the health probe on localhost passed. The production stage now sets RACK_ENV and RAILS_ENV to production.

Nineteen Fixes Before Production: Build Context, Clear-Text Login, Broken Templates, and an Installer That Brings Docker

September 6, 2026 | v1.6.27
  • Build context: a single path longer than 100 bytes made the tar archive fail (GNU headers have no prefix field); long paths now travel as GNU long links. sh0 also replaced the project's .dockerignore with its own list, which excluded every *.md file: the project file is now read and completed, nothing is imposed when you bring your own Dockerfile, and the number of excluded files is logged.
  • Authentication: session and refresh cookies carry Secure whenever the request arrived over HTTPS, port 80 answers a 308 to https:// for every managed host (ACME challenges still pass), the installer warns that the first login by IP is unencrypted and shows how to put a TLS domain on the panel, and the fictitious [email protected] default credentials are gone from the installer, the panel and the server banner.
  • Stacks: Go builds any go.mod version (GOTOOLCHAIN=auto on a current image) and finds its module root when the binary lives in cmd/<name>; Ruby's Bundler configuration reaches the production stage and Puma binds once; .NET derives its entry assembly at build time, ships curl for its health probe and runs on .NET 10 with roll-forward. A redeploy or a scale no longer serves 502 on *.sh0.app while the preview cache expires.
  • Platform: unknown license strings are rejected instead of activating as Pro, the stack-quota message names the project holding the slot, `sh0 users list` reads the local database on the server box, the cloud proxy routes /api/* to your application instead of answering for it, the site's Active Servers tile reads real heartbeats, and get.sh0.dev installs Docker (or fails early with the exact command) before writing the systemd unit.

Two Fixes Before Dogfooding: Stopped Containers Survive Cleanup, On-Demand Certificates No Longer Need an E-mail

September 5, 2026 | v1.6.26
  • The hourly Docker cleanup removed every stopped sh0-managed container, including the container of an application that had just crashed: exit code, logs and the Start button were gone with it. A stopped container is now kept as long as any database row still references it; only true orphans are removed, and the step is skipped rather than run blind when the database cannot be read.
  • On-demand certificate issuance was only armed when an ACME e-mail was configured, although the log said it was armed either way -- on a default installation Caddy still issued at route load. The on-demand catch-all policy is now emitted whether or not an e-mail is set; the e-mail is simply omitted from the issuer when there is none.

Four Lots Ship Together: Instance Lock, On-Demand Certificates, and an Honest SSL Status

September 5, 2026 | v1.6.25
  • `sh0 serve --port 9001` next to a running instance used to install packages, migrate the database and stop the running Caddy before discovering it could not start. An advisory `flock` on `sh0.pid` now refuses the second instance immediately, names the PID that holds the directory, and touches nothing. A crash leaves the file behind harmlessly: the lock dies with the process, so the next start proceeds.
  • Certificates are issued on demand, at the first TLS handshake, instead of when the route is loaded -- so a domain whose DNS has not arrived yet no longer burns a Let's Encrypt attempt. A domain stays `pending` until someone connects, and the badge now says why (`ssl_pending_reason`). `ssl_expiry` was always `null`; it is now read from the certificate on disk. An `active` domain whose certificate vanishes goes back to `pending` after two consecutive checks.
  • A crash-looping container reported `running` because only the Docker state was read; the restart count and exit code are now part of the verdict. Docker streaming paths had unbounded in-memory buffers -- sh0 was OOM-killed at 7.3 GB in isolation -- and are bounded. The service of a Compose stack that receives the public route was picked by hash-map iteration order and could change between deploys; it is deterministic now.
  • Status-page subscriptions were dead by construction: the confirmation e-mail was never sent while the API claimed it was. The form now only appears when SMTP is configured, unconfirmed subscriptions are capped at 200 per domain, and unconfirmed rows are purged after 48 hours instead of 7 days.
  • "Run now" on a backup schedule ran an ad-hoc backup whose archives escaped every retention rule; it now executes the schedule itself. `restore-external` refused a database server that had never been provisioned but accepted one that was stopped; a stopped server is refused synchronously too.
  • The Compose importer required `image:` and silently ignored `build:`; it now refuses a `build:` service with a message that says what to do. `--acme-email` is persisted like `--panel-domain`, so renewals stop relying on an unverified fallback. And the API no longer announces `panel_domain_ssl: "provisioning"` when no provisioning is taking place.

Laravel Deploys, Compose Speaks Long Form, and the Port Stops Getting Lost

September 3, 2026 | v1.6.23 – v1.6.24
  • Every Laravel app was built as Node. The detector returned at the first `package.json`, and modern Laravel always ships one because that is how Vite builds assets, so the container died on `npm start` -- a script no Laravel `package.json` contains. PHP now wins that tie when the project carries real evidence: a framework in `composer.json`, an `artisan`, or a `public/index.php`. A Node project that merely vendors a PHP library stays Node.
  • Making Laravel detected was not the same as making Laravel work. The `package.json` now becomes an asset stage inside the PHP image, Composer installs in two passes because `artisan` does not exist yet at the first one, an `APP_KEY` is minted on the first deploy and stored encrypted rather than baked into the image, and the session, cache and queue drivers default to the ones that need no database. That last one was found by deploying to the demo box and getting a 500, not by reading code.
  • Compose files using the long form for `ports` and `volumes` failed to parse -- `invalid type: map, expected a string`. Both keys now accept either form, bare integers included, and `read_only: true` becomes a genuinely read-only mount. A long-form host path is still refused, but the refusal now names the policy instead of a serde type error.
  • On the Docker image path the resolved port was used to publish the container, then thrown away: an app deployed without a port reported `port: null`, and any domain attached to it routed to 3000 while the container listened elsewhere. The port is persisted on every path now, and the test added does not check the function -- it checks its callers, so a sixth deploy path that forgets will fail the suite.
  • The default branch was the literal string `main`. sh0 never asked the remote, so `laravel/laravel` (on `13.x`) and every repository still on `master` failed at step one with a libgit2 message. An empty branch now means the repository's default: the clone resolves the remote HEAD, writes the name it found onto the app, and a branch that does not exist is reported by naming the ones that do.

Guards That Never Ran: CSRF, Rate Limiting, and Ten Broken CLI Commands

September 3, 2026 | v1.6.22
  • `sh0 push` could not deploy at all. Eleven CSRF exemptions were written with an `/api/v1/` prefix that axum had already stripped, so the guard matched nothing and `/apps/upload` and `/auth/logout` answered 415. The exemptions now match the paths the layer actually sees; a deploy through `sh0 push` was measured at 25.7 s.
  • `sh0 login` failed with `Unexpected response format`. The command still deserialized a token the API no longer returns -- it sets HttpOnly cookies and answers with the CSRF token alone. Login now authenticates, clears the second factor when one is set, mints an API key and persists it; the key is named after the user, the machine and the date.
  • The global rate limiter was inert, and the defect was measured on the public demo instance itself before the fix: 1100 reads, 1100 answers, not a single 429, against an advertised 1000 reads and 500 writes per minute. Same root cause -- the layer is mounted inside the `/api/v1` nest, so its `/api/v1/...` paths never matched. This is a security fix.
  • `X-Forwarded-For` is trusted only when the request arrives from a loopback or private peer, which is what sh0's own Caddy child process is. Compatibility note: an installation fronted by a proxy on a public address -- Cloudflare and the like -- now counts all of its traffic in a single bucket, for lack of a declared proxy list.
  • Eight `list` commands were broken -- `database-servers`, `auth-servers`, `realtime-servers`, `function-servers`, `mail`, `file-storage`, `uptime` and `nodes` all died on `Failed to parse paginated response`. `sh0 templates info` and `sh0 db connection-info` failed on field names that had drifted server-side. The startup banner also announced systemd on macOS, where the installer registers no such service; it names launchd now, and stays silent when the server already runs under a LaunchAgent.

Eight Deploy Cases Played on a Real Machine, Three Walls Found

September 2, 2026 | v1.6.21
  • This release comes from a full deploy smoke matrix played end to end on a real machine -- eight cases, not a code review. Six pass: generic Node, Next with pnpm, a static site, an arbitrary Dockerfile, and the PostgreSQL and Redis templates. Two failed and stay open: Laravel and docker-compose.
  • The false green, and the worst of the three. The health check probed `docker-proxy`, which completes the TCP handshake whatever the container's own health, so a dead application was declared healthy. The probe now returns a three-valued state, and it is its outcome that decides rather than its failure. Measured: an `nginx:alpine` with no HEALTHCHECK on a deliberately wrong port now fails after 304,738 ms, where v1.6.20 called it green in under a minute.
  • Static sites: nginx died in a loop on its pidfile. The `chown` had not followed the pidfile's move to `/tmp/nginx`. Fixed, plus a test that checks each writable path individually -- the existing test only looked for the substring `chown` and stayed green throughout the episode.
  • The port typed in silently overrode the image's `EXPOSE`. sh0 now reads `EXPOSE` when no port is given, and warns on disagreement, across all five deploy paths.
  • pnpm. Dependencies with build scripts were no longer built, and pnpm pruning failed twice. Acceptance criterion met: `ts-nextjs-tailwind-starter` builds in 211 s and serves a 200.
  • 804 tests passing, `clippy --workspace --all-targets -- -D warnings` green.

Retention That Spares Manual Backups, and Restores That Reach Standalone Servers

August 31, 2026 | v1.6.20
  • Retention no longer touches manual backups. A schedule now counts and prunes only the archives it created; anything triggered by hand is immune by construction. Migration 057 links each backup to its schedule and adopts existing rows only when the attribution is certain -- a source with several schedules keeps its older rows untouched rather than guessing. This is the engine change v1.6.19 announced as future work.
  • The Schedule modal lists standalone database servers. The Database card counted them, the selection list did not, so a server visible in the count could not be scheduled. Both now show the same population -- app databases, services with an embedded database, and standalone servers marked with a DB Server badge -- and stepping back returns to the right list.
  • An external restore can target a standalone database server, whole server or single database. The whole-server path keeps the databases already present on the target, the guarantee introduced in v1.6.19. Three cases are now refused with a clear message instead of failing quietly: a server with no running container, a database name starting with a dash, and an unknown target.
  • A backup's restore state is written the moment its row is created. External restores reported an empty status until the first update -- a gap dating back to the original insert, surfaced by the new tests. The schedule-edit confirmation is also translated in all five dashboard languages instead of showing its raw key.

Restore Safety, Row Repair, and a Coherent Retention Contract

August 30, 2026 | v1.6.19
  • Restoring a whole-server PostgreSQL backup no longer destroys the target. The v1.6.18 restore dropped every database on the target server and then restored nothing -- a regression published with the pg_dumpall fix. The restore now filters the dump script at restore time, so archives already produced by v1.6.18 remain restorable.
  • MongoDB backups authenticate. mongodump and mongorestore were launched without credentials, so no MongoDB backup of a managed server had ever produced an archive -- every attempt died at the authentication wall. The password now reaches the tools through the container's exec environment.
  • CockroachDB whole-server backups are refused with a clear message instead of pretending. The image ships no PostgreSQL client tools, so the feature cannot work; the dead code path that suggested otherwise is gone.
  • Migration 056 repairs backup rows damaged by earlier releases: external restores recorded as archives (a path without a size) and rows left stuck in restoring by the old restore path. Two invariants measured across the entire release history make the repair safe; rows it cannot classify are left untouched and documented.
  • Retention now has one contract: default 7 everywhere, 0 means unlimited. The form said 30 while the API said 7, and typing 0 silently became 30 because zero is falsy. Bounds are 0-365, with labels updated in all five dashboard languages.
  • Retention counts every backup of a source -- scheduled and manual alike. The documentation promised manual backups were never counted; that was false against the engine, and the docs now say what actually happens. Making manual backups immune is a future engine change, tracked separately.

Backup Retention and Whole-Server Dumps

August 20, 2026 | v1.6.18
  • Backup retention now actually runs. The pruning function existed, was called from nowhere, and had never executed since v1.0.0: the panel's Retention field controlled nothing and repositories grew without bound. It now runs after every successful scheduled backup, with an hourly catch-up sweep for repositories that already overflowed.
  • A whole-server PostgreSQL backup now contains your data. The scheduler passed "postgres" as the database name, so it dumped the empty bootstrap database -- the archive was well-formed, restorable, and empty. Whole-server backups now use pg_dumpall, and restore no longer runs in a single transaction because creating databases forbids it.
  • Whole-server MySQL backups work at all. They had never run: the scheduler passed "--all-databases" as a database name, which validation rejected for its leading dash. Same cause as the PostgreSQL fault, opposite symptom -- this one failed loudly instead of lying quietly.
  • The panel read "Retention (days)" for what has always been a number of backups. Corrected in all five dashboard languages.
  • These two fixes are verified by tests, not yet on a live server. No test in the suite touches a real PostgreSQL instance: they assert the command that gets built, never its output. Treat this release as fixed-but-unproven for whole-server dumps until it has run on your own box.

Managed Database Backups -- Trigger, Schedule, Restore

August 20, 2026 | v1.6.17
  • A database created on a managed database server can now be backed up: the trigger only ever looked in the standalone-database table, so a managed database answered "backup source not found". Verified on a live server by opening the artifact -- it contains the table and its rows.
  • Backups of a whole database server can now be restored. The trigger accepted a source type the restore path did not recognise, so every such restore was accepted and then failed in the background; invalid targets are now refused up front instead.
  • An external restore no longer causes real backups to be purged. A restore record was written with the same "completed" status as an archive, so retention counted it and deleted genuine backups -- rows and stored objects -- to stay under quota. Backups and restore operations are now distinct kinds of record.
  • Managed databases can be put on a backup schedule, not only backed up on demand.
  • Python projects using a Pipfile now ship their dependencies in the production image. Confirmed by opening the built image rather than by a passing HTTP check -- the earlier fix passed its tests and still shipped an image without them.
  • pnpm projects build again: the generated image installs the exact pnpm version the lockfile was produced with, on a Node base new enough to run it. One case is still open -- a pnpm project whose dependencies run install scripts fails the build; a fix is under way.

Git Deploy Build Fixes -- Package Managers, Django, Health Checks

August 19, 2026 | v1.6.16
  • Projects using yarn or bun now build: the generated image installs the package manager instead of calling one that was never present. pnpm projects are still blocked -- live verification showed the Node base image is too old for the pnpm version Corepack downloads; fixed in 1.6.17.
  • Django deploys run collectstatic in the production stage, where the dependencies actually live -- the step used to fail silently and static assets returned 500 at runtime
  • Python projects using a Pipfile are not fixed yet: live verification showed the fix shipped here is incomplete -- the dependencies still do not reach the final image. Fixed and verified on a live server in 1.6.17.
  • Health checks on Alpine images probe 127.0.0.1 with an IPv6 fallback -- busybox wget resolved ::1 first, so IPv4-only servers were restarted as unhealthy
  • Rust projects build against a current toolchain (rust:1-alpine) -- the pinned 1.77 image could not even parse a modern Cargo.lock

Alerts & Monitoring Hardening -- RBAC, SSRF, Auth Servers

July 24, 2026 | v1.6.15
  • Alert rules and notification channels now enforce role permissions -- project viewers can no longer create or edit alerts, or read the secrets of notification routes
  • Uptime and alert checks are hardened against SSRF -- outbound requests can no longer be aimed at internal or private addresses
  • DNS rebinding is closed by pinning the resolved IP for the whole request, and the monitor now fails loudly if its SSRF-hardened HTTP client cannot be built instead of silently falling back to an unsafe one
  • Auth server provisioning (Logto) now grants only the roles it needs and fails loudly on partial creation, instead of leaving a half-created server marked ready
  • Dead notification routes were removed and configuration inputs across alerts and uptime are validated more strictly

BaaS Hub Fixes -- Secrets, Domains, Edge Functions

July 23, 2026 | v1.6.14
  • Database restores now replace the target database and roll back atomically on error, instead of silently merging rows into existing data
  • Realtime server credentials are only returned to developers and above -- project viewers no longer receive connection secrets
  • Realtime, auth and function servers are now reachable at their public *.sh0.app domains
  • Deno function servers start reliably -- a failed start is reported immediately instead of a false running status
  • Function servers get a working public domain with automatic HTTPS on create

Container Runtime Parity -- Reliability Fixes

July 23, 2026 | v1.6.13
  • External restore fixed -- restoring a backup stored on an external destination (S3, R2) now reads the uploaded artifact correctly
  • Preview parity -- preview containers now run under the same user as production, so apps that need elevated privileges no longer crash-loop in previews
  • Scaling parity -- replicas now inherit the primary container's user, so scaling an app whose image runs as root no longer produces crash-looping replicas
  • Template fixes -- Garage, Logto and Plane now start correctly out of the box

Backup & Storage Hardening

July 23, 2026 | v1.6.12
  • Backup restore fixed -- restoring a database or volume backup from the dashboard or API now completes correctly
  • Backup access control -- listing, triggering, downloading, restoring and deleting backups now require the right project permissions
  • Credentials protected -- file-storage endpoints no longer return shared administrator credentials, and backup destinations can only be managed by their owner
  • Scoped listings -- file-storage domains and access keys are visible and manageable only within your own projects and instances
  • Cleaner teardown -- deleting a backup removes its stored artifact, and deleting a file-storage instance tears down its proxy routes and TLS configuration

Deploy Reliability -- Follow-up Fixes

July 18, 2026 | v1.6.7
  • Git deploys are reachable -- an app's detected port is now remembered, so Next.js, Django, FastAPI and Rust projects respond on their preview URL without manual configuration
  • Cleaner TLS -- the origin no longer retries certificates it cannot obtain for *.sh0.app proxy domains, freeing the shared certificate quota
  • Plausible Analytics -- the template now provisions its PostgreSQL role and database, so the stack starts correctly
  • Zitadel -- the template now ships a working login screen out of the box
  • Convex -- the backend now boots (marked experimental while multi-port support is completed)
  • Cleaner teardown -- failed deployments no longer leave orphaned containers or images behind

Production Hardening -- Live-Audit Fix Batch

July 17, 2026 | v1.6.6
  • Popular stacks deploy correctly -- apps now receive their real Host header, so WordPress, Laravel and Django work behind *.sh0.app domains
  • Reliable deploy status -- crash-looping containers are detected and reported as failed instead of showing as running
  • Accurate SSL indicator -- domains show their real certificate state and links use HTTPS
  • Safer defaults -- secondary admin panels (database consoles, object storage, identity providers) are no longer exposed publicly by default
  • Next.js and Rust -- stock create-next-app scaffolds and real Rust projects now deploy without manual configuration
  • Template catalog repairs -- fixed credential handling for Zitadel, Chatwoot, Langfuse, SurrealDB and Convex; removed templates whose upstream images are no longer available

sh0 Manager -- Mobile Fleet Management

April 15, 2026 | v1.6.4
  • sh0 Manager mobile app -- manage all your sh0 instances from your phone (iOS & Android)
  • QR code claim flow -- scan from dashboard to link instances, secure time-limited tokens
  • Fleet overview -- see all instances, online/offline status, app count, plan info at a glance
  • Bearer token auth -- mobile app uses session-based auth via Authorization header
  • Dashboard Manager section -- generate claim tokens, view instance info, manage ownership

Streaming Build Logs, Upload Env Vars & Reliability

April 14, 2026 | v1.6.3
  • Live streaming build logs -- watch Docker builds in real-time with auto-scrolling log view
  • Upload env var race condition fixed -- env vars saved atomically before deploy pipeline starts
  • Build timeout increased to 30 minutes -- supports Rust and other long-compiling projects
  • Health score accuracy improved -- per-rule deduction cap, security rules demoted from blocking
  • Successfully deployed 0cron.dev (Rust backend + SvelteKit frontend) on sh0 -- first real Rust app
  • Heartbeat log noise eliminated -- routine logs at debug level, info only on IP change

Preview Domains -- Instant URLs for Every App

April 13, 2026 | v1.6.2
  • Every sh0 instance auto-registers with the cloud proxy on startup -- zero configuration
  • Flat subdomain format: undefined-undefined.sh0.app -- single wildcard cert, scales to 100K+ installs
  • Preview domain reverse proxy: Axum middleware routes traffic to the correct Docker container
  • No Cloudflare DNS records needed -- the *.sh0.app wildcard handles everything
  • Dashboard shows 'Preview' badge with clickable HTTPS links for all preview domains
  • Works on FREE tier -- no license required for preview domains

QA Round 1 -- Bug Fixes & Polish

April 12, 2026 | v1.6.1
  • Fixed SSL status badge showing 'pending' when HTTPS was active
  • Mail DNS: PTR record made optional (shared IPs can't set reverse DNS)
  • Mail URLs: removed random Docker ports, clean HTTPS endpoints via Caddy proxy
  • Added cPanel-style /webmail and /admin redirects for mail hostnames
  • Fixed mail Queue tab infinite browser refresh loop
  • Mail admin credentials now displayed on Overview tab with show/hide toggle
  • Cron jobs: auto-strip shell redirections (> /dev/null 2>&1) from commands
  • Cron execution history now shows stdout/stderr output with expandable viewer
  • Cron trigger: real-time modal with polling instead of silent toast
  • Fixed Auth server: correct Docker image (svhd/logto), proper entrypoint, DB seed on first run
  • Auth/Realtime/Functions: enhanced empty states with feature explanations and prerequisites
  • Internal errors now return actual messages (self-hosted product, user is admin)
  • Host terminal and file browser in Settings for direct server access
  • Files and Volumes tabs added to Function and Realtime server detail pages
  • Domain constraint fix for service_id (migration 049)
  • 475 new i18n keys and 4 pages converted to Paraglide on website

Security, Polish & Feature-Complete

April 11, 2026 | v1.6.0
  • Mail enhanced features: spam filter config, vacation auto-reply, Sieve filter rules, mail queue monitoring
  • DKIM key rotation and container log streaming for mail servers
  • License gating: all BaaS services gated to Pro+ with UpgradePrompt popups across 12 pages
  • Trustpilot review integration as fixed tab on all dashboard pages
  • BaaS quick stats on dashboard home page
  • 8 new SEO alternative pages: Vercel, Supabase, Firebase, Netlify, cPanel, Render, Railway, Fly.io
  • Homepage redesign with BaaS hero, capabilities section, and pricing strip
  • Pricing page feature comparison matrix with 27 rows
  • Investor relations page with white paper and presentation PDFs
  • AI system prompts updated with current features and capabilities
  • 979 i18n keys for SEO pages across 5 languages
  • 5 critical security fixes: Sieve injection, vacation auto-reply data loss, MCP scope bypass on 92 tools, path traversal in backups, Cloudflare state overwrite
  • 10 important security fixes: input validation, DNS cleanup, sandbox target validation, CLI confirmation prompts

File Storage, Database Servers & Mail

April 2026 | v1.5.0
  • Managed S3-compatible object storage powered by MinIO -- buckets, access keys, usage monitoring
  • Standalone database servers: PostgreSQL, MySQL, MariaDB, MongoDB, Redis with user management and grants
  • Managed email hosting via Stalwart with DKIM, SPF, and DMARC auto-configuration
  • 4-step DNS setup wizard for mail with Cloudflare auto-configure support
  • Mailboxes, aliases, catch-all rules, and deliverability monitoring
  • Database admin UI integration: dbGate, phpMyAdmin, Mongo Express
  • External database access with IP allowlist for secure remote connections
  • Global domains page: central view of all service URLs across every app
  • 50+ new API endpoints across file storage, database servers, and mail
  • Works out of the box with AWS SDK, Laravel, WordPress, and any S3-compatible client

Core Infrastructure

March 12, 2026 | Phases 1-6
  • Cargo workspace with 10 Rust crates, shared dependency management
  • sh0-db: SQLite with WAL mode, r2d2 pool, 30 migrations
  • sh0-docker: Full Docker Engine API client over Unix socket (hyper 1.x)
  • sh0-api: Axum REST server with CRUD, pagination, WebSocket log streaming
  • sh0-git: Git clone/pull (libgit2), GitHub/GitLab/Bitbucket webhook parsing with HMAC-SHA256
  • sh0-builder: 19 stack detectors, 15 Dockerfile templates, multi-stage builds
  • Code Health Check engine: 34 pure-Rust rules across 8 categories

Reverse Proxy & Full Deploy Pipeline

March 12, 2026 | Phases 7-8
  • sh0-proxy: Caddy child process management with JSON admin API
  • Auto-SSL via Let's Encrypt with configurable ACME email
  • End-to-end deploy pipeline: git pull -> analyze -> build -> deploy -> health check -> route
  • Blue-green container swap with zero-downtime deployment
  • Docker cleanup system: dangling images, old app images, build cache pruning
  • Pre-deploy disk space check (fails at >90%, warns at >80%)
  • Rollback via cached image reuse

Auth & Monitoring

March 12, 2026 | Phases 9-10
  • Argon2id password hashing, HS256 JWT (1h access + 30d refresh rotation)
  • TOTP 2FA with hashed backup codes and provisioning URI
  • API key system: SHA-256 hashed, sh0_ prefix, constant-time comparison
  • AES-256-GCM master encryption key (PBKDF2, 100K iterations)
  • Real-time metric collection: CPU, memory, network I/O per container
  • Alert evaluation: high_cpu, high_memory, app_down with 5-min cooldown

Backup Engine & Dashboard

March 12, 2026 | Phases 11-12
  • Database dumps via Docker exec: pg_dump, mysqldump, mongodump
  • Volume backup (tar + gzip) with AES-256-GCM encryption
  • Cron-based backup scheduling with retention pruning
  • SvelteKit 2 + Svelte 5 runes SPA with TailwindCSS 4
  • Dark/light theme, 5-language i18n (EN, FR, ES, PT, SW)
  • API client with auto Bearer token, WebSocket with auto-reconnect
  • Dashboard embedded into Rust binary via include_dir + build.rs

Dashboard Core & Extended Pages

March 12, 2026 | Phases 13-14
  • App detail page with 6-tab layout: Overview, Deployments, Logs, Domains, Environment, Settings
  • Real-time log viewer: WebSocket, terminal-style, auto-scroll, 1000-line buffer
  • Environment variable editor with AES-256-GCM encryption/reveal toggle
  • Database management page with engine selector (postgres, mysql, mongodb, redis, mariadb)
  • Backup manager: trigger, restore, delete, schedule creation with cron
  • Monitoring page: CPU/memory gauges, alert CRUD, auto-refresh
  • Settings: server info, TOTP 2FA setup/disable, API key management

CLI, Templates, Alerts & Compose

March 12, 2026 | Phases 15-18
  • CLI client: 7 commands (apps, deploy, logs, env, check, ssh, status)
  • Local Code Health Check via sh0 check with colored terminal output
  • 170 one-click YAML templates across 15 categories (WordPress to Ollama)
  • Variable substitution engine with auto-generation for secrets/passwords
  • 5 alert dispatch channels: email (SMTP), Slack, Discord, Telegram, webhook
  • Docker Compose v3 parser with multi-service deployment

RBAC, Export, Cron, Previews & Hooks

March 12, 2026 | Phases 19-23
  • 4-tier RBAC: Owner > Admin > Developer > Viewer (global + project-scoped)
  • Audit logging across all handlers (fire-and-forget async)
  • 7 export formats: Docker Compose, Kubernetes, AWS ECS, GCP Cloud Run, Vercel, Railway, Render
  • Cron job scheduler with tick-based execution, stdout/stderr capture, run history
  • Preview environments: PR-based auto-deploy with unique subdomains and TTL cleanup
  • Deploy hooks: pre_build, post_build, pre_deploy, post_deploy with timeout enforcement
  • sh0.yaml Infrastructure as Code: define entire stacks in one file

Scaling & Uptime Monitoring

March 12, 2026 | Phases 24-25
  • Horizontal scaling: 1-50 replicas with load balancing (round_robin, least_conn, random)
  • Autoscaling on CPU/memory thresholds with configurable cooldowns
  • Uptime monitoring: HTTP health checks with incident detection and lifecycle
  • Public status pages with per-domain uptime history
  • Email subscription system for status page notifications

51 Issues Found & Fixed

March 12, 2026 | Security Audits
  • 2 comprehensive security audits across all 103 phases
  • 13 critical fixes: constant-time API key comparison, WebSocket JWT auth, backup code hashing, per-app deploy locking
  • 20 high fixes: domain validation, path traversal prevention, secret redaction in build logs, URL-encoded Docker args
  • JWT expiry reduced (7d -> 1h) with 30-day refresh token rotation
  • CSRF protection (Content-Type enforcement + double-submit pattern)
  • Rate limiting: login (10/15min), TOTP (5/5min), global (1000/500s)
  • Security headers: HSTS, CSP, X-Frame-Options, X-Content-Type-Options

Stack-Based Architecture & Polish

March 13-16, 2026 | Dashboard Redesign
  • Icon-only 56px sidebar with tooltips + 240px context sidebar for project browsing
  • Stack-based architecture: group services by project (frontend + backend + db + cache)
  • Deploy Hub: unified wizard with 183 deploy options, category tabs, featured section
  • 7 deploy form components: FormGit, FormUpload, FormDockerImage, FormDockerfile, FormCompose, FormService, FormFramework
  • Cmd+K command palette: search pages, stacks, and services instantly
  • Mobile-responsive context sidebar with drawer + hamburger button
  • Home page redesign with server stats, stacks grid, quick actions
  • 16-tab app detail view: Overview, Deployments, Logs, Terminal, Files, Storage, Domains & SSL, Environment, Redirects, Previews, Hooks, Scaling, Uptime, Cron, Services, Settings

Docker Image, Dockerfile, ZIP & PHP/Ruby/.NET

March 14, 2026 | Deploy Features
  • Docker Image direct deploy: pull image, skip git/build pipeline
  • Dockerfile-only deploy: paste content, build locally
  • ZIP file upload deploy with 500MB limit and path traversal prevention
  • PHP, Ruby, and .NET Dockerfile templates added
  • App names scoped per project (composite unique index)
  • Container naming with project prefix: sh0-undefined-undefined
  • 45 new tests (scaling, compose, YAML)

Templates, Terminal, File Browser, Volumes

March 15, 2026 | Deploy Hub & Services
  • Deploy Hub page with 183 deploy catalog options
  • Deployment progress bar with step tracking in logs
  • 170 templates: databases (33), CMS (18), analytics (7), auth (6), AI/ML (8), DevTools (12), and more
  • Sub-service management (MySQL, phpMyAdmin, dbGate containers)
  • Service access URLs: internal/local/external endpoints with external toggle
  • Web terminal: xterm.js with shell selector (sh/bash/ash), SSH TTY support
  • File browser: Docker Desktop-style two-panel explorer with inline editor
  • Persistent storage: Docker volume management, bind mounts, file mounts
  • API docs page with live playground (26 endpoint groups)

Connection URLs, Credentials, Bulk Env Vars

March 16, 2026 | Database & Credentials
  • Database connection URLs: internal and external, auto-computed from container config
  • Database credentials card: username, password, connection strings
  • Env var bulk operations with .env editor mode
  • Template credentials persistence (env vars stored in DB)

SSL Certificates, Caddy Reliability, Storage Providers

March 17-18, 2026 | Enterprise & Security Hardening
  • HTTP-only cookie migration with CSRF double-submit pattern (SameSite=Strict)
  • Master key auto-generation (derived from JWT secret on first run)
  • Custom SSL certificates: CSR generation (rcgen), PEM upload (x509-parser), per-domain ssl_mode
  • Private key encryption (AES-256-GCM) with restricted file permissions (0o600)
  • Caddy proxy reliability: kill stale processes, retry config loads, DB route sync
  • 13 storage providers via OpenDAL: S3, R2, Wasabi, Backblaze B2, SFTP, FTP/FTPS, Dropbox, Google Drive, and more
  • FTP/FTPS IPv6 fix (EPSV mode, direct suppaftp client)
  • Plans/pricing alignment: Free ($0), Pro ($19/mo), Business ($97/mo)

CronBuilder, Redirects, Backup Restore, Monitoring Refactor

March 19, 2026 | Monitoring & Automation
  • CronBuilder component with 18 presets and bidirectional sync
  • URL redirect rules: exact, prefix, regex matches with 301/302/307/308 status codes
  • Backup restore: Flow A (sh0 backups) and Flow B (external source upload)
  • Monitoring page refactor: 4-tab dashboard (Overview, Apps, Uptime, Alerts)
  • Health check false positive reduction (80+ new tests, skip env/lock/doc files)
  • DNS configuration modal with real server IP and Cloudflare guidance
  • ACME email configuration (stored in DB, runtime updates)

Multi-Server BYOS

March 20-21, 2026 | Phase 29
  • Remote node registration with SSH tunnel management (russh)
  • SSH TOFU (Trust on First Use) with host key fingerprint verification
  • Docker image transfer via save/load over SSH tunnels
  • Node-aware deploy pipeline dispatching builds to remote servers
  • Node management dashboard with status indicators and health monitoring
  • Node hardening: error handling, connection validation, graceful cleanup

MCP Server & AI Assistant

March 22-25, 2026 | AI Phases 1-5
  • MCP server with 103 tools: Streamable HTTP transport at /mcp (JSON-RPC 2.0)
  • OpenAPI-driven tool generation via utoipa with x-mcp-* extensions
  • 3-tier safety: API key scopes (read/standard/admin), per-tool risk classification, confirmation tokens for destructive ops
  • AI gateway: 3-way chat routing (MCP connector, legacy, docs expert)
  • AI Sandbox: Alpine 3.19 sidecar container per app with root shell, package install, writable volumes
  • Claude MCP Connector integration for server-side agentic tool execution
  • Dashboard AI chat with model selector, conversation history, processing steps timeline
  • Capabilities modal: 24 capabilities across 5 categories, translated in 5 languages
  • Prepaid wallet billing with per-token pricing (Haiku, Sonnet, Opus)
  • BYOK support on Business plan (Anthropic or OpenRouter API keys)
  • 10 audit rounds across 5 phases -- 0 critical issues reached production

Free Tier, Docker & Distribution

March 26 - April 1, 2026 | v1.4.1 - v1.4.6
  • Backups opened to Free plan (local storage); cloud storage gated to Pro+
  • Docker image published to Docker Hub (zerosuiteinc/sh0) and GHCR
  • Self-update system: sh0 update checks GitHub Releases and upgrades in-place
  • Systemd service auto-setup on Linux (sh0 serve installs and enables the unit)
  • Replaced full-page upgrade blockers with inline banners on monitoring, backups, team
  • License feature matrix refined: granular backups (local/cloud) and monitoring (metrics/alerts/uptime)
  • Uninstall command: sh0 uninstall cleanly removes binary, systemd unit, and data
  • Startup banner with version, dashboard URL, and systemd tip

Everything in v1.6.0

Deploy Engine

  • 5 deploy pipelines: Git, Docker Image, Dockerfile, ZIP upload, Template/Compose
  • 19 stack auto-detection with 15 optimized Dockerfile templates
  • Blue-green deployments with zero-downtime swap and rollback
  • 183 deployment options in the Deploy Hub
  • Docker Compose v3 parser with multi-service deployment

Template Store

  • 170 one-click templates across 15 categories
  • Databases, CMS, AI/ML, DevTools, analytics, auth, email, queues, search
  • Variable substitution with auto-generation (secrets, passwords)
  • Multi-service deployment with topological ordering

Domains & SSL

  • Auto-SSL via Let's Encrypt with Caddy ACME
  • Custom SSL certificate upload with CSR generation
  • URL redirect rules (prefix, exact, regex)
  • Load balancing: round_robin, least_conn, random

Monitoring & Alerts

  • Real-time CPU, memory, network metrics with sparkline charts
  • 5 alert channels: Email, Slack, Discord, Telegram, Webhook
  • Uptime monitoring with public status pages
  • Autoscaling on CPU/memory thresholds (1-50 replicas)

Backup & Storage

  • Database dumps (pg_dump, mysqldump, mongodump) + volume backups
  • AES-256-GCM encryption with scheduled cron-based backups
  • 13 storage providers via OpenDAL (S3, R2, SFTP, Dropbox, Google Drive)
  • Restore from backup or external storage

Security

  • 51 security issues found and fixed across 2 audits
  • Argon2id passwords, JWT + refresh rotation, TOTP 2FA, Google OAuth
  • AES-256-GCM encryption for all secrets (env vars, tokens, keys)
  • CSRF, SSRF prevention, rate limiting, container hardening

Developer Experience

  • Browser-based terminal (xterm.js) with shell selection
  • File browser with inline editor, mkdir, delete
  • Code Health Check: 34+ rules scanned before every build
  • Export to 7 platforms: K8s, AWS, GCP, Vercel, Railway, Render, Compose

Dashboard

  • 22 pages, 70+ components, 16-tab app detail view
  • Cmd+K command palette, dark/light theme, 5 languages
  • Stack-based architecture with dual sidebar
  • API docs with live playground (26 endpoint groups)

Automation

  • Cron jobs with CronBuilder (18 presets, 50 jobs/app)
  • Deploy hooks: pre_build, post_build, pre_deploy, post_deploy
  • Preview environments from PR webhooks with TTL cleanup
  • Infrastructure as Code via sh0.yaml

Team & RBAC

  • 4-tier roles: Owner, Admin, Developer, Viewer
  • Project-scoped access control
  • Audit logging on all operations
  • License system: Free, Pro ($19/mo), Business ($97/mo)

AI Assistant

  • MCP server with 103 tools (37 read, 44 write, 16 destructive, 5 sandbox, 1 confirm)
  • AI Sandbox: per-app Alpine container for debugging and development
  • Connect from Claude Desktop, Cursor, or any MCP client
  • AI gateway with web chat, model selection, conversation history
  • Prepaid wallet with BYOK option on Business plan

By the numbers

10

Rust crates

52

Migrations

230+

API endpoints

22

Dashboard pages

70+

UI components

6

Languages

170

Deploy templates

103

MCP tools

34+

Health rules

7

Export formats

13

Storage providers

5

Alert channels

414+

Tests passing

v0.1.0-alpha Deprecated 2025

Legacy Prototype (Python/FastAPI)

The original sh0, built in Python. Agent-based architecture with stack detection, health checks, and Dockerfile generation. Served as the blueprint for the Rust rewrite.

Why Rust? Rewrote in Rust for: ~30MB idle memory (vs ~500MB Python), single binary distribution, direct Docker socket API (no CLI shelling), zero GC pauses under load.

Ready to deploy?

Install sh0 in under 60 seconds and deploy your first app.