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

Commands

NAILS is one binary that lives on the hidden volume. Run every command with sudo and the binary’s full path, for example sudo /mnt/data/nails status. sudo does not search the hidden volume, so a bare sudo nails is not found. nails <command> --help prints the help for each command, with examples, and nails --version prints the version.

activate and deactivate soft-reboot the machine, so every program closes and you are logged out. emergency kills the hidden session at once and returns to the decoy or powers off.

  • init <PATH> lays out a mounted hidden volume, once. It refuses a target that is not a volume of its own before it writes or probes anything.
  • check reports readiness to activate and changes nothing. It runs the same pre-flight checks as activate. doctor is an alias.
  • activate soft-reboots into the hidden session.
  • recover removes what an interrupted activation left in /run and resets the state. It never reboots, and it refuses to run in the hidden session.
  • reset-upper <PATH> deletes a path’s hidden copy from the upper layer, so the decoy’s own files show through again. It is refused in the hidden session.
  • deactivate soft-reboots back to the plain decoy. Every program gets an orderly stop first, so applications can save.
  • verify scans for known artifacts, live or with --offline against a mounted decoy image. A clean result is evidence, not proof.
  • emergency kills every hidden program at once and soft-reboots back to the decoy, or powers off. It never asks for confirmation. On the decoy, it removes what an interrupted activation left.
  • status shows the session, the security posture and the hidden volume’s free space.
  • rehearse plant puts a canary in a hidden session, and rehearse scan then searches the raw decoy disk for it, from the decoy or a later session. rehearse alone shows where the rehearsal stands, and rehearse reset removes it from the hidden volume.
  • config path, config show and config validate locate, inspect or check the configuration file.
  • completions <SHELL> prints a completion script for bash, elvish, fish, powershell or zsh. It installs nothing, so source <(/mnt/data/nails completions bash) in the hidden session leaves no completion file on the decoy.

Ending a session compares deactivate, emergency and recover. Check your setup walks through check, verify and rehearse.

  • activate --dry-run previews the activation steps without changing anything.
  • activate --overlay-only enters with the decoy’s own NixOS system, without building the hidden configuration. The hidden /etc and home still apply.
  • activate --nixos-flake <REF> builds the hidden NixOS system from a flake, such as /mnt/data/nixos#my-host. check takes --overlay-only and --nixos-flake too, to check what that activation would do.
  • --accept-persistent-decoy on activate and check accepts a decoy that keeps a persistent journal or runs a syslog or audit daemon, instead of refusing it. What the decoy logs while it shuts down for the soft-reboot, and while it starts again afterwards, then survives on its disk. It never overrides the swap check.
  • --allow-decoy-storage on init, activate and check turns the refusal of a hidden root that this computer’s own storage seems to hold into a warning. Use it only for storage you know the decoy does not use, such as a disk its fstab lists but never mounts. It never accepts a folder, a root with a second filesystem mounted over it or storage that cannot be judged.
  • activate --no-preflight skips the pre-flight checks. The help marks it as dangerous and for expert use only. It never skips the check that the hidden root is a volume of its own, the backend-entries check or the refusal to build the decoy’s own NixOS flake.
  • deactivate --poweroff powers off instead of returning to the decoy. Powering off lets RAM decay, so leave the machine off for a few minutes before you hand it over.
  • emergency --poweroff powers off instead of returning to the decoy, whatever the outcome.
  • emergency --reboot reboots fully, through the firmware and boot loader. A warm reboot does not clear RAM, so prefer --poweroff when RAM residue matters.
  • emergency --countdown <SECS> waits first, with a hint that Ctrl+C aborts. Without it, emergency acts at once.
  • verify --deep adds homes, shell histories, temporary and log directories and the journal to the scan.
  • verify --offline --root <MOUNT> scans a decoy image or partition mounted read-only, instead of the live host.
  • verify --offline --examiner adds the setup traces an examiner looks for, such as encryption headers, containers and crypttab lines, and reports them without changing anything. --disk <DEVICE> adds the partition-level checks.
  • status --details adds resolved paths and overlay details.
  • init --no-hardware-config skips the hidden NixOS layout, and init --hardware-config <PATH> seeds it from another file than /etc/nixos/hardware-configuration.nix.

activate, deactivate and reset-upper ask for confirmation on a terminal. Without a terminal they refuse with exit code 2 and the error code needs_confirmation, so add -y to every script, key binding or unit that runs them. emergency never asks. Answering n cancels with exit code 1 and changes nothing.

During an activation, Ctrl+C or a SIGTERM cancels it at its next step, up to the soft-reboot, and removes everything it built, with exit code 130. A step that runs a program to its end, such as the hidden NixOS system’s activation, finishes first. At the confirmation prompt of activate, Ctrl+C ends the prompt at once. Troubleshooting says what to do when a command refuses or an activation is interrupted.

nails init does not take --config, and nails emergency reads no configuration. Otherwise these options work with every command, in any position.

OptionEffect
-c, --config <PATH>Read this configuration file instead of the one next to the binary
-v, --verboseMore diagnostics on standard error. -vv adds trace output
-q, --quietPrint only errors and the final result
--color <WHEN>auto (the default), always or never
--asciiUse ASCII markers instead of Unicode symbols and box drawing
--jsonPrint machine-readable JSON on standard output
--no-log-fileDo not write the log file on the hidden volume for this run

With auto, NAILS colors a stream only when it is a terminal, TERM is not dumb and NO_COLOR is unset or empty.

Every command returns one of these codes, and scripts can rely on them.

CodeMeaning
0Success
1The operation failed
2Command-line usage error
3The configuration file or environment was unusable
4A pre-flight check refused the operation
5The operation needs privileges it did not have
6The on-disk state could not be trusted
7A degraded or interrupted session, or an incomplete cleanup. nails status names the fix, usually nails recover
8The host is not one NAILS can operate on
9Completed, but with a warning the caller should act on
10verify found warnings
11verify found critical artifacts, or rehearse scan found its canary
130Canceled by SIGINT (Ctrl+C) or SIGTERM. An activation in progress is rolled back

Some commands use these codes in a specific way:

  • check exits with 4 while a check fails.
  • status exits with 7 on a degraded posture or after an interrupted activation, and with 6 on a state file it cannot trust.
  • verify exits with 0, 10 or 11 for no known artifacts, warnings or critical findings, with 1 when the scan could not run and with 3 when the configuration could not be loaded.
  • rehearse scan exits with 0 when the canary is not found, 11 when it is, and 9 when a device could not be scanned completely.
  • emergency exits with 7 when it leaves a mount behind or can only ask the kernel to power off.
  • A command that changes something exits with 5 when run without root. activate, deactivate, emergency and recover refuse before they write anything and print the full sudo line to use.

With --json, every command writes one line with a stable envelope. It holds 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. Log lines go to standard error. A new schema version signals an incompatible change. The schemas are in the repository’s docs/schemas directory.

The CLI reference is generated from the code and lists every flag. It also ships as the nails.1 man page. The README’s Commands section has the same command overview.