Files
deepseek-harness/native/landlock-run/docs/cli-contract.md
kingwl 0a486f09c9 chore: adopt node-addon-landlock-run source as native/ subtree
Bring the node-addon-landlock-run tree (tag v0.0.1, commit 614f7fd) into
native/landlock-run as its source of record: launcher development happens
here, next to the harness consumers, and the standalone repository becomes
the release mirror the tree is exported to for packing and publishing
(procedure in native/README.md). The subtree keeps its own pnpm workspace
and lockfile and is NOT added to the harness workspace: harness installs,
gates, and CI never touch it. The mirror's .github/ stays out of the
subtree; a separate manually-dispatched workflow
(.github/workflows/landlock-run.yml) runs the subtree's CI legs — the
per-architecture native builds, real-kernel launcher proofs, and pack
rehearsal — adapted with working-directory/cache paths.

eslint ignores the subtree like vendor/; AGENTS.md gains the native/
layout line (+5 words on its budget ceiling).
2026-07-14 23:39:58 +08:00

2.5 KiB

CLI contract: landlock-run

This file pins the launcher's externally observable behavior — the cross-repo compatibility surface between the binaries and every consumer. Consumers interact with it only through the entry package (launcherPath/probe/grantArgs); changing anything below requires a version bump for the whole package family and a note in the release notes.

Invocation grammar

landlock-run [--ro <path>]... [--rw <path>]... -- <argv>...
landlock-run --probe
  • --ro <path>: grant read + execute beneath <path>.
  • --rw <path>: grant full filesystem access beneath <path> (every access the negotiated kernel ABI can govern).
  • Everything not granted is denied — Landlock rulesets are allow-lists.
  • A grant on a non-directory keeps only its file-compatible access bits (this is how a --rw /dev/null grant works).
  • --: mandatory separator; everything after it is the command argv, exec'd via execvp with the launcher's environment unchanged.
  • --probe: mutually exclusive with grants and a command.
  • No other flags, no environment-variable inputs.

Exit codes

  • 125 (LAUNCHER_FAILURE_EXIT): every launcher-level failure — usage error, kernel that cannot enforce Landlock, unopenable grant root, failed exec. The wrapped command was NOT run (fail-closed; the one exception is exec itself failing after restriction, which by definition never ran the command either).
  • Any other status: the wrapped command's own exit status, passed through unchanged.
  • --probe: 0 when the kernel enforces (fully or partially), 125 otherwise.

Report lines

  • Probe success prints exactly one stdout line: landlock: fully enforced or landlock: partially enforced (older ABI). The entry package's probe() maps these to full/partial; a non-zero probe exit maps to unusable.
  • A confined run under a partial-ABI kernel prints one stderr line landlock-run: partial enforcement (older Landlock ABI) and proceeds — still confined for everything the kernel supports.
  • Every fatal error prints one stderr line prefixed landlock-run: before exiting 125.

Confinement semantics

The launcher sets no_new_privs, installs the ruleset on itself, and execs the command; the ruleset is inherited across execve, so every descendant process is equally confined. The ruleset governs the filesystem accesses of the kernel's negotiated Landlock ABI (up to ABI 5); accesses newer than the running ABI are not governed and are the difference between full and partial.