NAILS v0.3.0-rc.3: one design for NixOS, Debian and Ubuntu
NAILS v0.3.0-rc.3 was published on October 20, 2026. It is a release candidate of NAILS 0.3 and the first 0.3 build on GitHub Releases. NAILS 0.3 replaces the per-directory overlays of earlier releases with one overlay over the whole decoy root, entered and left with a soft-reboot. Debian 13, Ubuntu 24.04 and Ubuntu 26.04 join NixOS as decoys.
Older posts and articles about NAILS describe 0.1 or 0.2. Those releases ran on
NixOS only, overlaid directories such as /etc, /home and /var of the
running decoy, and read a YAML configuration. NAILS 0.3 works differently, and a
setup made for 0.2 must be converted before you run 0.3. The sections below
cover what changed.
NAILS is alpha software. Interfaces and operator workflows can still change between alpha releases. Use it only after testing your exact hardware, storage backend, activation path and recovery procedure.
What a release candidate is
The
release process
defines it: “X.Y.Z is a stable release and X.Y.Z-rc.N a release candidate,
published as a prerelease.” Once published, a release, its files and its tag
never change. A problem found in a release candidate is fixed in a new -rc.N,
or in the next patch version.
0.3.0-rc.1 and 0.3.0-rc.2 were tagged but never published. The release run of rc.1 stopped at the SBOM attestation. The GitHub releases feed also lists tags that never got a release, because it shows a version as soon as its tag is pushed. A version is out once GitHub Releases lists it with its files.
The latest stable release is still v0.2.0, the earlier design for NixOS only. The docs on this site do not apply to it.
One overlay over the whole root
Earlier releases overlaid the running decoy directory by directory. The decoy kept running across activation, so every process that existed before had to be found and restarted or killed. A writer the scan missed, such as a service in its own mount namespace or a periodic writer that held no file open during the scan, went on writing to the decoy, and nothing reported it.
In 0.3, nails activate builds one OverlayFS overlay over the whole decoy root
at /run/nextroot, with the decoy root as its read-only lower layer and its
upper layer on your hidden volume. It then enters the overlay with
systemctl soft-reboot: systemd stops every decoy program, switches root into
the overlay and starts a fresh userspace there. Every process of the hidden
session starts inside the overlay, so there is nothing to find, restart or kill,
and what the session writes through the decoy’s mounted filesystems lands in the
upper layer. The few writes that go around the mount layer, such as EFI
variables or a disk the session mounts itself, are listed as residual channels
in the threat model.
nails deactivate soft-reboots back into the plain decoy and closes the hidden
volume on the way. nails emergency does the same at once, or powers the
machine off. Nothing that starts the hidden session is installed on the decoy:
no boot entry, initrd change, unit or binary. The nails binary stays on the
hidden volume, and you run it by its full path.
The design has costs. Both transitions log you out, because the desktop itself
runs inside the overlay. Kernel and service-manager state is not reset. The boot
ID, the uptime, the kernel ring buffer, the soft-reboot counter of systemd 256
and newer, and the network state the hidden session set up carry over until the
machine powers off. The decoy’s NetworkManager also records a link it adopts on
the decoy disk as soon as the decoy starts. Take down the connections you
brought up and delete the links you created before you deactivate, or end with
nails deactivate --poweroff. A soft-reboot keeps RAM too, so power off after a
session when that matters.
The decoy needs systemd 254 or newer. How it works walks through the session, and the architecture overview explains why it takes the whole root.
What NAILS does and does not do
As before, NAILS encrypts nothing itself. The hidden volume, and its deniability, come from the storage backend you choose, for example a VeraCrypt hidden volume. NAILS adds nothing to the backend’s claim. What it does is narrow what an offline examination of the decoy can find. It is built for one adversary: someone who gets offline access to the disks after the fact and may make you unlock the decoy once.
0.3 adds tools to check your own setup. nails check reports whether the
machine is ready to activate and changes nothing. nails rehearse plants a
random canary in a hidden session, then searches the raw decoy disk for it.
nails verify can now also scan an image of the decoy offline for known
artifacts. A clean nails verify is evidence, not proof.
Check your setup shows how to use these tools.
NAILS does not protect against anyone with access to the running machine, against memory forensics or cold-boot attacks, against network and remote records, or against compelled disclosure of the hidden volume. The threat model sets out the adversary, the assumptions, the residual channels and the other limits.
Debian and Ubuntu decoys
On NixOS, a declarative hidden configuration is built and entered in the same
step. Debian 13, Ubuntu 24.04 and Ubuntu 26.04 run the same session, with
whatever the hidden /etc holds. A later Debian or Ubuntu release, such as
Ubuntu 26.10, still runs, but init, check and activate warn that NAILS has
not been tested on it. nails check names the detected host in its platform
row. Derivatives whose ID_LIKE names debian or ubuntu are accepted
provisionally.
Debian 12 and Ubuntu 22.04 are refused, because their systemd is too old for a soft-reboot. Fedora and Arch are refused too. Support for them is planned, as the roadmap says. Automated tests run the session in NixOS, Debian 13 and Ubuntu 24.04 virtual machines. Requirements lists the supported platforms and the refused setups.
A known issue on Ubuntu 26.04
On Ubuntu 26.04, chrony (time sync) and CUPS (printing) fail after the switch
into the hidden session. AppArmor blocks their messages to systemd and to the
journal, whose sockets were set up before the switch. Other services that
AppArmor confines may fail the same way. In the hidden session the clock is not
synchronized and printing times out. After the way back, the decoy lists chrony
as failed until its next full reboot, which hints that a soft-reboot happened.
Reboot fully after a session to clear it. The fault lies outside NAILS: a plain
systemctl soft-reboot into another root, without NAILS, breaks chrony the same
way.
Upgrading from 0.2
0.3 breaks compatibility with the state, configuration and command line of 0.2,
and there are no compatibility shims. Upgrade only while NAILS is inactive: end
a 0.2 session with its own nails deactivate, or reboot, before you replace the
binary.
The configuration moves from config/nails.yaml to a strict config/nails.toml
next to the binary. 0.3 never reads the YAML file and does not warn about it, so
an unconverted setup runs with the defaults. Convert each key by hand.
nails init writes a documented template, and nails config show --example
prints every key.
The other changes, in short:
- The hidden volume must be storage of its own. NAILS refuses a folder, a subvolume or bind mount of a filesystem the decoy uses, a volume in the decoy’s volume group, an image file on a decoy filesystem, and overlay, FUSE, network and ZFS filesystems.
- The decoy root must be on a block device, with ext4, xfs, btrfs or f2fs.
An overlay root, a ZFS root and a tmpfs root, such as
nixos-impermanenceon/, are refused. A/varor/homeon its own tmpfs, wiped at boot, is fine. - A new layout. The hidden root’s layers live under
<hidden-root>/root/, and the uppers of the 0.2 overlays are not read. Move what you need into a hidden session, then delete them. - NixOS flake decoys. A decoy whose
/etc/nixosis a flake needs a self-contained hidden flake. - Commands and scripts.
activateanddeactivatenow ask for confirmation, so scripts, keybindings and units that run them need-y. Several flags were renamed or removed, and the exit codes, the JSON output and the state file changed. Delete astate.jsonthat 0.2 wrote, from a clean inactive system.
The upgrade notes in the changelog cover each of these in full, and the changelog’s Removed section lists every dropped key, flag and command. Upgrading from 0.2 in the install guide sums them up.
Verifying a release without a GitHub account
This is the first release with Sigstore bundles. It carries its three
attestations as provenance.sigstore.json, sbom.sigstore.json and
vex.sigstore.json. You check them against the downloaded files with cosign
3.1.3 or later, or 2.6.5 or later on the older 2.x line. The check asks GitHub
for nothing, so you need no GitHub account. By default cosign fetches Sigstore’s
public keys when it runs. On a computer without a network connection, you give
it the keys as a file instead. The release workflow runs the same check before
it drafts a release. A release run now boots every NixOS VM test on the tagged
commit, apart from the informational performance tests.
Never download or verify NAILS on the decoy. A copy of nails on the decoy
disk, even a deleted one, can be recovered and identifies the tool. For a first
installation, use a live environment that keeps nothing, or another machine.
First install from Tails walks through it with
Tails. For an update, download from inside a hidden session. The
download page has the files and the commands.
Security fixes in rc.3
Besides the Sigstore bundles, the security section of the rc.3 notes lists two fixes.
- Login and boot records. The way back no longer leaves a login or boot
record on the decoy that an ordinary power cycle never writes. On Debian with
wtmpdb, the boot row was named
soft-reboot. On systemd 255 and earlier, as on Ubuntu 24.04, the classicwtmpboot record carried the decoy’s first boot time. Now the decoy’s login and boot records read as one clean power cycle. A weaker tell remains, which a later release will address: the process numbers of the getty and login records written after the way back continue from the decoy’s boot. - Decoy swap and mounts. Some
/etc/fstabentries name a device by a tag whose value holds a character that udev encodes, such as a space, or is in single quotes. Others use a path with a.component, such as/mnt/./backup. For such an entry, NAILS masked a unit name that systemd never generates and left the real unit free, so the hidden session could swap onto a decoy disk or mount a decoy filesystem. NAILS now masks the unit under the name systemd gives it.
Until you run a build with the first fix, end Debian 13 and Ubuntu 24.04
sessions with nails deactivate --poweroff, or nails emergency --poweroff. A
power-off has no way back, so none of these login or boot records are written.
NAILS now keeps the hidden layers of a decoy filesystem overlaid at a mount
point with a . component under the name of its unit, such as
<hidden-root>/root/mounts/mnt-backup for /mnt/./backup, where 0.3.0-rc.2
used mnt-.-backup. If an earlier build left such a directory, rename it while
NAILS is inactive, or the hidden files in it stay out of view.
Security support
On the day of this release, the
security policy
says that only the most recent 0.2.x release carries security fixes. A problem
in a release candidate is fixed in a new -rc.N, because a published release
never changes. A fix reaches you as a new release, so the
releases feed is enough
to hear of it. Follow it inside a hidden session or on another device, never on
the decoy. To report a vulnerability, use the private channels on the
security page.
Read more
- The NAILS docs walk through setup and use in task order.
- Download has the release the docs describe and the steps to verify it.
- The changelog has every change. The notes of rc.3 on GitHub are its changelog section, so they hold only what changed since rc.2. The 0.3.0-rc.2 section holds the rest of the changes since 0.2, the upgrade notes among them.
- The FAQ answers common questions, and the comparison page shows how NAILS differs from other tools.