NAILS is a command-line tool that runs a hidden session on an ordinary-looking ("decoy") Linux system. It soft-reboots the machine into one OverlayFS overlay over the whole decoy root, with every change written to an encrypted hidden volume you provide, and soft-reboots back to the plain decoy when you are done. Nothing that starts the hidden session is installed on the decoy.
Website and documentation: nails.run
NixOS is the first-class platform (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.
Warning
NAILS is alpha security software. It is meant for technically experienced users who can test their exact machine, storage backend and procedure before relying on it. It narrows what a forensic examination of the decoy can find; it does not make you invisible. Read the operator guide and the threat model first.
Contents: Threat model in brief · Requirements · Installation · Quick start · The session lifecycle · Safety notes · Commands · Documentation · Development
There are two environments on one machine:
- the decoy: the installed system, which should stay mundane and plausible;
- the hidden session: the same system started again on an overlay whose writable layer lives on a hidden volume that you unlock and mount yourself (for example a VeraCrypt hidden volume or a LUKS2 volume).
nails activate builds the overlay at /run/nextroot and enters it 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,
from the desktop to the services, starts inside the overlay, so whatever it
writes through the decoy's mounted filesystems lands in the upper on the hidden
volume. The few writes that go around the mount layer (a disk the session mounts
itself, EFI variables) are listed under
residual channels.
nails deactivate soft-reboots back into the plain decoy root and closes the
hidden volume on the way. nails emergency does the
same at once, or powers the machine off.
NAILS does not encrypt anything itself. The hidden volume, and its deniability, come from the storage backend you choose; see docs/backends.md.
The architecture overview explains why the session takes the whole root and what that costs.
NAILS is built for one adversary: someone who gets offline access to the disks after the fact, typically by seizing a powered-off machine, and may make you unlock the decoy once. After a clean session on a correctly prepared decoy, the decoy disk holds nothing of the hidden session except a short, documented list of residual channels, and no hook that would start it.
What it helps with:
- keeping the hidden session's files, logs, shell history and package builds off the decoy disk (they all land in the overlay's upper on the hidden volume);
- leaving no enabler on the decoy: no boot entry, initrd change, unit or binary;
- checking your own setup:
nails checkbefore entry,nails verifyfor known artifacts, andnails rehearseto plant a canary and search the raw decoy disk for it.
What it does not protect against:
- anyone with access to the running machine, memory forensics, or cold-boot attacks (power off after a session: a soft-reboot keeps RAM);
- network and remote records: the hidden session shares the decoy's kernel, machine-id, hostname, MAC addresses and, by default, its user account and home; ISPs, VPNs and online services see whatever it does;
- compelled disclosure of the hidden volume, physical or repeated coercion, or an adversary who images the disk more than once and compares;
- a persistent decoy: a journal, syslog or audit log on the decoy disk records the soft-reboots. Activation refuses such a decoy unless you override it;
- firmware or hardware compromise, keyloggers, side channels, or a compromised root on the decoy.
The full adversary, assumptions, residual channels and evaluation scope are in docs/threat-model.md. It is the only place the threat model is written down; read it before relying on NAILS.
| Requirement | Notes |
|---|---|
| A supported host | NixOS, Debian 13, or Ubuntu 24.04 or 26.04, with systemd 254 or newer. A later Debian or Ubuntu release runs with a warning that NAILS has not been tested on it; other hosts are refused (see below). |
| An amnesic decoy | The decoy must keep no durable record of the session: volatile journald, no rsyslog, syslog-ng or auditd, no disk swap. Activation fails closed otherwise; --accept-persistent-decoy overrides the journal, syslog and audit refusals, never the swap check. See docs/decoy-requirements.md. |
| A decoy root on a block device | ext4, xfs, btrfs or f2fs. The way back mounts the decoy root again by its device. |
| A hidden volume | Storage of its own: not a folder, a subvolume or a bind mount of a filesystem the computer uses, nor a volume inside its storage. Mounted, writable and kernel-managed (dm-crypt, a loop device), with a filesystem that can host an overlayfs upper: ext4, xfs, btrfs or f2fs. See docs/backends.md. |
Root (sudo) |
activate, deactivate, emergency, recover and the writing rehearse steps refuse to run without it. Run init, check, status, verify, config and reset-upper with sudo too, as the examples do, so they can read and write root-owned files: the configuration init writes is readable by root only. |
| A local account | The account you run sudo from must be in /etc/passwd: the static binary does not use NSS, so LDAP or SSSD accounts cannot be resolved. |
Platform matrix and refused setups
NAILS detects the host from /etc/os-release and the running init and names it
in the platform row of nails check; the pre-flight checks then confirm it can
soft-reboot.
| Platform | Support | Notes |
|---|---|---|
| NixOS | Full | A hidden NixOS configuration (flake or configuration.nix) is built into the hidden root's store and entered in the same soft-reboot. It must run on the decoy's kernel. Pinned nixpkgs without soft-reboot.target/systemd-soft-reboot.service need a small module, which nails check prints. |
| Debian 13 (trixie) | Root overlay | The same whole-root session; the hidden configuration is whatever the upper's /etc holds. Requires the merged-/usr layout. |
| Ubuntu 24.04 and 26.04 | Root overlay | As Debian. On 26.04, chrony and CUPS fail in the hidden session and the decoy shows chrony failed until a full reboot; see the Ubuntu notes. |
| Other releases: Debian after 13, Ubuntu after 24.04 | Untested | Accepted on the same terms, with a warning in init, check and activate that NAILS has not been tested on the release, such as Ubuntu 25.04 or 26.10. |
| Debian-family derivatives | Provisional | A host whose ID_LIKE names debian or ubuntu is accepted on the same terms when its systemd is new enough; its base release is not version-gated. |
| Debian 12, Ubuntu 22.04 | Refused | systemd is too old for a soft-reboot (252 and 249). |
| Fedora, Arch, other distributions | Refused | No validated backend yet. Planned; see ROADMAP.md. |
| ostree and image-based hosts | Refused | NAILS cannot overlay an immutable system. |
| Non-systemd hosts (OpenRC, runit, ...) | Refused | The session is entered and left with a systemd soft-reboot. |
Also refused by the pre-flight checks:
- a decoy root on tmpfs (an impermanent root), ZFS or an overlay (
decoy-root); - active disk swap; only RAM-backed zram is allowed (
swap); - a persistent journal or an active syslog or audit daemon, unless overridden
(
decoy-persistence); - a hidden volume that is not storage of its own: a folder, a btrfs subvolume
or bind mount of a filesystem the decoy uses, a volume stacked on the
decoy's storage, an image file on a decoy filesystem, a filesystem that shows
no disk it stores its data on (an overlay, FUSE, a network filesystem), or a
root with a second filesystem mounted over it. The attach gate refuses these
before anything is written,
--no-preflightincluded;--allow-decoy-storageturns the refusal of the decoy's storage into a warning, and never covers a folder, a covered root or storage that cannot be judged (hidden-volume); - a decoy
/etc/crypttabor/etc/fstabline that opens or mounts the hidden volume (backend-entries, which--no-preflightdoes not skip either); - a hidden volume whose filesystem cannot host an overlayfs upper: NFS,
CIFS/SMB, 9p, most FUSE mounts, FAT, exFAT and NTFS (
upper-fs-capability); - running inside a container or another mount namespace than PID 1's
(
mount-namespace).
nails check runs every check, changes nothing and prints what it would do.
For the strongest posture, keep state such as /var and /home on storage that
is wiped at boot (impermanence-style); the decoy root itself must stay on a
disk. See
docs/decoy-requirements.md.
NAILS is installed by copying a verified static binary onto the mounted hidden volume, and in no other way. No package, registry or decoy-side install is supported; docs/distribution.md explains why.
-
Outside the decoy system (a live environment that keeps nothing, such as Tails, or a separate machine), download the latest bundle from GitHub Releases and verify it as docs/verify-release.md describes (checksum and build provenance). For more assurance, rebuild it: docs/release-artifact-reproducibility.md.
-
Unlock and mount the hidden volume, then put the binary directly on it. Never leave a copy on the decoy disk:
sudo cryptsetup open --type tcrypt --veracrypt --tcrypt-hidden \ "$HOME/Downloads/family_photos" data sudo mount /dev/mapper/data /mnt/data sudo install -m0755 /path/to/verified/nails /mnt/data/nails
The examples use the mapping name data and the mount point /mnt/data. Keep
yours neutral too: names surface in /dev/mapper, /dev/disk/by-label, mount
unit names and the journal, and NAILS warns when one contains a telling word.
Upgrading the binary
New releases appear in the releases feed, which any feed reader can follow without a GitHub account. Follow it inside a hidden session or on another device, never on the decoy.
Replace the binary only while no session is active, as the
operator guide says. Fetch or build the
new one inside a hidden session, so the downloads and build caches (~/.cargo,
/nix/store, the result GC root) land on the hidden upper and never on the
decoy, and leave it next to the old one on the hidden volume:
# Inside a hidden session: a verified release (see docs/verify-release.md) ...
sudo install -m0755 /path/to/verified/nails /mnt/data/nails.new
# ... or a build from source
git clone https://github.com/WitteShadovv/nails /mnt/data/src/nails
cd /mnt/data/src/nails
cargo build --release # or: nix build .#nails (then use ./result/bin/nails)
sudo install -m0755 ./target/release/nails /mnt/data/nails.newThen end the session. The next time you have mounted the hidden volume on the decoy, and before you activate, put the new binary in place:
sudo mv /mnt/data/nails.new /mnt/data/nailsNever build or download on the decoy: before a session there is no hidden upper to catch the caches.
Zero-config path discovery trusts the binary installed on the hidden volume. If
you run NAILS from a build directory or /nix/store, set hidden_volume_root
explicitly or pass --config.
The shortest runnable path. The operator guide walks the
same steps in full, organised by where each one runs, with a recovery runbook.
Type the decoy-side commands in a shell with history off (set +o history).
-
Mount the hidden volume (see Installation). docs/backends.md has the commands for a whole-device VeraCrypt volume and for LUKS2. Never add
--allow-discards. -
Lay out the hidden volume once:
sudo /mnt/data/nails init /mnt/data
initrefuses a directory that is not a volume of its own (a folder, or a subvolume, bind mount or image file on the computer's own storage) before it writes or probes anything, so the layout never lands on the decoy disk; see Where setup happens. On NixOS it seeds the hidden NixOS layout from/etc/nixos/hardware-configuration.nix. It ends by printing the next steps. -
Optionally review the configuration, but not on the decoy: an editor leaves its own traces there (swap and backup files,
~/.viminfo, recent-file lists). Every key is optional, and the templateinitwrites uses the defaults. Prepareconfig/nails.tomlon the separate machine, before you copy it to the hidden volume (initkeeps a file that is already there), or inside a hidden session as a new file that you move into place after the session, as for a binary update:# inside a hidden session; the configuration directory is root's sudoedit /mnt/data/config/nails.toml.new sudo /mnt/data/nails config validate /mnt/data/config/nails.toml.new sudo /mnt/data/nails --config /mnt/data/config/nails.toml.new config show # effective values and their sources
Leave
hidden_volume_rootunset to derive it from the binary's location. On NixOS, change the hidden module/mnt/data/config/nixos/configuration.nixinside a hidden session (see the operator guide's Changing the hidden configuration), or build the hidden system from a flake that imports that module. Activation finds one in/mnt/data/nixosby itself, and a decoy whose/etc/nixosis a flake needs one: write and lock it inside a hidden session, whichactivate --overlay-onlyenters, never on the decoy, and keep it self-contained (see the operator guide's How it works).nixos_flakenames another flake or attribute:nixos_flake = "/mnt/data/nixos#my-host"
docs/configuration.md lists every key.
-
Check, then activate:
sudo /mnt/data/nails check sudo /mnt/data/nails activate
checkruns the same pre-flight checks asactivateand changes nothing.activateasks for confirmation, prepares the hidden root and soft-reboots: every program closes and you are logged out. Log in again at the hidden session's login screen. NAILS logs nobody in, but a display manager set to log an account in automatically logs it in again, as at boot; the operator guide says how to turn that off for the hidden session alone. -
Verify, then return from inside the hidden session:
sudo /mnt/data/nails verify --deep sudo /mnt/data/nails deactivate --poweroff
deactivatewithout--poweroffreturns to the decoy's login screen (or, with display-manager autologin, to its desktop). Powering off is what clears RAM; leave the machine off for a few minutes before handing it over.
What nails check prints
Captured from the session-readme-captures NixOS VM test, on a decoy set up as
docs/decoy-requirements.md recommends; see
docs/assets/README.md for how each screenshot is made.
flowchart LR
D["Decoy<br/>plain decoy root"]
H["Hidden session<br/>whole-root overlay,<br/>upper on the hidden volume"]
O["Powered off<br/>RAM decays"]
D -->|"nails activate<br/>(soft-reboot)"| H
H -->|"nails deactivate<br/>(orderly stop, soft-reboot)"| D
H -->|"nails emergency<br/>(kill everything, soft-reboot)"| D
H -.->|"--poweroff"| O
D -.->|"nails recover<br/>(after an interrupted activation)"| D
| Step | Runs on | What happens | What you see |
|---|---|---|---|
activate |
the decoy | Pre-flight checks, a read-only residue baseline of the decoy (report on the hidden volume), the root overlay at /run/nextroot, on NixOS the hidden system built into the overlay, then the soft-reboot. Ctrl+C before the switch rolls everything back. |
Progress and a summary, then your terminal closes and the screen shows a text console for a few seconds. The hidden session starts at its own login screen; a display manager set to log an account in automatically logs it in again. |
deactivate |
the hidden session | Mounts the decoy root for the way back, marks the hidden volume for closing, and soft-reboots. The orderly shutdown stops every hidden program; the last step scrubs the top of /run of what the session added and deletes the runtime journal, which the decoy shares with it. Network links and addresses the session set up stay. |
Programs close as at a logout. The decoy starts at its own login screen; a display manager set to log an account in automatically logs it in again. The hidden volume is unmounted and closed. |
emergency |
the hidden session | The same way back, but every hidden program is killed at once (cgroup.kill). It acts immediately; --countdown adds an abortable wait. If the way back cannot be prepared, it powers off. |
A few seconds of text console, then the decoy's normal login screen: log in with the decoy password. NAILS logs nobody in; a display manager set to log an account in automatically logs it in again, as at boot. |
--poweroff |
either command | Powers off instead of returning to the decoy. | The machine turns off. |
recover |
the decoy | Removes what an interrupted activation left in /run and resets the state. Never reboots. |
A short report. |
Both transitions log you out. Save your work first.
What nails status --json reports in the hidden session
Every command accepts --json and prints one line with a versioned envelope
(schema_version, command, ok, then data or error), except activate,
which prints a progress line before its result (one JSON object per line). The
schemas are in docs/schemas/.
- NAILS does not mount, unlock or protect the hidden volume. That is your backend's job, and so is its deniability.
- Run it by its full path on the hidden volume (
sudo /mnt/data/nails ...). Never put a copy, symlink orPATHentry on the decoy: the binary holds identifiable strings that survive deletion. - The hidden volume must stay mounted for the whole session. It is kernel-managed for that reason: a FUSE mount dies with the programs at the soft-reboot.
- A soft-reboot leaves traces in RAM: the kernel ring buffer, the soft-reboot counter (two per session, on systemd 256 and newer) and the unchanged boot ID and uptime. Power off after a session when that matters. A warm reboot does not clear RAM either.
- The way back keeps the session's network state: the links, addresses,
routes and VPN tunnels it set up stay up in the decoy, and the decoy's
NetworkManager records a link it adopts on the decoy disk as soon as the
decoy starts. Take down what you brought up and delete the links you created
before you deactivate, or end with
deactivate --poweroff. See the operator guide. - Do not suspend during a session. Suspend keeps the whole hidden session
and the volume key in RAM. Set the lid switch to
lockand considersleep_policy(see docs/configuration.md). - Do not start the decoy's sync and account clients in the hidden session for sensitive work: what they send stays on the remote side, and a decoy client can pull it back after the session. See the operator guide.
- A clean
nails verifyis evidence, not proof. It checks for known artifacts and lists what it did not check. - Panic button: bind a key to a root-owned unit that runs
nails emergency --poweroff, defined only in the hidden configuration. Never give the binary a passwordlesssudorule. See the operator guide.
Hidden-volume space, copy-up and shadowed decoy updates
The hidden root's upper persists across sessions. OverlayFS copies a decoy file into the upper in full the first time it is written, truncated or re-chmod/chowned, so the first write to a large file (the Nix database, a browser profile, a VM image) can stall on a slow or encrypted backend, and the upper only grows. On NixOS the first entry also copies the hidden system's whole closure onto the hidden volume, which can take several GB and minutes. A file copied up earlier also shadows later decoy updates of that file: after a decoy package upgrade or rebuild, the hidden session still sees its own older copy.
- watch free space with
df -h /mnt/data;nails checkfails when it drops belowminimum_space_mb; - run
nix-collect-garbage -dand clear caches inside the hidden session; - from the decoy,
sudo /mnt/data/nails reset-upper <PATH>deletes a path's hidden copy so the decoy's own files show through again (it asks first, and is refused in the hidden session).
See the operator guide.
Troubleshooting
In the hidden session the way out is always deactivate, then emergency, then a
power-off. A power-off is always safe: nothing of the session is installed on the
decoy and everything NAILS puts in /run is gone at power-off.
| Situation | What to do |
|---|---|
nails check fails |
Read the -> hint under each failing check; it names the fix. |
| Activation was interrupted (killed, crashed) | nails status exits 7 and says to run nails recover. Run it on the decoy. Ctrl+C during activation rolls back by itself. |
deactivate refuses or fails |
Run nails emergency. |
| The hidden session hangs | Switch to a text console (Ctrl+Alt+F2), log in and run nails emergency. A stuck service can delay deactivate by up to 90 s; emergency does not wait. |
| The hidden volume failed (USB pulled, I/O errors) | Run the RAM copy activation left behind: sudo /run/overlay-runtime/control emergency. Power off if that does not respond. |
| The hidden volume is full | Free space inside the session, or deactivate and reset-upper a large path from the decoy. Never delete files out of the upper by hand. |
| Power loss or crash | The decoy boots normally. If nails status reports a mismatched state, run nails recover once. |
The full runbook, including cleaning up by hand when recover cannot run, is in
the operator guide.
FAQ
Does NAILS hide that a hidden volume exists? No. That depends on the backend: VeraCrypt's own claim is conditional — it should be impossible to prove a hidden volume exists if VeraCrypt's requirements are followed, and its documentation notes that multiple disk snapshots over time, wear-levelling or TRIM can still betray one — while a LUKS volume is visibly an encrypted volume. NAILS adds nothing to the backend's claim. It keeps the session off the decoy.
Can I keep working on the decoy while the hidden session runs? No. Only one of them runs at a time; switching logs you out of the other.
Why does it need a soft-reboot? Every process of the hidden session then starts inside the overlay, so there is nothing on the decoy side to find, restart or kill. The older design that overlaid a running system directory by directory could miss a writer silently; see the architecture overview.
Does the hidden session get its own account? Not yet. By default it runs as
the decoy user in the decoy home seen through the overlay; the fresh-home
persona gives it an empty home instead. See
persona modes.
Does it work on a persistent decoy? Only with --accept-persistent-decoy,
and then the decoy's journal records the soft-reboots and the activation
command. Prefer an amnesic decoy.
How do I know it works on my machine? Run nails rehearse (plant a canary in
a hidden session, then search the raw decoy disk for it) and
nails verify --offline --examiner against an image of the decoy. See
the operator guide.
The binary lives on the hidden volume, so every invocation uses its full path.
sudo's secure_path does not include the hidden volume, so a bare
sudo nails would not resolve.
| Command | Where | Purpose |
|---|---|---|
init <PATH> |
decoy | Lay out a mounted hidden volume |
check |
decoy | Report readiness to activate, changing nothing (alias doctor) |
activate |
decoy | Soft-reboot into the hidden session |
deactivate |
hidden session | Soft-reboot back to the plain decoy (--poweroff to power off) |
emergency |
either | Kill the hidden session at once and return (or power off); on the decoy, remove an interrupted activation |
recover |
decoy | Remove what an interrupted activation left in /run |
status |
either | The live session, security posture and hidden-volume space |
verify |
hidden session | Scan for known artifacts (live, or --offline against a decoy image) |
rehearse |
both | Plant a canary in a hidden session, then scan the raw decoy disk for it |
reset-upper <PATH> |
decoy | Delete a path's hidden copy from the hidden root's upper |
config path|show|validate |
either | Locate, inspect or check the configuration file |
completions <SHELL> |
either | Print a shell completion script |
The full reference, generated from the code, is docs/cli.md (also
shipped as the nails.1 man page).
Exit codes and JSON output
Every command returns one of these codes; scripts can rely on them.
| Code | Meaning |
|---|---|
0 |
Success |
1 |
The operation failed |
2 |
Command-line usage error |
3 |
The configuration file or environment was unusable |
4 |
A pre-flight check refused the operation |
5 |
The operation needs privileges it did not have |
6 |
The on-disk state could not be trusted |
7 |
A degraded or interrupted session, or an incomplete cleanup; nails status names the fix (usually nails recover) |
8 |
The host is not one NAILS can operate on |
9 |
Completed, but with a warning the caller should act on |
10 |
verify found warnings |
11 |
verify found critical artifacts, or rehearse scan found its canary |
130 |
Cancelled by a signal (SIGINT/SIGTERM); an entry in progress is rolled back |
With --json, every command writes one line: a stable envelope with
schema_version (currently 1), the command name, an ok boolean, and either
a typed data object or an error object with a snake_case code, a message
and an optional hint. activate is the exception: it prints a progress line
first and then its result, one JSON object per line
(docs/schemas/activate.schema.json). A new
schema version signals an incompatible change.
Upgrading from an earlier release
Upgrade only while NAILS is inactive: end a 0.2 session with its own
nails deactivate (a reboot ends it too) before replacing the binary. This
release changes operator-facing interfaces, with no compatibility shim: convert
your setup before running the new binary.
- Session mode. The per-directory overlays (
/etc,/home,/var, ...) are gone. The only mode is the whole-root session entered and left with a soft-reboot, which needs systemd 254 or newer. Besides NixOS, Debian 13, Ubuntu 24.04 and Ubuntu 26.04 are now supported as decoys, and a later Debian or Ubuntu release runs with a warning. The hidden root's layers live under<hidden-root>/root/(among themupper,workandmounts); the uppers of the old mode (<hidden-root>/<name>and<hidden-root>/.work) are not read. Move what you need from them into the hidden session, then delete them. On NixOS the hidden hardware configuration moves to<hidden-root>/root/upper/etc/nixos/; runnails initagain to place it. - NixOS flake decoys. A decoy whose
/etc/nixosis a flake needs a hidden flake: activation refuses to build the decoy's own flake,--no-preflightincluded, also whennixos_flakeor--nixos-flakenames it (/etc/nixos#host, the form 0.2's examples used,path:/etc/nixos,git+file:///etc/nixos,path:/etc?dir=nixosor a link to it) and when<hidden-root>/nixosis a link to it, because it would leave out the hidden module and the hidden hardware configuration. Write a self-contained flake in<hidden-root>/nixosthat imports the hidden module (<hidden-root>/config/nixos/configuration.nix) and reads none of the decoy's files, write and lock it inside a hidden session (activate --overlay-onlyenters one), never on the decoy, and build it there withnixos-rebuild build --flake <hidden-root>/nixos#<host> --impure,<host>being the decoy's hostname;nixos_flakenames such a flake elsewhere (see How it works in the operator guide). - A volume of its own. The hidden root must be a volume of its own. NAILS
now refuses a root that 0.2 accepted: a folder (no mount point), a btrfs
subvolume or bind of a folder of a filesystem the decoy uses, a logical volume
in the decoy's volume group, an image file on a decoy filesystem, and an
overlay, FUSE, network or ZFS filesystem. Move the hidden volume to a device
of its own (see
Where setup happens in the
operator guide). An unprivileged
checkorstatuson a distribution whose/etc/crypttabonly root can read getsstorage_unjudgedand logs to stderr only; run it withsudo. - Configuration format. Configuration is strict TOML, in
config/nails.tomlnext to the binary on the hidden volume. The new binary never reads the YAML file of 0.2 and does not warn about it: an unconverted setup runs with the defaults. Convert each key by hand (see docs/configuration.md); the hidden volume's key is nowhidden_volume_root, which you can leave unset to derive it from the binary's location. Unknown keys, wrong types and syntax errors are rejected with a one-line message, and so are the keys 0.3 dropped, those of the per-directory overlay mode among them; the changelog's "Removed" section lists every one.nails initwrites a documented template, andnails config show --exampleprints every key. - Invocation. Always run the binary by its full path on the hidden volume.
No copy, symlink or
PATHentry on the decoy is supported. - Flags and commands. The output flags are global and accepted in any
position, and the old per-command output flags are replaced by
--color,--ascii,--quiet,--verboseand--no-log-file.activate --flakeis now--nixos-flake, andstatus -vis nowstatus --details. The flags of the live-overlay mode (the history, session, pivot and prompt options ofactivateanddeactivate, and the countdown switch ofemergency) and its notification command are removed; the changelog's "Removed" section names each of them. - Confirmation.
activateanddeactivatenow ask for confirmation, and without a terminal they refuse with exit 2 (needs_confirmation); 0.2 skipped the prompt unless--interactivewas given. Add-yto every script, keybinding or unit that runs them;emergencynever prompts. - Exit codes and JSON. The process returns the documented exit codes, and
--jsonemits the versioned envelope (schema_version = 1). - State schema.
state.jsoncarries aversionfield. The states areInactive,ActiveandDegraded;nails statusreads the live session from the mount table and the session plan in/run, because a soft-reboot keeps the boot ID. The new binary cannot verify a state file that 0.2 wrote:statusreports the state as unknown andactivaterefuses (exit 6) until you delete the file, from a clean inactive system; do not hand-edit it.
The release changelog mirrors these notes under its "Upgrade notes" heading; keep CHANGELOG.md and this section in step when cutting a release.
The full index is docs/README.md. The essentials:
- Operator guide: the whole workflow by where each step runs, the recovery runbook and the honest limits
- Threat model: adversary, assumptions, residual channels
- Decoy requirements: what the decoy must and must not keep, per platform
- Storage backends: which backends work and the hazards of each
- Configuration and CLI reference
- Verify a release
- Architecture: why the session takes the whole root, the crates and the runtime layers
- Project site, changelog, roadmap
NAILS is a Cargo workspace of seven crates (the nails binary and six
libraries); docs/architecture.md shows how they fit
together.
Developer quick start
git clone https://github.com/WitteShadovv/nails.git
cd nails
nix develop # the pinned toolchain and every tool the checks use
just check # formatting, clippy, file sizes and the tests
nix flake check # Nix, shell and workflow linters (no VM tests)
nix build .#e2e.session-lifecycle --no-link -L # one NixOS VM test (needs KVM)- CONTRIBUTING.md: setup, conventions, sign-off
- Development guide and testing strategy
- VM test suite and CI
- Releasing
- NAILS (this repository) is the CLI and workflow for running a hidden session over an existing decoy system you operate.
- NAILS OS is a separate
installable NixOS distribution with its own live ISO, installer, persistence
model and system-level security design. Its root is a tmpfs, and NAILS needs
a decoy root on a block device (the
decoy-rootcheck), so NAILS does not run on NAILS OS.
They are related, but not interchangeable, and do not share the same installation or threat-model boundaries.
NAILS is actively maintained and released as 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.
- Security claims are bounded by the documented threat model and by the checks you run on your own setup.
- How releases are produced, numbered and verified: RELEASING.md.
Policies: contributing · security · support · code of conduct · code owners
NAILS has been developed with AI-assisted tooling for drafting code, tests, and documentation. All security-relevant design decisions, code changes, and published documentation remain the responsibility of the maintainer and are intended to be reviewed and validated before release.
Copyright (C) 2025-2026 WitteShadovv.
This project is licensed under the GNU General Public License v3.0 or later. See LICENSE.
Caution
This software is provided for educational and security research purposes. It comes with no warranty, and it should not be treated as a guarantee of deniability or forensic invisibility in any specific threat environment.
Maintained by WitteShadovv.