34 KiB
Changelog
Release notes for consumers of the werkdock CLI, Werkator first among them.
Each entry says what changed, what still works unchanged, and what a consumer
should adapt — and by when.
A release is a merge to main; there is no separate release step. So each
branch sets the version in internal/cli/cli.go (what werkdock version
prints) and titles its section here with that version and the merge date,
before it is merged. Nothing on main is ever "unreleased". A branch that
changes nothing a consumer notices gets a section marked as internal.
0.13.3 (2026-10-20)
Theme: bwrap starts with an empty environment. Since werkator PR#166 werkdock's own environment holds the values of a build's variables, with names a branch may choose; bwrap, a host process, inherited them. It needs none: its arguments and fd 4 carry everything.
For consumers
- Nothing changes in the CLI, the API or what the sandbox sees.
0.13.2 (2026-10-20)
Theme: secrets off the command line. werkdock passed each variable
of the sandbox as bwrap --setenv KEY VALUE, so every ps showed the
secrets of a build. bwrap now reads them from a pipe (--args 4);
its command line names none. And each instance, also of run --rm,
wrote its environment to instances/<id>/config.json with mode 0644;
an instance directory is now 0700, its files and logs 0600.
For consumers
- Nothing changes in the CLI, the API or what the sandbox sees.
- A value with a NUL byte is refused; no environment can hold one.
- Another account no longer reads an instance's files; a tool that did must run as the account that owns the store.
- Pass a secret as a bare
-e KEYwith the value in werkdock's own environment, as Werkator does since werkator PR#166; a-e KEY=VALUEstands in werkdock's own command line.
0.13.1 (2026-10-20)
Theme: the Go that builds werkdock. go.mod names the toolchain
go1.26.10. With GOTOOLCHAIN=auto, the default, an older Go loads it
and builds with it: the CI's build image has Go 1.24.4.
Until now the Go of the machine built the binary, and with it the
standard library: govulncheck found 7 vulnerabilities that werkdock calls
in go1.26.8, among them in net/http, which serves the API socket, and
more in older Go. The binary on mih09 came from go1.22.2.
For consumers
- Nothing changes in the CLI or the API. Install the new binary on
mih09;
go version -m werkdocknames go1.26.10. - Building from source with an older Go loads go1.26.10 by default
(
GOTOOLCHAIN=auto) and needs the network for it once; withGOTOOLCHAIN=localthe older Go builds, as before.
0.13.0 (2026-10-20)
Theme: the verb that was "not planned".
werkdock build builds an image from a Dockerfile on the target itself — no second machine, no upload.
RFC 0004 had ruled it out because it would need "writable rootfs snapshots per step and pull for FROM";
pull arrived in 0.3.0, and per-step snapshots turned out to be what a build cache needs, not what a build needs.
RFC 0006 has the measurements.
Added
werkdock build [-f Dockerfile] -t IMAGE CONTEXT(docker's flags).FROMresolves through the store and pulls on a miss, one writable work tree is created over the base, the steps run against it, and the result is an ordinary flat werkdock image —images,inspect,rmi,runand--mount type=imageapply to it unchanged. Staging is the mechanismimporthas used since 0.11.0: build in<image>.tmp, hold that image'sflock,renameat the end, so a failed build leaves no half image and a build racing animportof the same reference waits for it.FROM scratchstarts from an empty tree, as docker's reserved name does; nothing is looked up or pulled.- Stage 1 of the instruction set:
FROM,RUN,COPY, plus the metadata instructionsimport -calready knew —ENTRYPOINT,CMD,ENV,WORKDIR,USER,VOLUME,LABEL— through the samestore.ApplyChange, so a Dockerfile line and a--changeline are parsed by one piece of code. Line continuations with\, comments and blank lines are handled;RUNtakes the shell and the exec form. - Both uid levers are werkdock's business, not the Dockerfile's, so the same Dockerfile still builds under docker.
Measured on a Debian trixie rootfs with one mapped uid:
plain
apt-get updatedies withseteuid 42 failedandMethod http has died unexpectedly, so werkdock writesAPT::Sandbox::User "root"into the work tree for the duration of the build and removes it at commit.apt-get install nginxthen still fails —chown: changing ownership of '/var/log/nginx': Invalid argument, dpkg exit 1 — andfakerootalone does not fix it: itschownwrapper calls the real syscall and reports itsEINVAL.fakerootplusFAKEROOTDONTTRYCHOWN=1does, soRUNsteps are wrapped infakerootwhen the base image has one, andbuildsays so up front when it has not. - Two backends behind one interface, of which one is implemented.
The copy backend (a real full copy of the base, root bound read-write) works on the bwrap 0.8 the webspaces run today.
The overlay backend is designed, not written:
--overlay-srcstacks unprivileged on bwrap 0.11 and its upper directory is already a layer diff — measured, including deletions appearing as character devices 0:0, which is what OCI writes as.wh.NAME. It is what the per-step cache waits for; without a union mount a cached step would cost a whole tree copy. The copy backend stays as the fallback. doctorprobes the writable root bind and names the backendbuildwill use, so the two never disagree.
Changed
engine.RunSpechasWritableRoot: the rootfs is bound with--bindinstead of--ro-bind. Only build steps set it. A sandbox with a writable root also gets no implicit tmpfs over/tmp,/rootand/run— it does not need one, and with one, what aRUNstep writes there would be gone in the next step while docker keeps it in the image. Runs are unchanged: read-only root, tmpfs as before.werkdock image buildworks asdocker image builddoes, next to the top-level verb.
Refused rather than ignored
EXPOSE,HEALTHCHECK,STOPSIGNAL,ONBUILDandMAINTAINERfail at parse time with the reason, before a 2 GiB tree is copied.ARG,SHELL,ADDand multi-stage builds (FROM … AS,COPY --from) name the stage of RFC 0006 that brings them.COPY --chown/--chmodis refused: the sandbox maps one uid, and ownership is dropped when an image is unpacked anyway. On the flag side--build-arg,--target,--platform,--secret,--ssh,--cache-from,--cache-to,--squash,--network,--output,--load,--pushand--progresseach fail with what they would need.--no-cacheis accepted and reported as the no-op it is.mknodandmountinside aRUNstep cannot be made to work:CapEffis0000000000000000in the sandbox, measured, so every mount has to stand in bwrap's argv. BuildKit'sRUN --mount=type=cacheis out for that reason.
Known limitation
- No build cache.
Every build starts at
FROMand re-runs every step, networkRUNsteps included. This is the copy backend's one real cost and the reason the overlay backend is worth writing after the Debian 13 upgrade. A tree copy itself is cheap: 0.31 s for 129 MiB over 5448 inodes on local ext4, and 6.8 s for a whole build over a 143 MB base with anapt-get install nginx jqin it. For a ~2 GiB build image on shared webspace storage, budget tens of seconds — that is an extrapolation, not a measurement. Peak disk is base plus work tree until the commit'srename. - One
-tper build: a second reference for one image needs atagverb, which werkdock does not have yet.
For consumers
- Werkator can build its build environment on the target:
werkdock build -t werkator-buildenv:trixie .in place ofdocker buildelsewhere plusdocker save, upload andwerkdock load. The base image needsfakerootinstalled (oneapt-get install fakeroot), or every package whose postinst chowns will fail;buildreports the omission before it starts. Nothing existing changes:import,load,pull,runand the API behave as in 0.11.0, and an image built this way is indistinguishable from a loaded one.
0.12.1 (2026-10-20)
Internal: werkdock follows the shared agentic workflow of the Werk repositories, with reviews by werkrevisor, and its build checks the licenses and the called vulnerabilities of its dependencies. The CLI is unchanged.
0.11.0 (2026-10-05)
Theme: two imports at once, and a command that cannot start. Both seen on mih09, where Werkator 1.37.0 started a CI build and a pull-request review of one repository together, and both imported the same new rootfs.
Fixed
- Imports of one image wait for each other, across processes. Import
stages in
<image>.tmpand renames it into place, and it removes that directory first, so the next import cleans up after an interrupted one. A second import running at the same time removed the first one's files under it: one failed withlatest.tmp/image.json: no such file or directory, the other's sandbox could not execute/bin/sh. An import now holds an exclusiveflockonlocks/import-<hash>.lockbelow$WERKDOCK_HOME; the kernel drops it when the holder dies. The import that waited finds the image and refuses withalready exists, exactly as an import after it would.
Changed
werkdock runanswers 127 or 126 like docker when the command cannot be started: 127 when no executable of that name is in the image (missing: executable file not found in $PATH), 126 when one is there but cannot be executed (/bin/sh: permission denied). Until now bwrap's own exit code 1 came back, which a caller cannot tell from the command's own failure. The lookup follows execvp: the name itself when it holds a slash, else everyPATHdirectory in order, symlinks resolved inside the rootfs. A path under a mount, the implicit tmpfs areas included, is not checked, since its content is not known before the sandbox starts. Nothing is created for a refused command.createandstart, and so the Docker API, are unchanged: docker's create does not check the command either.
For consumers
- Werkator needs no change: since 1.37.2 it imports one image at a time
per instance itself, and the lock here also covers a second instance
or a shell. An exit code 127 or 126 of
werkdock runcan still come from the command —sh -canswers 127 for a missing tool — so it is no proof that the sandbox failed; the message on stderr says which.
0.10.0 (2026-09-11)
Theme: the port that lives in a file. Mosquitto, listed in 0.9.0 as the image werkdock cannot move, can be moved after all — not by anything mosquitto offers, but by the one thing werkdock can do that a flag or an environment variable cannot: hand the server a different config file.
New
- A quirk can name a config file.
PortFilesays which file the image ships names the port and how the line reads (listener 1883). Werkdock copies that file into the instance directory with the line rewritten to the chosen port and binds the copy read-only over the image's original, which is never touched. The mount is the last one ininspect, at the file's own path. It applies only while the command still names the file, and not when the client binds a file of their own over that path — then the copy would not be what the server reads, and the port is reported unmoved instead. - Mosquitto (
eclipse-mosquitto) moves this way: both listeners, MQTT on 1883 and the dashboard's HTTP API on 9883, out of/mosquitto/config/mosquitto.conf. Measured with 2.1.2 through the API socket: two instances at once, each on its own ports, each accepting an anonymous client and answering on its dashboard port, with 1883 left free. What was tried and does not work, so nobody tries again:-pis ignored once a config file declares a listener (the manual says so; measured), no environment variable names the port, and the config file expands none.
Refused rather than ignored
- An image whose config file no longer has the line werkdock expects is refused at create with the line and the file named, rather than started on a port werkdock only claimed. A quirk is measured knowledge about one image version; a changed image is a reason to measure again, not to guess.
Known limitation
- A file copied in through
PUT /containers/{id}/archiveafter create (Testcontainers'withCopyFileToContainer) lands over the rewritten copy, because copies bind after everything else. A client that supplies mosquitto's config that way gets the ports that config says, not the ones werkdock reported. Supply it as a bind (withFileSystemBind) instead, which werkdock sees at create and steps aside for.
0.9.0 (2026-09-11)
Theme: messaging. NATS, RabbitMQ and Mosquitto join the quirk table, each measured against the running image rather than its documentation — and each taught the table something it could not express before.
New
- NATS (
nats): moved with--portonnats-server, which wins over the port in the config file the image ships. Nothing else needed. - RabbitMQ (
rabbitmq): moved withRABBITMQ_NODE_PORT, runs asrabbitmqbecause its entrypoint chowns as root. It also needs a cap on werkdock's choice, which is new: the Erlang distribution port is the node port plus 20000, so anything above 45535 refuses to boot with "Invalid Erlang distribution TCP port". The kernel's ephemeral range starts at 32768, so more than half of werkdock's previous choices would have been wrong. A quirk can now sayMaxHostPort, and werkdock looks for a free port itself when there is one. - Mosquitto (
eclipse-mosquitto): runs asmosquitto— it drops privileges itself and cannot setgroups in a user namespace — and is listed with no port knob at all. Its port comes from the config the image ships, whose explicitlistenerswitches off the default one that-pwould set; measured, the flag is ignored. So werkdock reports 1883 unmoved, which means one instance at a time, the same rule that holds for every image not in the table. An entry that only names a user is a legitimate entry.
Refused rather than ignored
- A host port above an image's cap answers 400 with the reason, rather than starting a server that dies in an Erlang stack trace.
Unchanged
- Every CLI verb, every endpoint, the instance directory format, and the four images that were already in the table.
0.8.1 (2026-09-11)
Theme: a copy that stops one level too early. MariaDBContainer could not
start; anything that copies into a directory the image fills could not
either.
Fixed
-
A copied tree is merged at whatever depth the two first differ, not refused at the top. 0.7.0 decided once, at the top level of the archive: a directory the image filled was refused. Testcontainers copies mariadb's configuration to
/etc/mysql/conf.d, and docker's clients put the whole path in the tar, so the top level wasetc— refused, and the container never started. The rule now descends: bind where the image has nothing, descend where it has a directory, bind the file where it has a file. Copying into/etc/mysql/conf.dnow binds one file four levels down and leaves/etcalone.With it goes the 501 that said werkdock "would have to hide them": there is no longer a case where it would.
Unchanged
- Everything else about the endpoint, including what it still refuses: a copy into a running instance, symlinks and device nodes, entries that climb out of the destination, and archives above 64 entries.
0.8.0 (2026-09-11)
Theme: the quirk table, measured instead of assumed. Three of its four
entries were wrong, and each would have handed a client a port number that
nothing was listening on. A consumer running redis, mariadb, or mysql
through the API should take this release.
Fixed
redis,mariadb, andmysqlwere published on the wrong port. All three were listed as moving their port through an environment variable —REDIS_PORT,MARIADB_TCP_PORT,MYSQL_TCP_PORT. None of the three images reads one: redis-server takes--port, and the two database entrypoints read the port out of the server's own configuration. Werkdock chose a free port, told nobody, and reported it. Measured against the real images, not guessed: all three now move by a command-line flag and were verified to answer on the port they report.rediscould not start at all. Its entrypoint chowns its data directory as root, which an unprivileged user namespace cannot do; it now runs asredis, the same treatmentpostgresalready had.- An image's
/runwas empty./runis the instance's own directory so that sockets survive a stop, which meant nothing of the image's showed through — andmariadbdied with "Bind on unix socket: No such file or directory" because/run/mysqldwas missing. The instance's/runis now seeded from the image's: directories, and files small enough to be a word rather than data.
New
- A quirk may move a port by a command-line flag, not only by an environment variable, and does so only while the container still runs the image's own program — werkdock does not append a flag to a command a client chose. Where it cannot, the port is reported unmoved, as before.
Unchanged
postgreswas right and is untouched: it really does readPGPORT.- Every CLI verb, every endpoint, and the instance directory format.
0.7.0 (2026-09-11)
Theme: a file into a container — PUT /containers/{id}/archive, the last
endpoint Testcontainers asks for that werkdock answered with a 501. Purely
additive; nothing that worked before behaves differently.
New
PUT /containers/{id}/archiveunpacks a tar into an instance, which is whatwithCopyFileToContainerandMountableFilesend. An init script copied to/docker-entrypoint-initdb.dis now run by the image's own entrypoint, as on docker.- The copy does not go into the image. A rootfs in the store is shared by every instance of that image and bound read-only, so the tar is unpacked into the instance directory and bound in — whole, where the image has nothing at the destination, and file by file where it does, so that the image's own files stay visible beside the copied ones. That is docker's merge, reached by another road.
Refused rather than ignored
- Copying into a running instance answers 501 with the reason: a
sandbox's mounts are fixed when it starts, and werkdock has no writable
container layer to put the file in. Copy before start, as
withCopyFileToContainerdoes, or write through a volume. - Copying a directory onto one the image fills answers 501: the bind would hide what is inside, and werkdock will not do that quietly.
- A symlink, hard link, device node, or fifo in the archive answers 400 — each would have to mean something inside the sandbox that werkdock cannot promise — as does an entry that climbs out of its destination.
- An archive of more than 64 entries answers 400: every entry may become a bind, and a client that wants to populate a tree wants an image.
Unchanged
- Every CLI verb, every other endpoint, and the instance directory format
(
config.jsonstays version 1; a copy adds mounts to the spec it already holds).
0.6.0 (2026-09-10)
Theme: the API socket — RFC 0005 milestone 3, and with it milestone 4: Testcontainers starts a real PostgreSQL in werkdock. Purely additive; no CLI verb changes behaviour.
New
werkdock system service [--time D] [unix://PATH]serves the Docker Engine API subset a client actually uses, on$XDG_RUNTIME_DIR/werkdock.sockby default (podman's verb and podman's manners: optional, started when needed,--timeto exit when idle). A client is pointed at it withDOCKER_HOST=unix://<path>.- Testcontainers (validated with 1.19.3,
PostgreSQLContainer) works against it: pull, create, start, logs with the wait strategy, exec, kill, remove, and the session cleanup. It needsTESTCONTAINERS_RYUK_DISABLED=true, because Ryuk wants the API socket inside a container of its own. - The endpoints are those measured from a real run, written down in
docs/testcontainers-api.md. Everything outside the subset answers 501 with the reason, never 404, so a client that needs more says so plainly. - Instances carry docker labels (
config.json), and the instance list filters on them, which is how a client finds what it left behind. - An exec is a record of its own with an id and an exit code, because the API creates, starts, and inspects it in three calls.
Refused rather than ignored
logsparameters werkdock cannot honour say so with 501 and the reason:tail(the output is streamed whole),timestampsand a non-zerosince/until(no clock is recorded beside the output). What a client actually sends —stdout,stderr,follow,since=0,tail=all— is served.
Ports, the deliberate deviation
Werkdock shares the host's network and cannot map a port. Where a
client asks for a published port, werkdock therefore picks a free one
and tells the service to bind it, through the environment variable
the image honours (PGPORT, MARIADB_TCP_PORT, MYSQL_TCP_PORT,
REDIS_PORT). An image outside that small table keeps its own port,
which is right as long as one instance of it runs at a time; asking to
move it fails with the reason instead of silently reporting a port
nothing listens on. The same table carries the user an image needs when
its entrypoint expects to drop privileges itself (postgres), which
werkdock cannot do with one uid.
Version reporting
/version and /info report the Docker Engine version whose API is
served (25.0.0), not werkdock's: clients compare that field against a
minimum, and 0.x fails it forever. Werkdock's own version is beside it
in Components and in /info's OperatingSystem.
For Werkator
- Nothing to change. The service is not started unless someone asks for it, and no CLI verb behaves differently.
- New possibility: an integration test in a build sandbox can get a
real database. Bind the socket into the sandbox
(
-v $XDG_RUNTIME_DIR/werkdock.sock:/run/docker.sock) and setDOCKER_HOSTandTESTCONTAINERS_RYUK_DISABLEDin the build.
0.5.0 (2026-09-10)
Theme: the instance lifecycle — RFC 0005 milestone 2. A service image
runs in the background, can be stopped, restarted, queried, and talked
to. Additive for run --rm; two small semantic changes below.
New
run -dstarts the instance under a background supervisor and prints its 64-hex id;--name NAMEnames it (default: image basename plus six characters of the id);-ipasses stdin (no pty:-tis refused).ps [-a] [-q] [--format FMT]lists instances with docker's columns (CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES; PORTS stays empty, the network is the host's);stop [-t SECONDS],kill [-s SIG],rm [-f],wait,logs [-f],create,start, andinspecton an instance id, unique prefix, or name. Each prints what docker prints (the argument on success,werkdock: …and exit 125 per failure).exec [-e K=V] [-w DIR] [-u USER] [-i] INSTANCE CMD...runs a second command in the instance's sandbox: same rootfs, mounts, uid, env,/runand volumes; not the same pid namespace.- docker's management form is accepted:
container ls|start|stop|...,image ls|rm|pull|.... - State lives in
$WERKDOCK_HOME/instances/<id>/:config.json(version: 1),state.json,stdout,stderr,run/,volumes/. A state left "running" by a dead supervisor (reboot,kill -9) is repaired to exited 255 on the next read; an exited--rminstance found that way is removed.
Changed
- A
runwithout--rmno longer fails ("persistent instances are not implemented yet"); the instance stays forps -a,logs,start,rm. - The command is pid 1 of its sandbox (
bwrap --as-pid-1), as in docker, sostop's SIGTERM can reach it. As in docker, an entrypoint without a SIGTERM handler (sh -c '…') is only ended by the SIGKILL after the timeout, and orphaned children are its own to reap. - A foreground
runforwards the first SIGINT/SIGTERM/SIGHUP to the command and kills it on the second, or after 10 seconds; bwrap runs in its own process group, so Ctrl-C no longer kills bwrap (and with it the command) outright. Werkator's builds see the same exit codes as before; ash -cbuild interrupted with SIGTERM now ends 10 seconds later, on the SIGKILL, unless the script traps TERM. - Anonymous volumes (the image's
VOLUMEs) and/runare directories of the instance instead of tmpfs, so they survivestop/startand are visible toexec. For a--rmrun the difference is disk instead of RAM for those paths, and nothing else.
For Werkator
- Nothing to change:
run --rm …behaves as before. Thepsoutput is new and Werkator does not parse it.
0.4.0 (2026-09-10)
Theme: service images — the first milestone of RFC 0005 (PostgreSQL in the
sandbox, Testcontainers as the destination). Additive; a run without the
new flags on an image without USER/VOLUME behaves exactly as before.
New
run --user USER[:GROUP](-u) makes the caller appear as that user inside the sandbox: a name is resolved in the image's/etc/passwdand/etc/group, a number needs no entry.HOMEfollows the passwd entry when there is one. Still one uid only — the caller is mapped to it; there is no second user.run --userns=keep-id(podman's spelling) maps the caller to their own uid and gid. Other--usernsmodes are refused with the reason.- An image's
USER(OCI config) applies when neither flag is given, as with podman; unknown names fail withimage USER: …. Default stays uid 0. - An image's
VOLUMEpaths are mounted as anonymous tmpfs unless a-vor--mountcovers them, sopostgres:16starts without knowing its data and socket directories by heart. /runis a tmpfs in every sandbox, beside/tmpand/root./var/runpoints there in practically every image, and services keep pid files, locks and sockets under it without declaring aVOLUME(postgresneeds/var/run/postgresqlwritable). A--tmpfs/-von a path below/runstill works, it just lands inside the tmpfs.import -cacceptsUSER user[:group]andVOLUME /path(or a JSON array);inspectshowsUserandVolumesinConfig.
For Werkator
- Nothing to change. Builds keep running as uid 0. If a build image ever sets
USER, that user applies; add--user 0to keep root.
Podman before Docker
- Where the two differ, werkdock follows podman from now on (RFC 0005):
--userns=keep-idis the first case.
0.3.0 (2026-09-10)
Theme: werkdock pull — images come straight from a registry, no docker
on any machine needed (RFC 0002 level 2, RFC 0004). Additive; nothing a
consumer calls today changes.
New
werkdock pull [--platform OS/ARCH] [REGISTRY/]REPOSITORY[:TAG]fetches an image and stores it exactly asload -iwould: layers flattened, OCI config kept, listed byimagesunder the reference given. PrintsPulled image: REF; exit 125 with the reason if the registry refuses, the image already exists, or crane is missing.--platformdefaults tolinux/<host arch>.- The registry side is the
cranebinary (github.com/google/go-containerregistry, one static executable, no privileges). werkdock looks for it inPATH, then beside its own binary, or atWERKDOCK_CRANE. A deployment on a managed webspace ships it next towerkdock. Credentials come from~/.docker/config.json(DOCKER_CONFIG), as with docker. werkdock rmi IMAGE...removes images (docker rmi): printsUntagged: REFper image, tries every argument and exits 125 if one failed. Works on the legacy flat layout too, so Werkator's pre-0.2.0 build image can be removed with it.pullandloadnow namermiin their "already exists" message.werkdock imagestakes docker's--format: a Go template over.Repository,.Tag,.ID,.CreatedAt,.CreatedSince,.Size, ortable [TEMPLATE]for docker's column layout (--format tableprintsREPOSITORY TAG IMAGE ID CREATED SIZE);-qprints IDs; an optionalREPOSITORY[:TAG]argument filters. Without flags the output is unchanged: one short reference per line.inspectgains anIdfield. The ID is a hash of the image metadata, shared by tags of the same image; it is not docker's config digest.werkdock doctorwarns when crane is not inPATH(a warning, not a failure: everything butpullworks without it).
For Werkator
- Optional: replace "download archive,
werkdock load -i" with a singlewerkdock pull IMAGEwhere the image is a public registry image; the self-built archives keep going throughimport/load. - Deployment: put
cranebesidewerkdockifpullis to be used. - Optional: retire old build images with
werkdock rmiinstead of deleting store directories.
0.2.0 (2026-09-10)
Theme: the run surface, image names, and the image store move closer to
docker run --rm (RFC 0002, RFC 0004). Everything Werkator invokes today keeps working; two of its calls
now have a preferred docker-shaped form.
Compatible — nothing to change
werkdock imagesstill prints one image per line and still prints a bare name for images loaded without a tag. Werkator's exact-line existence check (image in loaded) matches as before. Images that carry a tag other thanlatestprint asrepository:tag.werkdock load -i ARCHIVE --name NAMEstill imports a rootfs archive; it now additionally prints one note on stderr pointing towerkdock import. Exit code and stdout are unchanged (Loaded image: NAMEbecameImported image: NAME— adjust if the stdout line is parsed).werkdock run --rm -v ... --tmpfs ... -e ... -w ... IMAGE CMD...is unchanged in syntax and mount order. Two semantic corrections match docker: a bare-e KEYwhose variable the host does not have is no longer passed asKEY=(it is omitted); a missing-w DIRis created when it lies in a writable area (tmpfs or a read-write bind) instead of failing with bwrap's "Can't chdir".- Existing stores: the flat layout
images/NAME/written by earlier versions is still read asNAME:latest. No reload after the upgrade; new imports land inimages/REPOSITORY/TAG/. The pre-existingimage.jsonfiles lack the new metadata fields and read back as an image without ENTRYPOINT/CMD/Env, which is exactly what they were. werkdock doctor,version, exit codes (125 for werkdock's own errors, the command's code otherwise): unchanged.
Preferred new forms — adapt at the next convenient change
werkdock import ARCHIVE [REPOSITORY[:TAG]]replacesload -i --namefor rootfs archives (docker importsemantics). Plan:load -i --namestays accepted until Werkator has switched, then it is removed together with the stderr note.werkdock load -i ARCHIVEloads adocker savearchive (legacy and OCI layout) or a plain OCI image layout: layers flattened into one rootfs, whiteouts applied, the OCI config kept as image metadata, the image named by its RepoTags (every tag becomes a hardlinked copy). A multi-arch index picks the host platform. This is the intended path for build environments from now on:docker build,docker save, upload,werkdock load— with ENTRYPOINT, ENV, and WORKDIR intact (RFC 0004). Layers compressed with zstd need thezstdbinary, asimportdoes.- Image names may carry a tag:
werkator-buildenv-<hash>keeps working and equalswerkator-buildenv-<hash>:latest; a consumer may also usewerkator-buildenv:<hash>or a registry-stylegit.javagil.de/mi/buildenv:<hash>. Name grammar:[registry/]repository[:tag], repository components lowercase[a-z0-9]with.,_,-between them, tag[A-Za-z0-9_][A-Za-z0-9_.-]*, max 128 characters. The.tmptag suffix is reserved. import -c 'ENTRYPOINT [...]',-c 'CMD ...',-c 'ENV K=V',-c 'WORKDIR /d',-c 'LABEL k=v'store OCI metadata with the image;werkdock inspect IMAGEprints it as a docker-shaped JSON array (RepoTags,Created,Architecture,Os,Config, plus aWerkdocksection withSourceandRootFS). A consumer that wants to check existence likedocker image inspectcan useinspect(exit 125 when the image is missing) instead of scanningimages.werkdock run IMAGEwithout a command runs the image's ENTRYPOINT+CMD;--entrypoint PROGRAMoverrides ENTRYPOINT and drops CMD; positional arguments replace CMD;-eand-woverride the image's Env and WorkingDir.--mount type=bind,src=SRC,dst=DEST[,readonly]and--mount type=tmpfs,dst=DESTare accepted beside-vand--tmpfs; all three feed one ordered mount list (the git-metadata mask ordering still holds across them).--network host(and--net host) is accepted as a no-op, so a build script can pass a docker-compatible command line unchanged. Other modes fail with:network mode "X" is not available in unprivileged shared-webspace environments; only "host" is supported.-p/--publishremain refused.
Incompatible
- None for the calls Werkator makes. For everyone else: an image name with an
uppercase letter or a leading
./-/_in the repository was accepted by the old name check and is refused now (docker's grammar);..-style names were refused before and still are.