# Changelog **Docs:** [README](README.md) · [SPEC](SPEC.md) · [ARCHITECTURE](docs/ARCHITECTURE.md) · [RESEARCH](docs/RESEARCH.md) · [COMPARISON](COMPARISON.md) · [AGENTS](AGENTS.md) · [CHANGELOG](CHANGELOG.md) · [CI](docs/CI.md) ## 1.6.0 - 2026-10-25 - `--mount`/`--davmount` with a `dav://`, `davs://`, `http://` or `https://` source: auto-mounts WebDAV. doos downloads, GPG-verifies against a pinned key and installs `rclone` once under `$DOOS_HOME/tools/` (never the system, never `PATH`, same trust model as the catalog images), then drives it on the host with an on-the-fly connection string (no config file, password passed through `rclone obscure`, never on the mount's command line) before the container's namespaces exist. The mount is deduplicated by target (url + credentials + vendor), reused across containers and attaches, and runs detached so it outlives the `attach` that created it. `--dav-user`, `--dav-pass`, `--dav-vendor` and `--dav-insecure` configure a target without putting credentials in the URL. Tracked as gap G13 in [RESEARCH](docs/RESEARCH.md); measured there against a local `rclone serve webdav` fixture (install, reuse at ~40 ms, write-back durability, and each failure mode: wrong credentials, unreachable server, non-WebDAV endpoint). - `doos davmount list` / `doos davmount umount [--yes] ID`: inspect or tear down a managed WebDAV mount; dry-run by default like `delete`/`image delete`. - Fix (found while building the above): the classic `--mount SRC:DST[:ro]` could not take credentials with `:`/`;` in them because mounts were serialized as one delimited line; container `meta` now stores mounts as indexed keys (one field per line), with the old single-line format still read for containers created under 1.5.0. - Refactor: `curl`/gpg download-and-verify helpers moved from `image.cpp` into `util.{hpp,cpp}` (`fetchText`, `fetchFile`, `TempDir`, `isHex64`, `verifyDetachedSignature`, `verifyClearsignedSignature`) so `davmount.cpp` reuses the exact same, already-tested verification code instead of a second copy; no behavior change (full image signature test suite re-passes). - New dependency, only for davmount: `unzip` (to unpack rclone's release archive) and `fusermount3`/`fusermount` (to unmount it); documented in README/AGENTS. - Tests: 10 new end-to-end checks against a real local WebDAV server (read, write durability, idempotent reuse, `--davmount` with `--dav-user`/`--dav-pass`, read-only, wrong-password classification, unknown vendor rejected, `davmount list`/`umount`). - Docs: [COMPARISON](COMPARISON.md) section 4's "Where Docker is better" no longer lists bind mounts as a doos gap (stale since 1.5.0); charts and generated numbers re-measured. ## 1.5.0 - 2026-10-24 - `--mount SRC:DST[:ro]` (repeatable, max 8): bind-mounts a host directory into the container, outside the `--size` cap. Lets a container reach storage that is not local, for example a WebDAV share mounted on the host first with `rclone mount` or `davfs2`; doos never speaks a network filesystem protocol itself, it only bind-mounts what the host already mounted. Changeable like `--memory`/`--pids`/`--cpus`: replaces the whole mount list, applies at the next start. Tracked as gap G12 in [RESEARCH](docs/RESEARCH.md); 5 new end-to-end checks (read, write, read-only, missing source rejected, too many rejected). - Docs: [COMPARISON](COMPARISON.md) section 1 and the gaps list updated (doos now has host-dir bind mounts); charts and generated numbers re-measured (`make bench && make charts`). ## 1.4.0 - 2026-10-24 - CI: `.gitea/workflows/ci.yml` (Gitea Actions) runs `build`, `valgrind`, `test` and `audit` on every push. `build`/`valgrind` run on any Docker-mode runner; `test`/`audit` need a self-hosted host-mode runner (real namespaces, cgroup v2 delegation, the AppArmor profile), documented with its one-time setup in the new [docs/CI.md](docs/CI.md). Every step wraps its own `timeout`, since Gitea Actions parses but does not enforce `timeout-minutes`/`concurrency` (checked against the 1.25 docs). - Fixed, from [review2.md](review2.md): `parseSize` (`src/util.cpp`) overflowed silently on an oversized input (`--size 16777217t` was accepted as exactly `1.0T` instead of rejected); now checked before the shift and rejected. Tracked as gap G9 in [RESEARCH](docs/RESEARCH.md); regression case added to `tests/run.sh`. - Fixed, from [review2.md](review2.md): `newuidmap`, `newgidmap`, `slirp4netns` and `pasta` were each pinned to one hardcoded `/usr/bin/...` path; they now go through `resolveBinary()` (state-dir override, then a fixed list of system directories), the same resolution `curl`/`tar`/`sha256sum`/`gpg` already used. Tracked as gap G10. - Docs: the `setns`-before-hardening ordering invariant in `Sandbox::join` and `Sandbox::start` (seccomp denies `setns`, and a filter is inherited for life) is now a comment at both call sites, not only in reviewer heads. The hand-rolled Alpine/Debian catalog parsers are logged as an accepted trade-off, gap G11, rather than left undocumented. - Added `LICENSE` (MIT). - Docs: README's two overlapping, partly stale intro blocks (a leftover `currently 1.1.3` version line, a narrower duplicate usage example) collapsed into one accurate one. ## 1.3.1 - 2026-10-24 - Docs: gap G8 (`fsutil::mkdirs` following a planted symlink, [RESEARCH](docs/RESEARCH.md)) marked fixed, re-measured with `make -C audit/CVE-2021-30465 test` (PASS); removed from [COMPARISON](COMPARISON.md)'s gaps list. The fix itself shipped in 1.3.0 as part of the security-review hardening; this corrects the docs to match. ## 1.3.0 - 2026-10-24 - Security review applied ([review.md](review.md)): `slirp4netns`, `pasta`, `newuidmap` and `newgidmap` are never searched on `PATH`; they exec via absolute paths with availability checked up front, and the helpers run with a scrubbed `PATH`. - Compat-directory mounts pin the parent through an `O_PATH` fd so a symlink swap between check and use cannot happen (TOCTOU); symlinks whose target cannot be pinned are not recreated, fail closed instead. - `--i-understand` is now required for `--hardening relaxed` and `--net host`; reduced sandboxing is also reported loudly at attach time. - Tests: the e2e suite covers all of the above (`passed=88 failed=0`). Docs: [RESEARCH](docs/RESEARCH.md), [SPEC](SPEC.md), [README](README.md) updated in the same change. ## 1.2.1 - 2026-10-23 - `scripts/release.sh`: commit trailer model name corrected (no functional change). ## 1.2.0 - 2026-10-23 - `audit/`: 10 more named, sourced CVEs (18 total): the overlayfs-in-unprivileged-userns family (`CVE-2021-3493`, `CVE-2023-2640`, `CVE-2023-32629`, `CVE-2015-1328`, directly doos's own mount on every attach), `CVE-2022-0492` (cgroup v1 release_agent; doos mounts no `/sys`), `CVE-2022-27650` (inheritable capabilities at container start), `CVE-2019-16884` and `CVE-2023-28642` (runc's `/proc` mount-target handling, tested against doos's own `/proc` mount), `CVE-2018-12020` (GnuPG SigSpoof, static check of the exact `gpg` invocation in `src/image.cpp`), `CVE-2020-7039` (libslirp memory-safety cluster, version check against `slirp4netns --version`). ## 1.1.5 - 2026-10-23 - `audit/`: 8 named, sourced CVEs relevant to doos's own mechanisms (overlay writes, tar extraction, mount/directory creation, net namespace sharing, the mount-family seccomp denylist, kernel user-namespace exposure), each with its own `README.md`, `Makefile` and non-destructive `reproduce.sh` against `build/doos`. `make audit` runs them concurrently (`-j8`, recursive make); `make -C audit list|report` also available. Kernel memory-corruption CVEs (Dirty Pipe, Dirty COW, `legacy_parse_param`) are checked by kernel version only, no exploit code. - Found by the audit and disclosed: `fsutil::mkdirs` follows a symlink planted at a container's path, so a symlink under `$DOOS_HOME/containers/` redirects where that container's state is written (low severity: needs write access to the caller's own `$DOOS_HOME`). Tracked as gap G8 in [RESEARCH](docs/RESEARCH.md) and in [COMPARISON](COMPARISON.md); not yet fixed. - Docs: `scripts/check-docs.sh` excludes `audit/*.md` (a separate, self-contained doc set); `README.md` and `AGENTS.md` link to `audit/README.md`. ## 1.1.4 - 2026-10-23 - Docs: [COMPARISON](COMPARISON.md) section 6.1 answers whether doos is a security-wise Docker replacement (same class as rootless Podman, not for untrusted code), summarized in the README safety summary. ## 1.1.3 - 2026-10-23 - Charts: startup time, executable size and CPU limit as SVG (light and dark palettes, doos highlighted), embedded in [COMPARISON](COMPARISON.md) and [RESEARCH](docs/RESEARCH.md). - Reproducible measurements: `scripts/bench.sh` writes `docs/charts/bench.json`; `docs/charts/make_charts.py` builds the charts and rewrites every generated number and table in the documents from it, so text, tables and charts cannot drift apart. - `Makefile` wrapper (`make help`), expanded `.gitignore`. ## 1.1.2 - 2026-10-23 - Docs: new [COMPARISON](COMPARISON.md) (doos against Docker, rootless Docker, Podman, bubblewrap, systemd-nspawn, LXC/Incus, Apptainer, Distrobox/Toolbx, gVisor, Kata/Firecracker) with sourced pros and cons, startup and disk measurements against Docker, Podman and bubblewrap on the same machine, a gaps list tied to code, and a conclusion. - Docs: every document now has a navigation line and links to every other document. `scripts/check-docs.sh` enforces it (and that relative links resolve) and runs inside `scripts/release.sh`. ## 1.1.1 - 2026-10-23 - `doos apparmor` prints a profile for the exact path of the running binary (own profile name per path, so several installs coexist). A start that fails because of AppArmor now prints the install command instead of only `mount bind /: Permission denied`. Verified with a copy in `~/.local/bin`. - `apparmor/doos` (packaging) covers more standard install paths. - Docs: Networking section with a verified example per mode (slirp, pasta, host, none), the inbound/loopback behavior of each, and the pasta port-detection delay. - Tests: 86 checks (adds apparmor output, `none`, slirp has no inbound path, pasta container to container). ## 1.1.0 - 2026-10-23 - Limits: `--memory`, `--pids`, `--cpus` (cgroup v2; whole-container OOM kill; reports OOM kills and pid-limit hits). Later attaches run in their own cgroup leaf with the same limits. - Hardening (default): Docker's default capability set, a seccomp filter (arch check, x32 guard, clone3 ENOSYS, no nested user namespaces, no mounts/keyrings/bpf/perf/io_uring/...), host `/etc` allowlist. `--hardening relaxed` turns it off per container. - Networking: `--net pasta` (host-like reachability, private namespace and abstract sockets). `--net host` now prints a note. - Size cap: measured from inside the id map (counts other uids), paced by scan cost (2 s floor), `RLIMIT_FSIZE` bounds single files. - Images: Debian in the catalog (`debian`, `debian-RELEASE`); Alpine and Ubuntu signatures verified against pinned key fingerprints. - Fix: a failed `get_or_create` no longer leaves an unreadable container directory behind. - Tests: 83 end-to-end checks. ## 1.0.0 - 2026-10-23 Breaking: state layout changed (private `/etc`, `meta` keys). Containers from 0.x must be deleted and recreated. - Images: `doos image list|add|delete`, `--image NAME` on `get_or_create`/`attach` (fetched on demand). Catalog: `alpine`, `ubuntu`, `ubuntu-VERSION`; also https URL with `--sha256`, or a local tarball. sha256 verified before extraction. - `--size`: cap for what a container writes (default 4g). Warns at 80%, freezes at 100% via cgroup v2, thaws below 95%. - `--ids`: subuid mapping through newuidmap/newgidmap by default (chown, su and apt's `_apt` work); falls back to one id. - Host mode fixes: directory skeleton so host-owned directories are writable, private copy of `/etc`, pre-seeded `/var` and dpkg state. `apt-get install` now completes. - Deleting trees with subuid-owned files works (runs inside the id map). - Usage measurement counts hard links once. - Tests: 56 end-to-end checks, including real image downloads, apt/apk installs and the size freeze. ## 0.1.3 - 2026-10-23 - Docs: field trial of apt and common tools, new gap G7 (host-owned lower layer), README limits section. No code change. ## 0.1.2 - 2026-10-23 - Docs: researched solutions for all known gaps (G1 to G6) in `docs/RESEARCH.md`, with measurements. Nothing implemented yet. ## 0.1.1 - 2026-10-23 - Docs: AGENTS.md release workflow, ARCHITECTURE.md, expanded README, this changelog. - Tooling: `scripts/release.sh` (bump, build, test, commit, tag). ## 0.1.0 - 2026-10-23 - First release: `list`, `get_or_create`, `attach`, `delete`. - overlayfs over host `/usr /etc /opt`, user/mount/pid/uts/ipc/net namespaces. - Networking: slirp4netns (default), `host`, `none`. - End-to-end test suite and AppArmor profile for Ubuntu 24.04+.