Skip to content
NAILS on GitHub
These docs describe NAILS 0.3, published so far as the release candidate v0.3.0-rc.3. They do not apply to v0.2.0, the latest stable release. Download and verify NAILS 0.3

Ending a session

In the hidden session, the way out is always deactivate, then emergency, then a power-off. A power-off is always safe, because nothing of the session is installed on the decoy and everything NAILS puts in /run is gone at power-off.

CommandUse it forRuns in
deactivateThe normal end of a session. Every program stops in order and can save its work.The hidden session
emergencyAn imminent threat. Every hidden program is killed at once.The hidden session
recoverCleaning up after an activation that died part-way.The decoy

deactivate and emergency end at the decoy’s login screen, or at its desktop if the display manager logs an account in automatically. --poweroff powers off instead, and emergency --reboot reboots fully. recover leaves you on the decoy.

Terminal window
sudo /mnt/data/nails deactivate
sudo /mnt/data/nails deactivate --poweroff

deactivate asks for confirmation first. -y skips the prompt, and without a terminal it refuses to run unless you pass -y. It unmounts the disks you mounted during the session, and every hidden program then gets an orderly stop. The decoy starts again at its own login screen, and the switch unmounts the hidden volume and closes its encryption mapping. With --poweroff the machine powers off instead.

A user service that does not stop promptly can delay an orderly deactivation by up to 90 seconds with systemd’s default timeout. emergency does not wait.

deactivate refuses while a disk is mounted over the hidden volume. Unmount that disk, then run it again. It also refuses when it cannot make the decoy’s mounts again as the decoy had them, for example because the hidden session unmounted a decoy filesystem that the session carries for the way back. emergency is the way out in both cases. In the second, it powers off instead of returning to the decoy.

Terminal window
sudo /mnt/data/nails emergency
sudo /mnt/data/nails emergency --poweroff
sudo /mnt/data/nails emergency --countdown 5
  • It acts immediately and never asks for confirmation. It kills every hidden program at once, then takes the same way back as deactivate. --countdown adds a wait that Ctrl+C aborts.
  • The screen shows a text console for a few seconds, then the decoy’s normal login screen. Log in with the decoy password. NAILS logs nobody in, but a display manager set to log an account in automatically logs it in again, as at boot, and the decoy’s desktop then opens without a password.
  • --poweroff powers off instead, which lets RAM decay. --reboot reboots fully, which does not clear RAM.
  • If the way back cannot be prepared, emergency powers off instead. If even the power-off cannot be queued, it asks the kernel to power off directly.
  • It reads no configuration, and nothing on the hidden volume, to act.
  • On the decoy, emergency removes what an interrupted activation left, as recover does. It then powers off or reboots if you passed --poweroff or --reboot.

If the hidden volume itself has failed, for example because a USB drive was pulled, run the copy that activation left in RAM:

Terminal window
sudo /run/overlay-runtime/control emergency

Power off if that does not respond. If the hidden session hangs, switch to a text console with Ctrl+Alt+F2, log in and run nails emergency there.

recover runs on the decoy after an activation that died part-way, for example after a crash or a killed process. nails status then exits with code 7 and says to run it.

Terminal window
sudo /mnt/data/nails recover

It removes what the interrupted activation left in /run and resets the recorded state. It closes the hidden volume’s mappings only when nothing uses them. If the hidden volume is still mounted, as it is when you run NAILS from it, recover leaves it open and says so. Unmount and close it yourself when you are done. It never reboots, and in the hidden session it refuses and points to deactivate. Pressing Ctrl+C during activation rolls back by itself, and a plain reboot after an interrupted activation needs no recover.

The way back returns to the decoy on the same running kernel, and some state of the session stays. A session that ends with --poweroff has no way back and leaves none of this state.

  • Network state. Links, addresses, routes and VPN tunnels the session set up stay up in the decoy until power-off. As soon as the decoy starts, its NetworkManager adopts a link the session created and records it, with the time, in /var/lib/NetworkManager/timestamps on the decoy disk. Before you deactivate, take down every connection and tunnel you brought up and delete every link you created with ip link delete, because ip link set down keeps it. Or end with deactivate --poweroff, which powers off before the decoy’s NetworkManager starts.
  • A different network identity. The way back does not undo the optional network-identity snippet. Its random MAC address can stay on the decoy’s network device, and so can the hidden hostname on a decoy without /etc/hostname. End a session that used it with deactivate --poweroff or emergency --poweroff.
  • The journal. On a volatile decoy, the way back deletes the runtime journal that the decoy shares with the hidden session, the decoy’s own records from before the session included. Until power-off, anyone with a shell sees a journal that starts at the return, mid-uptime, while uptime and dmesg still go back to the boot.
  • Login and boot records. On Debian and Ubuntu, the way back keeps the boot record writer from naming a soft-reboot and corrects a backdated boot record, so the decoy’s login and boot records read as one clean power cycle. On every platform, the getty and login records written after the return carry process numbers that continue from the decoy’s boot, where a fresh kernel starts them low. A build from before this correction also leaves a login or boot record that an ordinary power cycle never writes. With such a build, end Debian 13 and Ubuntu 24.04 sessions with --poweroff, as the changelog says.
  • Ubuntu 26.04. 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 there.
  • Kernel state. The kernel ring buffer keeps the session’s kernel messages. On systemd 256 and newer, systemctl show reports the soft-reboot counter, two per session. The boot ID and the uptime run on across both transitions.

The threat model lists every residual channel.

A soft-reboot back to the decoy keeps the kernel running and frees the hidden session’s memory without wiping it. A warm reboot does not clear RAM either. Only a power-off, followed by a few minutes without power, lets memory contents decay.

  • End a session with --poweroff before anyone else handles the machine.
  • Boot the decoy with init_on_free=1 page_poison=1, so freed memory is wiped. The hidden session runs on the same kernel, so this covers it too. The memory-wipe check reports whether init_on_free=1 is set.

There is no complete software defense against a memory dump of a running system. The protection is preventing access while it runs.

Bind a key to a root-owned systemd unit that runs nails emergency --poweroff. Define the unit, and a polkit rule scoped to that one unit, only in the hidden configuration. The decoy then carries nothing that names the tool.

On NixOS, in the hidden configuration, with your user name in place of operator:

systemd.services.panic = {
description = "Session teardown";
serviceConfig = {
Type = "oneshot";
ExecStart = "/mnt/data/nails emergency --poweroff";
};
};
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "panic.service" &&
subject.user == "operator") {
return polkit.Result.YES;
}
});
'';

Then bind a key to systemctl start panic.service. Drop --poweroff to return to the decoy instead. emergency never asks for confirmation, so the unit needs no -y. A key or unit that runs deactivate needs -y, because without a terminal deactivate refuses to run. On Debian and Ubuntu, define the same unit and rule from inside the hidden session, so they live in the upper layer’s /etc.