§ 00 What's new

Changelog

Every release, what changed, and why — curated. Follow GitHub Releases for the raw feed.

  1. v0.3.0

    Latest

    Platform-level release. SSO now covers all four major protocols (OIDC + OAuth2 + SAML + LDAP) with group-to-role mapping, RBAC v2.1 ships with 5 built-in roles and role-scoped permissions, and the install flow runs through a web wizard instead of the CLI. The bell-icon notification center fires for nine event classes (alerts, backups, agent offline, image updates, migrations, CVE, …) with per-user read state. WebSocket auth tickets are now permission-scoped — a viewer can no longer reach /ws/exec by minting a ticket through the standard flow.

    Added
    • SSO: SAML 2.0, LDAP / Active Directory, and generic OAuth2 join OIDC. Each kind has group-mapping support — any IdP group can map to any built-in or custom role. Unified GET /auth/providers returns all four kinds in one fetch with a `kind` discriminator.
    • RBAC v2.1: 5 built-in roles (viewer / operator / deployer / host-admin / admin), 50 permissions in `resource.verb` naming, role-level scopes (`host_tag = team-frontend`) on top of per-user scopes — both layers AND together.
    • Web setup wizard replaces `dockmesh init`. First boot lands on /setup; admin account, optional registries / OIDC / proxy / backup target are configured from the browser. The wizard locks itself after first install.
    • Two-phase deploy: preflight (port conflict, image pull, env render) runs before the actual deploy commits. The verdict is shown before anything starts moving.
    • Invite-link flow: admin generates a one-time URL, shares it manually (Slack, signal, whatever). Recipient hits /invite/ and sets username + password. No SMTP dependency.
    • Stack recovery: when compose.yaml is empty/missing but containers carrying its compose-project label are still running, the recovery panel rebuilds the compose file from the live containers.
    • Backup per-run log capture: executor narrates phase boundaries and captures hook stdout/stderr (capped per hook). Run detail page polls /log every 2s while a run is in flight.
    • Backup archive download: streams the tar.gz of a successful run, transparently decrypted for age-encrypted archives.
    • Proxy per-route enable/disable flag + per-route metrics endpoint (rpm, p95, status-bucket distribution scraped from Caddy).
    • Alerts mute_schedule: recurring quiet-hours JSON on rules and channels (`{type: "weekly", ranges: [{days, from, to}]}`).
    • Image-update watcher: every 30 minutes asks each running container's registry for a fresher manifest. Container list shows an upgrade badge when an update is available; GET /images/updates exposes the snapshot map.
    • Container list: `restart_count` (parallel inspects) + `image_update_available` columns.
    • Notification center wired for 9 event kinds — alert fires, backup ok/fail, agent offline, image update, system upgrade, migration done/failed, CVE found (HIGH/CRITICAL only), invite redeemed, git auto-deploy. Read state is per-user; brand-new accounts never see notifications emitted before they joined.
    • Container detail file browser: tree view + file preview + small-file edit, served via volume-tar helper for both local and remote hosts.
    Changed
    • Frontend rewritten on the editorial design system. Consistent surface hierarchy across pages (`--bg` = page canvas only, `--bg-elevated` = input slots, `--surface` = cards) — 81 callsites swept in one pass.
    • Login page redesign: form lives in a proper card, dot-mesh background and marketing-style watermark / welcome blurb removed.
    • Notifications migrated from shared "broadcast" rows to per-user fan-out at emit time. Read-state is now actually per-user; deleting one user's notification no longer removes it for everyone.
    Security
    • WebSocket exec / logs / stats / events used to accept any authenticated ticket regardless of role. Tickets are now minted via POST /ws/ticket?for= against the caller's permission set, and each endpoint asserts the bound permission matches. A viewer can no longer reach /ws/exec.
    • Tokens RBAC: /settings/api-tokens routes were admin-only; operators couldn't list or create their own tokens. The route gates are now PermTokensView / Create / Delete. CreateAPIToken additionally verifies that the caller's permissions are a superset of the requested role — was previously possible for any user with tokens.create to mint an admin token.
    • Audit log: role mutations (POST/PUT/DELETE /roles) used to log under user.create. Distinct ActionRoleCreate / Update / Delete constants now.
    Fixed
    • StackListEntry containers + has_volumes were declared in the response type but always returned NULL. Now populated from container labels + a top-level compose.yaml scan.
    • RemoteHost.InspectImage used to return 501 because the agent protocol didn't carry an image-inspect frame. Wired through.
    • Sort jitter on Alerts → Channels and Hosts → "Stacks deployed here": both endpoints iterated over Go maps (randomised order), so the 10s auto-refresh re-shuffled rows. Both now return stable order.
    • Proxy metrics card rendered solid white in dark mode because `--ed-surface` was never defined and fell through to the `#fff` fallback. Now uses the real theme tokens.
    • Copy buttons across the app fell silently to "Copy failed" when accessed over plain HTTP (e.g. on a LAN address like 192.168.x.x:8080) because navigator.clipboard requires a secure context. All 17 callsites now fall back to a transient-textarea + document.execCommand path.
  2. v0.2.9

    Stack delete now lets you choose what else to clean up (containers, project networks, volumes, images) with per-category warnings and a server-side preview. Deploy / stop / delete buttons survive navigating away and back while an operation is still running — no more accidental double-deploys.

    Added
    • Stack delete modal has four checkboxes: stop & remove containers, remove project-scoped networks, remove volumes (opt-in, hard red warning, unrecoverable), remove images (opt-in, skips images shared with other projects). External volumes are never touched; bind-mount paths are not reachable from the cleanup logic by design.
    • New `GET /api/v1/stacks/{name}/cleanup-preview` endpoint returns the concrete list of networks / volumes / images a delete would touch so the modal can show the real names before the user commits.
    • Stacks page + stack detail page now share a global in-flight lock keyed by `host:stack`. A "Deploying…" badge replaces the action buttons on the card and the header while an op runs, and the state survives page navigation so you can't fire two deploys on the same stack from two tabs.
    Fixed
    • Double-deploy race: the old "busy" state was local to one page mount, so navigating away and back during a long deploy re-enabled the Deploy button while the request was still in flight.
  3. v0.2.8

    Roles permission viewer in the UI, image-delete fix for digest-referenced images, and svelte-check zero-warning baseline.

    Fixed
    • Deleting an image whose canonical reference is its `sha256:…` digest failed with "invalid reference format: repository name must be lowercase" because the frontend's URL-encoded colon (`%3A`) was never decoded on the server before hitting the Docker daemon. `RemoveImage` now `url.PathUnescape`s the chi path param the same way `ScanImage` already did.
    • `svelte-check` baseline went to 0 errors / 0 warnings. Removed two `state_referenced_locally` warnings in `AnimatedNumber`, fixed a Map tuple typing on the dashboard, a11y label in the templates page, and a wrong method name call (`api.auth.me()` → `api.users.me()`) on the users page.
    Added
    • Users & Roles → Roles tab: permission-count column is now a clickable link that opens a read-only modal showing every permission the role holds, grouped by category. Works for built-in roles (admin / operator / viewer) too, so their defaults are finally inspectable.
  4. v0.2.7

    CPU + Memory dashboard tiles now reflect actual Docker VM utilisation (aggregated from running-container stats) when Docker is resource-limited, instead of the meaningless host-wide numbers. Host view is shown alongside for comparison.

    Fixed
    • Memory tile pinned at 100% on macOS + Windows because v0.2.6 capped host-measured memory usage (whole Mac: ~30 GB) against Docker's VM allocation (e.g. 8 GB). Now sums container stats to get actual Docker-VM memory usage — "1.2 GB of 8 GB allocated" as expected.
    • CPU tile on Docker-limited hosts reported whole-machine CPU (macOS + Chrome + Slack + Docker VM combined). Now aggregates per-container CPU% across running containers, normalised against Docker's allocated cores — matches what `docker stats` shows in its CPU column.
    Added
    • Dashboard CPU + Memory tiles show BOTH views when Docker is limited: the primary row is Docker-allocation usage, a smaller row below shows host usage with "Host: 1.23 / 10 cores (12%)" style — so operators can see "Docker is saturated but host has headroom, maybe raise the allocation" at a glance.
    • Container-stats aggregation runs in-parallel (max 8 concurrent goroutines, 3-second timeout) so a slow container can't stall the sampler loop. Only runs on macOS + Windows where a Docker-limited VM is the norm.
    Known limitations
    • Disk tile remains host-based on Docker-limited systems. Docker Desktop does not expose the VM's disk total via any API — we can query `docker system df` for used bytes, but without a total to divide against we can't produce a meaningful percent. The host root filesystem stays as the shown reference.
  5. v0.2.6

    Dashboard now reflects Docker Desktop resource limits. When the operator has capped CPU / RAM in Settings → Resources, those numbers drive the dashboard tiles; the raw host hardware is shown alongside so divergence is visible at a glance.

    Added
    • Metrics sampler queries `docker info` and overrides `CPUCores` + `MemTotal` with the daemon's configured view (Docker Desktop VM caps on macOS/Windows, cgroup limits on constrained Linux). Host hardware is kept separately in `host_cpu_cores` / `host_mem_total`.
    • Dashboard CPU + Memory tiles render a `Docker cap` badge and a "Host: …" sub-line when the two views diverge, so operators see "6 cores Docker-allocated, Host: 10 cores" at a glance on a macOS homelab.
    Fixed
    • Dockmesh binary's `dockmesh --version` path still reads the env file even when not needed — harmless on root/service-user shells, surfaces as silent nothing on a non-privileged shell. Deferred to a separate fix.
  6. v0.2.5

    Real macOS CPU + memory metrics. The v0.2.4 darwin impl relied on `sysctl kern.cp_time` which doesn't exist on Darwin (it's FreeBSD-only) — now replaced with `top -l 2 -s 1` parsing.

    Fixed
    • CPU tile on macOS was pinned at 0% because `sysctl kern.cp_time` returns nothing on Darwin. Sampler now shells out to `top -l 2 -s 1 -n 0` and parses the second snapshot's `CPU usage:` line — that's the proper 1-second-window delta.
    • Memory tile showed inflated values because the vm_stat-based formula double-counted file-backed active pages as "used". Switched to parsing top's own `PhysMem:` line which uses Apple's Activity-Monitor-equivalent accounting.
    • Sampler cadence dropped from 500 ms to 10 s on macOS to match the cost of `top -l 2 -s 1` (~1 s per invocation). Frontend polls /system/metrics every 10 s anyway, so dashboard freshness is unchanged.
  7. v0.2.4

    UI polish — macOS dashboard metrics impl (first attempt), containers-page uptime fix, and network member-count fix.

    Fixed
    • Containers page showed "8d 23h" as uptime for a container that had been stopped 8 days ago. The calculation was using `Created` for every container regardless of state; now non-running containers display Docker's own Status string ("Exited (0) 2 hours ago") and only running containers get the live duration.
    • Networks page reported "0 containers" for every network even when containers were attached — Docker's `NetworkList` endpoint only populates that field for `NetworkInspect` responses. The backend now enriches each network row from the container list (reverse-indexing via `NetworkSettings.Networks`) so the count + member list are correct on both single-host and fan-out queries.
    • Dashboard CPU / Memory / Disk tiles on macOS went from all-zeros to a native implementation (superseded by v0.2.5 after the initial attempt was found to depend on FreeBSD sysctl nodes). Disk path on macOS is `/` rather than `/var/lib/docker` since Docker Desktop keeps volumes inside a VM that isn't visible from the host.
  8. v0.2.3

    Interactive password login for dmctl, RBAC migration fix so `stack.adopt` actually works on existing installs, and UX polish on login errors + bundle ignores.

    Fixed
    • POST /stacks/adopt returned 403 on installs predating v0.2.0 because migration 016 seeded admin permissions before `stack.adopt` existed. New migration 033 grants it to the admin role idempotently; `Store.AllowedDB` now has the permission cached on next refresh.
    • `dmctl stack adopt` no longer tars up `.claude/`, `.next/`, `.nuxt/`, `coverage/`, `.terraform/` and `.gradle/` workspace scratch — keeps bundle size sane without excluding anything that might be a compose build context.
    • Docs referenced the old `Settings → API Tokens` path; token management lives under `User Profile → API Tokens` now.
    Added
    • `dmctl login --user admin` — interactive username + password login. Prompts for the TOTP code if MFA is on. Saves access + refresh tokens; auto-refreshes on 401 so sessions last as long as the refresh token.
    • Clearer `dmctl login` usage examples + custom error when the server URL is missing (replaces Cobra's default "accepts 1 arg(s)" noise).
    • Scheme auto-detection in `dmctl login`: `localhost`, `*.local`, `*.lan`, `*.internal` and RFC1918 addresses default to `http://`; everything else gets `https://`.
  9. v0.2.2

    Installer ships `dmctl` alongside the server binary so `dmctl stack adopt` works out of the box.

    Fixed
    • Release tarball has always contained `dmctl`, but `install.sh` only placed `dockmesh` in `INSTALL_DIR`. Fresh installs and upgrades now drop `dmctl` into the same PATH directory.
  10. v0.2.1

    Graceful Docker-daemon disconnect handling (macOS boot race, daemon restarts) + interactive uninstaller.

    Fixed
    • dockmesh no longer hangs at startup waiting for Docker. If the daemon isn't up yet (macOS launchd firing dockmesh before Docker Desktop, systemd race with docker.service), the server still starts — container endpoints return 503 until the daemon is reachable and reconnect automatically once it is. No service restart needed.
    • Background probe every 10s updates the connection state; `/api/v1/system/health` has a dedicated `docker` check, and the dashboard shows a non-alarming banner while disconnected.
    Added
    • `scripts/uninstall.sh` — interactive wizard with per-component choices (stop service, remove unit, remove binary, remove data dir, remove service user). `--yes`, `--purge`, `--dry-run`. Running containers are explicitly left alone.
    • Served via `get.dockmesh.dev/uninstall` for `curl | sudo bash` symmetry with install.
    • New docs page `/docs/operations/uninstall/`.
  11. v0.2.0

    Adopt running compose projects into dockmesh management without recreating containers. Zero-downtime migration from plain `docker compose up`, Portainer, Dockge.

    Added
    • `dmctl stack adopt [path]` — tars the folder (compose.yaml + build contexts + config files), uploads to the server, binds running containers via their `com.docker.compose.project` label. No docker operations against the containers. Dry-run mode, interactive diff, Mac/Linux.
    • `GET /stacks/discovered` + `POST /stacks/adopt` backend endpoints with OpenAPI drift test coverage. New RBAC permission `stack.adopt`, new audit action `stack.adopt`.
    • "Discovered unmanaged compose projects" banner on the Stacks page, with metadata-only paste adoption for pure-registry-image stacks.
    • New docs page `/docs/guides/adopt-existing-stack/` with concrete walkthrough (audiobookshelf + audnexus). Cross-links from the Portainer + Docker Compose migrate guides.
  12. v0.1.13

    Init wizard surfaces the actual launchctl error + diagnoses what's holding port 8080 when bootstrap fails.

    Fixed
    • `dockmesh init` now surfaces the underlying launchctl error message (e.g. "Bootstrap failed: 5: Input/output error") instead of just "exit status 5". When bootstrap fails, it also checks the listen port with `lsof` and prints the blocking PID + process name with a concrete kill-and-retry suggestion.
    • Post-install summary box shows platform-correct service commands — `sudo launchctl print system/dev.dockmesh.service` + `tail -f /usr/local/var/log/dockmesh.log` on macOS instead of the systemd ones.
  13. v0.1.12

    Installer fix for non-US locales — awk was emitting comma-decimals which broke downstream math.

    Fixed
    • Installer pinned `LC_ALL=C` so `awk printf "%.1f"` uses `.` as decimal separator everywhere. Previously German / French / … locales saw "0,6" captured into a shell variable, then fed into the next `awk` as "BEGIN { if (0,6>0) … }" which is a parse error. Affected every non-US locale, most visibly macOS (bash 3.2 forced the BSD-date fallback path).
  14. v0.1.11

    macOS installer fixes — correct darwin tarball, BSD date, shasum fallback, lsof port check, Docker Desktop socket warning.

    Fixed
    • Installer hardcoded `dockmesh_linux_${ARCH}.tar.gz` and downloaded the Linux binary on Macs. Now uses `${OS}_${ARCH}` so darwin hosts pull the right artifact.
    • `get_time` detects BSD date's `.N` literal output and falls back to whole-second precision (bash 3.2 on macOS doesn't have `$EPOCHREALTIME`).
    • `sha256` tool detection falls back to `shasum -a 256` when `sha256sum` is absent (macOS default).
    • Port check uses `lsof` on darwin — BSD `netstat -tln` doesn't have GNU's `-t` flag and was always reporting "free".
    • Warns when `/var/run/docker.sock` is missing on macOS even though `docker info` succeeds, and points at Docker Desktop → Settings → Advanced → "Allow the default Docker socket to be used".
    • Agent binary filename includes `${OS}` so darwin installs don't stash a darwin binary under a linux filename.
    • macOS PATH prepends `/opt/homebrew/bin` + `/usr/local/bin` during tool detection so `sudo bash` sees brew-installed deps.
  15. v0.1.10

    Five P0 fixes from the end-to-end clean-install dogfooding pass. Fresh install + multi-host enrollment now work cleanly.

    Fixed
    • Service crash-looped on fresh install because `dockmesh init` didn't write `DOCKMESH_SECRETS_PATH` / `DOCKMESH_AUDIT_GENESIS_PATH` to the env file — config loader fell back to relative paths which resolved against systemd cwd=`/`.
    • Agent PKI init opened `./data/agents-ca.crt` via a hardcoded relative path — now resolves via `filepath.Dir(cfg.DBPath)` so overrides propagate.
    • CLI admin commands (`dockmesh admin list-users`, `reset-password`, `unlock`) couldn't find the DB from a plain root shell — now auto-loads `/var/lib/dockmesh/dockmesh.env` (or the macOS equivalent) if no `DOCKMESH_DB_PATH` is set.
    • Agent enrollment returned 503 because `install-agent.sh` + `dockmesh-agent-linux-*` weren't installed anywhere — the installer now drops them into `/usr/local/share/dockmesh/` (configurable via `DOCKMESH_ASSET_DIR`, auto-detected by `dockmesh init`).
    • Remote agents failed mTLS handshake with "certificate is valid for 127.0.0.1, ::1, not " because `dockmesh init` didn't derive `DOCKMESH_AGENT_SANS` from the base URL. The agent-server cert now includes the hostname + public IP automatically.
    Changed
    • Installer derives the asset directory from `DOCKMESH_INSTALL_DIR` (`/opt/bin` → `/opt/share/dockmesh`) rather than hardcoding a path.
    • Upgrade flow refreshes bundled agent assets alongside the main binary.
  16. v0.1.9

    Installer bundles agent assets (install-agent.sh + agent binary) into /usr/local/share/dockmesh.

    Fixed
    • `/install/agent.sh` returned 503 because the server read install-agent.sh via a relative path that failed under systemd.
  17. v0.1.8

    CLI auto-loads dockmesh.env from the default data directory.

    Fixed
    • `sudo dockmesh admin list-users` (and all other admin commands) returned `(no users)` because the CLI couldn't find the DB without an explicit `--env-file`.
  18. v0.1.7

    Agent PKI respects the configured data directory instead of hardcoded "./data".

    Fixed
    • Fresh install failed with "agent pki init failed: ca: open data/agents-ca.crt: permission denied" because `pki.New` was called with a relative path.
  19. v0.1.6

    Env file now writes DOCKMESH_SECRETS_PATH + DOCKMESH_AUDIT_GENESIS_PATH.

    Fixed
    • Fresh install crash-looped with `jwt secret: open ./data/secrets.env: permission denied` because these two paths weren't exported to the service environment.
  20. v0.1.5

    Native macOS support — server + agent ship with Intel + Apple Silicon binaries.

    Added
    • macOS server + agent binaries (amd64 + arm64) in every GitHub Release
    • `dockmesh init` detects platform and writes a launchd plist on macOS (`/Library/LaunchDaemons/dev.dockmesh.service.plist`) instead of a systemd unit — boots at host startup like a standard Mac service
    • `dockmesh serve --env-file PATH` loads key=value env files directly (launchd has no `EnvironmentFile=` equivalent)
    • Installer (`curl | sudo bash`) works end-to-end on macOS — detects OS via `uname`, uses Homebrew-style data dir `/usr/local/var/dockmesh`, restarts via `launchctl kickstart` on upgrade
    Changed
    • Data directory default is now platform-aware: `/var/lib/dockmesh` on Linux, `/usr/local/var/dockmesh` on macOS
    • `--systemd` flag on `dockmesh init` renamed to `--service` (old name still works as an alias)
  21. v0.1.4

    Service runs as dedicated non-root user. Auto-migration on upgrade.

    Security
    • `dockmesh init` creates a system user `dockmesh` and runs the service as that user with `docker`-group membership — no more root service daemon
    • Installer auto-migrates existing root-owned installs on the next upgrade run: creates the user, chowns the data dir, patches `User=` into the unit
    • Systemd unit ships hardened by default: `NoNewPrivileges`, `ProtectSystem=strict`, `ProtectHome`, `ReadWritePaths` scoped to data dir + docker socket
    Docs
    • Hardening guide rewritten to match — documents the service-account model and the `docker` group → root-equivalence caveat honestly
  22. v0.1.3

    Settings restructure + brand polish + installer hardening.

    Added
    • Audit Log, Users & Roles, Authentication, Registries, Account, API Tokens promoted to top-level nav — Settings is now a focused System-only page
    • User avatar menu with Profile & security / API tokens / Sign out shortcuts
    • Self-update check with release banner on every page (2 h poll, configurable)
    • `dockmesh admin unlock --user X` — clears lockout without touching the password
    • Installer upgrade-detection: auto-restart systemd + health-probe + rollback hint
    • Compact `/containers/summary` endpoint — dashboard payload 15 KB → 1 KB
    • Distro-aware preflight in `install.sh` (apt / dnf / apk / pacman / zypper)
    Changed
    • Canonical brand lowercased to `dockmesh` across UI, docs, README, marketing
    • Wordmark typeset in Space Grotesk (Medium 500 / Bold 700), baked as SVG paths
    • Light-theme contrast overhauled — `--fg-subtle` 3:1 → 4.9:1, brand accents theme-adaptive
    • Lockout error message now tells the user the exact retry time + emits `Retry-After` header
    • `dockmesh init` re-run no longer silently accepts a new password — points at `admin reset-password`
    Fixed
    • Dashboard runaway-fetch loop (Svelte 5 `$effect` tracking a state the same `load()` wrote back)
    • Installer `$VERSION` clobbered by `/etc/os-release` (shell namespace collision)
    • Upgrade-summary box borders misaligned by 2 chars
    • Clipboard fallback for non-HTTPS origins (self-hosted on plain IP)
  23. v0.1.2

    Polished installer + init wizard: distro detection, auto-systemd, health probe.

    Added
    • Installer step-numbering with elapsed-time per step, banner, Unicode box summaries
    • Upgrade path auto-detects existing installs and restarts the service after replace
    • `dockmesh init` auto-enables + starts the systemd unit, then probes /api/v1/health
    • Generated admin password shown in a bordered box so it cannot be missed
    • `?fresh=1` bypass on `get.dockmesh.dev` Cloudflare worker (dev cache-busting)
  24. v0.1.1

    Critical fix for dashboard fetch loop.

    Fixed
    • Dashboard polled at wire-speed (not 10 s) due to a reactive-tracking bug introduced by the flicker-free auto-refresh guard — wrapped the guard reads in `untrack()` so the effect no longer re-runs on its own writes
  25. v0.1.0

    First public release. Single binary, multi-host, opinionated defaults.

    Added
    • Compose-first stack management — stacks live at `stacks//compose.yaml`, filesystem is source of truth
    • Multi-host fleet via outbound-only mTLS agents (no inbound ports on remote hosts)
    • Stack migration across hosts (preflight + volume stream, auto-rollback on failure)
    • Custom RBAC roles with host-tag scope + OIDC SSO (Azure AD, Google, Keycloak, Authentik, Dex)
    • TOTP 2FA with single-use recovery codes
    • SHA-256 hash-chained audit log + webhook streaming
    • Scheduled backups with age-encrypted payloads — Local, SMB, SFTP, WebDAV, S3 targets
    • Embedded Caddy reverse proxy with automatic HTTPS
    • Embedded Grype vulnerability scanner (image-level + fix-in column)
    • Private registry credentials (age-encrypted at rest)
    • `dmctl` CLI for CI/CD + scripted deploys (cross-platform: linux/macOS/Windows)
    • Single static binary (~35 MB), linux/amd64 + linux/arm64

Older releases are archived on GitHub.

Following semantic versioning — breaking changes only on major version bumps.

Stay in the loop

Watch the GitHub repo to get notified on new releases, or follow the RSS feed.