Combinators
nono.nix combinators are the building blocks to create Permissions,
which grant a program specific permissions at runtime.
These permissions can be passed into the third argument to nono
function, as well as
basePermissions.
A Permission is a State -> State function where State is an
internal type that accumulates Landlock grants, network policy, and
other nono flags before building the final profile and invocation.
The type of State is not part of the public API and may change.
add-cleanup
add-cleanup :: String -> Permission
Adds arbitrary logic to run when the sandbox exits.
This is designed to be an easy way to register cleanup actions for things
created in add-runtime. These scripts run in the same
scope as add-runtime so any shell variables defined there will be in
scope.
The cleanup actions may run even if the runtime doesn't— for example if a previous runtime exits non-zero the sandbox will exit prematurely, but the cleanup actions will still run.
Example:
compose [
(add-runtime ''
TMP_FILE=$(mktemp)
do-something "$TMP_FILE"
'')
(add-cleanup ''
if [ -e "''${TMP_FILE-}" ]; then
rm "$TMP_FILE"
fi
'')
]
add-path
add-path :: String -> Permission
Prepends the passed string to $PATH.
add-pkg-deps
add-pkg-deps :: [Package] -> Permission
Adds the packages' bin directory to $PATH and grants read access to
the packages (so the Landlock policy allows them to be executed).
add-runtime
add-runtime :: String -> Permission
Adds arbitrary logic to run at runtime, before the sandbox starts.
You can push additional nono flags by appending to the bash array
$NONO_EXTRA_FLAGS. This lets you make sandbox flags depend on runtime
conditions (e.g. paths that contain $XDG_RUNTIME_DIR).
Note that anything added here is not run inside the sandbox. To run arbitrary things at runtime inside the sandbox see wrap-entry.
Example:
add-runtime ''
# grant read access to /foo only if /bar exists on the host
if [ -e /bar ]; then
NONO_EXTRA_FLAGS+=(--read /foo)
fi
''
If you create any resources in add-runtime that you want to automatically clean up when the sandbox exits use add-cleanup.
allow-command
allow-command :: String -> Permission
Adds cmd to the child-process execution allowlist.
Once any allow-command is set, the sandbox blocks execution of any
command not in the list. Use this to lock down which binaries the sandboxed
process can spawn as children.
(allow-command "/run/current-system/sw/bin/curl")
To allow multiple commands, apply this combinator once per command or compose them:
(compose [
(allow-command "/run/current-system/sw/bin/curl")
(allow-command "/run/current-system/sw/bin/git")
])
allow-cwd
allow-cwd :: Permission
Grants read-write access to the current working directory.
Alias for mount-cwd.
allow-domain
allow-domain :: String -> Permission
Allows outbound connections to a domain.
Supports wildcards like *.example.com. Implies network proxy mode.
Example:
allow-domain "api.openai.com"
allow-domain "*.anthropic.com"
allow-endpoint
allow-endpoint :: String -> Permission
Allows a specific HTTP endpoint in the format SERVICE:METHOD:PATH.
Example:
allow-endpoint "openai:POST:/v1/chat/completions"
allow-endpoint "anthropic:POST:/v1/messages"
allow-gpu
allow-gpu :: Permission
Exposes GPUs to the sandboxed application via nono's --allow-gpu flag.
This is the explicit form of gpu. Both set allowGpu = true in
the sandbox state, which causes the nono invocation to include --allow-gpu.
nono handles the details automatically — DRM, NVIDIA, AMD, Metal, and WSL2
are all covered by that single flag.
allow-launch-services
allow-launch-services :: Permission
(macOS only) Allows the sandbox to open URLs and files through macOS Launch Services.
Without this, calls to open or NSWorkspace.open inside the sandbox
are blocked. Enable it when the sandboxed tool legitimately needs to
launch browsers, open documents in other apps, or handle URL schemes.
allow-unix-socket
allow-unix-socket :: String -> Permission
Allows the sandboxed process to connect to the unix socket at path.
If path contains shell variable references (e.g. $XDG_RUNTIME_DIR/bus),
wrap it with noescape to prevent shell-escaping:
(allow-unix-socket (noescape "$XDG_RUNTIME_DIR/bus"))
allow-unix-socket-bind
allow-unix-socket-bind :: String -> Permission
Allows the sandboxed process to create (bind) a unix socket at path.
If path contains shell variable references (e.g. $XDG_RUNTIME_DIR/foo.sock),
wrap it with noescape to prevent shell-escaping:
(allow-unix-socket-bind (noescape "$XDG_RUNTIME_DIR/foo.sock"))
allow-unix-socket-dir
allow-unix-socket-dir :: String -> Permission
Allows the sandboxed process to connect to any unix socket one level deep
inside dir. Sockets in subdirectories are not covered — use
allow-unix-socket-subtree for recursive access.
If dir contains shell variable references, wrap it with
noescape:
(allow-unix-socket-dir (noescape "$XDG_RUNTIME_DIR"))
allow-unix-socket-dir-bind
allow-unix-socket-dir-bind :: String -> Permission
Allows the sandboxed process to create (bind) unix sockets one level deep
inside dir. For recursive permission, use
allow-unix-socket-subtree-bind.
If dir contains shell variable references, wrap it with
noescape:
(allow-unix-socket-dir-bind (noescape "$XDG_RUNTIME_DIR"))
allow-unix-socket-subtree
allow-unix-socket-subtree :: String -> Permission
Allows the sandboxed process to connect to any unix socket anywhere under
dir, recursively. For one-level-only access, use
allow-unix-socket-dir.
If dir contains shell variable references, wrap it with
noescape:
(allow-unix-socket-subtree (noescape "$XDG_RUNTIME_DIR"))
allow-unix-socket-subtree-bind
allow-unix-socket-subtree-bind :: String -> Permission
Allows the sandboxed process to create (bind) unix sockets anywhere under
dir, recursively. For one-level-only permission, use
allow-unix-socket-dir-bind.
If dir contains shell variable references, wrap it with
noescape:
(allow-unix-socket-subtree-bind (noescape "$XDG_RUNTIME_DIR"))
audit-integrity
audit-integrity :: Permission
Enables integrity verification of the audit chain.
When set, nono verifies that no audit entries have been tampered with before processing them. Any gap or modification in the chain causes the verification to fail loudly rather than silently accepting a potentially altered record.
Pair with audit-sign-key if you also want entries cryptographically signed at write time.
audit-sign-key
audit-sign-key :: String -> Permission
Signs audit entries with the given key reference.
key is a URI pointing at the signing key. Supported schemes:
op://vault/item/field— 1Password secret referenceenv://VAR— read from an environment variablefile://path— read from a file on disk
Each audit entry gets a signature nono can verify later. Combine with audit-integrity to also verify the chain hasn't been truncated or reordered.
block-command
block-command :: String -> Permission
Blocks the sandboxed process from executing cmd.
Useful for preventing specific subcommands or tools from running inside the sandbox without building a full allowlist via allow-command.
(block-command "/run/current-system/sw/bin/curl")
block-net
block-net :: Permission
Blocks all network access for the sandboxed application.
bypass-protection
bypass-protection :: String -> Permission
Bypasses nono's built-in protection for path.
Nono protects certain paths by default (e.g. ~/.ssh, ~/.gnupg). If a
legitimate app genuinely needs access to one of those paths, use this
combinator to carve out an exception. The path still needs to be granted
by the profile — this just lifts the extra protection layer on top.
Example:
bypass-protection "~/.ssh/known_hosts"
camera
camera :: Permission
Allows access to webcams and other V4L2 video devices at /dev/video*.
capability-elevation
capability-elevation :: Permission
Enables runtime approval prompts for privileged operations.
When the sandboxed process tries to exceed its declared grants, nono normally denies the operation silently. With this combinator, nono shows an interactive approval dialog instead, letting you approve or deny the escalation in real time.
Requires supervised exec mode (nono run). Useful during profile
development to catch missing grants without having to rerun from scratch.
compose
compose :: [Permission] -> Permission
Composes a list of permissions into a single permission.
credential
credential :: String -> Permission
Injects credentials for a service via nono's credential proxy.
The service argument identifies the credential to inject (e.g., openai,
anthropic, gemini). Requires exec-mode "run" (nono supervised mode).
Example:
credential "anthropic"
defer
defer :: Permission -> Permission
Defers the specified permission to be applied after all non-deferred permissions.
This is useful if a permission needs to read state set by other permissions.
deny
deny :: String -> Permission
Explicitly denies access to a path, overriding any broader grants.
Useful for blocking sensitive subdirectories when a parent directory has
been granted. For example, granting ~/.config but blocking
~/.config/secrets.
Example:
compose [
(readwrite "~/.config")
(deny "~/.config/secrets")
]
deny-env
deny-env :: String -> Permission
Prevents an environment variable from reaching the sandbox.
Supports PREFIX_* prefix patterns to deny whole namespaces. Takes
precedence over fwd-env and try-fwd-env.
Example:
deny-env "AWS_SECRET_ACCESS_KEY"
deny-env "AWS_*"
env-credential
env-credential :: String -> Permission
Injects a credential from a secret store as an environment variable.
The key is a URI identifying the credential source:
* op://vault/item/field — 1Password
* env://VAR — from an environment variable on the host
* file://path — from a file on the host
Example:
env-credential "op://Personal/OpenAI/credential"
env-credential "env://ANTHROPIC_API_KEY"
env-credential-map
env-credential-map :: String -> String -> Permission
Injects a credential at a URI into a specific environment variable.
Like env-credential, but lets you name the environment variable explicitly rather than using the credential's default name.
Example:
env-credential-map "op://Personal/OpenAI/credential" "OPENAI_API_KEY"
escape
escape :: String -> String
Shell escapes the passed string.
Use noescape to prevent escaping.
escape and noescape don't return Permissions, but they are useful
helpers to expose when defining jails and writing custom combinators, so
they are exposed with the rest of the combinators for convenience.
Example:
nono-nix.lib.extend {
inherit pkgs;
additionalCombinators = combinators: with combinators; {
# a combinator that grants read access to an escaped path
my-combinator = path: unsafe-add-raw-args "--read ${escape path}";
};
}
exec-mode
exec-mode :: String -> Permission
Sets the nono execution mode. Valid values are "run" and "wrap".
"run" (the default) uses supervised mode: nono stays alive alongside
the sandboxed process, enabling audit logging, rollback snapshots,
credential injection, and capability elevation prompts.
"wrap" uses exec-replace mode: nono execs directly into the target
process for lower overhead. You lose the supervised features but get a
simpler process tree and slightly faster startup.
Example:
exec-mode "wrap"
extends-profile
extends-profile :: String -> Permission
Extends a named nono profile from the built-in profile registry.
The generated profile inherits all grants from name and then applies
whatever additional combinators you stack on top. Built-in profiles
include things like "default", "claude-code", and "opencode".
Example:
compose [
(extends-profile "claude-code")
(groups-include "network")
]
fwd-env
fwd-env :: String -> Permission
Forwards the specified environment variable to the sandboxed process.
If the variable is not set when the sandboxed application runs, it will exit non-zero (variable unbound error).
If you want to tolerate an unset variable, use try-fwd-env instead.
gpu
gpu :: Permission
Exposes GPUs to the sandboxed application.
Sets allowGpu = true in the sandbox state, which causes the nono invocation
to include --allow-gpu. nono handles the details automatically — DRM,
NVIDIA, AMD, Metal, and WSL2 are all covered by that single flag.
groups-exclude
groups-exclude :: String -> Permission
Excludes a named permission group from the profile.
Use this to pull a group out of an inherited profile. For example, if you
extend "default" but don't want the "clipboard" group it includes, you
can exclude it here rather than rewriting the whole profile from scratch.
Example:
compose [
(extends-profile "default")
(groups-exclude "clipboard")
]
groups-include
groups-include :: String -> Permission
Includes a named permission group from the profile's group registry.
Groups bundle related grants together under a single name (e.g. "network",
"fonts", "clipboard"). This combinator opts the sandbox into one of
those bundles without having to spell out every grant individually.
Example:
compose [
(groups-include "network")
(groups-include "fonts")
]
gui
gui :: Permission
Exposes everything required to get graphical applications working.
This composes pulse, pipewire, wayland, and forwards/grants read access to a few other paths to get fonts and cursor rendering to work correctly.
include-once
include-once :: String -> Permission -> Permission
Only run the passed permission if include-once hasn't been previously called with the specified key.
This is useful when writing your own combinators.
let
nono = nono-nix.lib.extend {
inherit pkgs;
additionalCombinators = combinators: with combinators; {
# foo isn't `include-once` so each call to it adds a new echo
foo = add-runtime "echo foo";
# bar will only be included once, no matter how many times it is called
bar = include-once "bar" (add-runtime "echo bar");
};
};
in
# Prints:
# foo
# foo
# foo
# bar
# Hello, world!
nono "test" pkgs.hello (c: with c; [
foo
foo
foo
bar
bar
bar
])
ipc-mode
ipc-mode :: String -> Permission
Controls IPC access for the sandboxed process.
Valid values:
"shared_memory_only"— restricts IPC to shared memory; message queues and semaphores are blocked"full"— allows all IPC mechanisms
The default when this combinator is not used depends on nono's built-in policy.
jail-to-host-channel
jail-to-host-channel :: String -> String -> Combinator
Allows programs in the sandbox to execute and pass messages to a specific handler that runs outside of the sandbox.
The first parameter specifies a name of a program that is exposed to the sandbox that, when called, sends its first argument to the script passed to the second parameter. The script runs outside of the sandbox. Any stdout generated by the script is relayed back as stdout from the program in the sandbox.
Current limitations:
- Only a single argument is supported. For more arguments you will need to use an encoding like JSON.
- Only stdout is relayed back to the sandbox, stderr will be visible in your terminal but the sandbox won't be able to read it.
- The first parameter must be a valid POSIX variable name.
Example:
jail-to-host-channel "getHostFileSize" ''
# This runs *outside* of the sandbox
if [ -f "$1" ]; then
wc -c < "$1"
else
echo "$1 is not a file on the host"
fi
''
This exposes a program inside the sandbox called getHostFileSize
that returns the size of the file passed in without needing to
give the sandbox read access to any files.
The channel program inside the sandbox finds the FIFO via the
J2H_CHAN_<NAME>_TMP environment variable that is forwarded
automatically.
listen-port
listen-port :: Int -> Permission
Allows the sandbox to bind and listen on a port.
Example:
listen-port 3000
mount-cwd
mount-cwd :: Permission
Grants read-write access to the working directory at runtime ($PWD).
network
network :: Permission
Grants network access to the sandbox.
Since nono runs on the real network namespace, no namespace sharing is needed. This combinator grants read access to the files required for DNS resolution, TLS certificate verification, and timezone handling.
Exposed paths:
/etc/hosts/etc/nsswitch.conf/etc/resolv.conf/etc/ssl/etc/static/ssl(optional — NixOS symlink chain to CA bundle)/run/systemd/resolve(optional — silently skipped if absent)- Timezone via time-zone
- CA certificate bundle (
pkgs.cacert) viaSSL_CERT_FILE
network-profile
network-profile :: String -> Permission
Sets the nono network profile for the sandbox.
Available profiles: minimal, developer, claude-code, opencode,
enterprise. Implies proxy mode.
Example:
network-profile "developer"
no-audit
no-audit :: Permission
Disables nono's audit chain for this invocation.
By default nono records every access decision to an audit log. This combinator turns that off. Only use it during development — production sandboxes should leave auditing on so you can trace what happened if something goes wrong.
noescape
noescape :: String -> NoEscapedString
Prevent the passed string from being automatically shell escaped.
escape and noescape don't return Permissions, but they are useful
helpers to expose when defining sandboxes and writing custom combinators, so
they are exposed with the rest of the combinators for convenience.
It is the caller's responsibility to ensure anything passed to this is correctly escaped.
# Probably doesn't do what you intended since "~/foo" is shell escaped:
(readonly "~/foo")
# This properly makes $HOME/foo readonly in the sandbox:
(readonly (noescape "~/foo"))
# Binds the path specified by the runtime $FOO variable as read only.
#
# Note that we must properly quote this to ensure bash correctly keeps it
# as a single argument, even if it contains spaces:
(readonly (noescape "\"$FOO\""))
nono-name
nono-name :: String -> Permission
Sets the nono session name (passed as --name).
The session name identifies this invocation in nono status output and
audit logs. If you're running multiple sandboxes at once, giving each one
a distinct name makes it much easier to tell them apart.
Example:
nono-name "my-tool"
open-port
open-port :: Int -> Permission
Opens a localhost port for the sandbox to connect out on.
Example:
open-port 8080
open-urls-in-browser
open-urls-in-browser :: Permission
Allows access to open URLs in $BROWSER.
This works by creating a pipe that is mounted into the sandbox that forwards
all URLs to the $BROWSER outside of the sandbox. This way the sandboxed
program can launch your browser, even if it has a subset of the
permissions your browser has.
Only URLs beginning with http(s):// will be forwarded.
pipewire
pipewire :: Permission
Exposes PipeWire to the sandboxed application.
Forwards XDG_RUNTIME_DIR and allows the PipeWire Unix socket at
$XDG_RUNTIME_DIR/pipewire-0.
process-info-mode
process-info-mode :: String -> Permission
Controls whether the sandboxed process can read /proc entries for other
processes.
Valid values:
"isolated"— blocks access to other processes'/procentries; the process can only see its own"allow_same_sandbox"— allows reading/procentries for processes in the same sandbox"allow_all"— unrestricted/procaccess across all processes
The default when this combinator is not used depends on nono's built-in policy.
pulse
pulse :: Permission
Exposes PulseAudio to the sandboxed application.
Forwards XDG_RUNTIME_DIR and optionally PULSE_SERVER, then allows
the PulseAudio Unix sockets in $XDG_RUNTIME_DIR/pulse and /run/pulse.
readonly
readonly :: String -> Permission
Grants read-only access to the specified path.
The path is checked at launch time: if it does not exist the sandbox refuses to start with a clear error. Use try-readonly if a missing path should be silently skipped instead.
Pass a noescape value to reference ~ or $VAR, which are
expanded by the shell at launch.
readonly-file
readonly-file :: String -> Permission
Grants read-only access to a single file.
Unlike readonly which grants a directory subtree,
this grants access only to the specific file at path.
The file is checked at launch time: if it does not exist the sandbox refuses to start with a clear error. Use try-readonly-file if a missing file should be silently skipped instead.
Example:
readonly-file "/etc/ssl/certs/ca-certificates.crt"
readonly-paths-from-var
readonly-paths-from-var :: String -> String -> Permission
Grants read-only access to multiple paths specified by a single runtime environment variable.
The first argument is the runtime environment variable that contains a
list of paths. The second argument is the delimiter used to split the
paths (typically " " or ":").
This is useful for variables like XDG_DATA_DIRS, GTK_PATH,
XCURSOR_PATH, etc.
Example:
compose [
(readonly-paths-from-var "XDG_DATA_DIRS" ":")
(readonly-paths-from-var "XCURSOR_PATH" " ")
]
readonly-runtime-args
readonly-runtime-args :: Permission
Grants read-only access to any valid paths passed as arguments to the sandboxed program at runtime.
readwrite
readwrite :: String -> Permission
Grants read-write access to the specified path.
The path is checked at launch time: if it does not exist the sandbox refuses to start with a clear error. Use try-readwrite if a missing path should be silently skipped instead.
Pass a noescape value to reference ~ or $VAR, which are
expanded by the shell at launch.
readwrite-file
readwrite-file :: String -> Permission
Grants read-write access to a single file.
Unlike readwrite which grants a directory subtree,
this grants access only to the specific file at path.
The file is checked at launch time: if it does not exist the sandbox refuses to start with a clear error. Use try-readwrite-file if a missing file should be silently skipped instead.
Example:
readwrite-file "~/.config/myapp/state.json"
readwrite-runtime-args
readwrite-runtime-args :: Permission
Grants read-write access to any valid paths passed as arguments to the sandboxed program at runtime.
reset
reset :: Permission
Resets the sandbox state back to the initial empty state.
All permissions before this one (including ones set in
basePermissions) are
removed.
This is useful if you have some permissions set in
basePermissions that you
want for most of your sandboxes, but want to remove them for a one-off sandbox.
Example:
nono "some-package" some-package (combinators: with combinators; [
# First, use reset to remove the base combinators:
reset
# Then, re-apply the base combinators you want:
base
bind-nix-store-runtime-closure
(readwrite "/some/path")
])
rollback
rollback :: Permission
Enables atomic rollback snapshots for this sandbox.
When enabled, filesystem changes made by the sandboxed process can be rolled back on exit or failure. Nono captures a snapshot before the process runs and restores it if the process exits non-zero or if rollback is triggered manually.
Requires supervised exec mode (nono run). Use with rollback-all
if you want to capture all changes regardless of what the profile grants.
rollback-all
rollback-all :: Permission
Tracks all filesystem changes for rollback, regardless of what the profile grants.
Normally rollback only covers paths within the profile's declared grants. This flag tells nono to snapshot everything the process touches, even paths not explicitly listed in the profile. Useful when you want a complete before/after snapshot and don't want gaps.
Combine with rollback to enable the rollback feature itself.
rollback-exclude
rollback-exclude :: String -> Permission
Excludes a glob pattern from rollback tracking.
Paths matching pattern won't be snapshotted or restored when rollback
runs. Useful for excluding things like cache directories or log files
that you don't want rewound.
Example:
compose [
rollback
(rollback-exclude "~/.cache/**")
(rollback-exclude "/tmp/**")
]
rollback-exclude-glob
rollback-exclude-glob :: String -> Permission
Excludes files matching a glob from rollback tracking.
Unlike rollback-exclude which matches against path
components, this glob is matched against the filename only. For example,
"*.log" excludes every file ending in .log anywhere under the snapshot.
Example:
compose [
rollback
(rollback-exclude-glob "*.log")
(rollback-exclude-glob "*.tmp")
]
rollback-include
rollback-include :: String -> Permission
Adds an extra path to rollback tracking coverage.
By default, rollback only tracks paths covered by the active profile's grants. Use this to pull in additional paths that the profile doesn't explicitly grant but that you still want snapshotted.
Example:
compose [
rollback
(rollback-include "~/.config/myapp")
]
set-argv
set-argv :: [String] -> Permission
Overrides the current argv that is passed to the sandboxed executable.
By default argv is set to noescape "$@" which will forward whatever
arguments are provided to the wrapper script at runtime. Calling this
will override the current value.
set-env
set-env :: String -> String -> Permission
Sets the specified environment variable for the sandboxed process.
This will throw if the variable name is not a valid POSIX variable name.
signal-mode
signal-mode :: String -> Permission
Controls which processes can send signals to the sandboxed process.
Valid values:
"isolated"— blocks all external signals; only the process itself can signal itself"allow_same_sandbox"— allows signals from processes in the same sandbox"allow_all"— any process on the system can signal the sandboxed process
The default when this combinator is not used depends on nono's built-in policy.
startup-timeout
startup-timeout :: Int -> Permission
Sets the startup timeout in seconds.
If the sandboxed process hasn't signalled readiness within secs seconds
of launch, nono terminates it. This guards against hangs during
initialisation — particularly useful for services that are expected to
start promptly.
Example:
startup-timeout 30
suppress-save-prompt
suppress-save-prompt :: String -> Permission
Suppresses the nono save-prompt dialog for writes to path.
When the sandbox tries to write to a path nono considers sensitive, it
normally shows an interactive "save here?" dialog. This combinator
pre-approves writes to path so the dialog never appears — useful for
automated tools that write to a known location and can't handle
interactive prompts.
Example:
suppress-save-prompt "~/.config/myapp/state.json"
time-zone
time-zone :: Permission
Exposes your timezone to the sandbox.
If /etc/localtime is a symlink (the common case on systemd systems),
the real target is also granted read access so the timezone data is
actually readable inside the sandbox.
trust-override
trust-override :: Permission
Overrides trust checks, allowing the sandbox to run even when trust verification would otherwise block it.
Nono normally refuses to run if the binary or profile fails a trust check (e.g. unsigned profile, unknown binary hash). This combinator bypasses that gate. Use sparingly — it exists for development workflows and controlled environments, not for production sandboxes.
try-fwd-env
try-fwd-env :: String -> Permission
Forwards the specified environment variable to the sandboxed process (if set).
try-readonly
try-readonly :: String -> Permission
Grants read-only access to the path if it exists, silently skips otherwise.
Automatically handles both files and directories. Unlike readonly, a missing path does not fail the sandbox.
Pass a noescape value to reference ~ or $VAR, which are
expanded by the shell at launch.
try-readonly-file
try-readonly-file :: String -> Permission
Grants read-only access to a single file if it exists, silently skips otherwise.
Same as readonly-file but doesn't fail when the file is absent at launch.
Example:
try-readonly-file "~/.gitconfig"
try-readwrite
try-readwrite :: String -> Permission
Grants read-write access to the path if it exists, silently skips otherwise.
Automatically handles both files and directories. Unlike readwrite, a missing path does not fail the sandbox.
Pass a noescape value to reference ~ or $VAR, which are
expanded by the shell at launch.
try-readwrite-file
try-readwrite-file :: String -> Permission
Grants read-write access to a single file if it exists, silently skips otherwise.
Same as readwrite-file but doesn't fail when the file is absent at launch.
Example:
try-readwrite-file "~/.config/myapp/state.json"
try-write-only
try-write-only :: String -> Permission
Grants write-only access to a path if it exists, silently skips otherwise.
Automatically handles both files and directories. Unlike write-only, a missing path does not fail the sandbox.
Example:
try-write-only "/var/log/myapp"
unsafe-add-raw-args
unsafe-add-raw-args :: String -> Permission
Appends the raw string as flags to the nono invocation.
Nothing is escaped — it is the caller's responsibility to ensure everything is properly escaped. Shell variable references in the string expand at runtime.
A noescape value is accepted and unwrapped to its raw
string, so combinators that build flags containing $VAR references can
pass noescape without it being coerced into the profile.
unsafe-dbus
unsafe-dbus :: Permission
Exposes the D-Bus session bus to the sandboxed program.
This does no message filtering so it is marked as unsafe. If you want more control over the messages that can be sent/received, consider using the dbus combinator instead.
Forwards DBUS_SESSION_BUS_ADDRESS and XDG_RUNTIME_DIR, then allows
the session bus socket at $XDG_RUNTIME_DIR/bus.
unsafe-x11
unsafe-x11 :: Permission
Exposes X11 to the sandboxed application.
Note that applications may be able to break out of the sandbox because X11 is not designed to be a security boundary.
For a safer alternative, consider using the xwayland combinator inside of a Wayland compositor.
upstream-bypass
upstream-bypass :: String -> Permission
Bypasses the upstream proxy for a domain.
Example:
upstream-bypass "internal.corp.example.com"
upstream-proxy
upstream-proxy :: String -> Permission
Routes traffic through an upstream proxy at HOST:PORT.
Example:
upstream-proxy "proxy.corp.example.com:8080"
wayland
wayland :: Permission
Exposes your Wayland compositor to the sandbox.
Forwards WAYLAND_DISPLAY, XDG_RUNTIME_DIR, and XDG_SESSION_TYPE,
then allows the compositor's Unix socket at $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY.
workdir
workdir :: String -> Permission
Sets the working directory for the sandboxed process (passed as --workdir).
The sandboxed process starts with its current directory set to path.
If unset, nono inherits the caller's working directory.
Example:
workdir "/var/lib/myapp"
wrap-entry
wrap-entry :: (String -> String) -> Permission
Wraps the binary to be sandboxed in a bash script that will be the new entrypoint to the sandbox.
This similar in spirit to the add-runtime combinator,
except that this runs inside the sandbox, while add-runtime runs before
the sandbox starts.
Example:
wrap-entry (entry: ''
echo 'Inside the sandbox!'
${entry}
echo 'Cleaning up...'
'')
write-only
write-only :: String -> Permission
Grants write-only access to a path.
The sandboxed process can write files under path but cannot read them
back. Use this for append-only log sinks or output directories where
read access isn't needed.
The path is checked at launch time: if it does not exist the sandbox refuses to start with a clear error. Use try-write-only if a missing path should be silently skipped instead.
Pass a noescape value to reference ~ or $VAR, which are
expanded by the shell at launch.
Example:
write-only "/var/log/myapp"
write-only-file
write-only-file :: String -> Permission
Grants write-only access to a single file.
The sandboxed process can write to the file at path but cannot read
it. Unlike write-only which covers a directory,
this pins access to one file.
The file is checked at launch time: if it does not exist the sandbox refuses to start with a clear error.
Example:
write-only-file "/var/log/myapp/output.log"
write-text
write-text :: String -> String -> Permission
Creates a read-only text file in the nix store and grants the sandbox access to it.
Note: the file is accessible at its nix store path, not at the path
argument — nono cannot remap paths. The path argument is used only
for derivation naming.
Example:
# Creates a text file and grants the sandbox read access to it.
# Find it at its nix store path, e.g. /nix/store/...-nono-write-text-hello-txt
write-text "/hello.txt" "Hello, world!"
xwayland
xwayland :: Permission
Safely allow X11 apps to render to a Wayland compositor.
This combinator runs xwayland-satellite inside the sandbox and only exposes wayland combinator.
This has the advantage of not allowing multiple sandboxed X11 applications to see each other since each sandboxed application gets its own xwayland-satellite server.
However, doing it this way does mean that every sandboxed application you run with this combinator will spin up its own personal xwayland-satellite server, which will consume more resources than having a global one.
Default Included Combinators
The following combinators are enabled by default, and do not need to be
explicitly added to your sandboxes unless you override
basePermissions.
base
base :: Permission
Sets up the minimal environment every sandboxed program needs.
Because nono runs as the real user on the real filesystem (no namespace
isolation), there's no need to fake /proc, /dev, or /tmp.
This combinator just makes sure coreutils is on $PATH and that
the basic environment variables are forwarded in.
This combinator:
- Adds coreutils to the package deps (
add-pkg-deps [ pkgs.coreutils ]) - Forwards
HOMEinto the sandbox - Tries to forward
LANGandTERM(silently skipped if unset)
This is included in the base permissions by default so you shouldn't need
to include it unless you override base permissions. It is exposed as a
combinator (like the other default included combinators) so that you can
use it in a custom
basePermissions.
bind-nix-store-runtime-closure
bind-nix-store-runtime-closure :: Permission
Grants read access to all /nix/store paths in the runtime closure of the
sandboxed application via Landlock.
At Nix build time the full closure is computed with exportReferencesGraph
and written to a text file. At runtime that file is read and each path is
added to NONO_EXTRA_FLAGS as --read <path>.
If you don't have any sensitive nix store paths, you may consider just
granting read access to /nix/store directly instead.
For example, the sandbox defined by
# Note that this combinator is included in base permissions, so it does
# not need to be provided:
let listNixStore = pkgs.writeShellScriptBin "list-nix-store" "ls -l /nix/store";
in nono "list-nix-store" listNixStore [];
will only expose the closure paths for list-nix-store, rather than your
entire nix store.
Deprecated Combinators
The following combinators have been deprecated, and may be removed in the future.