Releases: wslkit/skrog
Release list
Skrog 0.4.1
v0.4.0 shipped a bug that hangs
docker run -i. If you are on v0.4.0,
this is the release to take. (#237)
A patch release: no rename, no new engine, no migration. The rootfs is unchanged
(29.8.0-2), so skrog install pulls the same engine v0.4.0 did.
Install
irm https://wslkit.github.io/skrog/install.ps1 | iexAlready on v0.4.0? skrog upgrade reports it; the installer above replaces the
binaries in place and your distro, images and volumes are untouched.
The hang
echo hi | docker run -i --rm alpine cat never returned on v0.4.0, and neither
did docker exec -i.
The bridge wraps its connections to count bytes for skrog status --stats. That
wrapper embedded io.ReadWriteCloser — an interface, whose method set is
exactly {Read, Write, Close}. Embedding an interface in Go gives you that
interface's methods, not the concrete type's, so CloseWrite was never promoted
and the type assertion for it failed. The docker CLI half-closes to signal stdin
EOF; the signal was swallowed, the container's stdin stayed open, and cat
waited forever.
The wrapper now asserts through to the underlying connection. A test that had
passed throughout asserted the wrapper was still a net.Conn, which it always
was — it never asked whether the half-close survived.
Also fixed
Verifying a download actually works now. SHA256SUMS was written with CRLF,
so sha256sum -c printed no file was verified on every release to date — the
trailing \r became part of the filename it looked for. The hashes were right;
the format was not. Past releases are signed as published and cannot be
corrected, so the workaround for them is documented.
(#248)
Three fixes where a guard was not guarding:
- A policy file that has never parsed now refuses requests instead of
allowing everything. A rule file present but unreadable meant every container
was judged by no rules at all, silently — the opposite of what configuring one
asks for. (#254) allow-bind-sourcesjudgesnpipemounts and resolves traversal first.
A named-pipe mount is a bind mount by the time the bridge is done with it, and
a path with..in it was compared before being resolved.
(#256)- Every listener that serves the engine enforces policy and audits. One did
not. (#257)
And a run of state-handling bugs, each of which left something in a worse place
than it claimed:
- the agent is told where its secret is rather than trusting a default that
moved with the rename (#239) - a volume
migratecreated but could not fill is no longer reported as already
migrated (#251) - a failed snapshot restore says where your data is, and can be retried
(#252) relocatestarts the engine before deleting the archive, not after
(#253)- a self-heal that fails records the engine actually installed
(#264) compactno longer leaves the engine stopped and pinned down in silence
(#265)- a snapshot whose record cannot be written no longer orphans its archive
(#267) - the offline bundle no longer names the file it is extracted to
(#260)
New
skrog runner checkreports whether the machine sleeps on mains power —
the setting that quietly kills a self-hosted runner overnight.
(#268)skrog statusnames the endpoint it is serving, in the text output and
underendpointin--json, with both the pipe and itsDOCKER_HOST
spelling. It reports what the running supervisor bound, read back from the
supervisor's own record rather than recomputed — Docker Desktop can start or
stop after Skrog chose, and a recomputed answer could name a pipe nothing is
serving. (#273)
Docs
The site now has canonical URLs, a sitemap crawlers can find, and link previews.
Every page distinguishes running with Docker Desktop from running without
it — the old text handed you --context skrog as the happy path even on a
machine where Skrog holds the default pipe and no flag is needed. The README
says where Rancher Desktop, Podman Desktop and Docker Desktop each win, and when
to pick one of them instead.
Verifying this release
gh attestation verify skrog_0.4.1_windows_amd64.zip --owner wslkit
sha256sum -c SHA256SUMS --ignore-missing
The second one works on this release. It did not on any earlier one.
Still not code-signed — SmartScreen warns on first run. A SignPath Foundation
application is in. (#77)
Full changelog: v0.4.0...v0.4.1
Skrog 0.4.0
Hawser is now Skrog. Everything is renamed: the binaries, the WSL distro,
the docker context, the state directory, the config and lock files, the pipes
and the environment variables. There is no in-place upgrade from v0.3.1 —
what you download here is a different program, not a newer copy of the one you
have. If you have images or volumes worth keeping, read
coming from Hawser
before you uninstall anything. (#1)
The old name was chosen on the belief that its namespace was nearly empty. That
was true of trademark registries and false of common-law use:
Finsys/hawser, a 606-star Go agent for
Docker host management, predated this project by nine months. A search for
"hawser" returned dictionaries and that project, never this one — so the
discoverability the name was chosen for could never have worked.
Skrog is Norwegian for hull: the body of the ship that carries the cargo
and keeps the sea out. It is closer to what this is than a mooring line ever
was — the thing containers live inside, not the rope to the dock.
Install
irm https://wslkit.github.io/skrog/install.ps1 | iexWhat changed
| was | now | |
|---|---|---|
| binaries | hawser.exe, hawserw.exe, hawsertray.exe |
skrog.exe, skrogw.exe, skrogtray.exe |
| WSL distro | hawser-engine |
skrog-engine |
| docker context | hawser |
skrog |
| state directory | %LOCALAPPDATA%\Hawser |
%LOCALAPPDATA%\Skrog |
| config / lock | hawser.yaml, hawser.lock |
skrog.yaml, skrog.lock |
| environment | HAWSER_* |
SKROG_* |
| org | hawserhq |
wslkit |
| docs | hawserhq.github.io/hawser |
wslkit.github.io/skrog |
The GitHub repository URLs redirect. The Pages URLs do not — so any
bookmark to the old documentation site is already broken, and the install
one-liner above is the current one.
Coming from Hawser
Your images and volumes live inside the hawser-engine distro, and uninstalling
removes them. Because every name above differs, the two installs can coexist —
which gives you a way to keep them:
skrog install
skrog start
skrog migrate --from-context hawser --dry-run # what would move, and how big
skrog migrate --from-context hawserskrog migrate is documented for Docker Desktop, but the mechanism is
engine-to-engine and --from-context takes any docker context. It never writes
to the source, so an interrupted run leaves the Hawser side untouched. This
path has not been exercised against a Hawser install — run the --dry-run
first and keep Hawser until you have checked what arrived.
A new mark
The coiled rope was a picture of the old name. The mark is now a hull's body
plan — the section a naval architect draws at each frame, with the centreline
through it. It is drawn as an outline, so the stroke weight is corrected per
size: 15 design units on a 512 px tile, 28 at 16 px, which is what keeps the
tray icon and favicon from greying out. (#231)
Fixed
- The installer was broken by the rename and only the published one-liner
showed it — the asset it fetched no longer existed under that name.
(#229) - The installer was not republished when it changed, only when the docs did,
so the fix above merged green without reaching the URL people run.
(#230) - The engine manifest pointed at rootfs tarballs that do not exist. The
rename rewrote the filenames of assets that were already published, so every
skrog installandskrog engine rollbackwould have 404'd. It passed CI
because the rename also rewrote the test that guards it. A network-tagged
check now asks GitHub whether each published URL resolves.
(#232)
Verifying this release
# 1. Did GitHub Actions build this, from this repository, at a known commit?
gh attestation verify skrog_0.4.0_windows_amd64.zip --owner wslkit
# 2. Is the checksum list itself authentic?
cosign verify-blob --bundle SHA256SUMS.cosign.bundle `
--certificate-identity-regexp '^https://github.com/wslkit/skrog/' `
--certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
# 3. Does your download match the signed list?
sha256sum -c SHA256SUMS --ignore-missingBinaries are still not Authenticode-signed, so SmartScreen will warn. That
is tracked in #77.
Hawser 0.3.1
This release predates the rename. The project was called Hawser until
after v0.3.1; it is now Skrog, under thewslkitorg. The assets below
keep the names they were published with and still verify. The install
command has been corrected to the current URL — GitHub redirects repository
URLs after an org rename but not GitHub Pages URLs, so the original link no
longer resolves. See #1.
Releases are verifiable from this one onward. Every artifact now carries SLSA build provenance, and SHA256SUMS is signed with cosign keyless — two independent checks that v0.3.0 could not offer, because both landed two days after it was tagged.
# 1. Did GitHub Actions build this, from this repository, at a known commit?
gh attestation verify hawser_0.3.1_windows_amd64.zip --owner hawserhq
# 2. Is the checksum list itself authentic?
cosign verify-blob --bundle SHA256SUMS.cosign.bundle `
--certificate-identity-regexp '^https://github.com/hawserhq/hawser/' `
--certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
# 3. Does your download match the signed list?
sha256sum -c SHA256SUMS --ignore-missingBinaries are still not Authenticode-signed, so SmartScreen will warn. That is tracked in #77.
Install
irm https://wslkit.github.io/skrog/install.ps1 | iexNew in this release: it downloads the newest release, verifies it against SHA256SUMS, unpacks it and adds it to your PATH. It deliberately stops there — hawser install provisions the engine, and that is not something a piped command should do unasked.
New
hawser policy— local admission control for the docker API. Refuse--privileged, capabilities, host namespaces, bind sources or registries this machine has ruled out, before the engine sees them. A guardrail against mistakes, not a boundary against a hostile local user, and it says so. (#120)hawser upgrade— one answer to "am I current?" across the app, the engine and the bundled docker CLI, and it applies the two it owns. It also surfaces an ordering dependency nothing else showed: the engineshawser engine upgradecan reach are pinned in the binary you are running, so a newer engine can need a newer hawser first. (#191)hawser restart --supervisor— the supervisor process could not be restarted at all before; the only way was to kill a PID by hand. (#202)- A documentation site at https://hawserhq.github.io/hawser/, generated from
docs/, with a command reference generated from the binary's own--helpso it cannot drift. (#197, #209) - Experimental AMD GPU support through the same hookless CDI spec as NVIDIA. Untested on real hardware — if you have a Radeon, #185 wants to hear from you.
Fixed
hawser config set audit ondid nothing. The supervisor read the setting once at startup, and the help text told you to runhawser restart— which bounces the engine, not the supervisor holding the setting. The command exited 0, printed no error, and produced no audit log. Settings are now re-read live wherever they are consumed. (#202)- Corporate-network settings were captured at supervisor launch, so
hawser config set network.proxy …followed byhawser restartre-applied whatever was set at logon. (#202) hawser uninstalldeleted the sharedhawserdocker context, breaking any other install on the machine — including the developer's own, after running the acceptance suite. It now hands the context back. (#217)- The recorded rootfs URL depended on a GitHub redirect that can lapse or be taken over. It is normalised to the project's current home. (#212)
hawser audit tail -n 5was rejected, though that is the form the command's own usage line advertises.- The tray flashed a console window on every status poll, and spawned a process every four seconds to recompute state the supervisor already publishes. (#195, #192)
- The engine rootfs shipped no licence text for the software inside it. It now ships all five components' licences. (#205)
Upgrading from 0.3.0
hawser upgrade does not exist in 0.3.0, so this one is a manual step — run the installer above, or download the zip and unpack it over your existing install. Stop the engine first (hawser stop, and quit the tray) so the binaries are not locked. From 0.3.1 onward, hawser upgrade reports and applies.
Your engine, images, containers and volumes are untouched by an app upgrade.
rootfs 29.8.0-2
A revision bump with no engine change: containerd (2.1.4), runc (1.3.0), BuildKit (0.27.0), Alpine (3.24.1) and nvidia-cdi-hook (1.20.0) are identical to 29.8.0-1, and the engine is still moby docker-v29.8.0 pinned to commit 3ce5872b7950c63ba2ffbc5123101019ff3e6682.
What changed is inside the tarball: the in-distro agent is skrog-agent, not hawser-agent. Renaming the project renamed the binary the rootfs build produces, but every rootfs published before this one still carries the old name — a name crossing a boundary that cannot be renamed with it. Skrog looks for either, so nothing broke; this release is what lets that compatibility lookup go away.
The asset names change with it, for the same reason: this is the first rootfs published as skrog-rootfs-*. Earlier releases keep the hawser-rootfs-* names they were published under, and internal/release/manifest.json records them that way (#232) — a published asset's filename is a fact, not text to be renamed.
Verify before use:
# the checksum the manifest pins
sha256sum -c skrog-rootfs-29.8.0-2.tar.gz.sha256
# and that GitHub Actions signed that checksum list
cosign verify-blob --bundle skrog-rootfs-29.8.0-2.tar.gz.sha256.cosign.bundle `
--certificate-identity-regexp '^https://github.com/wslkit/skrog/' `
--certificate-oidc-issuer https://token.actions.githubusercontent.com `
skrog-rootfs-29.8.0-2.tar.gz.sha256rootfs 29.8.0-1
Rootfs 29.8.0-1 moves the engine to moby docker-v29.8.0 (upstream released 2026-09-03), from 29.7.2. It also closes a drift: the bundled docker CLI was already 29.8.0, so hawser cli install had been putting a 29.8.0 client in front of a 29.7.2 daemon.
containerd (2.1.4), runc (1.3.0), BuildKit (0.27.0), Alpine (3.24.1) and nvidia-cdi-hook (1.20.0) are unchanged. Those pins are a tested combination rather than a race to the newest tag, and moving one is a separate, justified decision — see docs/bumping-upstream.md.
The moby tag is pinned to commit 3ce5872b7950c63ba2ffbc5123101019ff3e6682, which the build verifies before compiling.
rootfs 29.7.2-4
Rootfs 29.7.2-4 adds a statically linked nvidia-cdi-hook (v1.20.0, Apache-2.0) so moby registers its NVIDIA GPU driver at daemon start and docker run --gpus all routes to the hookless CDI spec that hawser enable-gpu installs. The hook is never executed — its presence is what enables the driver — and VS Code Dev Containers' "hostRequirements": {"gpu": true} goes through the same path.
Engine, containerd, runc and BuildKit pins are unchanged from 29.7.2-3.
Hawser 0.3.0
Third milestone: the diagnosis-and-configuration layer, plus the security hardening from an independent multi-agent review of v0.2.
Highlights
hawser doctor— turns the WSL2 quirk zoo into a diagnosable surface. Eight checks seeded from failures this project actually hit (WSL platform, whichdocker.exewins on PATH, credential-helper resolution, engine install, docker context, supervisor/engine agreement, disk space, unattended-startup readiness). Human text,--jsonfor CI,--report(Markdown to paste into an issue), and--fixfor the safe subset.- Validated engine settings —
hawser config set engine.<key>edits the engine'sdaemon.json(registry mirrors, insecure registries, DNS, logging, concurrency) through a curated allowlist. Every change is run throughdockerd --validatebefore it replaces the live file, applied atomically, and rolled back if the engine does not come back — where Docker Desktop offers a raw JSON textbox that can brick the daemon. - Lifecycle hooks — run your own script on
post-start/pre-stop/on-idle-stop/on-wake, time-bounded and best-effort (a failing hook is logged, never blocks the engine).hawser config set hook.post-start <path>. - Declarative installs —
hawser install --config hawser.yamlprovisions and converges a whole install (distro, engine version, idle timeout, engine keys, hooks, integrations, autostart) idempotently;hawser config exportround-trips an existing machine into a file. Infrastructure-as-code for a fleet — the free answer to Docker Desktop's paywalled Settings Management. - Security hardening (independent 4-agent review of v0.2, all findings fixed) — per-install mutual authentication on the vsock channel; the docker pipe DACL scoped to the installing user instead of all interactive users; the supervisor's single-instance lock rewritten from a thread-affine named mutex to a file lock; a health probe that no longer boots a stopped distro; rootfs built from SHA-verified upstream sources; and a documented trust-boundary model.
- Idle-stop bug fixed — the shared-socket "busy" veto was counting Hawser's own health probe and keeping the engine up forever; it is now scoped to installs that actually share the socket via
wsl-integrate.
Install
Download hawser_0.3.0_windows_amd64.zip (or arm64), unzip, and run hawser install. The rootfs is checksum-verified against the embedded manifest. Requires any docker CLI on PATH (Docker Desktop's works; a bundled CLI is planned for a later release).
New in v0.3: run hawser doctor if anything misbehaves, and see docs/hooks.md and docs/declarative-install.md.
Notes
- Unsigned (signing lands in v0.4) — SmartScreen will warn; verify against
SHA256SUMS. - Unattended/CI use needs a logged-on session — see docs/auto-logon-runner.md.
- Pre-release: validated end-to-end on Windows 11 + WSL2 via the 22-stage acceptance suite, on a single machine. Field-test before relying on it broadly.
Engine: dockerd 29.7.2, containerd 2.1.4, runc 1.3.0, buildkit 0.27.0 (rootfs 29.7.2-3, built from source).
Rootfs 29.7.2-3
Engine rootfs, packaging revision 3 of engine 29.7.2. Engine components are unchanged from 29.7.2-2 (dockerd 29.7.2, containerd 2.1.4, runc 1.3.0, buildkit 0.27.0, Alpine 3.24); this revision rebuilds the in-distro hawser-agent with the v0.2.0 security-review hardening:
- Mutual authentication (#81) — the agent (now
hawser-agent/2) proves a per-install secret in the vsock handshake, so a sibling distro cannot impersonate it while the engine is idle-stopped. Backward compatible: a host without the secret still connects. - fd-race fix (#84) —
CloseWritepins the fd viaSyscallConn, so a concurrent close can't half-close a stranger's connection under load. - Hardening (#92) — a handshake read deadline and EMFILE-tolerant accept loop.
Also, the build now verifies each engine component's commit SHA and pins the Alpine base by digest (#88).
Assets (attached by the rootfs workflow): hawser-rootfs-29.7.2-3.tar.gz, its .sha256, and an SPDX SBOM. Becomes the default once the manifest PR lands.
Hawser 0.2.0
Second milestone: the always-on layer, native-speed transport, and Docker Desktop parity for trying it out.
Highlights
- Always-on supervisor — starts at logon, keeps the engine alive across crashes,
wsl --shutdown, and sleep/resume.hawser start|stop|restart|status, autostart via a windowless launcher, and a Warn+ mirror to the Windows Event Log. - vsock guest agent — replaces per-connection
socatwith an in-distrohawser-agentover AF_HYPERV.docker versionis ~80 ms — at parity with Docker Desktop (~83 ms) — and interrupted-client connection leaks are gone. Falls back to socat automatically on an older rootfs. - Idle timeout + on-demand start —
hawser config set idle-timeout 30m: the engine stops when the bridge is quiet and no containers run, then cold-starts (~0.8 s) on the nextdockercommand.hawser statusreportsidledistinctly from stopped. hawser wsl-integrate <distro>— share the engine into your own WSL distros (DOCKER_HOSTto a/mnt/wslsocket), usable as the ordinary user.hawser migrate --from-desktop— copy images and named volumes out of Docker Desktop, non-destructively;--dry-run,--only, resumable.- Status-light tray (
hawsertray.exe, optional) — a six-item menu that shells out to the CLI; no control panel, ever. - Interactive
docker run -i/exec -ifixed — the byte-mode named pipe could not carry a half-close, so piping into containers hung; the bridge now serves the pipe in message mode. Interactive TTY (-it), resize, and Ctrl-C forwarding validated against Docker Desktop parity.
Install
Download hawser_0.2.0_windows_amd64.zip (or arm64), unzip, and run hawser install. The rootfs is checksum-verified against the embedded manifest.
Notes
- Unsigned (signing lands in v0.4) — SmartScreen will warn; verify against
SHA256SUMS. - Unattended/CI use needs a logged-on session — see docs/auto-logon-runner.md.
- Pre-release: validated end-to-end on Windows 11 + WSL2 via the 18-stage acceptance suite, on a single machine. Field-test before relying on it broadly.
Engine: dockerd 29.7.2, containerd 2.1.4, runc 1.3.0, buildkit 0.27.0 (rootfs 29.7.2-2, built from source).
Hawser 0.1.0
The first installable Hawser: the upstream Docker Engine on Windows via WSL2, reached by stock docker.exe over a named pipe. No license fees, no Electron, no Kubernetes.
What works
hawser install # verified rootfs download -> WSL2 distro -> engine start -> docker context
hawser proxy # serves the pipe, translates Windows bind paths, coexists with Docker Desktop
docker --context hawser run --rm hello-world
hawser uninstall # removes everything it created, restores your previous context- The real Docker API — compose, bind mounts with Windows paths (
-v C:\src:/app),exec,logs -f, and anything that speaks todocker.sockwork unmodified - Coexists with Docker Desktop: never takes the default pipe if Desktop holds it; both engines usable side by side
- Deterministic by contract: the engine (dockerd 29.7.2, containerd 2.1.4, runc 1.3.0, buildkit 0.27.0) is pinned in an embedded manifest, checksum-verified before import, and built from upstream source — the SBOM points at commits
- Verified by a 13-stage acceptance suite on real WSL2, including a compose stack, an interrupted client, and a Docker-Desktop-still-works check
Honest limits
- Pre-release, unsigned binaries — SmartScreen will warn; verify against
SHA256SUMS(signing lands in v0.4) - A logged-on session is required (a WSL2 constraint that binds every WSL-based engine; auto-logon covers headless runners)
hawser proxyruns in the foreground; the always-on supervisor is v0.2- Interrupted clients can leave relay processes alive for up to 5 minutes (bounded; fully fixed by the v0.2 agent)
- Linux containers only, x64 and arm64 Windows
Install
- Download the zip for your architecture below and extract
hawser.exe hawser.exe installhawser.exe proxy(keep it running)docker --context hawser run --rm hello-world
Binaries are attached automatically by the release workflow, with SHA256SUMS.