SYD(2)
🦋 SYD(2) — the syd(2) manual, but make it make sense 🦋
okay bestie, so this is from the System Calls Manual, it's SYD(2), and it's literally the whole /dev/syd virtual system call interface. it's giving "kernel magic but cute" and i'm obsessed. let's go 💅
✨ NAME ✨
/dev/syd virtual system call interface — yes that's the whole name, it ate and left no crumbs 👑
🌸 SYNOPSIS 🌸
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct stat stat;
/* Execute sandbox commands */
int stat("/dev/syd/[command]", &stat); // $ syd -m command
int stat("/dev/syd/[config]?", &stat);
int stat("/dev/syd/[config]:[value]", &stat); // $ syd -m config:value
int stat("/dev/syd/[list]+[value]", &stat); // $ syd -m list+value
int stat("/dev/syd/[list]-[value]", &stat); // $ syd -m list-value
int stat("/dev/syd/[list]^[value]", &stat); // $ syd -m list^value
int stat("/dev/syd/[command]![value]", &stat);
/* Read sandbox state as JSON */
int open("/dev/syd", O_RDONLY);
/* Read syd.el which is the Emacs Lisp implementation of the API */
int open("/dev/syd.el", O_RDONLY);
/* Read syd.sh which exports esyd shell function */
int open("/dev/syd.sh", O_RDONLY);
no cap, you're literally calling stat(2) on fake paths and the sandbox listens. main character energy fr fr 🎀
💖 DESCRIPTION 💖
so the /dev/syd virtual system call interface is this unique mechanism (their word, not mine, but honestly? accurate) designed for runtime configuration of the Syd sandbox environment. it lets sandboxed processes talk to the Syd process directly — dynamically tweaking sandbox settings or querying its state. like a little group chat between your process and the sandbox 🫶
the whole convo happens through virtual system calls, specifically via the stat(2) system call, aimed at specially constructed paths under /dev/syd. this interface unlocks a whole range of operations, including:
- enabling or disabling sandbox features
- appending or removing elements from lists
- querying the sandbox state
- executing special Syd commands
operations are specified through paths built with /dev/syd as the prefix, followed by a sandbox command and an operation character that says what you actually want to do:
:for setting a value (boolean, integer, string)?for querying a value+for appending to a string vector-for removing an element from a string vector^for removing all matching elements from a string vector, and!for executing a special Syd command
quick side quest — the type string-map is similar to string-vec, except the operator ^ does not accept an argument and removes all elements from the string map. the - operator of a string map is functionally equivalent to the ^ operator of a string vector, in that both remove all matching elements from the respective set. slay, but make it specific 💀
this interface is a flexible and powerful method for managing sandbox policies dynamically, allowing for real-time adjustments to the security and operational behavior of sandboxed processes. and because they're not gonna make you hand-roll all this:
libsydis a full-featured C library designed for interfacing with the Syd stat interface. it offers functionalities for managing sandbox states, and facilitating runtime configuration and interaction with the Syd sandboxing environment.gosydis a Go moduleplsydis a Perl modulepysydis a Python modulerbsydis a Ruby modulesyd.elis an Emacs Lisp moduleesyd.shis a POSIX sh module
all of them use the Syd stat interface, so pick your fave language and go off 🌈
👑 COMMANDS 👑
the /dev/syd interface supports the following commands for runtime configuration of the sandbox. each command can be invoked through the stat(2) system call on special paths under /dev/syd. Syd provides similar interfaces for:
- the
stat(2)interface - the
-mcommand-line option - the configuration file
some commands may be used with lock:drop mode to further reduce privileges like pledge(2). those ones are noted as drop in the descriptions below. some sandbox commands only take effect when they're submitted on startup, such as unshare/user and sandbox/lock — those are noted as static below. and some sandbox commands are only available when Syd is built with the trusted Cargo feature — those are noted as trusted below. read the labels, bestie, they matter 💅
🌟 dump 🌟
this command causes Syd to output sandbox state in pretty formatted JSON to standard error. pretty JSON supremacy 🔥
🌟 stat 🌟
this command causes Syd to output sandbox state in human readable format to standard error. for when JSON is too much and you just want vibes.
🌟 panic 🌟
this command causes Syd to exit immediately with code 127.
⚠️ due to security reasons, this command is only available via the virtualstat(2)call — it may not be used with the-mcommand line switch or in a configuration file. no shortcuts, sorry 🫶
🌟 ghost 🌟
this command initiates Ghost mode. Ghost mode is irreversible, so you can call this command only once during Syd runtime — there is no undo, no take-backs, no "wait nvm" 😭
refer to the "Ghost mode" section of the syd(7) manual page for more information (go read it, i'll wait 💜).
this command implies lock:on to ensure the sandbox can not be edited after initiation of ghost mode.
⚠️ Ghost mode is only available via the virtualstat(2)call, it can not be used with the-mcommand line switch or in a configuration file.
🌟 ipc 🌟
the type deets, since we can't do a table here:
- type —
string - static —
yes
configure sandbox during runtime using the given UNIX socket address with kernel-validated peer authentication. authentication leverages SCM_CREDENTIALS and SO_PASSCRED mechanisms to verify that connecting processes share identical UID and GID with the IPC worker process. authentication UID and GID may be overridden by ipc/uid and ipc/gid options at startup.
this kernel-enforced authentication prevents privilege escalation and unauthorized access by validating credentials on every message, so that only the specified user and group or the system administrator can execute IPC commands. the way that the kernel checks every single message? iconic 🔒
if the argument starts with the character @, the address is taken to be an abstract UNIX socket. use the keywords none or off to unset a previously set IPC address.
the IPC implementation is inspired by HAProxy's stats socket implementation. all responses except the stats command are in compact JSON. the user is recommended to use the version command to check the API version prior to use.
⚠️ as a safety measure, the IPC service is provided as long as the sandbox is unlocked. when the sandbox is locked, thesyd_ipcthread exits. this thread makes no attempt tounlink(2)the UNIX domain socket path at startup or exit. the user should perform the cleanup or use abstract sockets, which is recommended. like, do the cleanup bestie, don't leave your socket lying around 🙈
to access the socket, an external utility such as socat(1) is required. Socat is a swiss-army knife to connect anything to anything — we use it to connect terminals to the socket, or a couple of stdin/stdout pipes to it for scripts. the two main syntaxes we'll use are the following:
# socat ~/.syd/sandbox.sock stdio
# socat ~/.syd/sandbox.sock readline
the first one is used with scripts. it is possible to send the output of a script to Syd, and pass Syd's output to another script. that's useful for retrieving sandbox configuration as JSON for example. the second one is only useful for issuing commands by hand. it has the benefit that the terminal is handled by the readline library which supports line editing and history, which is very convenient when issuing repeated commands (eg: watch a counter). it's giving ✨quality of life✨
the socket supports three operation modes:
- non-interactive, silent
- interactive, silent
- interactive with prompt
the non-interactive mode is the default when socat(1) connects to the socket. in this mode, a single line may be sent. it is processed as a whole, responses are sent back, and the connection closes after the end of the response. this is the mode that scripts and monitoring tools use. a single command may be sent at a time only.
the interactive mode allows new commands to be sent after the ones from the previous lines finish. it exists in two variants:
- one silent, which works like the non-interactive mode except that the socket waits for a new command instead of closing
- one where a prompt is displayed (
;) at the beginning of the line
the interactive mode is preferred for advanced tools, while the prompt mode is preferred for humans. (humans 🤝 prompts, it's a thing)
the mode can be changed using the prompt command. by default, it toggles the interactive+prompt modes. entering prompt in interactive mode will switch to prompt mode. the command optionally takes a specific mode among the following:
n: non-interactive mode (single command and quits)i: interactive mode (multiple commands, no prompt)p: prompt mode (multiple commands with a prompt)
since the default mode is non-interactive, prompt must be used as the first command in order to switch it, otherwise the previous command will cause the connection to be closed. switching to non-interactive mode will result in the connection to be closed after all the commands of the same line complete.
for this reason, when debugging by hand, it's quite common to start with the prompt command:
# socat ~/.syd/sandbox.sock readline
prompt
; stats
...
;
interactive tools might prefer starting with prompt i to switch to interactive mode without the prompt. lowkey genius 🧚
the following commands are supported in addition to the syd(2) API:
load: Loads sandbox configuration from the attached file descriptor.stat: Prints sandbox state in compact JSON.stats: Prints sandbox state in human-readable format.version: Prints IPC api version in compact JSON.
the commands quit and exit may be used to close a socket connection. the command ping is supported for aliveness checks. (ping it back, it just wants to know you're okay 🥺)
as of version 3.56.0, the IPC socket supports unix(7) file descriptor passing. an authenticated SCM_RIGHTS file descriptor received over the socket is a configuration load request — the file descriptor passing analogue of /dev/syd/load with identical validations. the request must be unambiguous.
the specifics, bestie, because they will test you:
⚠️ the message carrying the descriptor must consist of the sole commandload, or it is rejected withEBADMSG("Not a data message") and the connection is closed — reserving other messages for future uses of file descriptor passing. conversely, aloadcommand without a descriptor is rejected withEBADF("Bad file number"), and a message containing unexpected control messages is rejected withEPROTO("Protocol error").
the file descriptor must refer to a regular file, include directives are rejected, and loads are denied whilst the lock is in drop mode. at most one file descriptor is accepted per message, and Syd replies with ACK on success or a JSON-encoded errno(3) on failure.
the syd-fd(1) utility implements this mode with its -u option. check for IPC API version 3.2 prior to use. 💀 (yes, check the version, i'm serious)
ipc/uid
- type:
uid_t - default:
Uid::current - static:
yes
okay so this one is the user ID override for IPC auth 💅 it's literally the UID that connecting processes gotta have to authenticate with the IPC worker. Heads up: it takes only numeric user IDs, not user names — so no alice, just the digits, bestie. Defaults to the current process UID, grabbed via getuid(2).
When you set it, the IPC worker validates that all connecting clients have this exact UID, using SCM_CREDENTIALS authentication. It's giving privilege delegation ✨ — the scenario where the IPC worker runs as one user but happily accepts connections from processes running as a totally different, specific UID. And if you want UID auth gone for IPC? Set the option to none or off. Simple as that 🫶
ipc/gid
- type:
gid_t - default:
Gid::current - static:
yes
the group-flavoured twin 🎀 Group ID override for IPC auth — specifies the GID connecting processes must possess to authenticate with the IPC worker. Accepts only numeric group IDs, not group names. Defaults to the current process GID via getgid(2).
Set it and the IPC worker checks that every connecting client has this exact GID through SCM_CREDENTIALS authentication. This is the group-based access control moment: multiple users in the same group can all get at the IPC interface. Same escape hatch as its sibling — set it to none or off to disable GID auth for IPC 💖
ipc/max
- type:
integer - default:
64 - static:
yes
the literal capacity limit — maximum number of concurrent IPC connections 🔥 Connections accepted past this limit get closed immediately, which bounds the memory and file descriptor usage of the IPC worker. Accepts a positive integer. The value 0 is rejected (do not try it, bestie 💀). If your actual goal is to kill the IPC interface entirely, set the option to ipc:off instead.
ipc/idle
- type:
duration - default:
60 - static:
yes
idle timeout for IPC connections, aka the "say something or I'm leaving" rule 😭 A connection that delivers no message within this duration gets closed. The deadline refreshes on every received message, so as long as you keep talking, you're fine ✨
Accepts a duration in human readable format. The value 0 is rejected. For the full deets on duration syntax, refer to the documentation of the dur crate 🦋
lock
- type:
string
right so this one sets the state of the sandbox lock — arguably the main character of this whole section 👑
Possible values: on, off, exec, ipc, drop and read, or the short forms 1, 0, x, d, i, and r. The values are case-sensitive (so ON is not it 💀). The values ro, readonly and read-only are also permitted for read mode — that was added as of version 3.39.0. And specifying just lock with no value, or the short l, is permitted as a short-hand for lock:on ✨
Now the vibes of each mode:
- If the sandbox lock is
on, no sandbox commands are allowed. Periodt. - If it's
read, only reads are allowed — no edits. A read locked sandbox makes available only the read-onlyopen(2)hooks of thesyd(2)virtual system call API to the sandbox process.stat(2)hooks for edits are not permitted in a read locked sandbox. - If it's
drop(added as of version 3.50.0), sandbox commands may only be used to further drop privileges. - If it's
exec, the sandbox lock is set toonfor all processes except the initial process, aka the Syd exec child. - If it's
ipc, sandbox commands may only be specified using the IPC socket. And like — the IPC socket is a UNIX socket which may or may not be accessible from within the sandbox depending on sandbox ACL rules, iykyk 🎀
The transition tea ☕: going from lock modes off, exec, and ipc into one of read and on is one-way and idempotent. It results in the sandbox policy getting sealed in memory using the mseal(2) system call, either immediately or right before sandbox process startup. Transitions between lock modes read and on are not permitted. No takebacks, bestie 🚫
Default history lesson time, because this matters: the sandbox lock used to default toexec, but as a hardening measure and to ensure security by default, as of version 3.17.0 this was changed so the default is unset. If nolockclause has been specified by the time Syd executes the initial sandbox process, then the sandbox lock is automatically set toon. Which means: if nolockclause shows up in any of the profiles, configuration files or-mCLI arguments, the lock isonby default. As of version 3.35.2, this default is set toipcif theipccommand was specified butlockwas not set explicitly.
Setting lock to on at any point during configuration parsing prevents further commands from being emitted by the sandbox. Iconic feature actually — you can lock site-wide defaults for a Syd login shell by adding a lock:on clause at the end of the site-wide configuration file. That stops Syd from subsequently parsing the user configuration file, practically enforcing the site-wide defaults 💅
Also: setting lock to off, exec, or ipc at startup makes Syd skip preventing execve(2) and execveat(2) system calls as part of the Execution Control (EEC) feature. Why? To allow the cmd/exec command to execute commands outside the sandbox. This filter to prevent exec(3) is only applied when the sandbox is locked 🔒
log/level
- type:
string - default:
warn
set the log level 🎚️ Available levels: emerg, alert, crit, error, warn, notice, info, and debug. Defaults to warn — unless the SYD_LOG environment variable is set at startup (sneaky 🫶).
An integer in the closed range of [0,7] can also be used as an argument to set the log level, where 0 corresponds to emerg and 7 corresponds to debug.
Which level does what, exactly:
- All access violations except the walk, list, stat and chdir categories are logged with the
warnlevel. - Walk, List, Stat, and Chdir categories are logged with the
noticelevel. - Startup messages are logged with the
infolevel 🌸
log/lock/same_exec_off
- type:
boolean - default:
off
Disables logging of denied accesses originating from the thread creating the landlock(7) domain, plus its children, as long as they continue running the same executable code (i.e., without an intervening execve(2) call). This is intended for programs that execute unknown code without invoking execve(2) — script interpreters being the classic example 🐍
Programs that only sandbox themselves should not set this flag, so users can be notified of unauthorized access attempts via system logs. Don't silence your own security alerts, bestie 😭
This option requires landlock(7) ABI 7 support, which is new in Linux-6.15. Setting this option is a NO-OP otherwise. Setting this option is also a NO-OP when sandbox/lock is off. Multiple options may be set or unset at once by passing them as a comma-delimited list. Environment variables in the value are expanded ✨
log/lock/new_exec_on
- type:
boolean - default:
off
Enables logging of denied accesses after an execve(2) call, giving you visibility into unauthorized access attempts by newly executed programs inside the created landlock(7) domain 👀
This flag is recommended only when all potential executables in the domain are expected to comply with the access restrictions — because excessive audit log entries could make it more difficult to identify critical events. (Cluttered logs = no thoughts, head empty 💀)
This option requires landlock(7) ABI 7 support, new in Linux-6.15. Setting this option is a NO-OP otherwise. Setting this option is also a NO-OP when sandbox/lock is off. Multiple options may be set or unset at once by passing them as a comma-delimited list. Environment variables in the value are expanded.
A sandboxer should not log denied access requests to avoid spamming logs, therefore this option is off by default. Use this option to test audit logging 🧪
log/lock/subdomains_off
- type:
boolean - default:
off
Disables logging of denied accesses originating from nested landlock(7) domains created by the caller or its descendants 🪆
This flag should be set according to runtime configuration, not hardcoded, to avoid suppressing important security events. fr fr, don't hardcode this one.
It's useful for container runtimes or sandboxing tools that may launch programs which themselves create landlock(7) domains and could otherwise generate excessive logs. And here's the key difference: unlike log/lock/same_exec_off, this flag only affects future nested domains, not the one being created 🌈
This option requires landlock(7) ABI 7 support which is new in Linux-6.15. Setting this option is a NO-OP otherwise. Setting this option is also a NO-OP when sandbox/lock is off. Multiple options may be set or unset at once by passing them as a comma-delimited list. Environment variables in the value are expanded 💖
log/verbose
- type:
u8 - default:
0
Set the verbose logging level 🗣️ Syd acquires and logs various additional information depending on the level of verbosity. The supported verbosity levels are given below:
0: Raw logs only.1: Logioctlnames(2) under thectlkey. This is the default.
- Log process name change attempts with the
PR_SET_NAMEprctl(2). - Enrich
seccomp(2)requests under thereqkey. 🎀
Levels above 2 are for malware analysis, bestie — like, forensic girlie hours 🔍. And if you set the log level to something above the highest supported level, it just behaves the same as setting verbosity to the highest supported level. No drama, no error, it just caps out. 💅
🗂️ log/rlimit_interval
- type:
duration - default:
5s
Okay so this is the rate-limiting interval for identical log messages. Here's the vibe: inside each interval window, at most log/rlimit_burst copies of any one message get emitted. Everything past that? Suppressed 😶. And when the interval expires, you get one single summary line telling you how many got squashed. Fr fr, one line, the receipts. 🧾
You can give the value as a raw integer in nanoseconds, or as a human-readable duration string parsed by the dur crate. Go read the dur crate docs for the deets, iykyk. And setting the interval to 0? That disables rate limiting entirely — no cap, no filter, raw output energy. 🌈
💥 log/rlimit_burst
- type:
u32 - default:
10
The max number of identical log messages allowed inside a single log/rlimit_interval window before suppression kicks in. After that many copies of the same message have been emitted, further duplicates get silently counted (they don't get to speak 🫥) and then a summary drops when the interval expires.
Setting the burst to 0 suppresses all messages unconditionally. Like, every single one. Zero notes, total silence. 💀
📐 pty/row
- type:
ushort - default:
<inherit> - static:
yes
Sets the row size for PTY sandboxing. Default is to inherit the window-size (very "I get it from my parent" of it 🧬). Use the keyword none to unset a previously set value. Also, you may shortly refer to this option as pty/x — cute little alias, we love a nickname. ✨
📏 pty/col
- type:
ushort - default:
<inherit> - static:
yes
Sets the column size for PTY sandboxing. Default is to inherit the window-size. Use the keyword none to unset a previously set value. And yep, you may shortly refer to this option as pty/y. Matching set, iconic. 🫶
🌱 setenv
- type:
command - static:
yes
Set an environment variable from within a Syd profile. Environment variables in the value are expanded before calling setenv(3) — so the substitution happens first, then it lands. Setting internal Syd environment variables — meaning any that start with the prefix "SYD_" — isn't permitted 🚫. Examples, because reading is hard and examples are everything:
setenv!HOME=/tmp
setenv!HOME=${HOME}/.syd
setenv!HOME=${HOME:-/tmp}/.syd
🧹 unsetenv
- type:
command - static:
yes
Unset an environment variable from within a Syd profile. Same deal: unsetting internal Syd environment variables, i.e. those starting with the prefix "SYD_", isn't permitted. Examples below, take notes bestie:
unsetenv!PWD
unsetenv!TZ
🫧 clearenv
- type:
command - static:
yes
Wipes all the environment variables from within a Syd profile. Big reset energy 🌪️. But — and this is the important part — this command does not clear internal Syd environment variables, i.e. those that start with the prefix "SYD_". Those ones stay. They're built different. 👑
🎟️ passenv
- type:
string-vec - static:
yes
Specify unsafe environment variables to pass-through to the sandbox process. This argument is a glob(3) pattern — so yes, wildcards, go off. Passing through internal Syd environment variables, i.e. those that start with the prefix "SYD_", isn't permitted 🚫.
By default, Syd clears a set of environment variables from the environment of the sandbox process — run syd-ls env for the full list. And for the whole picture, refer to the "Restricting environment and trace/allow_unsafe_env" section of the syd(7) manual page. 📚
🔌 passfd
- type:
u32-set - default:
stdio - static:
yes
Specify which file descriptors get passed to the sandbox process. All the other inherited file descriptors are closed just before execve(2) so they don't leak into the sandbox process — no sneaky fds getting through the door 🚪💨. By default only the standard I/O descriptors (0, 1, 2) are passed.
The argument is a comma-separated list of tokens, and each token is one of:
- a single descriptor
N - an inclusive range
N-M - or one of the keywords
all(every descriptor),stdio(descriptors0,1,2),stdin(0),stdout(1), andstderr(2)
Descending ranges are rejected. 🚩 And heads up: any descriptor listed in nullfd is implicitly passed regardless of this list — so its redirection to /dev/null does not get undone by the close before execve(2). The redirect stays. Periodt. 💅
Examples, serving looks:
passfd+stdio— adds the standard I/O descriptors.passfd+0-2,10-30,40-50— adds the listed descriptors and ranges.passfd+all— passes every descriptor, closing none.passfd-stdio,4-6— stops passing the standard I/O descriptors and4-6.passfd-all— stops passing every descriptor.passfd^— resets the list so that no descriptor is passed.
🕳️ nullfd
- type:
u32-set - static:
yes
Specify file descriptors to redirect to /dev/null in the sandbox process. Each listed descriptor gets pointed at /dev/null just before execve(2) — goodbye output, straight into the void 🌀.
The argument is a comma-separated list of tokens, each one either a single descriptor N or one of the keywords stdio (descriptors 0, 1, 2), stdin (0), stdout (1), and stderr (2). Unlike passfd there is no all keyword and ranges are not supported. Know the difference, bestie 😤.
A descriptor listed here is implicitly passed to the sandbox process, so you don't also need to add it to passfd. The /dev/null redirection takes precedence over passfd and is preserved across execve(2). It wins. Always. 👑
Examples:
nullfd+stdout— redirects standard output to/dev/null.nullfd+1,2— redirects standard output and standard error.nullfd-stderr— stops redirecting standard error.nullfd^— resets the list so that no descriptor is redirected.
📁 sandbox/fs
- type:
boolean - drop:
yes, on only - query:
yes
Turn Filesystem sandboxing on or off. That's it, that's the whole option. Simple, clean, does exactly what it says on the tin 🎀.
🚶 sandbox/walk
- type:
boolean - default:
off - drop:
yes, on only - query:
yes
Turn Walk sandboxing on or off.
And listen — for performance reasons, this sandboxing is off by default ⚠️. So don't assume it's watching, because it is not. 🫣
📋 sandbox/list
- type:
boolean - default:
off - drop:
yes, on only - query:
yes - static:
yes
Turn List sandboxing on or off.
Same warning as above, bestie: for performance reasons, this sandboxing is off by default. It's giving "resting, not dead." 😴
🔍 sandbox/stat
- type:
boolean - default:
on - drop:
yes, on only - query:
yes - static:
yes
Turn Stat sandboxing on or off. And this one's default is on — different from walk and list, so keep your eyes peeled 👀.
📖 sandbox/read
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turn Read sandboxing on or off. Also defaulting to on, slay 💖.
💅 sandbox/write 💅
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Write sandboxing: on or off, your call, bestie. ✨
🦋 sandbox/exec 🦋
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Same deal for Exec sandboxing — flip it on, flip it off. 🫶
🔥 sandbox/ioctl 🔥
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes - static:
yes
Ioctl sandboxing on or off. 🎀
Heads up, this one has a static vibe — for performance reasons, it only works at startup. If you don't give it at startup, Syd will literally just allow the ioctl(2) system call at seccomp-bpf level. Turning this sandboxing off during runtime is still possible tho — in that case the respective system calls handlers will skip the access checks. 💀
🌈 sandbox/create 🌈
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Create sandboxing, on or off. 🌸
🍓 sandbox/delete 🍓
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Delete sandboxing, on or off. 🧚
⭐ sandbox/rename ⭐
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Rename sandboxing, on or off. 💫
🎀 sandbox/readlink 🎀
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes - static:
yes
Readlink sandboxing, on or off. 👑
And here's the tea: for performance reasons, this sandboxing is off by default. (Yeah, the table above says on — that's what the page says, so that's what you get, iykyk. 🙈)
💜 sandbox/symlink 💜
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Symlink sandboxing, on or off. 🦋
🌸 sandbox/truncate 🌸
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Truncate sandboxing, on or off. ✂️ (okay there's no scissors emoji rule but it fits, trust)
🧚 sandbox/chdir 🧚
- type:
boolean - default:
off - drop:
yes,ononly - query:
yes - static:
yes
Chdir sandboxing, on or off. ⭐
Big caveat, bestie: for performance reasons this sandboxing is off by default, and setting it on only works at startup. If you don't give it at startup, Syd will just allowchdir(2)andfchdir(2)system calls at seccomp-bpf level. Turning this sandboxing off during runtime is still possible — in that case the respective system calls handlers will skip the access checks. 😭
🫶 sandbox/readdir 🫶
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Readdir sandboxing, on or off. 🌈
💖 sandbox/mkdir 💖
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Mkdir sandboxing, on or off. 🌸
🦋 sandbox/rmdir 🦋
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Rmdir sandboxing, on or off. 💀
👑 sandbox/chown 👑
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Chown sandboxing, on or off. ✨
🔥 sandbox/chgrp 🔥
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Chgrp sandboxing, on or off. 🎀
🍓 sandbox/chmod 🍓
- type:
boolean - default:
on - drop:
yes,ononly - query:
yes
Chmod sandboxing, on or off. periodt. 💅
sandbox/chattr
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Chattr sandboxing on or off. 💅
sandbox/chroot
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Chroot sandboxing on or off. 🎀
sandbox/notify
- type:
boolean - default:
on - drop:
yes, on only - query:
yes - static:
yes
Turns Notify sandboxing on or off. ✨
sandbox/utime
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Utime sandboxing on or off. 🦋
sandbox/mkdev
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Mkdev sandboxing on or off. 🌸
sandbox/mkfifo
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Mkfifo sandboxing on or off. 🫶
sandbox/mktemp
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Mktemp sandboxing on or off. 😭
sandbox/net
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Network sandboxing on or off. 🔥
sandbox/sendfd
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns SendFd sandboxing on or off. 👑
sandbox/recvfd
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns RecvFd sandboxing on or off. 🌈
sandbox/lock
- type:
boolean - drop:
yes, on only - query:
yes - static:
yes
Turns Landlock sandboxing on or off. ⭐ (yes bestie, notice there's no default line here — that's how the page lists it, not me being sloppy 💀)
sandbox/uring
- type:
boolean - default:
on - drop:
yes, on only - query:
yes - static:
yes
Turns Uring sandboxing on or off. 💀
sandbox/force
- type:
boolean - drop:
yes, on only - query:
yes - static:
yes
Turns Force sandboxing on or off. 🙈 (again, no default row — periodt.)
sandbox/tpe
- type:
boolean - default:
on - drop:
yes, on only - query:
yes
Turns Trusted Path Execution (TPE) sandboxing on or off. 🍓
sandbox/proxy
- type:
boolean - default:
off - drop:
yes, on only - query:
yes - static:
yes - oci:
no
Turns Proxy sandboxing on or off. 🧚
So like, defaults to proxying through TOR — that's the whole vibe. Wanna go somewhere else? Check the options proxy/addr, proxy/port, proxy/ext/host, and proxy/ext/port to configure a different proxy. 💜
Heads up bestie: this implies unshare/net:true. Not optional, it just comes with the package. 🎁
Also this needs the syd-tor(1) helper utility to be under PATH. How does it run? syd-tor(1) is executed once at startup, it runs as a single process, and this process runs at most as long as the owner Syd process. That's the whole lifecycle, no cap. Refer to the syd-tor(1) manual page for more info. 📖
sandbox/pty
- type:
boolean - default:
on - drop:
yes, on only - query:
yes - static:
yes - oci:
no
Turns PTY sandboxing on or off. 💖
Requires the syd-pty(1) helper utility to be under PATH. Same deal as above: syd-pty(1) is executed once at startup, it runs as a single process, and this process runs at most as long as the owner Syd process. Go read the syd-pty(1) manual page for the full tea. ☕
Ok but the important bit: this option has no effect unless both standard input and standard output are attached to a TTY at startup. Like, both of them. If you're piping stuff around, it simply does nothing — iykyk. 🫠
sandbox/mem
type—booleandefault—offdrop—yes, on onlyquery—yesstatic—yes
Turn Memory sandboxing on or off. 🧠✨
Okay so here's the tea: for performance reasons, this only works at startup. Like, it's a startup-only kind of vibe. If you don't give it at startup, Syd will just allow brk(2), mmap(2), mmap2(2), and mremap(2) system calls at seccomp-bpf level. Turning this sandboxing off during runtime is still possible — and in this case the respective system call handlers will skip the access checks. So it's flexible, but the on switch has to be flipped early, iykyk. 💅
sandbox/pid
type—booleandefault—offdrop—yes, on onlyquery—yesstatic—yes
Turn PID sandboxing on or off. 🆔🔒
That's it. That's the whole entry. Short and iconic. 🎀
default/fs
type—stringdefault—denydrop—yes, raise only
Specify the default action for Filesystem sandboxing access violations. 📁👀
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
bestie, note the word exactly — no typos, no vibes-based spelling, no "Allow" with a capital A. The parser is not feeling generous. 😭
default/walk
type—stringdefault—denydrop—yes, raise only
Specify the default action for Walk sandboxing access violations. 🚶♀️🌸
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/list
type—stringdefault—denydrop—yes, raise only
Specify the default action for List sandboxing access violations. 📋✨
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/stat
type—stringdefault—denydrop—yes, raise only
Specify the default action for Stat sandboxing access violations. 📊🔍
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/read
type—stringdefault—denydrop—yes, raise only
Specify the default action for Read sandboxing access violations. 📖💖
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/write
type—stringdefault—denydrop—yes, raise only
Specify the default action for Write sandboxing access violations. ✍️🔥
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/exec
type—stringdefault—denydrop—yes, raise only
Specify the default action for Exec sandboxing access violations. ⚡👑
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/ioctl
type—stringdefault—denydrop—yes, raise only
Specify the default action for Ioctl sandboxing access violations. 🎛️💀
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/create
type—stringdefault—denydrop—yes, raise only
Specify the default action for Create sandboxing access violations. 🆕🦋
The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
default/delete
type—stringdefault—denydrop—yes, raise only
Specify the default action for Delete sandboxing access violations. 🗑️😭
The value must be exactly one of "allow", "warn", "filter", "deny", "stop", "abort", "kill", "panic", or "exit", where the default is "deny".
⚠️ lowkey important: see how"panic"is hanging out after"kill"here instead of before"stop"? Yeah. This entry's list is in a different order than the others. Same nine values, different lineup. Don't come for me, I'm just the messenger — copy the order as written. 💀
default/rename
type—stringdefault—denydrop—yes, raise only
Specify the default action for Rename sandboxing access violations. 🏷️✨
The value must be exactly one of "allow", "warn", "filter", "deny", "stop", "abort", "kill", "panic", or "exit", where the default is "deny".
same deal asdefault/delete—"panic"sits after"kill"in this list. Respect the order, bestie. 🙈
default/readlink
type—stringdefault—denydrop—yes, raise only
Specify the default action for Readlink sandboxing access violations. 🔗👀
The value must be exactly one of "allow", "warn", "filter", "deny", "stop", "abort", "kill", "panic", or "exit", where the default is "deny".
default/symlink
type—stringdefault—denydrop—yes, raise only
Specify the default action for Symlink sandboxing access violations. 🪢💜
The value must be exactly one of "allow", "warn", "filter", "deny", "stop", "abort", "kill", "panic", or "exit", where the default is "deny".
default/truncate
- type:
string - default:
deny - drop: yes, raise only
So this one's the default action for Truncate sandboxing access violations. Bestie, the value has to be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit" — no freestyling, no vibes-based spelling — and the default is "deny". 💅
default/chdir
- type:
string - default:
deny - drop: yes, raise only
Sets the default action for Chdir sandboxing access violations. Value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". 🎀
default/readdir
- type:
string - default:
deny - drop: yes, raise only
Default action for Readdir sandboxing access violations. Again: exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", and the default is "deny". ✨
default/mkdir
- type:
string - default:
deny - drop: yes, raise only
This is the default action for Mkdir sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit" — default "deny". 🦋
default/rmdir
- type:
string - default:
deny - drop: yes, raise only
Default action for Rmdir sandboxing access violations. Same deal, bestie: the value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". 🌸
default/chown
- type:
string - default:
deny - drop: yes, raise only
Specifies the default action for Chown sandboxing access violations. Value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". 🫶
default/chgrp
- type:
string - default:
deny - drop: yes, raise only
The default action for Chgrp sandboxing access violations lives here. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", and the default is "deny". 💖
default/chmod
- type:
string - default:
deny - drop: yes, raise only
Default action for Chmod sandboxing access violations. Must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit"; default is "deny". 🔥
default/chattr
- type:
string - default:
deny - drop: yes, raise only
This is the default action for Chattr sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". 🌈
default/chroot
- type:
string - default:
deny - drop: yes, raise only
Default action for Chroot sandboxing access violations — and ngl this one's kind of main character energy, so get it right. Value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", default "deny". 👑
default/notify
- type:
string - default:
deny - drop: yes, raise only
Specifies the default action for Notify sandboxing access violations. Must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". ⭐
default/utime
- type:
string - default:
deny - drop: yes, raise only
The default action for Utime sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", and the default is "deny". 🍓
default/mkdev
- type:
string - default:
deny - drop: yes, raise only
Default action for Mkdev sandboxing access violations. Exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit" — default "deny". 🧚
default/mkfifo
- type:
string - default:
deny - drop: yes, raise only
Sets the default action for Mkfifo sandboxing access violations. Value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". 💜
default/mktemp
- type:
string - default:
deny - drop: yes, raise only
Last one in this lil' family: the default action for Mktemp sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", and the default is "deny". 😭
Quick vibe check before you speed-run the next section: every single one of these is "deny" out of the box, so if you want your sandbox to be chill instead of locked-down, you're gonna have to say so explicitly. No cap. 💀
💅 default/net
- type —
string - default —
deny - drop —
yes, raise only
Bestie, this sets the default action for Network sandboxing access violations. The value has to be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit" — and yeah, the default is "deny". 💅
🫶 default/sendfd
- type —
string - default —
deny - drop —
yes, raise only
Default action for SendFd sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
🎀 default/recvfd
- type —
string - default —
deny - drop —
yes, raise only
Default action for RecvFd sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
🌸 default/block
- type —
string - default —
deny - drop —
yes, raise only
This one's the action for IP blocklist violations. Value must be exactly one of "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny". Notice allow isn't on the menu here — it's giving "girl, you are not getting through" energy. 🙈
💜 default/force
- type —
string - default —
deny - drop —
yes, raise only
For force sandboxing, this is where you define the default action to take when the path of a binary is not in the Integrity Force map. The value must be either one of "warn", "filter", "deny", "panic", "stop", "abort", "kill", "exit", where the default is "deny".
🦋 default/segvguard
- type —
string - default —
deny - drop —
yes, raise only
Action for SegvGuard access violations. The value must be exactly one of "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
⭐ default/tpe
- type —
string - default —
deny - drop —
yes, raise only
Action for TPE sandboxing access violations. The value must be exactly one of "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
🌈 default/mem
- type —
string - default —
deny - drop —
yes, raise only
Action for Memory sandboxing access violations. The value must be exactly one of "allow", "warn", "filter", "deny", "panic", "stop", "abort", "kill", or "exit", where the default is "deny".
🔥 default/pid
- type —
string - default —
kill - drop —
yes, raise only
Action for PID sandboxing access violations. Heads up, the default here is not deny — it's "kill" 😭. The value must be either one of "warn", "filter", "stop", "abort", "kill", "exit", where the default is "kill".
👑 default/lock
- type —
string - default —
kill - drop —
yes, raise only - static —
yes
Specify the compatibility level for Lock sandboxing. The value must be either one of kill, deny, warn. So what do those actually mean, bestie?
killstands for the Landlock compatibility level hard-requirementdenystands for soft-requirementwarnstands for best-effort
As of version 3.35.0, the default level has been promoted fromwarntokillto adhere to the principle of secure defaults. Again, as of this versionENOENT("No such file or directory") errors are fatal unless compatibility level is set to best-effort at startup usingdefault/lock:warn. ⚠️
For more information on Landlock compatibility levels, see: https://landlock.io/rust-landlock/landlock/trait.Compatible.html
🧚 unshare/mount
- type —
boolean - drop —
yes, on only - query —
yes - static —
yes - oci —
no
Create Mount namespace on startup — and lowkey it implies unshare/pid:true too. ✨
🍓 unshare/uts
- type —
boolean - drop —
yes, on only - query —
yes - static —
yes - oci —
no
Create UTS namespace on startup.
💀 unshare/ipc
- type —
boolean - drop —
yes, on only - query —
yes - static —
yes - oci —
no
Create IPC namespace on startup.
🌸 unshare/user
- type —
boolean - drop —
yes, on only - query —
yes - static —
yes - oci —
no
Create User namespace on startup.
💖 unshare/pid
- type —
boolean - drop —
yes, on only - query —
yes - static —
yes - oci —
no
Create Pid namespace on startup — and plot twist, it implies unshare/mount:true. 🌀
Syd mounts a private procfs(5) in this mode.
As of version 3.37.2 procfs(5) is mounted with hidepid=4 option which is Linux>=5.8.
As of version 3.39.0 procfs(5) is mounted with subset=pid option which is Linux>=5.8, unless trace/allow_unsafe_proc_files:true is set at startup.
🕸️ unshare/net
- type:
boolean - drop: yes, on only
- query: yes
- static: yes
- oci: no
Create a Net namespace on startup. network vibes fully quarantined, slay 💅
🧺 unshare/cgroup
- type:
boolean - drop: yes, on only
- query: yes
- static: yes
- oci: no
Create a CGroup namespace on startup.
⏰ unshare/time
- type:
boolean - drop: yes, on only
- query: yes
- static: yes
- oci: no
Create a Time namespace on startup. Syd resets the boot-time clock so that uptime(1) reports container uptime instead of host uptime — no cap, your sandbox gets its own little clock and it's kind of iconic 🎀 Use the time command to override the default and set an alternative time.
🌱 root
- type:
string - static: yes
- oci: no
Change the root mount to the given new root directory at startup using pivot_root(2). Destination path arguments of bind commands are interpreted relative to this directory. The directories $root/dev and $root/proc must exist to mount private filesystems. On top of that, target paths of the bind commands must also be manually created by the user — Syd is not gonna do your homework for you bestie 😭
This option does nothing without unshare/mount:1.
As of version 3.23.14, symbolic links are not followed in any part of the root directory, and path traversal using ".." is not permitted. Also: the root directory must be an absolute path — relative paths are not permitted, periodt.
As of version 3.35.0, the special keyword tmpfs, or shortly tmp, or just t, is supported to make Syd mount a private tmpfs(5) filesystem as the root directory. In this mode, Syd is going to attempt to create target paths inside the private temporary filesystem. Similarly, as of version 3.45.0, the special keyword ramfs, or shortly ram, or just r, is supported to make Syd mount a private ramfs(5) filesystem as the root directory. Heads up though 🫶 ramfs(5) is limited compared to tmpfs(5) and should only be preferred when the host Linux kernel isn't configured with the CONFIG_TMPFS option. The private root directory is mounted with the options nodev, noexec, nosuid, nosymfollow, noatime, and mode=700.
As of version 3.35.2, the special keywords none and off may be used to unset a previously set root directory. very demure, very mindful 💜
🗺️ root/map
- type:
boolean - static: yes
- oci: no
Map the current user to root in the sandbox on startup.
This option does nothing without "unshare/user:1".
🎭 root/fake
- type:
boolean - static: yes
In fakeroot mode, the system will return a user/group id of 0, mimicking the root user. This allows users to execute commands with apparent root privileges, without actual superuser rights — it's giving main character energy without the actual crown 👑 It's useful for tasks like package building where a root-like environment is needed, but not actual root permissions.
ngl this is fake root, not real root. The permissions are cosmetic. Don't go thinking you're actually root fr fr 💀
🕰️ time
- type:
i64 - static: yes
- oci: no
Set clock boottime and monotonic offset (seconds) in the Time Namespace. To set boottime and monotonic offsets separately, use the options time/boot and time/mono. This option is a shorthand to set both at the same time to the same offset. Use the keywords none or off to unset a previously set offset.
🌅 time/boot
- type:
i64 - static: yes
- oci: no
Set the clock boottime offset (seconds) in the Time Namespace. Use the keywords none or off to unset a previously set offset.
📈 time/mono
- type:
i64 - static: yes
- oci: no
Set the clock monotonic offset (seconds) in the Time Namespace. Use the keywords none or off to unset a previously set offset.
⏳ timeout
- type:
duration - default:
none
Set a sandbox timeout, after which Syd will exit with code 124 and all sandbox processes running under it will exit with Syd. Use none or off to disable it — that's the default, no pressure ✨ Duration may be specified in human readable format. Refer to the documentation of the dur crate for more information, iykyk.
🏷️ uts/host
- type:
string - default:
localhost
Set the UTS host name in the sandbox. The name is limited to 64 characters. The name may be empty. The name may not have nul bytes. Default is localhost. Use the keywords none or off to unset it, and then Syd will pass-through the original hostname to the sandbox process.
Useful when combined with unshare/uts:true. As of version 3.40.0, the value of this option is returned at the uname(2) boundary in the nodename field of the utsname structure regardless of the unshare/uts option.
As of version 3.39.3, this value may be changed at runtime.
As of version 3.49.0, this value must NOT be empty and must be a safe filename. Refer to the "Enhanced Path Integrity Measures" section of the syd(7) manual page for information on safe filenames.
🌐 uts/domain
- type:
string - default:
(none)
Set the NIS/YP domain name in the sandbox. The name is limited to 64 characters. The name may be empty. The name may not have nul bytes. The default is (none). Use the keywords none or off to unset it, and then Syd will pass-through the original domainname to the sandbox process.
Useful when combined with unshare/uts:true. As of version 3.40.0, the value of this option is returned at the uname(2) boundary in the domainname field of the utsname structure regardless of the unshare/uts option.
As of version 3.39.3, this value may be changed at runtime.
As of version 3.49.0, this value must NOT be empty and must be a safe filename. Refer to the "Enhanced Path Integrity Measures" section of the syd(7) manual page for information on safe filenames.
📦 uts/version
- type:
string - default:
<random>
Set the version level of the operating system as returned in the version field of the utsname structure at the uname(2) boundary. The name is limited to 64 characters. The name may be empty. The name may not have nul bytes. The default is determined randomly at startup — cute little surprise every time 🎀 Use the keywords none or off to unset it, and then Syd will pass-through the original version to the sandbox process.
As of version 3.56.0, this value must NOT be empty and must be a safe filename. Refer to the "Enhanced Path Integrity Measures" section of the syd(7) manual page for information on safe filenames.
🎛️ allow/ioctl
- type:
integerorstring - default:
[...] - drop: yes, remove only
So basically: prefix with 0x for hexadecimal and 0o for octal, bestie — and use allow/ioctl+<request> to add stuff to the allowlist, allow/ioctl-<request> to yeet it back out. ✨ As of version 3.38.0, you can also name ioctl(2) requests by case-insensitive name, and you can add or remove a whole bunch at once by comma-delimiting them. Naming your requests is strongly recommended because request numbers can vary by architecture, and naming them handles that transparently for you. 🫶 As of version 3.38.6, you can prepend the name with an optional exclamation mark, !, to tell Syd not to return EINVAL ("Invalid argument") errno(3) when the name isn't defined for any of the currently supported architectures — iconic for writing rules that stay generic across multiple incompatible architectures. 💅 As of version 3.50.0, shell-style glob(3) patterns work for ioctl(2) names: the glob(3) characters *, ?, and [...] match multiple ioctl(2) names at once, like allow/ioctl+DRM_IOCTL_I915_* to allow every Intel GPU ioctl, or allow/ioctl+FIO* to allow all the generic file ioctls. Glob matching is case-insensitive and combines with comma-separated literals, e.g. allow/ioctl+FIO*,FIBMAP. The ! applies per element, no cap.
By default the allowlist contains these ioctl(2) requests: FIOCLEX, FIONCLEX, FIONBIO, FIONREAD, FIOASYNC, FIOQSIZE, FIDEDUPERANGE, PIDFD_GET_INFO, and RNDGETENTCNT. 📋
For rules added at startup, deny rules have precedence over allow rules because the denylist gets checked at kernel-space while the allowlist gets checked at user-space. For rules added after startup, the last matching rule wins. 🏁
👑 deny/ioctl
Alright so the vibe check on this one:
- type — integer or string
- default —
[...](there's a whole list, hang on) - drop — yes, add only
- static — add is dynamic, remove is partly static
Add to or remove a request from the ioctl(2) request denylist. Accepts an unsigned 64-bit integer as argument. Prefix with 0x for hexadecimal and 0o for octal input. Use deny/ioctl+<request> to add to, and deny/ioctl-<request> to remove from the allowlist — and yeah, the page does say "allowlist" right there in the deny section, that's just how it's written, we move. 💀
As of version 3.38.0, ioctl(2) requests can also be specified by case-insensitive name, and you can add or remove multiple by comma-delimiting. Naming is strongly recommended since request numbers vary by architecture and that's handled transparently when you use a name. ✨ As of version 3.38.6, the ioctl(2) name can be prepended with an optional exclamation mark, !, meaning Syd shouldn't return EINVAL ("Invalid argument") errno(3) if the name isn't defined for any of the currently supported architectures — perfect for generic rules across incompatible architectures. As of version 3.50.0, shell-style glob(3) patterns are accepted for ioctl(2) names: *, ?, and [...] match multiple names at once, e.g. deny/ioctl+SECCOMP_IOCTL_* to deny all the seccomp(2) ioctls. Glob matching is case-insensitive and combines with comma-separated literals. The ! applies per element. 🎀
As of version 3.58.0, ioctl(2) names corresponding to variable-sized ioctl(2) requests — also known as extensible-argument ioctls, e.g. SPI_IOC_MESSAGE, EVIOCGNAME, SECCOMP_IOCTL_NOTIF_ADDFD or PIDFD_GET_INFO — are supported on the denylist and matched with the size field wildcarded, so the request is denied for every payload size. 🔥 Parameterized ioctl(2) requests whose command number varies with an argument, e.g. EVIOCGABS, EVIOCSABS and EVIOCGBIT, are likewise matched with the varying command-number bits — and, where applicable, size bits — wildcarded, so a whole request family is covered by a single rule. Slay.
By default the denylisted ioctl(2) requests are FIFREEZE, FITHAW, FS_IOC_FIEMAP, FIGETBSZ, FICLONE, FICLONERANGE, FS_IOC_GETFSUUID, FS_IOC_GETFSSYSFSPATH, FIBMAP, FS_IOC_FSGETXATTR, FS_IOC_FSSETXATTR, FS_IOC_SETFLAGS, FS_IOC32_SETFLAGS, KDSETKEYCODE, KDSIGACCEPT, RNDADDTOENTCNT, RNDGETPOOL, RNDADDENTROPY, RNDZAPENTCNT, RNDCLEARPOOL, SECCOMP_IOCTL_NOTIF_RECV, SECCOMP_IOCTL_NOTIF_SEND, SECCOMP_IOCTL_NOTIF_ID_VALID, SECCOMP_IOCTL_NOTIF_ADDFD, SECCOMP_IOCTL_NOTIF_SET_FLAGS, TIOCCONS, TIOCLINUX, TIOCSETD, TIOCSTI, TIOCSSERIAL, KCOV_INIT_TRACE, KCOV_ENABLE, KCOV_DISABLE, KCOV_RESET_TRACE, KCOV_REMOTE_ENABLE, and DRM_IOCTL_GEM_CHANGE_HANDLE. 👀
For security reasons, the ioctl(2) denylist is applied at the parent seccomp-bpf filter at startup. That means the Syd process is included in this restriction too. It also means removing elements from this list after startup has no effect. But — if Ioctl sandboxing was enabled at startup, adding new elements to the ioctl(2) denylist will further restrict the ioctl(2) request space. 😭
For rules added at startup, deny rules have precedence over allow rules, because the denylist is checked at kernel-space and the allowlist is checked at user-space. For rules added after startup, the last matching rule wins. 🏁
Further reading about denylisted ioctl(2) requests, if you're in your research era:
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1523
- https://a13xp0p0v.github.io/2017/03/24/CVE-2017-2636.html
- http://phrack.org/issues/52/6.html#article
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=83efeeeb3d04b22aaed1df99bc70a48fe9d22c4d
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d1b43f6a6df7bcea20982ad376a000d90906b42
- https://seclists.org/oss-sec/2024/q1/13
- https://seclists.org/oss-sec/2024/q1/14
- https://forums.grsecurity.net/viewtopic.php?f=7&t=2522
- http://lkml.indiana.edu/hypermail/linux/kernel/9907.0/0132.html
- http://linux.derkeiler.com/Mailing-Lists/Kernel/2007-11/msg07723.html
🍓 mem/max
- type — positive integer (
u64) - default —
0 - drop — yes, reduce only
This one sets the limit on per-process memory usage. Setting it to 0 disables testing for this type of memory usage. The value is parsed using the parse-size crate — go check their docs for how to format it. 📏 Setting a non-zero value here implies sandbox/mem:on.
As of version 3.58.1, when this setting is non-zero, the sysinfo(2) handler honours the limit. Check the "Cryptographically Randomized Sysinfo" section of the syd(7) manual page for more. 🔍
🦋 mem/vm_max
- type — positive integer (
u64) - default —
0 - drop — yes, reduce only
Same energy but for virtual memory: this sets the limit on per-process virtual memory usage. 0 disables testing for this type of memory usage. Value is parsed using the parse-size crate (docs, bestie, docs). Setting a non-zero value with this option implies sandbox/mem:on. 💖
🌸 pid/max
- type — positive integer (
u64) - default —
0 - drop — yes, reduce only
This setting specifies the limit on the number of running tasks for pid sandboxing. Setting it to 0 is functionally equivalent to setting sandbox/pid to off. Setting a non-zero value here implies sandbox/pid:on. 🔢
As of version 3.40.0, when unshare/pid:true is set, PID sandboxing counts and enforces the limit in the current PID namespace; on Linux 6.14 and newer, to account for the kernel's 300 reserved PIDs, the namespaced kernel.pid_max is set to max(pid/max, 301) (or 512 on s390x), while on older kernels the kernel.pid_max sysctl(8) is left unchanged. No cap, well — actually, cap. 😌
🧚 pipe/max
- type — positive integer (
i32) - default —
${SYD_PAGE_SIZE} - drop — yes, reduce only
- static — reduce is dynamic, raise is static
This setting is the max pipe(2) buffer size in bytes, bestie 💅 and the value has got to be at least the system page size — which, fun fact, is also the Syd default. Smaller limits literally cannot be enforced. You can reduce it after startup but you cannot raise it, because Syd installs a seccomp(2) filter at startup that confines the F_SETPIPE_SZ(2const) fcntl(2) operation. And when pipe(2) creation is confined by Create sandboxing? Every single created pipe(2) gets its buffer size set to this value. no cap. 🫶
🎀 xattr/max 🎀
- type — positive integer (
size_t) - default —
512 - drop — yes, reduce only
This one sets the maximum extended attribute value size in bytes for the setxattr(2), lsetxattr(2), fsetxattr(2) and setxattrat(2) system calls. Default is 512 bytes. Minimum permitted value is 0. Maximum permitted value is 65536. A write whose value exceeds this limit gets denied with E2BIG ("Argument list too long") 😭 and honestly that's the point — it mitigates kernel heap spraying via attacker-sized, attacker-controlled extended attribute allocations. icon behavior fr fr 💜
🌸 rlimit/as 🌸
- type — positive integer (
rlim_t) - static — yes
Limits the maximum size of the sandbox process's virtual memory (address space) using rlimit(2). The limit is specified in bytes and is rounded down to the system page size. The value is parsed using the parse-size crate — go read their documentation for the formatting tea ☕. Use the keywords none or off to unset a previously set value. The keyword inf denotes no limit on the resource. slay.
🦋 rlimit/core 🦋
- type — positive integer (
rlim_t) - default —
0 - static — yes
Limits the maximum size of a core(5) file in bytes using rlimit(2). When it's 0 (the default) no core dump files are created. When it's nonzero, larger dumps get truncated to this size. The value is parsed using the parse-size crate — check their docs for formatting. Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource. 💀
⭐ rlimit/cpu ⭐
- type — positive integer (
rlim_t) - static — yes
Limits the amount of CPU time in seconds using rlimit(2). The value is parsed using the dur crate — yes, a different crate, refer to their documentation for formatting info 🙈. Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource.
🍓 rlimit/data 🍓
- type — positive integer (
rlim_t) - static — yes
Limits the maximum size of the sandbox process's data segment — that's initialized data, uninitialized data, and heap, all of it 😌 — using rlimit(2). The limit is in bytes and is rounded down to the system page size. Parsed with the parse-size crate, docs are right there bestie. Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource.
💖 rlimit/fsize 💖
- type — positive integer (
rlim_t) - static — yes
Limits the maximum size in bytes of files that the sandbox process may create, using rlimit(2). Parsed with the parse-size crate (their docs, go look 💅). Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource.
Heads up bestie: this limit is applied to the Syd process as well, so that system calls liketruncate(2)andfallocate(2)are correctly limited. Care should be taken, because setting this limit to a value lower than what Syd's internal file handling requires may result in unexpected errors 😭 — don't do it, or at least don't say I didn't warn you.
🧚 rlimit/memlock 🧚
- type — positive integer (
rlim_t) - default —
0 - static — yes
Limits the maximum number of bytes of memory that may be locked into RAM using rlimit(2). This limit is in effect rounded down to the nearest multiple of the system page size. Default is zero. Parsed via the parse-size crate — docs, formatting, you know the drill ✨. Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource.
🌈 rlimit/msgqueue 🌈
- type — positive integer (
rlim_t) - default —
0 - static — yes
Limits the number of bytes that can be allocated for POSIX message queues for the real user ID of the sandbox process, using rlimit(2). The limit is enforced for mq_open(3). Default is zero. The value is parsed using the parse-size crate — refer to their documentation for formatting info 🫶. Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource.
👑 rlimit/nice 👑
- type — integer (
-20..=20) - default —
20 - static — yes
Specifies a ceiling to which the sandbox process's nice value can be raised, using setpriority(2) or nice(2) via rlimit(2). The accepted range for this limit is from -20 to 20 (the default). Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource.
Default is 20. And btw — the option trace/allow_unsafe_nice must also be set to permit the sandbox process to change its nice value. iykyk 😌
🔥 rlimit/nofile 🔥
- type — positive integer (
rlim_t) - static — yes
Limits the maximum file descriptor number that can be opened, using resource limits via rlimit(2). Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource. periodt 💅
💜 rlimit/nproc 💜
- type — positive integer (
rlim_t) - static — yes
Limits the number of extant process (or, more precisely on Linux, threads) for the real user ID of the sandbox process, using rlimit(2). Use none or off to unset a previously set value. The keyword inf denotes no limit on the resource. literally the thread-counting one 🧵✨
🌸 rlimit/rtprio 🌸
type— positive integer (rlim_t)default—0static— yes
Okay so this one lets you put a literal ceiling on the real-time priority that the sandbox process is allowed to set for itself, via sched_setscheduler(2) and sched_setparam(2), all enforced through rlimit(2). Default is zero. Wanna unset something you set earlier? Use the keywords none or off. And the keyword inf means "no limit on this resource" — infinite vibes, no cap, literally. ✨
🦋 rlimit/rttime 🦋
type— positive integer (rlim_t)default—0static— yes
This caps (in microseconds) how much CPU time a sandbox process can burn while running under a real-time scheduling policy without making a blocking system call — enforced through rlimit(2). Default is zero. The value gets parsed using the dur crate, so go read their docs for the formatting tea 🫖. Same keywords as always: none or off to unset a previously set value, inf for no limit on the resource.
🎀 rlimit/sigpending 🎀
type— positive integer (rlim_t)static— yes
(and no, bestie, there is genuinely no default row in this table — I'm not gatekeeping it from you 💀)
This one limits how many signals can be queued for the real user ID of the sandbox process, using rlimit(2). Both standard and real-time signals count toward this limit, so nothing sneaks past. Use none or off to unset a previously set value, inf for no limit on the resource.
🌈 rlimit/stack 🌈
type— positive integer (rlim_t)static— yes
(again, no default row on this one either — the table just doesn't have one 🙈)
Caps the maximum size of the sandbox process stack, in bytes, via rlimit(2). The value is parsed using the parse-size crate — their docs have the formatting deets. Use none or off to unset a previously set value, and inf means no limit on the resource. Periodt.
💅 bind 💅
type—string-vecstatic— yesoci— no
This command makes Syd bind mount a directory on startup. The format is source-dir:target-dir:mount-options,... — and yes, the source and target directories are allowed to be equal, no drama.
As of version 3.55.0, bind mounts are secure by default. And I mean regardless of whether the source is an absolute path or a filesystem type, every single bind gets mounted with nodev, noexec, nosuid, and nosymfollow applied. So if you want the relaxed behaviour, you have to explicitly opt back in upfront with the options below. Each option weakens exactly one of the secure defaults:
dev— to interpret character and block special devices on the filesystem. By default withnodev, device nodes are not interpreted.exec— to permit direct execution of binaries on the mounted filesystem. By default withnoexec, execution is not permitted.suid— to honour set-user-ID and set-group-ID bits and file capabilities when executing programs from this filesystem. By default withnosuidthey are ignored. In addition, SELinux domain transitions require permissionnosuid_transition, which in turn needs also policy capabilitynnp_nosuid_transition.symfollow— to follow symbolic links when resolving paths. By default withnosymfollow, symbolic links are not followed. They can still be created, andreadlink(1),readlink(2),realpath(1), andrealpath(3)all still work properly. In addition,romounts the filesystem read-only.rwresets a previousrooption, and is a no-op otherwise.
Access time of a bind mount is not part of the secure defaults, because changing it is privileged in unprivileged user_namespaces(7). The access time options that stay available as opt-in flags are:
noatime— to not update inode access times on this filesystem (e.g. for faster access on the news spool to speed up news servers). This works for all inode types (directories too), so it impliesnodiratime.nodiratime— to not update directory inode access times on this filesystem. (This option is implied whennoatimeis set.)relatime— to update inode access times relative to modify or change time. This is the default.strictatime— to always update inode access times on every access. This may increase I/O and is rarely needed.
The legacy restrictive options nodev, noexec, nosuid, and nosymfollow are now the default and are accepted only as no-ops for backwards compatibility. The access time options (noatime, relatime, strictatime) are mutually exclusive — specifying conflicting options is an error, so don't even try it 🙅
Mount options may be omitted entirely. If the source directory isn't an absolute path, it's interpreted as the filesystem type rather than the source directory. That's how you mount special filesystems like cgroupfs, overlayfs or tmpfs(5) into the mount namespace. In that case, any mount options supported by that filesystem type can go in the options argument — not just the ones listed above. Some iconic examples:
bind+/:/:robind+tmpfs:/tmp:size=16Mbind+tmpfs:/run:exec,size=16Mbind+/dev:/dev:devbind+overlay:/tmp/target:exec,lowerdir=/tmp/lower,upperdir=/tmp/upper,workdir=/tmp/workbind+devpts:/dev/pts:dev,newinstance,ptmxmode=0600,mode=600bind+ramfs:/tmpbind+sysfs:/sysbind+mqueue:/dev/mqueue
You can use this command to create immutable containers 👑. For example, bind+/:/:ro is functionally equivalent to deny/write+/***, except the restriction happens at the kernel VFS layer rather than at user level using seccomp(2) notify. Alternatively you can achieve the same thing at the kernel level using landlock(7).
As of version 3.23.14, symbolic links are not followed in any part of the source or target directory paths, and path traversal using .. is not permitted. Also the target directory must be an absolute path — relative paths are simply not permitted, no exceptions.
As of version 3.23.14, mounting the special proc(5) filesystem under a custom path is not permitted. Syd handles that mount itself, specially, after all bind mounts are processed. Main character behaviour, honestly.
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This command does nothing without unshare/mount:true. Like, nothing at all. Don't come for me.
🧚 bind-try 🧚
type—string-vecstatic— yesoci— no
Behaves identically to bind except for one lil thing: it warns and continues on the ENOENT ("No such file or directory") errno(3) on the source directory. All other errors stay fatal, no mercy. This is the one for portable profiles that reference optional paths which might not exist on every system 🫶. Format is the same as bind: source-dir:target-dir:mount-options,...
Take "bind-try+/opt/custom:/opt/custom:ro" as an example. If /opt/custom doesn't exist, the mount is skipped with a warning and execution continues on its merry way. But if the source does exist and just can't be opened for any other reason (e.g. EACCES, ELOOP), Syd reports the error and terminates as usual. So it's forgiving, not reckless 💅
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option does nothing without unshare/mount:true.
⭐ mkdir ⭐
type—string-vecstatic— yesoci— no
This command has Syd create a directory node in the mount namespace on startup using mkdirat(2). Format is path or path:mode, where path is an absolute path and mode is an optional octal permission mode. No mode? It defaults to 0700, cute and private by default 🎀
mkdir+/dev/shmmkdir+/dev/shm:0755mkdir+/tmp/work:0700
The path must be absolute. Relative paths and any path components containing .. get rejected with EINVAL ("Invalid argument"). Symbolic links are not followed in any component of the path. Parent directories are opened with openat2(2) using RESOLVE_NO_XDEV to prevent cross-device path traversal. Parent directories are created automatically using mkdirat(2) with safe fd-based traversal. The node itself is created using mkdirat(2), which does not follow symbolic links in the final component. A secure umask(2) of 0077 is applied during mount namespace setup, which strips group and other permission bits from the requested mode. In addition, setuid and setgid bits are stripped explicitly. Only user permission bits and the sticky bit survive in the final mode — everything else gets left on read 💀✨
...and the escape thing? yeah it works in path args across all of these: "\:" drops a literal colon, "\\" drops a literal backslash. Very "trust me bestie" of them but it's real. 💅
🚨 heads up: every single one of these needsroot:ramfsorroot:tmpfs. No ramfs(5)/tmpfs(5) root? Then the operation is silently ignored — like, not an error, just nothing, which is the most annoying kind of nothing, ngl. And they all do absolutely zero withoutunshare/mount:true. Periodt.
🎀 mkdir-try 🎀
type—string-vecstatic—yesoci—no
Basically mkdir's chill twin 😌 — it does the exact same thing, except it warns and keeps going when it hits EEXIST ("File exists") errno(3). Every other error stays fatal, no exceptions. Super useful for making directories that might already be there, like the classic standard paths in a tmpfs root. Same format as mkdir: path or path:mode.
Take "mkdir-try+/dev/shm:0755" as an example. If /dev/shm already exists, creation gets skipped with a warning and execution just carries on. If the path can't be created for any other reason (e.g. EACCES, ENOENT), Syd reports the error and terminates as usual. 😭
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, mkdir-try operations are silently ignored. This option does nothing without unshare/mount:true.
🦋 mkfile 🦋
type—string-vecstatic—yesoci—no
Makes Syd create a regular file node in the mount namespace on startup using mknodat(2). Format is path or path:mode where path is an absolute path and mode is an optional octal permission mode. Skip the mode? Defaults to 0400. 🎀
mkfile+/dev/nullmkfile+/dev/null:0666mkfile+/tmp/placeholder:0600
The path must be absolute. Relative paths and path components containing .. get rejected with EINVAL ("Invalid argument"). Symbolic links are not followed in any component of the path — none, not one. Parent directories are opened with openat2(2) using RESOLVE_NO_XDEV to prevent cross-device path traversal. Parent directories are created automatically using mkdirat(2) with safe fd-based traversal. The node itself is created using mknodat(2), which doesn't follow symbolic links in the final component. A secure umask(2) of 0077 is applied during mount namespace setup, which strips group and other permission bits from the requested mode. On top of that, setuid and setgid bits are stripped explicitly. Only user permission bits and the sticky bit survive in the final mode. Iconic security behaviour, honestly. 👑
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, mkfile operations are silently ignored. This option does nothing without unshare/mount:true.
🌸 mkfile-try 🌸
type—string-vecstatic—yesoci—no
Same energy as mkfile, except it warns and continues on EEXIST ("File exists") errno(3). All other errors remain fatal. Great for file nodes that may already exist, like device nodes in a tmpfs root. Format's identical to mkfile: path or path:mode.
Take "mkfile-try+/dev/null:0666" as an example. If /dev/null already exists, the creation is skipped with a warning and execution continues. If the path can't be created for any other reason (e.g. EACCES, ENOENT), Syd reports the error and terminates as usual.
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, mkfile-try operations are silently ignored. This option does nothing without unshare/mount:true.
🍓 mkfifo 🍓
type—string-vecstatic—yesoci—no
Creates a named pipe (FIFO) node in the mount namespace on startup using mknodat(2) with S_IFIFO. Format: path or path:mode, path absolute, mode an optional octal permission mode. Omit the mode and it defaults to 0600.
mkfifo+/tmp/myfifomkfifo+/tmp/myfifo:0644mkfifo+/run/syd/ctl:0600
Path must be absolute. Relative paths and path components containing .. → rejected with EINVAL ("Invalid argument"). Symbolic links are not followed in any component of the path. Parent directories are opened with openat2(2) using RESOLVE_NO_XDEV to prevent cross-device path traversal. Parent directories get created automatically using mkdirat(2) with safe fd-based traversal. The node is created using mknodat(2), which doesn't follow symbolic links in the final component. A secure umask(2) of 0077 is applied during mount namespace setup, stripping group and other permission bits from the requested mode. Plus setuid and setgid bits are stripped explicitly. Only user permission bits and the sticky bit are preserved in the final mode. 💅
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, mkfifo operations are silently ignored. This option does nothing without unshare/mount:true.
💜 mkfifo-try 💜
type—string-vecstatic—yesoci—no
mkfifo but it warns and continues on EEXIST ("File exists") errno(3). All other errors remain fatal. Perfect for FIFO nodes that may already exist in a tmpfs root. Same format as mkfifo: path or path:mode.
Take "mkfifo-try+/tmp/myfifo:0644" as an example. If /tmp/myfifo already exists, the creation is skipped with a warning and execution continues. If the path can't be created for any other reason (e.g. EACCES, ENOENT), Syd reports the error and terminates as usual.
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, mkfifo-try operations are silently ignored. This option does nothing without unshare/mount:true.
🔥 link 🔥
type—string-vecstatic—yesoci—no
Makes Syd create a hard link in the mount namespace on startup using linkat(2). Format is dst:src where dst is the absolute path of the new link and src is the absolute path of the existing file you're linking to.
link+/lib/libfoo.so:/usr/lib/libfoo.solink+/opt/bin/tool:/usr/bin/tool
Both dst and src must be absolute paths. Relative paths and path components containing .. get rejected with EINVAL ("Invalid argument"). Symbolic links are not followed in any component of either path. The source file is opened with openat2(2) using RESOLVE_NO_XDEV to prevent cross-device path traversal. Parent directories of dst are created automatically using mkdirat(2) with safe fd-based traversal.
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, link operations are silently ignored. This option does nothing without unshare/mount:true.
🧚 link-try 🧚
type—string-vecstatic—yesoci—no
Identical to link except it warns and continues on EEXIST ("File exists") errno(3). All other errors remain fatal. Handy for hard links that may already exist in a tmpfs root. Format is the same as link: dst:src.
Take "link-try+/lib/libfoo.so:/usr/lib/libfoo.so" as an example. If /lib/libfoo.so already exists, the creation is skipped with a warning and execution continues. If the link can't be created for any other reason (e.g. EACCES, ENOENT), Syd reports the error and terminates as usual. 😭
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, link-try operations are silently ignored. This option does nothing without unshare/mount:true.
🌈 symlink 🌈
type—string-vecstatic—yesoci—no
So basically this command makes Syd create a symbolic link inside the mount namespace at startup, using symlinkat(2). The format is dst:src — dst is the absolute path of the shiny new symlink, and src is whatever it points at. 🎀
symlink+/lib/libfoo.so:/usr/lib/libfoo.sosymlink+/usr/lib/libfoo.so.1:libfoo.so
The dst path has to be absolute, no exceptions bestie. Relative dst paths and any path components containing .. in either dst or src get rejected with EINVAL ("Invalid argument"). But unlike link, the src path can be relative — that gives you a relative symbolic link, which is resolved when the link is accessed, not when it's created. Symbolic links are not followed in the final component of dst. Parent directories of dst get created automatically using mkdirat(2) with safe fd-based traversal. ✨
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, symlink operations are silently ignored 👀. This option does nothing without unshare/mount:true.
symlink-try
- type:
string-vec - static: yes
- oci: no
This one behaves identically to symlink except that it warns and continues on EEXIST ("File exists") errno(3). Every other error stays fatal. Super useful for symbolic links that might already exist in a tmpfs root. Format is the same as symlink: dst:src
Take "symlink-try+/lib/libfoo.so:/usr/lib/libfoo.so" as an example. If /lib/libfoo.so already exists, creation is skipped with a warning and execution keeps going. If the symbolic link can't be created for any other reason (e.g. EACCES, ENOENT), Syd reports the error and terminates like usual. 💅
Escape sequences are supported in the path arguments: "\:" inserts a literal colon and "\\" inserts a literal backslash.
This option requires root:ramfs or root:tmpfs. Without a ramfs(5) or tmpfs(5) root, symlink-try operations are silently ignored. This option does nothing without unshare/mount:true.
workdir
- type:
string - static: yes
- oci: no
Sets the current working directory inside the mount namespace on startup. The working directory gets changed after all bind, link, symlink, mkdir, mkfile, and mkfifo commands have been applied. Use the keywords none or off to unset a previously set value. 🦋
workdir:/tmpworkdir:/opt/buildworkdir:none
The path must be absolute. Relative paths and path traversal using ".." are not permitted. Symbolic links are not followed. This command may be combined with mkdir to first create the target directory and then change into it — iconic combo honestly. ✨
This option does nothing without the root command. This option does nothing without unshare/mount:true.
force
- type:
string-vec - drop: yes, add only
Add or remove an integrity force rule for Force Sandboxing. The format is force+/path:algorithm:hashhex:action for adding and force-/path for removing. Use force^ to clear the Integrity Force map. Available actions are "warn", "filter", "deny", "panic", "stop", "abort", "kill" and "exit", and the default is "deny". Algorithm is any supported hash algorithm (see syd-sum(1) -a list), e.g. sha256, sha3-512, blake2b-256, crc32c. hashhex is the hexadecimal digest. 🔥
As of version 3.57.0, escape sequences are supported in the path: "\:" inserts a literal colon and "\\" inserts a literal backslash.
syd-sum(1)is a helper tool to calculate checksums of files.syd-path(1)is a helper tool to write integrity force rules for binaries underPATH.
proxy/addr
- type:
IP address - default:
127.0.0.1 - static: yes
- oci: no
Set the internal address for Proxy sandboxing. This must be an IPv4 or an IPv6 address. Defaults to 127.0.0.1.
proxy/port
- type:
integer - default:
9050 - static: yes
- oci: no
Set the internal port for Proxy sandboxing. Defaults to 9050. 🧚
proxy/ext/host
- type:
IP - default:
127.0.0.1 - static: yes
- oci: no
Set the external address for Proxy sandboxing. This must be either an IPv4 address or an IPv6 address.
Defaults to "127.0.0.1", which may be overridden with the environment variable SYD_PROXY_HOST at startup. 💜
proxy/ext/port
- type:
integer - default:
9050 - static: yes
- oci: no
Set the external port for Proxy sandboxing.
Defaults to 9050, which may be overridden with the environment variable SYD_PROXY_PORT at startup.
proxy/ext/unix
- type:
string - static: yes
- oci: no
Set the external UNIX domain socket for Proxy sandboxing. 🐚
The argument may also be set using the environment variable SYD_PROXY_UNIX at startup.
This option has precedence over the option "proxy/ext/host", ie. when both are given Syd will connect to the UNIX domain socket.
segvguard/expiry
- type:
duration - default:
120 - drop: yes, raise only
Specify the SegvGuard expiry timeout in seconds. Set to 0 to disable SegvGuard.
As of version 3.47.0, duration may be passed in human readable format. Refer to the documentation of the dur crate for more information. ⏳
segvguard/suspension
- type:
duration - default:
600 - drop: yes, raise only
Specify the SegvGuard suspension timeout in seconds. 0 is an invalid value. 🙅♀️
As of version 3.47.0, duration may be passed in human readable format. Refer to the documentation of the dur crate for more information.
segvguard/maxcrashes
- type:
integer (u8) - default:
5 - drop: yes, reduce only
Specify SegvGuard max crashes. 0 is an invalid value. 💀
tpe/gid
- type:
integer (gid_t)
Specify untrusted GID for Trusted Path Execution (TPE). By default, TPE is applied to users of all groups including root, and this setting can be used to limit it to a certain group. To unset a previously set GID and return to the default state, set "none" as the value. 🫶
tpe/negate
- type:
boolean
Negate GID logic for Trusted Path Execution (TPE). This flips "tpe/gid" from untrusted into trusted, so users belonging to this group are exempt from TPE. ✨
tpe/root_owned
- type:
boolean - drop: yes, on only
Ensure the file and parent directory are root-owned for Trusted Path Execution (TPE).
Heads up bestie — this option will misbehave with "unshare/user:1" if the real root user is not mapped inside the container. 😭
🫶 tpe/user_owned
- type —
boolean
Okay so this one is the Trusted Path Execution (TPE) bestie: it makes sure the file and its parent directory are either user-owned or root-owned. No weird third-party owner sneaking into your exec path, ty 💅
Heads up though — this option may misbehave with unshare/user:1 if the real root user is not mapped inside the container. Not a vibe, just a fact 😭
🌸 tpe/root_mount
- type —
boolean
Same TPE energy, different check: file and parent directory both have to live on the root filesystem. Use this if you wanna pin all your executions to a single safe mountpoint — one mount to rule them all 👑
🦋 allow/fs
- type —
string-vec - drop —
yes, remove only
This is your guest list for Filesystem sandboxing: which filesystem types are allowed in. Literally a list of types, that's it ✨
It accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input — yes bestie, you can get your hex girl on 🔥
⭐ allow/walk
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Walk sandboxing. Walking through the filesystem like it's a runway 🚶♀️💅
🎀 allow/list
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for List sandboxing. Peeking at the directory listing? Only if you're on the list, babe 📋
💜 allow/stat
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Stat sandboxing — as in, who gets to ask about file metadata. Nosy behavior, but make it consensual 🔍
🌈 allow/read
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Read sandboxing. Reading is fundamental, but like, only where permitted 📖
🍓 allow/write
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Write sandboxing. Put the pen down unless the pattern says otherwise ✍️
💀 allow/exec
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Exec sandboxing. This is the main-character permission honestly, guard it 👑
🧚 allow/create
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Create sandboxing. New file who dis 🐣
🦋 allow/delete
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Delete sandboxing. The trash can stays locked unless the pattern says slay 💅
🔥 allow/rename
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Rename sandboxing. Glow-up name changes only for approved paths ✨
🎀 allow/readlink
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Readlink sandboxing. Following the crumbs, but make it permissioned 🍪
💜 allow/symlink
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Symlink sandboxing. Making little shortcuts to other files, iykyk 🔗
🌸 allow/truncate
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Truncate sandboxing. Chopping a file down to size, respectfully ✂️
⭐ allow/chdir
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Chdir sandboxing. Where you're allowed to wander to — stay in your lane, gorgeous 🚪
🫶 allow/readdir
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Readdir sandboxing. Reading the contents of a directory, which is different from List, don't ask me why, that's just how it is 😌
🐣 allow/mkdir
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Mkdir sandboxing. Building new little homes for your files 🏠
💀 allow/rmdir
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Rmdir sandboxing. Demolition requires paperwork, fr fr 📄
👑 allow/chown
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Chown sandboxing. Changing who owns what — that's a whole power move, so obviously it's gated 🫡
🍓 allow/chgrp
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Chgrp sandboxing. Group project energy, but with permissions 👯
🌈 allow/chmod
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Chmod sandboxing. Changing the mode bits, aka the file's whole aesthetic 💅
🔥 allow/chattr
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Chattr sandboxing. Toggling those extra-special file attributes, the deep lore ones 📚
🦋 allow/chroot
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Chroot sandboxing. Changing your whole root directory?? Massive. Gated, obviously 🌍
🎀 allow/notify
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Notify sandboxing. Keeping an eye on things — surveillance but make it sandboxed 👀
🧚 allow/utime
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Utime sandboxing. Touching timestamps, the ultimate "time is a construct" permission ⏰
💜 allow/mkdev
- type —
string-vec - drop —
yes, remove only
List of glob(3p) patterns allowed for Mkdev sandboxing — creating device nodes…
Specifies a list of glob(3p) patterns to allow for Mkdev sandboxing. 💅
allow/mkfifo 🎀
type—string-vecdrop—yes, remove only
This one's the allowlist for Mkfifo sandboxing: it takes a list of glob(3p) patterns and that's the whole vibe. ✨
allow/mktemp 🦋
type—string-vecdrop—yes, remove only
Same energy for Mktemp sandboxing — a list of glob(3p) patterns, no cap. 💖
allow/net/bind 🌸
type—string-vecdrop—yes, remove only
This is the network address pattern allowlist for Bind network sandboxing. Lowkey crucial if you're locking down who gets to bind. 🔥
allow/net/accept 💫
type—string-vecdrop—yes, remove only
The network address pattern allowlist for Accept network sandboxing — yes bestie, accepting connections has its own little guest list. 🫶
allow/net/connect 🌈
type—string-vecdrop—yes, remove only
And here's the network address pattern allowlist for Connect network sandboxing. Outbound connections stay picky. 💅
allow/sendfd 👑
type—string-vecdrop—yes, remove only
Specifies a list of file descriptor names to allow for SendFd sandboxing. 💖
Real talk: the file descriptor being sent is matched, not the peer it is sent to. That distinction is doing main character energy. ✨
Accepts a file descriptor name, which begins with the character !:
!file— regular file!dir— directory!symlink— symbolic link!magiclink—proc(5)magic symbolic link!magicdir—proc(5)magic symbolic link to a directory!bdev— block device!cdev— character device!socket— socket!fifo— filesystem FIFO!pipe— anonymous pipe!notification_pipe— notification pipe!nsfs— namespace file descriptor!pidfd— process file descriptor!secretmem— secret memory file descriptor!memfd:NAME— memory file descriptor namedNAME!memfd-hugetlb:NAME— hugetlb memory file descriptor namedNAME!unknown— file descriptor with no name!all— every file descriptor (wildcard)
Other kernel objects take the name the kernel gives the object, e.g. !eventpoll for an epoll(7) file descriptor, !inotify, or !io_uring. This set is open. A name a newer kernel introduces may be used before Syd knows of it. 🧚
allow/recvfd 💜
type—string-vecdrop—yes, remove only
Specifies a list of file descriptor names to allow for RecvFd sandboxing. 🌟
The file descriptor being received is matched, not the peer it is received from. Fr fr, that's the detail that matters. 😭
Accepts a file descriptor name, which begins with the character !:
!file— regular file, the classic!dir— directory!symlink— symbolic link!magiclink—proc(5)magic symbolic link!magicdir—proc(5)magic symbolic link to a directory!bdev— block device!cdev— character device!socket— socket!fifo— filesystem FIFO!pipe— anonymous pipe!notification_pipe— notification pipe!nsfs— namespace file descriptor!pidfd— process file descriptor!secretmem— secret memory file descriptor!memfd:NAME— memory file descriptor named NAME!memfd-hugetlb:NAME— hugetlb memory file descriptor named NAME!unknown— file descriptor with no name!all— every file descriptor (wildcard, the "all of the above" of fds)
Other kernel objects take the name the kernel gives the object, e.g. !eventpoll for an epoll(7) file descriptor, !inotify, or !io_uring. This set is open. A name a newer kernel introduces may be used before Syd knows of it. (iconic "we'll figure it out when it happens" energy — Syd isn't gonna gatekeep your brand new fd type.)
allow/net/link
the little type table, but make it a list:
- type —
string-vec - drop — yes, remove only
- static — yes
Specifies a list of netlink families to allow for Link network sandboxing. (not the Zelda guy. the network sandbox feature.)
Accepts a comma-delimited list of the following items: route, usersock, firewall, sock_diag, nflog, xfrm, selinux, iscsi, audit, fib_lookup, connector, netfilter, ip6_fw, dnrtmsg, kobject_uevent, generic, scsitransport, ecryptfs, rdma, crypto, and smc. Use all to specify all families. yup, the whole menu, no substitutions.
allow/uring/op
- type —
string-vec - drop — yes, remove only
- static — yes
- default —
epoll_ctl,read,readv,write,writev
Specifies list of io_uring(7) submission queue operations to allow for the sandbox process using task-level io_uring(7) restrictions new in Linux-7.0. future tech era fr Any operation not in the list is denied, so by default the io_uring(7) interface cannot be used to bypass path and network sandboxing. None of the default operations can reference a path or network address, so they cannot bypass path or network sandboxing. An empty list, e.g. after allow/uring/op^, denies the io_uring(7) interface altogether. like, fully off. gone. not invited to the function.
Accepts a comma-delimited list of the following items: nop, readv, writev, fsync, read_fixed, write_fixed, poll_add, poll_remove, sync_file_range, sendmsg, recvmsg, timeout, timeout_remove, accept, async_cancel, link_timeout, connect, fallocate, openat, close, files_update, statx, read, write, fadvise, madvise, send, recv, openat2, epoll_ctl, splice, provide_buffers, remove_buffers, tee, shutdown, renameat, unlinkat, mkdirat, symlinkat, linkat, msg_ring, fsetxattr, setxattr, fgetxattr, getxattr, socket, uring_cmd, send_zc, sendmsg_zc, read_multishot, waitid, futex_wait, futex_wake, futex_waitv, fixed_fd_install, ftruncate, bind, listen, recv_zc, epoll_wait, readv_fixed, writev_fixed, pipe, nop128 and uring_cmd128. Use all to specify all operations.
Note thatio_uring(7)may also require a nonzeroRLIMIT_MEMLOCK, seerlimit/memlock. don't forget the linked page bestie, it's a whole thing 💅
allow/uring/reg
- type —
string-vec - drop — yes, remove only
- static — yes
- default —
register_probe
Specifies list of io_uring(7) registrations to allow for the sandbox process using task-level io_uring(7) restrictions new in Linux-7.0. Any registration not in the list is denied. Registrations configure a ring rather than submit work, so on their own they cannot be used to bypass path or network sandboxing. they're setup girlies, not doers. The default allows only register_probe, a read-only capability query, because the kernel enforces register restrictions only when at least one registration is allowlisted. so we gotta name at least one, that's the trick. Four further registrations are issued without reference to a ring and thus cannot be restricted: register_send_msg_ring, register_query, register_restrictions and register_bpf_filter. They are always permitted and are not accepted here. read that twice — "always permitted" and "not accepted here", both. don't try to list 'em, they're not on the menu.
Accepts a comma-delimited list of the following items: register_buffers, unregister_buffers, register_files, unregister_files, register_eventfd, unregister_eventfd, register_files_update, register_eventfd_async, register_probe, register_personality, unregister_personality, register_enable_rings, register_files2, register_files_update2, register_buffers2, register_buffers_update, register_iowq_aff, unregister_iowq_aff, register_iowq_max_workers, register_ring_fds, unregister_ring_fds, register_pbuf_ring, unregister_pbuf_ring, register_sync_cancel, register_file_alloc_range, register_pbuf_status, register_napi, unregister_napi, register_clock, register_clone_buffers, register_zcrx_ifq, register_resize_rings, register_mem_region and register_zcrx_ctrl. Use all to specify all registrations.
Note thatio_uring(7)may also require a nonzeroRLIMIT_MEMLOCK, seerlimit/memlock. same warning, same page, same vibe check 🫶
allow/uring/flag
- type —
string-vec - drop — yes, remove only
- static — yes
- default —
async
okay so this setting is literally the list of io_uring(7) submission queue entry flags that the sandbox process is allowed to use with the task-level io_uring(7) restrictions that are new in Linux-7.0 ✨. If a submission sets a flag that is not in this list, it gets rejected — no cap 🫶. The defaults are the set required to make PostgreSQL work under Syd, and they may change in the future 👀. These flags govern how a submission is sequenced and dispatched, not which operation it runs, so on their own they cannot be used to bypass path or network sandboxing 💅.
Accepts a comma-delimited list of the following items:
fixed_fileio_drainio_linkio_hardlinkasyncbuffer_selectcqe_skip_success
Use all to specify all flags.
Also ngl: io_uring(7) may also require a nonzero RLIMIT_MEMLOCK, see rlimit/memlock.
allow/lock/read 🦋
type:string-setstatic:yesdefault:("/dev/null", "/proc")
Specifies a set of beneath paths to grant file read access for Lock sandboxing 🔐. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_READ_FILE and only applies to the content of the directory not the directory itself. As of version 3.21.0, this set includes the paths "/dev/null" and "/proc" by default as Syd is included in the Landlock sandbox and Syd requires read access to these paths to function correctly 🫶.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/write 🌸
type:string-setstatic:yesdefault:("/dev/null")
Specifies a set of beneath paths to grant file write access for Lock sandboxing ✍️. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_WRITE_FILE and only applies to the content of the directory not the directory itself. As of version 3.21.0, this set includes the path "/dev/null" by default as Syd is included in the Landlock sandbox and Syd requires write access to this file to function correctly 💖.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/exec 🎀
type:string-setstatic:yes
Specifies a set of beneath paths to grant file execute access for Lock sandboxing 🏃♀️. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_EXECUTE and only applies to the content of the directory not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/ioctl ⚡
type:string-setstatic:yes
Specifies a set of beneath paths to grant ioctl(2) access for Lock sandboxing 🎛️. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_IOCTL_DEV and only applies to the content of the directory not the directory itself. Landlock ioctl(2) support requires ABI 5 or later. Fifth Landlock ABI was introduced with Linux 6.10. On older kernels, this command is a no-op and is not going to confine ioctl(2) operations 😭.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/create 🌱
type:string-setstatic:yes
Specifies a set of beneath paths to grant file creation, rename and link access for Lock sandboxing 🛠️. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_MAKE_REG and only applies to the content of the directory not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/delete 💀
type:string-setstatic:yes
Specifies a set of beneath paths to grant file unlink, rename and link access for Lock sandboxing 🗑️. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_REMOVE_FILE and only applies to the content of the directory not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/rename 🔁
type:string-setstatic:yes
Specifies a set of beneath paths to grant access to link or rename a file from or to a different directory (i.e. reparent a file hierarchy) for Lock sandboxing 🧳. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_REFER and only applies to the content of the directory not the directory itself. Landlock rename support requires ABI 2 or later. Second Landlock ABI was introduced with Linux 5.19. On older kernels, this type of access is always denied with Landlock 😤.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/symlink 🪢
type:string-setstatic:yes
Specifies a set of beneath paths to grant symbolic link creation, rename and link access for Lock sandboxing 🎀. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_MAKE_SYM and only applies to the content of the directory not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
allow/lock/truncate ✂️
type:string-setstatic:yesdefault:("/dev/null")
Landlock's LANDLOCK_ACCESS_FS_TRUNCATE right is what's behind this one, and it only applies to the content of the directory — not the directory itself, bestie, read that again 💅
Landlock file truncation support needs ABI 3 or later, and the third Landlock ABI showed up with Linux 6.2. On older kernels this command is a literal no-op and it is not going to confine file truncation operations — so don't be out here thinking you're protected when you're not 😭
As of version 3.21.0, this set includes the path "/dev/null" by default, because Syd is itself included in the Landlock sandbox and Syd requires truncation access to this file to function correctly. Yes, she sandboxes herself too, iconic 👑
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
🌸 allow/lock/readdir 🌸
- type:
string-set - static: yes
- default:
("/proc")
Specifies a set of beneath paths to grant directory list access for Lock sandboxing. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_READ_DIR and — heads up, this one's different from the truncate vibe — it applies to the directory and the directories beneath it 👀
As of version 3.21.0, this set includes the directory "/proc" by default, since Syd is included in the Landlock sandbox and Syd requires readdir access to this directory to function correctly. Self-care, fr fr 🫶
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
🎀 allow/lock/mkdir 🎀
- type:
string-set - static: yes
Specifies a set of beneath paths to grant directory creation and rename access for Lock sandboxing. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_MAKE_DIR and only applies to the content of the directory, not the directory itself ✨
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
🦋 allow/lock/rmdir 🦋
- type:
string-set - static: yes
Specifies a set of beneath paths to grant directory deletion and rename access for Lock sandboxing. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_REMOVE_DIR and only applies to the content of the directory, not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
💜 allow/lock/mkbdev 💜
- type:
string-set - static: yes
Specifies a set of beneath paths to grant block device creation access for Lock sandboxing. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_MAKE_BLOCK and only applies to the content of the directory, not the directory itself. Block devices, in this economy? okay 😭
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
🍓 allow/lock/mkcdev 🍓
- type:
string-set - static: yes
Specifies a set of beneath paths to grant character device creation access for Lock sandboxing. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_MAKE_CHAR and only applies to the content of the directory, not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
🧚 allow/lock/mkfifo 🧚
- type:
string-set - static: yes
Specifies a set of beneath paths to grant named pipe (FIFO) creation access for Lock sandboxing. This category corresponds to the Landlock access right LANDLOCK_ACCESS_FS_MAKE_FIFO and only applies to the content of the directory, not the directory itself.
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Path may be relative, in which case it is resolved relative to the directory where Syd was executed. Noop without sandbox/lock:on.
🔥 allow/lock/bind 🔥
- type:
(u16-set, string-set) - static: yes
Specifies a list of allowed bind(2) ports and UNIX domain socket paths for Lock sandboxing. This category corresponds to the landlock(7) access rights LANDLOCK_ACCESS_NET_BIND_TCP, LANDLOCK_ACCESS_NET_BIND_UDP, and LANDLOCK_ACCESS_FS_MAKE_SOCK — and that last one only applies to the content of the directory, not the directory itself, iykyk 💅
The argument is either:
- a single port,
- a closed range in format
port1-port2, - a comma-separated list of ports and/or ranges such as
80,443,8000-9000, - or an absolute UNIX domain socket path.
landlock(7) network support requires ABI 4 or later, and the fourth landlock(7) ABI was introduced with Linux 6.7. UDP port support requires ABI 10 or later, and the tenth landlock(7) ABI was introduced with Linux 7.2 🚀
On older kernels, this command is a no-op when specified with the unsupported argument kind and does not do any network confinement. So like, check your kernel first bestie 🫶
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Noop without sandbox/lock:on.
🌈 allow/lock/connect 🌈
- type:
(u16-set, string-set) - static: yes
Specifies a list of allowed connect(2) ports and pathname UNIX domain socket paths for Lock sandboxing. This category corresponds to the landlock(7) access rights LANDLOCK_ACCESS_NET_CONNECT_TCP, LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, and LANDLOCK_ACCESS_FS_RESOLVE_UNIX — and that last one restricts both connect(2) and sendmsg(2) with an explicit recipient address to UNIX domain sockets created outside the landlock(7) domain 👀✨
The argument is either:
- a single port,
- a closed range in format
port1-port2, - a comma-separated list of ports and/or ranges such as
80,443,8000-9000, - or an absolute UNIX domain socket path.
landlock(7) network support requires ABI 4 or later, and the fourth landlock(7) ABI was introduced with Linux 6.7. UNIX domain socket support requires ABI 9 or later, and the ninth landlock(7) ABI was introduced with Linux 7.1. UDP port support requires ABI 10 or later, and the tenth landlock(7) ABI was introduced with Linux 7.2 🌟
On older kernels, this command is a no-op when specified with the unsupported argument kind and does not do any confinement. Not her being quietly useless on your ancient kernel 💀
As of version 3.46.0, path must not contain magic symbolic links or parent ("..") components. Path is permitted to contain regular symbolic links. These regular symbolic symlinks are permitted to resolve to targets with parent ("..") components in them. Noop without sandbox/lock:on.
⭐ warn/fs ⭐
- type:
string-vec
Specifies a list of filesystem types to warn for Filesystem sandboxing. Like, a little heads-up instead of a whole lockdown, very "I'm not mad, I'm just disappointed" energy 🙈
Accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input — no decimal ambiguity allowed, periodt.
🎀 warn/walk 🎀
- type:
string-vec
Specifies a list of glob(3p) patterns to warn for Walk sandboxing. Patterns, bestie — so you can be as fancy with the matching as you like ✨
warn/list
- type:
string-vec
bestie this one's your little lookout list — a list of glob(3p) patterns to warn for List sandboxing 👀 nothing gets blocked, it just goes "hey girl, eyes up" ✨
warn/stat
- type:
string-vec
a list of glob(3p) patterns to warn for Stat sandboxing 🎀 so when something goes poking at file metadata, you hear about it, no cap 💅
warn/read
- type:
string-vec
a list of glob(3p) patterns to warn for Read sandboxing 📖 lowkey the "why is this thing reading my diary" alarm 🫶
warn/write
- type:
string-vec
a list of glob(3p) patterns to warn for Write sandboxing ✍️ somebody's about to touch your files? not me finding out from the group chat — you'll get a warning 🌸
warn/exec
- type:
string-vec
a list of glob(3p) patterns to warn for Exec sandboxing 🔥 your "who tf just ran that" list, but make it a heads-up instead of a block 😭
warn/create
- type:
string-vec
a list of glob(3p) patterns to warn for Create sandboxing 🎀 new files appearing out of nowhere? iconic, but you're gonna hear about it first ✨
warn/delete
- type:
string-vec
a list of glob(3p) patterns to warn for Delete sandboxing 💀 the "bestie something is about to be GONE" list, honestly terrifying, love that for us 🙈
warn/rename
- type:
string-vec
a list of glob(3p) patterns to warn for Rename sandboxing 🦋 moving things around? sure, but don't think you're doing it in silence 👑
warn/readlink
- type:
string-vec
a list of glob(3p) patterns to warn for Readlink sandboxing 🔗 for when someone's following the symlink breadcrumbs and you'd like a lil notification 🌸
warn/symlink
- type:
string-vec
a list of glob(3p) patterns to warn for Symlink sandboxing 🧚 pointing one thing at another — cute! — but you're gonna know ✨
warn/truncate
- type:
string-vec
a list of glob(3p) patterns to warn for Truncate sandboxing ✂️ file's about to get chopped, and you get a warning BEFORE the yassification of your data into nothing 😭
warn/chdir
- type:
string-vec
a list of glob(3p) patterns to warn for Chdir sandboxing 🚪 someone's walking through directories — main character energy, but you get the play-by-play 🫶
warn/readdir
- type:
string-vec
a list of glob(3p) patterns to warn for Readdir sandboxing 📂 they're scrolling through your directory like it's a feed, and you get told 💅
warn/mkdir
- type:
string-vec
a list of glob(3p) patterns to warn for Mkdir sandboxing 🎀 new folder, who dis — and yes you get a warning for it, periodt ⭐
warn/rmdir
- type:
string-vec
a list of glob(3p) patterns to warn for Rmdir sandboxing 🗑️ folder's about to vanish into the void and you're getting the heads-up fr fr 😭
warn/chown
- type:
string-vec
a list of glob(3p) patterns to warn for Chown sandboxing 👑 ownership changes are a big deal, so yeah, you're on the notify list 💜
warn/chgrp
- type:
string-vec
a list of glob(3p) patterns to warn for Chgrp sandboxing 🫶 group changes get a warning too — everybody eats, everybody knows 🌈
warn/chmod
- type:
string-vec
a list of glob(3p) patterns to warn for Chmod sandboxing 🔐 permissions getting flipped around? bestie you're getting a warning, that's the whole vibe ✨
warn/chattr
- type:
string-vec
a list of glob(3p) patterns to warn for Chattr sandboxing 🦋 attribute changes are sneaky little things, so this one keeps you in the loop 💅
warn/chroot
- type:
string-vec
a list of glob(3p) patterns to warn for Chroot sandboxing 🧚 somebody's changing their whole root reality — very main character of them, and you get to watch 👀
warn/notify
- type:
string-vec
a list of glob(3p) patterns to warn for Notify sandboxing 🔔 warnings about warnings, we love a meta queen ✨
warn/utime
- type:
string-vec
a list of glob(3p) patterns to warn for Utime sandboxing ⏰ timestamps being rewritten? that's literally editing history, so yes, it's on the warn list 💀
warn/mkdev
- type:
string-vec
a list of glob(3p) patterns to warn for Mkdev sandboxing 🎀 device nodes popping into existence is a whole moment, and you're getting a warning for it 🌸
warn/mkfifo
- type:
string-vec
a list of glob(3p) patterns to warn for Mkfifo sandboxing 🥤 a named pipe got made — communication era — and you get a warning, iykyk 🫶
warn/mktemp
- type:
string-vec
a list of glob(3p) patterns to warn for Mktemp sandboxing 🍓 temp files are supposed to be lowkey! but you're still getting a warning about them, no cap ✨
warn/net/bind
- type:
string-vec
a list of network address patterns to warn for Bind network sandboxing 🌐 this isn't globs, bestie, it's addresses — someone's grabbing a port, and you hear about it 📡
warn/net/accept
- type:
string-vec
a list of network address patterns to warn for Accept network sandboxing 🫶 incoming connections get the warning treatment — you're not being left on read 💅
warn/net/connect
- type:
string-vec
a list of network address patterns to warn for Connect network sandboxing 🔥 outbound connections, warned. it's giving "I know where you're going" 👀
warn/sendfd
- type:
string-vec
a list of file descriptor names to warn for SendFd sandboxing 📤 note the vibe shift: these are file descriptor names, not globs, not addresses — descriptors, bestie 🎀
warn/recvfd
- type:
string-vec
a list of file descriptor names to warn for RecvFd sandboxing 📥 same deal, receiving side — file descriptor names get the warning treatment ✨
okay now we flip the whole energy 😌 these next ones aren't warnings, they're denies — actual "no ma'am" energy. and heads up: every single one of them is tagged:
- drop: yes, add only
which means it drops stuff, and it's add-only, so this is a one-way street, bestie 💅
deny/fs
- type:
string-vec - drop: yes, add only
a list of filesystem types to deny for Filesystem sandboxing 🚫🗂️
and this one's got extra rules, so pay attention: it accepts a comma-delimited list of strings and unsigned 64-bit integers as argument 🧮 prefix with 0x for hexadecimal and 0o for octal input, periodt ✨
deny/walk
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Walk sandboxing 🚶♀️🚫 no strolling through there, thank you 💅
deny/list
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for List sandboxing 📂🚫 the window shopping is cancelled 😭
deny/stat
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Stat sandboxing 🚫 nobody's checking that metadata on your watch, no cap 🎀
deny/read
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Read sandboxing 📖🚫 eyes off, bestie, this one's off-limits 🫶
deny/write
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Write sandboxing ✍️🚫 not a single pen touch, absolutely not 🔥
deny/exec
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Exec sandboxing 🚫⚡ these do not run. full stop. periodt 💀
deny/create
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Create sandboxing 🎀🚫 no new files here, the vibes are closed for business 🌸
deny/delete
- type:
string-vec - drop: yes, add only
a list of glob(3p) patterns to deny for Delete sandboxing 🗑️🚫 nothing gets yeeted from these paths, not on your watch 👑
deny/rename
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Rename sandboxing. bestie said no renaming and honestly? she meant it 💅
deny/readlink
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Readlink sandboxing. no peeking behind the curtain, the curtain said no ✨
deny/symlink
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Symlink sandboxing. no making new little shortcuts to places, periodt 🎀
deny/truncate
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Truncate sandboxing. not the file getting chopped down to nothing, we're not doing that today 😭
deny/chdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Chdir sandboxing. you're not walking into that directory, the vibes are off in there 🚪
deny/readdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Readdir sandboxing. no listing, no scrolling the folder like it's a feed, we stay ignorant 🙈
deny/mkdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Mkdir sandboxing. no new little folders being born in there, we are not hosting a gender reveal 💀
deny/rmdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Rmdir sandboxing. nobody's deleting anybody's home directory on my watch 🫶
deny/chown
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Chown sandboxing. hands off, that file belongs to who it belongs to 👑
deny/chgrp
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Chgrp sandboxing. same energy as deny/chown but for the group chat membership, and the group chat is not accepting new members 🌸
deny/chmod
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Chmod sandboxing. nobody is out here changing permissions, that's giving chaos and we're giving peace ✌️
deny/chattr
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Chattr sandboxing. no little attribute tweaks, no sneaky glue, no notes in the margins 🦋
deny/chroot
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Chroot sandboxing. you're not redefining what "root" means today, that's main character energy and it's not your turn 🔥
deny/notify
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Notify sandboxing. no watch parties, no notifs, we left that on read ⭐
deny/utime
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Utime sandboxing. timestamps stay exactly as they are, stop trying to make it look freshly posted 💜
deny/mkdev
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Mkdev sandboxing. no new device nodes, we have device nodes at home 🌈
deny/mkfifo
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Mkfifo sandboxing. no pipes, no passing messages through the wall, the wall said not today 🧚
deny/mktemp
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to deny for Mktemp sandboxing. no spontaneous temp file situations, we are not doing that much admin 💅
deny/net/bind
- type:
string-vec - drop:
yes, add only
Specifies a list of network address patterns to deny for Bind network sandboxing. note the glow-up: these are network address patterns, not glob(3p) ones — read the spec right bestie, iykyk 📡
deny/net/accept
- type:
string-vec - drop:
yes, add only
Specifies a list of network address patterns to deny for Accept network sandboxing. she's not accepting new connections, her DMs are closed 🎀
deny/net/connect
- type:
string-vec - drop:
yes, add only
Specifies a list of network address patterns to deny for Connect network sandboxing. no reaching out, no "hey stranger", the connection dies in the drafts 💀
deny/sendfd
- type:
string-vec - drop:
yes, add only
Specifies a list of file descriptor names to deny for SendFd sandboxing. this one's file descriptor names, not patterns, okay? don't mix the two up 👀
deny/recvfd
- type:
string-vec - drop:
yes, add only
Specifies a list of file descriptor names to deny for RecvFd sandboxing. you're not receiving that package, no tracking number for you 📦
filter/fs
- type:
string-vec
Specifies a list of filesystem types to filter for Filesystem sandboxing.
Accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input. yes the different prefixes are real and yes they matter, type them exactly 🙏
quick note: this one's table has no drop row — only type is listed for filter/fs. lowkey important, don't assume 💖
filter/walk
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for Walk sandboxing. she's walking but she's filtering, it's giving curated stroll 🌸
filter/list
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for List sandboxing. not a full list, a filtered list, the difference is the whole point bestie ✨
filter/stat
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for Stat sandboxing. you get stats for what you're allowed to get stats for, that's the deal 🦋
filter/read
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for Read sandboxing. some pages are available, the rest are simply not, and that's the plot 🍓
filter/write
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for Write sandboxing. yes you can write, no you can't write everywhere, read the room 🫶
filter/exec
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for Exec sandboxing. not every binary gets to be the main character today 🎬
filter/create
- type:
string-vec
Specifies a list of glob(3p) patterns to filter for Create sandboxing. creation is allowed, but it's an invite-only kind of creation, no cap 👑
filter/delete 🗑️
type: string-vec
Specifies a list of glob(3p) patterns to filter for Delete sandboxing.
filter/rename ✏️
type: string-vec
Specifies a list of glob(3p) patterns to filter for Rename sandboxing.
filter/readlink 🔗
type: string-vec
Specifies a list of glob(3p) patterns to filter for Readlink sandboxing.
filter/symlink 🪢
type: string-vec
Specifies a list of glob(3p) patterns to filter for Symlink sandboxing.
filter/truncate ✂️
type: string-vec
Specifies a list of glob(3p) patterns to filter for Truncate sandboxing.
filter/chdir 📂
type: string-vec
Specifies a list of glob(3p) patterns to filter for Chdir sandboxing.
filter/readdir 📖
type: string-vec
Specifies a list of glob(3p) patterns to filter for Readdir sandboxing.
filter/mkdir 🆕
type: string-vec
Specifies a list of glob(3p) patterns to filter for Mkdir sandboxing.
filter/rmdir 🧹
type: string-vec
Specifies a list of glob(3p) patterns to filter for Rmdir sandboxing.
filter/chown 👑
type: string-vec
Specifies a list of glob(3p) patterns to filter for Chown sandboxing.
filter/chgrp 👥
type: string-vec
Specifies a list of glob(3p) patterns to filter for Chgrp sandboxing.
filter/chmod 🔐
type: string-vec
Specifies a list of glob(3p) patterns to filter for Chmod sandboxing.
filter/chattr 🏷️
type: string-vec
Specifies a list of glob(3p) patterns to filter for Chattr sandboxing.
filter/chroot 🏰
type: string-vec
Specifies a list of glob(3p) patterns to filter for Chroot sandboxing.
filter/notify 🔔
type: string-vec
Specifies a list of glob(3p) patterns to filter for Notify sandboxing.
filter/utime ⏰
type: string-vec
Specifies a list of glob(3p) patterns to filter for Utime sandboxing.
filter/mkdev 🎛️
type: string-vec
Specifies a list of glob(3p) patterns to filter for Mkdev sandboxing.
filter/mkfifo 🥤
type: string-vec
Specifies a list of glob(3p) patterns to filter for Mkfifo sandboxing.
filter/mktemp 🎲
type: string-vec
Specifies a list of glob(3p) patterns to filter for Mktemp sandboxing.
filter/net/bind 🪢
type: string-vec
Specifies a list of network address patterns to filter for Bind network sandboxing.
filter/net/accept 📥
type: string-vec
Specifies a list of network address patterns to filter for Accept network sandboxing.
filter/net/connect 📤
type: string-vec
Specifies a list of network address patterns to filter for Connect network sandboxing.
filter/sendfd 🫱
type: string-vec
Specifies a list of file descriptor names to filter for SendFd sandboxing.
filter/recvfd 🫲
type: string-vec
Specifies a list of file descriptor names to filter for RecvFd sandboxing.
okay and now the panic list 💀🔥 — same energy, but these are the "actually no, abort" ones, and heads up: they all have a drop of literally yes, add only, meaning it's add-only vibes.
panic/fs 💥
type: string-vec · drop: yes, add only
Specifies a list of filesystem types to panic for Filesystem sandboxing.
It accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input. 🧮✨
panic/walk 🚶♀️
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Walk sandboxing.
panic/list 📋
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for List sandboxing.
panic/stat 📊
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Stat sandboxing.
panic/read 📚
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Read sandboxing.
panic/write ✍️
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Write sandboxing.
panic/exec 🚀
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Exec sandboxing.
panic/create 🌱
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Create sandboxing.
panic/delete ☠️
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Delete sandboxing.
panic/rename 🏷️
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Rename sandboxing.
panic/readlink 🔍
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Readlink sandboxing.
panic/symlink 🪄
type: string-vec · drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Symlink sandboxing.
panic/truncate
- type:
string-vec - drop: yes, add only
Give it a list of glob(3p) patterns, and any match will panic Truncate sandboxing. 💅
panic/chdir
- type:
string-vec - drop: yes, add only
A list of glob(3p) patterns that make Chdir sandboxing panic. bestie behaviour honestly.
panic/readdir
- type:
string-vec - drop: yes, add only
Patterns — glob(3p) ones — that panic Readdir sandboxing.
panic/mkdir
- type:
string-vec - drop: yes, add only
Here's your list of glob(3p) patterns to panic for Mkdir sandboxing. no cap.
panic/rmdir
- type:
string-vec - drop: yes, add only
glob(3p) patterns that make Rmdir sandboxing panic. it's giving very thorough.
panic/chown
- type:
string-vec - drop: yes, add only
List of glob(3p) patterns to panic for Chown sandboxing. ✨
panic/chgrp
- type:
string-vec - drop: yes, add only
The glob(3p) patterns that panic Chgrp sandboxing go right here.
panic/chmod
- type:
string-vec - drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Chmod sandboxing. 🔥
panic/chattr
- type:
string-vec - drop: yes, add only
glob(3p) patterns → panic for Chattr sandboxing. simple as that.
panic/chroot
- type:
string-vec - drop: yes, add only
A list of glob(3p) patterns to panic for Chroot sandboxing. 🫶
panic/notify
- type:
string-vec - drop: yes, add only
Drop your glob(3p) patterns here and they'll panic Notify sandboxing.
panic/utime
- type:
string-vec - drop: yes, add only
glob(3p) patterns that panic Utime sandboxing. rent free, honestly.
panic/mkdev
- type:
string-vec - drop: yes, add only
Specifies a list of glob(3p) patterns to panic for Mkdev sandboxing. 🎀
panic/mkfifo
- type:
string-vec - drop: yes, add only
The glob(3p) patterns for panicking Mkfifo sandboxing. iconic.
panic/mktemp
- type:
string-vec - drop: yes, add only
A list of glob(3p) patterns to panic for Mktemp sandboxing.
panic/net/bind
- type:
string-vec - drop: yes, add only
Ok so this one's network address patterns, not globs — a list of them, and matching ones panic Bind network sandboxing. 🌐
panic/net/accept
- type:
string-vec - drop: yes, add only
Network address patterns, listed, to panic for Accept network sandboxing. 💜
panic/net/connect
- type:
string-vec - drop: yes, add only
Specifies a list of network address patterns to panic for Connect network sandboxing.
panic/sendfd
- type:
string-vec - drop: yes, add only
Not patterns this time — file descriptor names. List them and you panic SendFd sandboxing. 🦋
panic/recvfd
- type:
string-vec - drop: yes, add only
A list of file descriptor names to panic for RecvFd sandboxing. same vibe, other direction.
stop/fs
- type:
string-vec - drop: yes, add only
Filesystem types go here — a list of them, and those stop Filesystem sandboxing.
It takes a comma-delimited list of strings and unsigned 64-bit integers. Prefix with 0x for hexadecimal and 0o for octal input. 🔢
stop/walk
- type:
string-vec - drop: yes, add only
glob(3p) patterns to stop for Walk sandboxing. periodt.
stop/list
- type:
string-vec - drop: yes, add only
The glob(3p) pattern list that stops List sandboxing. 🌸
stop/stat
- type:
string-vec - drop: yes, add only
Specifies a list of glob(3p) patterns to stop for Stat sandboxing.
stop/read
- type:
string-vec - drop: yes, add only
glob(3p) patterns → stop Read sandboxing. lowkey essential.
stop/write
- type:
string-vec - drop: yes, add only
A list of glob(3p) patterns to stop for Write sandboxing. ✍️
stop/exec
- type:
string-vec - drop: yes, add only
Specifies a list of glob(3p) patterns to stop for Exec sandboxing. main character energy fr fr.
stop/create
- type:
string-vec - drop: yes, add only
Last one in this batch: glob(3p) patterns to stop for Create sandboxing. 🧚
🛑 stop/delete
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Delete sandboxing. Like, no cap, if a path matches, the delete is not happening bestie 💅
🛑 stop/rename
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Rename sandboxing. Renaming stuff around? Not on these paths you don't ✨
🛑 stop/readlink
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Readlink sandboxing. Lowkey the link-following gets blocked at these patterns 🦋
🛑 stop/symlink
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Symlink sandboxing. Making symlinks to these? The sandbox said nah 🙈
🛑 stop/truncate
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Truncate sandboxing. Your files stay their full length, periodt 💖
🛑 stop/chdir
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Chdir sandboxing. Can't cd into these, sorry babe 🎀
🛑 stop/readdir
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Readdir sandboxing. No peeking at these directories 👀 nope!
🛑 stop/mkdir
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Mkdir sandboxing. New folder? Not here, not today 🌸
🛑 stop/rmdir
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Rmdir sandboxing. Directory stays standing, fr fr 🧚
🛑 stop/chown
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Chown sandboxing. Ownership stays exactly where it is 👑
🛑 stop/chgrp
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Chgrp sandboxing. Group changes blocked on these patterns 💜
🛑 stop/chmod
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Chmod sandboxing. Permissions are frozen, bestie 🧊
🛑 stop/chattr
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Chattr sandboxing. Attribute tweaks on these patterns? Denied 😭
🛑 stop/chroot
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Chroot sandboxing. No chrooting into these, it's giving locked door 🚪
🛑 stop/notify
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Notify sandboxing. Watching these paths? The sandbox says not today 🫶
🛑 stop/utime
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Utime sandboxing. Timestamps stay untouched, iconic behaviour ⭐
🛑 stop/mkdev
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Mkdev sandboxing. Device nodes? Not being made here 🔥
🛑 stop/mkfifo
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Mkfifo sandboxing. No new FIFOs on these patterns, periodt 🍓
🛑 stop/mktemp
- type —
string-vec - drop —
yes, add only
Specifies a list of glob(3p) patterns to stop for Mktemp sandboxing. Temp files stay hypothetical 💀
🌐 stop/net/bind
- type —
string-vec - drop —
yes, add only
Specifies a list of network address patterns to stop for Bind network sandboxing. Note: network address patterns, not globs — different vibe, same energy 😌
🌐 stop/net/accept
- type —
string-vec - drop —
yes, add only
Specifies a list of network address patterns to stop for Accept network sandboxing.
🌐 stop/net/connect
- type —
string-vec - drop —
yes, add only
Specifies a list of network address patterns to stop for Connect network sandboxing. These connections? Blocked, obviously 🚫
📨 stop/sendfd
- type —
string-vec - drop —
yes, add only
Specifies a list of file descriptor names to stop for SendFd sandboxing. Yep — file descriptor names here, mind the difference bestie 💅
📨 stop/recvfd
- type —
string-vec - drop —
yes, add only
Specifies a list of file descriptor names to stop for RecvFd sandboxing.
💥 the abort/* family — different energy entirely
Heads up babes: these ones only havetype. There's nodroprow — so no "yes, add only" situation here 🎀
💥 abort/fs
- type —
string-vec
Specifies a list of filesystem types to abort for Filesystem sandboxing.
Accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input. Write that down 🖊️✨
💥 abort/walk
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Walk sandboxing.
💥 abort/list
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for List sandboxing.
💥 abort/stat
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Stat sandboxing.
💥 abort/read
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Read sandboxing.
💥 abort/write
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Write sandboxing. Writing to these? Aborted, no notes 💀🔥
abort/exec
- type —
string-vec - drop — yes, remove only
Specifies a list of glob(3p) patterns to abort for Exec sandboxing. Bestie, this is the "nope, absolutely not, not today" list 💅
abort/create
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Create sandboxing.
abort/delete
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Delete sandboxing.
abort/rename
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Rename sandboxing.
abort/readlink
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Readlink sandboxing.
abort/symlink
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Symlink sandboxing.
abort/truncate
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Truncate sandboxing.
abort/chdir
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Chdir sandboxing.
abort/readdir
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Readdir sandboxing.
abort/mkdir
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Mkdir sandboxing.
abort/rmdir
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Rmdir sandboxing.
abort/chown
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Chown sandboxing.
abort/chgrp
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Chgrp sandboxing.
abort/chmod
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Chmod sandboxing.
abort/chattr
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Chattr sandboxing.
abort/chroot
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Chroot sandboxing.
abort/notify
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Notify sandboxing.
abort/utime
- type —
string-vec - drop — yes, remove only
Specifies a list of glob(3p) patterns to abort for Utime sandboxing ✨
abort/mkdev
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Mkdev sandboxing.
abort/mkfifo
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Mkfifo sandboxing.
abort/mktemp
- type —
string-vec
Specifies a list of glob(3p) patterns to abort for Mktemp sandboxing.
abort/net/bind
- type —
string-vec
Specifies a list of network address patterns to abort for Bind network sandboxing 🌐 (note: network address patterns, not glob(3p) — she's different, okay?)
abort/net/accept
- type —
string-vec
Specifies a list of network address patterns to abort for Accept network sandboxing.
abort/net/connect
- type —
string-vec
Specifies a list of network address patterns to abort for Connect network sandboxing.
abort/sendfd
- type —
string-vec
Specifies a list of file descriptor names to abort for SendFd sandboxing.
abort/recvfd
- type —
string-vec
Specifies a list of file descriptor names to abort for RecvFd sandboxing.
Okay so now we flip the vibe entirely — the kill/* family 🔥 these are add-only, meaning dropping them later just subtracts from the pile, no take-backs. Note the drop row on every single one:
kill/fs
- type —
string-vec - drop — yes, add only
Specifies a list of filesystem types to kill for Filesystem sandboxing.
Accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input. Numbers are literally allowed here, bestie, don't @ me 🙈
kill/walk
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Walk sandboxing.
kill/list
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for List sandboxing.
kill/stat
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Stat sandboxing.
kill/read
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Read sandboxing.
kill/write
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Write sandboxing.
kill/exec
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Exec sandboxing.
kill/create
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Create sandboxing.
kill/delete
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Delete sandboxing.
kill/rename
- type —
string-vec - drop — yes, add only
Specifies a list of glob(3p) patterns to kill for Rename sandboxing.
kill/readlink
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Readlink sandboxing.
kill/symlink
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Symlink sandboxing.
kill/truncate
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Truncate sandboxing.
kill/chdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Chdir sandboxing.
kill/readdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Readdir sandboxing.
kill/mkdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Mkdir sandboxing.
kill/rmdir
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Rmdir sandboxing.
kill/chown
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Chown sandboxing.
kill/chgrp
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Chgrp sandboxing.
kill/chmod
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Chmod sandboxing.
kill/chattr
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Chattr sandboxing.
kill/chroot
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Chroot sandboxing.
kill/notify
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Notify sandboxing.
kill/utime
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Utime sandboxing.
kill/mkdev
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Mkdev sandboxing.
kill/mkfifo
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Mkfifo sandboxing.
kill/mktemp
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to kill for Mktemp sandboxing.
okay bestie the next three are the network ones 👀 — different flavor of pattern, same energy 💅
kill/net/bind
- type:
string-vec - drop:
yes, add only
Specifies a list of network address patterns to kill for Bind network sandboxing.
kill/net/accept
- type:
string-vec - drop:
yes, add only
Specifies a list of network address patterns to kill for Accept network sandboxing.
kill/net/connect
- type:
string-vec - drop:
yes, add only
Specifies a list of network address patterns to kill for Connect network sandboxing.
kill/sendfd
- type:
string-vec - drop:
yes, add only
Specifies a list of file descriptor names to kill for SendFd sandboxing. (yy, fd names, not paths — iykyk ✨)
kill/recvfd
- type:
string-vec - drop:
yes, add only
Specifies a list of file descriptor names to kill for RecvFd sandboxing.
exit/fs
- type:
string-vec - drop:
yes, add only
Specifies a list of filesystem types to exit for Filesystem sandboxing.
Accepts a comma-delimited list of strings and unsigned 64-bit integers as argument. Prefix with 0x for hexadecimal and 0o for octal input. 🎀
so like ext4,0x1234,0o755 — you're cooking with mixed types, slay. Just comma-delimit and go 💅
exit/walk
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to exit for Walk sandboxing.
exit/list
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to exit for List sandboxing.
exit/stat
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to exit for Stat sandboxing.
exit/read
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to exit for Read sandboxing.
exit/write
- type:
string-vec - drop:
yes, add only
Specifies a list of glob(3p) patterns to exit for Write sandboxing.
exit/exec
- type:
string-vec - drop:
yes, add only
…and it's a list of glob(3p) patterns you wanna exit for Exec sandboxing 🫶 — like, "anything matching these? we out, bye 💨".
🎀 exit/create
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Create sandboxing. making files? not on this watch, bestie 💅
🎀 exit/delete
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Delete sandboxing.
🎀 exit/rename
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Rename sandboxing.
🎀 exit/readlink
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Readlink sandboxing. no peeking at where the link goes 🔗✨
🎀 exit/symlink
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Symlink sandboxing.
🎀 exit/truncate
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Truncate sandboxing.
🎀 exit/chdir
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Chdir sandboxing. not me tryna cd into forbidden vibes 😭
🎀 exit/readdir
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Readdir sandboxing.
🎀 exit/mkdir
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Mkdir sandboxing.
🎀 exit/rmdir
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Rmdir sandboxing.
🎀 exit/chown
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Chown sandboxing.
🎀 exit/chgrp
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Chgrp sandboxing.
🎀 exit/chmod
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Chmod sandboxing.
🎀 exit/chattr
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Chattr sandboxing.
🎀 exit/chroot
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Chroot sandboxing.
🎀 exit/notify
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Notify sandboxing.
🎀 exit/utime
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Utime sandboxing.
🎀 exit/mkdev
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Mkdev sandboxing.
🎀 exit/mkfifo
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Mkfifo sandboxing.
🎀 exit/mktemp
type—string-vecdrop—yes, add only
Specifies a list of glob(3p) patterns to exit for Mktemp sandboxing.
okay now we're getting into the network ones 🌐✨ same shape, but the patterns are network address patterns instead of globs. slay, different flavour.
🦋 exit/net/bind
type—string-vecdrop—yes, add only
Specifies a list of network address patterns to exit for Bind network sandboxing.
🦋 exit/net/accept
type—string-vecdrop—yes, add only
Specifies a list of network address patterns to exit for Accept network sandboxing.
🦋 exit/net/connect
type—string-vecdrop—yes, add only
Specifies a list of network address patterns to exit for Connect network sandboxing. no, you cannot just connect to that, bestie 💀
back to fd business 🎀
🎀 exit/sendfd
type—string-vecdrop—yes, add only
Specifies a list of file descriptor names to exit for SendFd sandboxing.
🎀 exit/recvfd
type—string-vecdrop—yes, add only
Specifies a list of file descriptor names to exit for RecvFd sandboxing.
👑 mask
type—string-map
Specifies a list of glob(3p) patterns to mask for Read & Write sandboxing.
Okay so what masking actually does, because it's kinda iconic ngl 🫶: if a path is masked, Syd returns a file descriptor to /dev/null on any sandbox granted attempt to open(2) this path. So the open succeeds, it just hands you the void 💀. Masking can effectively be used to hide the contents of a file in a more relaxed and compatible way than denying read/write access to it. stat(2) calls on a masked file returns the original file metadata and a masked file may be executed. After a successful mask operation, the mask path is not checked for sandbox access.
As of version 3.35.1, the default mask path /dev/null may be changed by specifying a colon-separated extra path to the mask-add command, e.g.
mask+/dev/[fn]ull:/dev/zero
when both of the paths /dev/full and /dev/null will be masked with the path /dev/zero. 🧚✨
The mask path must be a fully canonicalized path without symbolic links. fr fr, no symlink games — canonical or it doesn't count 💅
As of version 3.36.0, the default mask path can be overridden for directories — you just tack an extra colon-separated path onto the mask-add command, like so: mask+/proc/acpi/***:/dev/null:/var/empty 🎀 and here's the tea: the path /proc/acpi/wakeup, which is a regular file, will return /dev/null at the open(2) boundary, but the directory /proc/acpi and any subdirectory inside it will return /var/empty at the open(2) boundary. Obsessed. One rule though — the mask path must be a fully canonicalized path with no symbolic links, periodt 💅
As of version 3.49.0, escape sequences are a thing ✨: "\:" inserts a literal colon and "\\" inserts a literal backslash. Which means patterns can contain colons now, e.g. mask+/path\:with\:colons 🦋
These escape sequences get resolved by the command parser before the resulting pattern is matched as a glob(3p) expression. Inside the glob, a backslash escapes the character that follows it, so a literal glob metacharacter (*, ? or [) is matched by putting a doubled backslash in front of it. The parser first collapses that doubled backslash down to a single backslash, and then the glob matcher applies it to the metacharacter right after. And a lone backslash that isn't part of a "\:" or "\\" sequence? Rejected with EINVAL ("Invalid argument") 😭 so don't try it.
As of version 3.51.0, masked paths return the information of the target path at the stat(2) boundary, and masked paths are protected against filesystem writes 🔥 iconic behaviour honestly.
This whole feature is a non-privileged alternative to the bind command, because it does not require creating a mount namespace, which is the whole reason it's giving main character energy. Plus, mask commands can be specified dynamically after startup using the syd(2) API, which lets you do fine-tuned and/or incremental confinement 🫶
🌸 block
┌──────┬───────────────┐
│ type │ ip-range │
├──────┼───────────────┤
│ drop │ yes, add only │
└──────┴───────────────┘
Specifies a range of IP networks to be blocked when they show up as the target address of the connect group of system calls — that's connect(2), sendto(2), sendmsg(2), sendmmsg(2) — and when they're received as the source address in the return from accept(2) and accept4(2) system calls, for IPv4 and IPv6 family sockets 👑
Use block+<net> and block-<net> to add and remove IP networks from the range. Alternatively the range can also be populated by including ipset and netset files from within Syd configuration. Use block^ to clear the list and block! to simplify the IP range by aggregating networks together. block! is super useful to call after importing big IP blocklists — it helps reduce memory consumption and improve matching performance ✨
Below is a configuration snippet that imports Feodo and DShield blocklists:
# Enable IP blocklists
# Source: https://github.com/firehol/blocklist-ipsets.git
include /usr/src/blocklist-ipsets/feodo.ipset
include /usr/src/blocklist-ipsets/feodo_badips.ipset
include /usr/src/blocklist-ipsets/dshield.netset
include /usr/src/blocklist-ipsets/dshield_1d.netset
include /usr/src/blocklist-ipsets/dshield_30d.netset
include /usr/src/blocklist-ipsets/dshield_7d.netset
include /usr/src/blocklist-ipsets/dshield_top_1000.ipset
block!
🦋 domain
┌──────┬────────────┐
│ type │ string-vec │
└──────┴────────────┘
Define, remove, or clear sandbox domains 💜 A domain is a named, self-contained sandbox policy. (Go read the "Domain Transitions" section of the syd(7) manual page for the whole model, iykyk.) The operators are:
domain+@<name>: Define a new, empty domain named <name>.domain+@<name>:<seed>: Define <name> and initialise its policy from <seed>, which is@<other>to clone an existing domain (e.g.@default), an absolute path to read a configuration file, or a built-in profile name. The leading character selects the form (@a domain,/a file, otherwise a profile name).domain-@<name>: Remove the domain <name>.domain^: Remove every domain exceptdefault.
The domain default always exists and may neither be redefined (EEXIST: "File exists") nor removed (EPERM: "Operation not permitted") 🚫 no cap.
A domain name is 1 to 16 bytes, letter-first ASCII alphanumeric with underscore ([A-Za-z][A-Za-z0-9_]*). A longer name fails with ENAMETOOLONG ("File name too long"), and the empty name or any byte that breaks the pattern (a leading digit or underscore, punctuation, whitespace, a control byte, NUL, or non-ASCII) fails with EILSEQ ("Invalid or incomplete multibyte or wide character"). At most 64 domains may be defined, including default — going over that fails with ENOSPC ("No space left on device"). And a domain that is the target of a move (see the move command) may not be removed while that move exists (EBUSY: "Device or resource busy") 🫠
Configuration is directed to a defined domain by prefixing a per-domain command with @<name>/, e.g. @web/allow/read+/etc/hosts. An unprefixed command applies to default. A reference to an undefined domain fails with EINVAL ("Invalid argument"). Also: a process-global setting (one documented as static) is not part of any domain's policy and may not be addressed to a domain — the attempt fails with EINVAL ("Invalid argument") 💀
⭐ move
┌──────┬────────────┐
│ type │ string-vec │
└──────┴────────────┘
Declare automatic domain transitions — these are called moves — for a domain. Again, the "Domain Transitions" section of syd(7) has the model, bestie 🌈 The forms are:
move/@<to>/<kind>+<pattern> # Add a move.
move/@<to>/<kind>-<pattern> # Remove that move.
move/@<to>/<kind>^ # Remove all moves of this kind.
each optionally prefixed with @<from>/ to attach it to a domain other than default. While the sandbox is in the source domain, an allowed operation of kind <kind> whose target matches <pattern> switches the active domain to <to>. Ngl the move only takes effect when the operation is itself permitted — it never grants a denied operation 🙅♀️
The kinds and their patterns are:
exit:exit_group(2)of a process whose executable path matches theglob(3p)pattern <pattern>.exec:execve(2)/execveat(2)of a binary whose path matches theglob(3p)pattern <pattern>.chdir:chdir(2)/fchdir(2)to a directory whose path matches theglob(3p)pattern <pattern>.mmap: Executablemmap(2)of a file whose path matches theglob(3p)pattern <pattern>.bind:bind(2)to an address matching the address pattern <pattern>.connect:connect(2)or connectedsend(2)to an address matching the address pattern <pattern>.accept:accept(2)/accept4(2)of a peer matching the address pattern <pattern>.
Target domain <to> and any source domain <from> must already be defined, otherwise the call fails with EINVAL ("Invalid argument"). Examples are given below:
move/@net/exec+/usr/bin/curl # In the default domain, exec curl -> @net
@web/move/@db/connect+0.0.0.0/0!5432 # In @web, an allowed :5432 connect -> @db
Network patterns for bind, connect, and accept use the same syntax as the network sandboxing rules — go check the "ADDRESS MATCHING" section for more info 🎀
💅 cmd/exec
┌──────┬─────────┐
│ type │ command │
└──────┴─────────┘
Makes Syd execute an external command without sandboxing. Here's the rundown of the whole setup, bestie:
- The process is executed in a new process group, with its standard input attached to
/dev/null. - Standard output and standard error file descriptors are inherited.
- Syd also ensures no non-standard file descriptors leak into the new process, utilizing the
close_range(2)system call. - Current working directory is changed to the root directory, i.e.
/. - The
umask(2)is set to077.
The program name and arguments must be separated with the US (unit separator, hex: 0x1f, octal: 037) character. To ease usage, the syd-exec(1) helper utility is provided to construct a sandbox command of this type:
; syd -puser -mlock:exec -- sh -c 'test -c $(syd-exec echo hello world)'
hello world
;
🧚 cmd/move
┌──────┬─────────┐
│ type │ command │
└──────┴─────────┘
Move the sandbox to a named domain — this is the manual form of a domain transition 🦋 (Refer to the "Domain Transitions" section of the syd(7) manual page, as always.) The active domain is switched with cmd/move!<name>. The @ sigil is optional after the ! operator, so cmd/move!web and cmd/move!@web are equivalent, cute right? The named domain must be defined, otherwise the command fails with EINVAL ("Invalid argument") 😭
🍓 load
┌──────┬────────────────────────┐
│ type │ integer (fd) or string │
│ │ (profile-name) │
└──────┴────────────────────────┘
Read configuration from the given file descriptor — the file must be a regular file opened for reading. Syd uses pidfd_getfd(2) to acquire the file descriptor and reads sandbox configuration from it. This command is useful to load a set of sandbox commands into Syd in a single step, e.g:
int fd = open("/tmp", O_RDWR | O_TMPFILE | O_CLOEXEC, 0);
if (fd == -1) errx(1, "Failed to open temporary file");
const char *syd = "sandbox:stat/on\nallow/stat+/***\ndeny/stat+/\nlock:on\n";
errx(write(fd, syd, strlen(syd)) == -1, "Failed to write config");
errx(lseek(fd, 0, SEEK_SET) == -1, "Failed to seek in file");
char load[64];
sprintf(load, "/dev/syd/load/%d", fd);
errx(stat(load, NULL) == -1, "Failed to load Syd profile");
errx(execvp("/bin/sh", (char *[]){"/bin/sh", "-l", NULL}) == -1, "execvp failed");
⚠️ Due to security reasons, this command is only available via the virtualstat(2)call — it may not be used with the-mcommand line switch or in a configuration file. Fr fr, don't try it 💀
As of version 3.30.0, this command may be used to load builtin profiles — Syd falls back to parsing the load argument as a profile name if parsing the argument as a file descriptor fails ✨
👑 trace/allow_safe_setuid
┌────────┬─────────┐
│ type │ boolean │
├────────┼─────────┤
│ static │ yes │
└────────┴─────────┘
...and this is literally the whole point: it turns on SafeSetID and keeps the Linux capability CAP_SETUID 💅. Bestie, this option is implied at startup if any UID transits were defined with the setuid command — so it just shows up like "hey, I'm here" ✨. This feature is what lets Syd change UID at the same time as the sandbox process 🔥. And because NPTL uses reserved signals to make sure all threads share the same UID/GID, setting this option disables the SROP mitigator — okay so noted, that's the tradeoff 😭. Go read the "Enhanced Execution Control (EEC)" section of the syd(7) manual page for the full lore 🫶.
trace/allow_safe_setgid
- type:
boolean - static:
yes
Same energy, GID edition 🎀. Enables SafeSetID and retains the Linux capability CAP_SETGID. This one is implied at startup if any GID transits were defined with the setuid command. It lets Syd change GID simultaneously with the sandbox process 🦋. And yep — because NPTL uses reserved signals to guarantee all threads share the same UID/GID, setting this option disables the SROP mitigator. Refer to the "Enhanced Execution Control (EEC)" section of the syd(7) manual page for more info, iykyk 💖.
setuid
- type:
[(uid_t, uid_t)] - static:
yes
Add a UID transition, remove one, or reset the whole UID transitions situation 💅. Heads up: this accepts only numeric user IDs, not user names — no nobody, just numbers, fr fr. Only a single transition from a source UID can be defined. And the target UID can not be less than or equal to the build default 11, which is typically the operator user 👑. Defining a UID transit with this option implies trace/allow_safe_setuid:true.
Usage:
setuid+0:65534 # Define a UID transition from root to nobody.
setuid-0:65534 # Remove a previously defined UID transition.
setuid^0 # Remove all UID transitions matching source UID.
setuid^ # Remove all UID transitions.
setgid
- type:
[(gid_t, gid_t)] - static:
yes
The GID twin of the above 🌸. Add a GID transition, remove one, or reset them all. Again: only numeric group IDs, not group names. Only a single transition from a source GID can be defined. Target GID can not be less than or equal to the build default 14, which is typically the uucp group. Defining a GID transit with this option implies trace/allow_safe_setgid:true 😌.
Usage:
setgid+0:65534 # Define a GID transition from root to nogroup.
setgid-0:65534 # Remove a previously defined GID transition.
setgid^0 # Remove all GID transitions matching source GID.
setgid^ # Remove all GID transitions.
trace/allow_unsafe_setid
- type:
boolean - static:
yes
A boolean that decides whether Syd denies with EPERM ("Operation not permitted") instead of terminating two very specific things 😬:
setuid(2)changes with target UID less than or equal to the build default11, which is typically the operator user.setgid(2)changes with target GID less than or equal to the build default14, which is typically theuucpgroup.
So like: deny politely vs. straight up end you. Choose wisely 💀.
trace/allow_unsafe_cbpf
- type:
boolean - static:
yes
Okay so this one's a whole essay, buckle up 🎢. A boolean specifying whether Syd should allow additional seccomp(2) cbpf filters to be installed by sandbox processes. By default this is denied, to mitigate confused deputy problems, and errno(3) is set to EINVAL ("Invalid argument") for compatibility reasons.
And here's the duality of man 😭: on the one hand, stacked seccomp(2) cbpf filters allow for incremental confinement and therefore added hardening — slay ✨. On the other hand, they may be abused to install system call filters with more precedent actions than user-notify, thereby bypassing Syd's own seccomp(2) cbpf filters — not slay 🙈.
To quote the seccomp_unotify(2): "... a user-space notifier can be bypassed if the existing filters allow the use of seccomp(2) or prctl(2) to install a filter that returns an action value with a higher precedence than SECCOMP_RET_USER_NOTIF (see seccomp(2))."
Setting the option trace/allow_unsafe_prctl:true overrides this option and allows the PR_SET_SECCOMP prctl(2) operation inside the sandbox 👀. This may be changed in the future for clearer separation of mitigations — so like, watch this space 💫.
trace/allow_unsafe_cbpf_speculative
- type:
boolean - static:
yes - trusted:
yes
Disables Speculative Store Bypass mitigations for seccomp(2) filters 😤.
This option has been split from trace/allow_unsafe_exec_speculative as of version 3.58.0 for clear separation of mitigations — glow-up era 💅.
This option requires the trusted Cargo feature to take effect. Without the feature, setting this option to true is ignored with a warning 🚨.
trace/allow_unsafe_ebpf
- type:
boolean - static:
yes
Allows direct eBPF use inside the Syd sandbox using the bpf(2) system call, whose unprivileged use is permitted since Linux-4.4. On the one hand, eBPF programs can be used for additional hardening 🌈. On the other hand, eBPF is a frequent source of vulnerabilities due to churn, complexity, improper validation and complexity of validation 😭. eBPF may also be abused to implement efficient and portable rootkits 💀 — yeah, that part.
As of version 3.37.0, Syd drops the capability CAP_BPF and denies the privileged bpf(2) commands BPF_MAP_CREATE, BPF_PROG_LOAD, and BPF_BTF_LOAD with the errno(3) EPERM ("Operation not permitted") regardless of the value of this option 👑. This is in consistence with the Linux kernel checks for the kernel.unprivileged_bpf_disabled sysctl(8). Consult the bpf(2) and capabilities(7) manual pages for more information about the CAP_BPF Linux capability, which is implemented in Linux-5.8 or newer ✨.
trace/allow_unsafe_dumpable
- type:
boolean - static:
yes - trusted:
yes
A boolean specifying whether Syd should skip setting its process dumpable attribute to false 🙈. This allows core dumps for the Syd process, and allows debugging/profiling/tracing the Syd process.
You should not set this option unless you're developing Syd, bestie. Like, respectfully, this is dev-only territory 🚧.
trace/allow_unsafe_exec_ldso
- type:
boolean - static:
yes
A boolean specifying whether ld.so(8) exec indirection should be allowed. This is not allowed by default, to harden noexec boundaries 🛡️.
trace/allow_unsafe_exec_libc
- type:
boolean - static:
yes
A boolean specifying whether turning on secure-execution mode for libc should be skipped 😳. Refer to the "Enforcing AT_SECURE and UID/GID Verification" section of the syd(7) manual page for more information 🫶.
trace/allow_unsafe_exec_memory
- type:
boolean - static:
yes
Specify whether the Memory-Deny-Write-Execute (MDWE) protections should be bypassed 🧠. Refer to the "Memory-Deny-Write-Execute Protections" section of the syd(7) manual page for more information ✨.
trace/allow_unsafe_exec_nopie
- type:
boolean - static:
yes
A boolean specifying whether execution of non-PIE binaries should be allowed. This is generally not recommended 😬, but may be necessary on some systems. Refer to the "Enforcing Position-Independent Executables (PIE)" section of the syd(7) manual page for more information 🎀.
trace/allow_unsafe_exec_noreg
- type:
boolean - static:
yes
A boolean specifying whether executable memory mapping of non-regular files should be allowed. By default Syd blocks executable mappings backed by non-regular files, consistent with WhiteEgret 🦢. This is generally not recommended, but may be necessary on some systems — so like, your call 🔥.
trace/allow_unsafe_exec_null
- type:
boolean - static:
yes
A boolean specifying whether exec calls with NULL argument and environment pointers should be allowed 👀. Refer to the "Enhanced execve and execveat Syscall Validation" section of the syd(7) manual page for more information 💜.
trace/allow_unsafe_exec_stack
- type:
boolean - static:
yes
(type/static vibes only for now — the story continues 💅)
...a boolean that says whether running binaries with an executable stack is allowed. Basically not recommended, but sometimes a system just needs it — you know how it is. More deets in the "Enforcing Non-Executable Stack" section of the syd(7) manual page. 🫶
trace/allow_unsafe_exec_textrel
- type:
boolean - static:
yes
A boolean that says whether running binaries that have text relocations is allowed. Bestie, text relocations need writable code segments and they defeat address space layout randomization, which is giving "villain arc". Generally not recommended, but may be necessary on some systems. 🙈
trace/allow_unsafe_exec_script
- type:
boolean - static:
yes
This one is the opt-out for file vetting on interpreted exec. When it's off (the default) on Linux 6.14 and newer, Syd sets SECBIT_EXEC_RESTRICT_FILE and SECBIT_EXEC_RESTRICT_FILE_LOCKED at startup, so interpreters and dynamic linkers must only execute a file if execveat(2) with the AT_EXECVE_CHECK flag on its file descriptor would succeed — and the FD-based check avoids TOCTOU, periodt. When it's on, Syd does not set these bits (legacy behavior, iykyk). No-op on kernels < 6.14. The bits are unprivileged-settable. The locks make the policy sticky across exec. Peep the "Securebits and Kernel-Assisted Executability" section of the syd(7) manual page for more. ✨
trace/allow_unsafe_exec_interactive
- type:
boolean - static:
yes
The opt-out for interactive snippet denial. When it's off (the default) on Linux 6.14 and newer, Syd sets SECBIT_EXEC_DENY_INTERACTIVE plus its lock, so interpreters refuse interactive code (-e, -c, REPL, etc.) unless the content arrives via a file descriptor and passes AT_EXECVE_CHECK of execveat(2). When it's on, Syd doesn't set these bits (legacy behavior). No-op on kernels < 6.14. Bits are unprivileged-settable. Locks persist the policy across exec. Check the "Securebits and Kernel-Assisted Executability" section of the syd(7) manual page for the full tea. 💅
trace/allow_unsafe_exec_speculative
- type:
boolean - static:
yes
A boolean saying whether speculation controls should not be set to enable Speculative Execution mitigations via the prctl(2) interface at startup. When this option is enabled, the prctl(2) operations PR_GET_SPECULATION_CTRL and PR_SET_SPECULATION_CTRL are allowed inside the sandbox. The "Speculative Execution Mitigation" section of the syd(7) manual page has more. 🌸
trace/allow_unsafe_kptr
- type:
boolean - static:
yes
A boolean saying whether syscall arguments that take pointers should NOT be checked for kernel pointers. Yes, that's the whole vibe. See the "Hardening against kernel pointer misuse" section of the syd(7) manual page for more. 🔥
trace/allow_unsafe_sys_ptrace
- type:
boolean - static:
yes - trusted:
yes
A boolean saying whether the Linux capability CAP_SYS_PTRACE should be passed to the sandbox process. By default this capability is not inherited by sandboxed processes — and note that Syd itself always retains CAP_SYS_PTRACE for internal use (e.g. pidfd_getfd(2)). This option only controls whether the capability is also made available inside the sandbox. 🧚
⚠️ Warning bestie: this option lets the sandbox process trivially break out of the sandbox, like by attaching to the Syd main thread withptrace(2)and getting a handle to theseccomp(2)notify file descriptor. So only use this in trusted environments, fr fr. 💀
As of version 3.55.0, this option is split from trace/allow_unsafe_ptrace so you get fine-grained control over capability inheritance, independently of ptrace(2) handler configuration. 👑
This option requires the trusted Cargo feature to take effect. Without the feature, setting this option to true is ignored with a warning. 😭
trace/allow_unsafe_ptrace
- type:
boolean - static:
yes - trusted:
yes
A boolean to disable Syd's use of ptrace(2). Setting this to true effectively removes the ptrace(2) dependency from the sandbox. This is necessary to trace Syd together with its children, e.g. with strace -f. 🦋
This option also disables or changes these features that depend on Syd's use of ptrace(2):
- Force sandboxing is disabled — because it depends on
PTRACE_EVENT_EXEC. - SegvGuard is disabled — because it depends on
PTRACE_EVENT_EXIT. - Domain transitions with kind
exitare disabled — because they depend onPTRACE_EVENT_EXIT. - The
chdir(2),fchdir(2),execve(2),execveat(2),mmap(2), andmmap2(2)handlers switch toseccomp_unotify(2)versions which do not have TOCTOU protections at syscall exit. Lowkey scary: a sandbox process may trivially change to an arbitrary directory, execute an arbitrary file, or map an arbitrary library using the well-known race conditions in system call wrappers that continue the system call in the sandbox process. 😭
As of version 3.50.0, this option requires the trusted Cargo feature to take effect. Without the feature, setting this option to true is ignored with a warning.
trace/allow_unsafe_perf
- type:
boolean - static:
yes
A boolean saying whether perf calls should be allowed within the sandbox. 🎀
As of version 3.40.0, the prctl(2) operations PR_TASK_PERF_EVENTS_ENABLE and PR_TASK_PERF_EVENTS_DISABLE are also allowed if this option is set at startup. ✨
trace/allow_unsafe_create
- type:
boolean - drop:
yes, off only
A boolean saying whether to allow unsafe file creation. Go read the "Trusted File Creation" section of the syd(7) manual page for the lore. 🌈
trace/allow_unsafe_deleted
- type:
boolean - drop:
yes, off only
A boolean saying whether access to deleted files — through open file descriptors and proc_pid_fd(5) magic links — should be allowed. The "Deleted File Access Mediation" section of the syd(7) manual page spills the rest. 🍓
trace/allow_unsafe_fcntl
- type:
boolean - static:
yes
A boolean saying whether fcntl(2) restrictions should be lifted. When it's disabled (the default), Syd denies F_SETOWN, F_SETOWN_EX, and F_SETSIG operations to prevent bypassing landlock(7) signal scoping via the SIGIO delivery path. Want the full list of denied fcntl(2) operations? Run syd-ls fcntl, she ate. And peep the "Restricting fcntl operations and trace/allow_unsafe_fcntl" section of the syd(7) manual page for more. 💜
trace/allow_unsafe_filename
- type:
boolean - drop:
yes, off only
A boolean saying whether the restrictions on file names should be lifted. By default, file names with control characters, forbidden characters, or invalid UTF-8 are denied with EINVAL as necessary. Read Enhanced Path Integrity Measures of the syd(7) manual page for more. 🌸
trace/allow_unsafe_futex
- type:
boolean - static:
yes
A boolean saying whether priority-inheritance futex(2) operations should be allowed. When it's disabled (the default), Syd denies FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI, FUTEX_UNLOCK_PI, FUTEX_WAIT_REQUEUE_PI and FUTEX_CMP_REQUEUE_PI — literally every operation gated by the CONFIG_FUTEX_PI configuration option in the Linux kernel — with ENOSYS ("Function not implemented"). 💅
trace/allow_unsafe_hardlinks
- type:
boolean - drop:
yes, off only
A boolean saying whether to allow unsafe hardlink targets. The "Trusted Hardlinks" section of the syd(7) manual page is where the details live. 🫶
trace/allow_unsafe_machine_id
- type:
boolean - drop:
yes, off only
Okay so bestie, this one is literally about your machine identity ✨ — like, do we let the sandbox show its real one or do we give it a whole fake persona? It controls whether Syd substitutes machine-id(5) with a synthetic, per-Syd random identifier, or just exposes the host value. The swap happens at the open(2) boundary, after the access checks have already granted access to this file. And it's not just machine-id(5) — /etc/hostid and /var/adm/hostid (both part of the gethostid(3) interface of POSIX.1-2008) plus /sys/class/dmi/id/product_uuid also get substituted as part of this mitigation 🎀.
When it's false (that's the default, btw), at startup Syd computes a SHA3-512 digest using AT_RANDOM bytes and formats the result as a 128-character lowercase hexadecimal sandbox ID. The first 32 characters of that string (which must not be all zeroes, no cap) get presented in place of machine-id(5), /etc/hostid, /var/adm/hostid, and /sys/class/dmi/id/product_uuid — all to limit information leakage 🔒.
You can override the sandbox ID yourself by setting the SYD_ID environment variable to a 128-character lowercase hexadecimal string that satisfies the same non-all-zero 32-character prefix constraint 💅.
When it's true, no substitution happens at all and the real system machine-id(5), /etc/hostid, /var/adm/hostid, and /sys/class/dmi/id/product_uuid files are made visible to the sandbox process — i.e. the mitigation is straight-up disabled 💀.
Refer to these links for more info:
- https://man7.org/linux/man-pages/man5/machine-id.5.html
- https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostid.html
trace/allow_unsafe_proc_dumpable
- type:
boolean - static:
yes
This one specifies whether the PR_SET_DUMPABLE prctl(2) operation with the dumpable argument set to SUID_DUMP_DISABLE should be turned into a noop, for compatibility reasons 🫶. You might lowkey need this to sandbox programs such as sftp(1), which use this operation to make their per-process proc(5) directory inaccessible to other programs — including Syd herself, iconic behaviour honestly 🙈.
trace/allow_unsafe_proc_files
- type:
boolean - static:
yes
Specifies whether the internal procfs(5) should NOT be mounted with the option subset=pid. Heads up though — this option is a no-op unless unshare/pid:true is also set 🌸.
trace/allow_unsafe_proc_name
- type:
boolean - static:
yes
Specifies whether the PR_SET_NAME prctl(2) operation — the one that changes the process name — should be allowed 🏷️. By default this operation is logged and denied with a success return, so from the sandbox process's perspective the call totally succeeds, but no process name change actually takes place. Gaslighting, but make it security 💅.
trace/allow_unsafe_proc_pid_status
- type:
boolean - drop:
yes, off only
A boolean specifying whether masking security-sensitive fields in proc_pid_status(5) files should be disabled 🙈. Refer to the "Hardening proc_pid_status(5)" section of the syd(7) manual page for more information.
trace/allow_unsafe_magiclinks
- type:
boolean - static:
yes
A boolean specifying whether /proc magic links should be followed even when the per-process directory id differs from the caller process id 🪄. Magic links are symbolic link-like objects that are most notably found in proc(5) — examples include /proc/pid/exe and /proc/pid/fd/*. Refer to the symlink(7) manual page for more details.
Unknowingly opening magic links can be risky for some applications, and examples of such risks include:
- If the process opening a pathname is a controlling process that currently has no controlling terminal (see
credentials(7)), then opening a magic link inside/proc/pid/fdthat happens to refer to a terminal would cause the process to acquire a controlling terminal 😭. - In a containerized environment, a magic link inside
/procmay refer to an object outside the container, and thus may provide a means to escape from the container 💀.
Because of such risks, Syd denies access to magic links which do not belong to the current process by default 👑.
trace/allow_unsafe_symlinks
- type:
boolean - drop:
yes, off only
A boolean specifying whether to allow following symlinks in untrusted directories 🦋. Untrusted directories are either group-writable, world-writable, or have the sticky-bit set. Refer to the "Trusted Symbolic Links" section of the syd(7) manual page for more information.
trace/allow_unsafe_sticky
- type:
boolean - drop:
yes, off only
A boolean specifying whether immutable setgid/sticky bit enforcement for directories should be disabled 🎀. By default, Syd preserves both the setgid and sticky bits on directories at the chmod(2) boundary: if a directory already has the setgid bit or sticky bit set, any chmod(2) call that would remove either bit is corrected to retain the bit(s), with a warning ⚠️.
Setting this option to true disables this enforcement, allowing both bits to be freely toggled. Refer to the "Immutable Sticky Bit" section of the syd(7) manual page for more information.
trace/allow_unsafe_namespace
- type:
string-vec - static:
yes
A list of namespaces to allow creation under the sandbox 🌈. It must be a comma-separated list of mount, uts, ipc, user, pid, net, cgroup and time. The special value all is supported as a placeholder to specify all namespaces ✨. The special values none and off are supported to reset to no namespaces.
An invocation of this command overrides all previous invocations — i.e. only the list of subnamespaces in the last invocation of this command will be allowed. By default, subnamespace creation is not allowed 🚫.
As of version 3.35.2, the system calls sethostname(2) and setdomainname(2) are only allowed in the sandbox if the uts subnamespace is allowed. This is similar to the mount family system calls, which are only allowed if the mount subnamespace is allowed.
trace/allow_unsafe_netlink
- type:
boolean - drop:
yes, off only
A boolean specifying whether the sandbox process may receive asynchronous kernel notifications on netlink(7) sockets 📡. When disabled (that's the default), Syd attaches a locked socket filter at the socket(2) boundary which discards received messages bearing a zero nlmsg_pid. Such messages originate from the kernel and report host network state changes and audit(8) records.
Their delivery to a confined process serves no purpose and exposes host activity useful for reconnaissance 🔍. Requests, replies and dumps are retained, so the process may still query the kernel. Applications which monitor netlink(7) events require this option 🫶.
trace/allow_unsafe_nice
- type:
boolean - static:
yes
A boolean specifying whether process and I/O priority changes are allowed for the sandbox ⚡. Refer to the "Process Priority and Resource Management" section of the syd(7) manual page for more information.
trace/allow_unsafe_nocookie
- type:
boolean - static:
yes
A boolean specifying whether enforcement of syscall argument cookies should be disabled 🍓. Refer to the "Syscall Argument Cookies" section of the syd(7) manual page for more information.
trace/allow_unsafe_nomseal
- type:
boolean - static:
yes
A boolean specifying whether read-only sealing of critical regions of the Syd sandbox policy using mseal(2) — you know, when the sandbox is locked — should be disabled 🔐. Refer to the "Memory Sealing of Sandbox Policy Regions on Lock" section of the syd(7) manual page for more information.
trace/allow_unsafe_noxom
- type:
boolean - static:
yes
A boolean specifying whether the hardening that enforces immutability and Execute-Only Memory (XOM) protections on Syd's own executable mappings during initialization should be disabled 👑. Refer to the "Hardening executable mappings" section of the syd(7) manual page for more information.
trace/allow_unsafe_recvmsg
- type:
boolean - static:
yes
A boolean specifying whether hooking the system calls recvmsg(2) and recvmmsg(2) should be disabled. Syd handles these system calls only to do credentials management for SCM_CREDENTIALS — no sandboxing happens for them, periodt. Disabling these hooks is literally necessary to receive O_PATH file descriptors with SCM_RIGHTS. iykyk 💅
🎀 trace/allow_unsafe_chown
type: booleanstatic: yes
Makes Syd keep the capability CAP_CHOWN and lets the sandbox process chown(2) files to any uid/gid it wants. Without this option, the chown(2) family calls are confined to the caller's own effective UID and GID — so attempts to change ownership to a different uid or gid get denied with EPERM ("Operation not permitted"). No sneaky ownership steals, bestie 🚫✨
🎀 trace/allow_unsafe_chroot
type: booleanstatic: yes
Disables Chroot sandboxing and turns the chroot(2) system call into a straight-up no-op. For the whole explanation of the chroot sandbox category, go read the "SANDBOXING" section of the syd(7) manual page 🦋
🎀 trace/allow_unsafe_pivot_root
type: booleanstatic: yes
Turns the pivot_root(2) system call into a no-op instead of unconditionally denying it with the errno(3) EPERM. Again, the deets on the chroot sandbox category live in the "SANDBOXING" section of syd(7) 💜
🎀 trace/allow_unsafe_copy
type: booleanstatic: yes
Allows the MSG_COPY flag for msgrcv(2). That's it, that's the whole vibe 🌸
🎀 trace/allow_unsafe_oob
type: booleanstatic: yes
Allows the MSG_OOB flag for the send(2), sendto(2), sendmsg(2), sendmmsg(2), recv(2), recvfrom(2), recvmsg(2), and recvmmsg(2) system calls so out-of-band data can be sent and received — and also allows the setsockopt(2) option SO_OOBINLINE, which selects the inline urgent data delivery path. The full tea is in the "Denying MSG_OOB Flag in send/recv System Calls" section of syd(7) 🫶
🎀 trace/allow_unsafe_open_kfd
type: booleandrop: yes, off only
A boolean specifying whether open(2) calls to AMD KFD character devices should be continued in the sandbox process rather than opening them in the Syd emulator thread and sending the file descriptor over. Here's the problem, bestie: the /dev/kfd character device needs per-application access to the GPU device, so opening the device in the Syd emulator thread and then continuing the subsequent ioctl(2) system calls in the sandbox process is going to return EBADF ("Bad file number"). Until Syd has a way to fully emulate the ioctl(2) request space and can call the ioctl(2) system call directly from Syd emulator threads, this option may be used to access such character devices.
💀 Ok but fr fr — read this warning: setting this option opens a TOCTOU attack vector, whereby the sandbox process can open an arbitrary file instead of the character device in question! Not a slay.
Syd applies the following mitigations to limit the scope of the attack vector:
- Syd continues the system call if and only if
O_RDWRis set in theflagsargument. - Syd does not continue the system call if at least one of the flags
O_CREAT,O_TRUNCorO_TMPFILEis set in theflagsargument. - Syd returns
ENOSYS("Function not implemented") for theopenat2(2)system call rather than continuing it in the sandbox process, to prevent thestruct open_howpointer indirection from bypassing the restrictions applied to theflagsargument. Refer to theopenat2(2)manual page for more information. - This option may be changed at runtime, and it is highly recommended to unset this option using the
syd(2)virtual system call API right after the character device is opened. Like, immediately. 👀
🎀 trace/allow_unsafe_open_path
type: booleandrop: yes, off only
A boolean specifying whether the mitigation that turns O_PATH file descriptors into O_RDONLY file descriptors for safe emulation should be disabled. With this option, Syd continues the open(2) system calls with the O_PATH in the sandbox process — which, yeah, opens a TOCTOU vector. You've been warned 😭
🎀 trace/allow_unsafe_open_suid
type: booleandrop: yes, off only
A boolean specifying whether the mitigation that prevents opening files with set-user-ID or set-group-ID mode bits should be disabled. With this option, Syd allows opening SUID/SGID files regardless of mode. When it's disabled, opening SUID/SGID files with write modes returns the EACCES ("Permission denied") errno(3). Not the vibe, but okay ✨
🎀 trace/allow_unsafe_mkbdev
type: booleanstatic: yes
Specifies whether unsafe block device access should be allowed. When set, Syd does not drop the capability CAP_MKNOD on startup for itself — but it's still dropped for the sandbox process. Which allows:
- block device creation with
mknod(2). - opening block devices with
open(2). - listing block devices with
getdents64(2).
🎀 trace/allow_unsafe_mkcdev
type: booleanstatic: yes
Specifies whether unsafe character device creation should be allowed. When set, Syd does not drop the capability CAP_MKNOD on startup for itself — but it's still dropped for the sandbox process. This allows creation of character devices with mknod(2). 🧚
🎀 trace/allow_unsafe_stat_bdev
type: booleandrop: yes, off onlystatic: yes
Specifies whether stat(2) family calls on block devices should return last access and modification times as-is. Check the "Device Sidechannel Mitigations" section of the syd(7) manual page for the full backstory 👑
🎀 trace/allow_unsafe_stat_cdev
type: booleandrop: yes, off onlystatic: yes
Specifies whether stat(2) family calls on character devices should return last access and modification times as-is. Same deal — "Device Sidechannel Mitigations" in syd(7) has all the lore 🌈
🎀 trace/allow_unsafe_notify_bdev
type: booleandrop: yes, off onlystatic: yes
Specifies whether unsafe event generation for the fanotify_mark(2) and inotify_add_watch(2) system calls should be allowed for block devices. Yep, "Device Sidechannel Mitigations" section of syd(7) — go read it 🍓
🎀 trace/allow_unsafe_notify_cdev
type: booleandrop: yes, off onlystatic: yes
Specifies whether unsafe event generation for the fanotify_mark(2) and inotify_add_watch(2) system calls should be allowed for character devices. Again: "Device Sidechannel Mitigations", syd(7). I'm not repeating myself, the manpage is 💅
🎀 trace/allow_unsafe_cpu
type: booleanstatic: yes
Specifies whether CPU emulation system calls should be allowed. By default, as of version 3.22.1, Syd denies the modify_ldt(2), subpage_prot(2), switch_endian(2), vm86(2), and vm86old(2) system calls, which are all associated with CPU emulation functionalities. Enabling this option (trace/allow_unsafe_cpu:1) permits these calls, thus relaxing the restriction.
⚠️ This option should be used with caution, because allowing these system calls can introduce potential vulnerabilities by enabling processes to modify CPU state or memory protections. Use this setting only in trusted environments where the execution of these system calls is necessary. Main character energy, but the risky kind 😭🔥
trace/allow_unsafe_deprecated
- type:
boolean - static:
yes
okay so this one is literally the "grandma syscalls" switch 💀 — it controls whether deprecated system calls like remap_file_pages(2), stime(2), and uselib(2) are allowed to happen. Wanna see the full roster of deprecated syscalls for your particular install? Run syd-ls deprecated and it'll spill the whole list. no cap.
trace/allow_unsafe_kcapi
- type:
boolean - static:
yes
This bad boy decides whether access to the Linux kernel cryptography API (aka "KCAPI") is allowed while network sandboxing is on. Important: this option has no effect when network sandboxing is off — like, none, zero, nada. 🙈
And bestie, listen — as most things in life, cryptography has good and evil uses. KCAPI is convenient because you can implement cryptography without depending on user-space libraries such as OpenSSL… but it can also let malicious code efficiently turn itself into ransomware. 😭 KCAPI is also vulnerable to attacks such as CVE-2026-31431 aka copy.fail. So, adhering to the goal of being secure by default, Syd disallows this access by default. periodt.
Fun technical fact: Syd does not hook into setsockopt(2), and the ALG_SET_KEY operation to set the encryption key is directly handled by the host kernel — therefore the encryption key is not copied into Syd's address space. 🫶
Syd hooks into bind(2), sendto(2), sendmsg(2), and sendmmsg(2) but not read(2), write(2), recv(2), or splice(2). So to reduce syscall overhead, the user is recommended to use the unhooked system calls when they can to interact with KCAPI. efficiency slay ✨
trace/allow_unsafe_kcmp
- type:
boolean - static:
yes
A boolean specifying whether the kcmp(2) system call should be allowed inside the sandbox. Heads up though — this call may be unsafe if it's directed to a process which the sandbox process does not own. 👀 so like, respect the boundaries bestie.
trace/allow_unsafe_keyring
- type:
boolean - static:
yes
Specify whether the add_key(2), keyctl(2), and request_key(2) system calls should be allowed. Turning this on permits key management within the sandbox, which can introduce security risks by allowing keyring manipulations. Use only in trusted environments. 🔐 that's the whole vibe — trusted environments only, bestie, don't be out here enabling this in the wild.
trace/allow_unsafe_pipe
- type:
boolean - static:
yes
Allow creating notification pipes using the O_NOTIFICATION_PIPE flag to the pipe2(2) system call. When disabled, such calls fail with ENOPKG ("Package not installed") — exactly like on a Linux kernel built without CONFIG_WATCH_QUEUE. 💅
Refer to the "Denying O_NOTIFICATION_PIPE Flag in pipe2" section of the syd(7) manual page for more info. This option was added as of version 3.58.0. 🎀
trace/allow_unsafe_pkey
- type:
boolean - static:
yes
Specifies whether the pkey_alloc(2), pkey_free(2), and pkey_mprotect(2) system calls should be allowed. By default, these calls are denied to enhance security. Setting this option to true enables these system calls, allowing the use of memory protection keys. 🗝️
This option should be used with caution and only in trusted environments where the use of these system calls is necessary. fr fr, be careful.
trace/allow_unsafe_madvise
- type:
boolean - static:
yes
Specifies whether the madvise(2) system call should NOT be hardened. By default, only a subset of advice is permitted — run syd-ls madvise to see which ones. Refer to the "madvise(2) Hardening" section of the syd(7) manual page for more info. 🧚
trace/allow_unsafe_numa
- type:
boolean - static:
yes
Specifies whether the NUMA memory-policy and page-migration system calls should be allowed. Wanna see the list of NUMA system calls? Check the output of syd-ls numa. By default these system calls are denied. 🚫
trace/allow_unsafe_page_cache
- type:
boolean - static:
yes
Specifies whether the cachestat(2) and mincore(2) system calls should be allowed. By default, these calls are denied to enhance security — because it's been documented that they can be misused to perform page-cache attacks. 😱
Refer to the "Mitigation against Page Cache Attacks" section of the syd(7) manual page for more info. 💜
trace/allow_unsafe_setsockopt
- type:
boolean - static:
yes
Specifies whether setsockopt(2) hardening should be disabled. Refer to the "setsockopt(2) Hardening" section of the syd(7) manual page for more info. that's it, that's the whole option 🫶
trace/allow_unsafe_socketcall
- type:
boolean - static:
yes
Specifies whether the socketcall(2) multiplexer system call should be allowed. ⚠️ Enabling this option gives the sandbox process a way to bypass "setsockopt(2) Hardening" on architectures x86, m68k, mips, mipsel, ppc, ppc64, ppc64le, s390, s390x, sheb, and sh. so like… know what you're doing, bestie.
This is only necessary for compatibility with libcs which don't yet support the newer non-multiplexed system calls available as of Linux-4.3. The receipts: GNU Libc added support for new system calls as of version 2.23 and removed support for the old socketcall(2) fallback as version 2.37. musl added support for new system calls as of version 1.2.2. 📜✨
trace/allow_unsafe_tcp_devmem
- type:
boolean - static:
yes
Specify whether devmem TCP should be allowed. This covers the SO_DEVMEM_* socket(2) options and the MSG_SOCK_DEVMEM recv(2) flag — both of which Syd denies with EOPNOTSUPP ("Operation not supported") by default. 🙅♀️
trace/allow_unsafe_tcp_fastopen
- type:
boolean - static:
yes
Specify whether TCP Fast Open should be allowed. This covers the TCP_FASTOPEN socket(2) options and the MSG_FASTOPEN send(2) flag — both of which Syd denies with EOPNOTSUPP ("Operation not supported") by default. ⚡ same energy as the one above, just fastopen flavored.
trace/allow_unsafe_time
- type:
boolean - static:
yes
A boolean specifying whether system calls which adjust the system time are allowed. This also causes Syd to keep the CAP_SYS_TIME capability. ⏰ Use syd-ls time to see the list of system calls allowed by this setting.
trace/allow_unsafe_xattr
- type:
boolean - drop:
yes, off only - static:
yes
A boolean specifying whether the extended attributes restrictions on security, system, and trusted namespaces should be lifted. If this option is not set, only sandbox processes with access to the sandbox lock can view or change these extended attribute namespaces. 🔒 iykyk.
trace/allow_unsafe_caps
- type:
boolean - static:
yes - trusted:
yes
A boolean specifying whether Syd should skip dropping Linux capabilities(7) at startup. This setting can be used to construct privileged containers and should be used with extreme care. 👑⚠️ like, extreme extreme.
As of version 3.50.0, this option requires the trusted Cargo feature to take effect. Without the feature, setting this option to true is ignored with a warning. 🙈
As of version 3.57.0, the following capabilities(7) are dropped regardless of the value of this option: CAP_AUDIT_CONTROL, CAP_BLOCK_SUSPEND, CAP_BPF, CAP_CHECKPOINT_RESTORE, CAP_IPC_OWNER, CAP_KILL, CAP_MAC_ADMIN, CAP_MAC_OVERRIDE, CAP_NET_ADMIN, CAP_SYS_ADMIN, CAP_SYS_BOOT, CAP_SYS_MODULE, CAP_SYS_PACCT, CAP_SYS_RAWIO, CAP_SYS_RESOURCE, CAP_SYS_TTY_CONFIG, and CAP_WAKE_ALARM. 🔥 no takebacks on those.
trace/allow_unsafe_cap_fixup
- type:
boolean - static:
yes
okay so this next one is the actual opposite of a glow-up — it's about turning the hardened behaviour OFF 💅 Opt out of hardened UID/capability transitions. When off (default), Syd clears SECBIT_KEEP_CAPS and SECBIT_NO_SETUID_FIXUP and applies their lock bits at startup so capabilities are dropped when all UIDs become nonzero. When on, Syd leaves SECBIT_KEEP_CAPS and SECBIT_NO_SETUID_FIXUP (and their locks) as inherited from the parent, preserving the Linux' traditional "setuid fixup" behavior and any PR_SET_KEEPCAPS use by the application (legacy behavior). This option is a no-op on kernels that do not support securebits. Refer to the "Securebits and Kernel-Assisted Executability" section of the syd(7) manual page for more information.
trace/allow_unsafe_env
- type:
boolean - static:
yes
Specify whether unsafe environment variables should be allowed into the environment of the sandbox process. Refer to the output of syd-ls env for the list of unsafe environment variables. (iykyk — go run it, bestie, the list is right there ✨)
trace/allow_safe_syslog
- type:
boolean - static:
yes
Specify whether unprivileged sandbox processes can access Syd's syslog(2) emulation using dmesg(8). Unprivileged processes include the set of all sandbox processes with the sandbox lock "off", and all but the initial sandbox process with the sandbox lock set to "exec". This option has nothing to do with access to the host syslog which is never allowed. 🚫
trace/allow_safe_bind
- type:
boolean - static:
yes
Specify whether the socket address arguments of successful bind(2) calls should be allowed for connect(2), sendto(2), sendmsg(2), and sendmmsg(2) system calls.
Note: these addresses are allowed globally and not per-process for usability reasons. Thus, for example, a process which forks to call bind(2) will have its address allowed for their parent as well. no cap, it's shared — the parent gets the vibe too 🙈
trace/allow_unsafe_bind
- type:
boolean - static:
yes
Specify whether the Linux capability CAP_NET_BIND_SERVICE, which allows a process to bind(2) to ports lower than 1024, should be retained. When this option is set, Syd keeps the capability on startup for itself, but it is still dropped for the sandbox process. 🔒
trace/allow_unsafe_any_addr
- type:
boolean - drop:
yes, off only
Specify whether connections to wildcard "any" addresses are allowed to cross bind(2), connect(2), sendto(2), sendmsg(2), and sendmmsg(2) boundaries unchanged. When unset (default), Syd rewrites IPv4 0.0.0.0 to 127.0.0.1 and IPv6 :: to ::1 at bind(2) to prevent unintended exposure on all interfaces, and denies connect(2), sendto(2), sendmsg(2), and sendmmsg(2) to wildcard addresses with the errno(3) ENETUNREACH ("Network is unreachable"), consistent with FreeBSD's net.inet.ip.connect_inaddr_wild sysctl. When set, both the rewrite and the rejection are disabled. (so like, setting this = letting the wildcard girlies through untouched 😭)
trace/allow_unsafe_ipv6_rthdr
- type:
boolean - drop:
yes, off only
Specify whether IPV6_RTHDR ancillary data on sendmsg(2) and sendmmsg(2) is allowed to cross the network sandbox unchanged, including RFC 2292 alias IPV6_2292RTHDR. When unset (default), Syd strips any such control message from message header before forwarding the call to Linux and logs the event. IPV6_RTHDR installs an IPv6 routing header on the outgoing packet. Type-2 routing header defined for Mobile IPv6 (RFC 6275) is unprivileged on Linux and doesn't require CAP_NET_RAW unlike IPV6_HOPOPTS, IPV6_DSTOPTS, and IPV6_RTHDRDSTOPTS. Linux rewrites the outgoing packet's destination address to the first segment of routing header, bypassing the address only access check that Syd performs on the syscall destination argument. 💀
trace/allow_unsafe_ipv6_scope
- type:
boolean - drop:
yes, off only
Specify whether a non-zero sin6_scope_id (RFC 4007 zone identifier) in the IPv6 socket address of bind(2), connect(2), sendto(2), sendmsg(2), and sendmmsg(2) is allowed to cross the network sandbox unchanged. When unset (default), Syd zeroes out sin6_scope_id before forwarding the call to Linux and logs the event. The address-only access check cannot match the egress interface encoded in sin6_scope_id, which Linux honours for link-scoped destinations (fe80::/10, ff02::/16). zone IDs are little sneaky gatekeepers fr
trace/allow_unsafe_ip_pktinfo
- type:
boolean - drop:
yes, off only - static:
yes
Specify whether IP_PKTINFO, IPV6_PKTINFO, and the RFC 2292 alias IPV6_2292PKTINFO ancillary data on sendmsg(2) and sendmmsg(2) are allowed to cross the network sandbox unchanged. When unset (default), Syd strips any such control message from message header before forwarding the call to Linux and logs the event. These control messages override the per-packet egress interface and source address, bypassing the address-only access check. When set, this option also allows the setsockopt(2) options IP_PKTINFO, IPV6_PKTINFO, IPV6_RECVPKTINFO, and IPV6_2292PKTINFO regardless of the value of the option trace/allow_unsafe_setsockopt. 🫶
trace/allow_unsafe_ip_retopts
- type:
boolean - drop:
yes, off only
Specify whether IP_RETOPTS ancillary data on sendmsg(2) and sendmmsg(2) is allowed to cross the network sandbox unchanged. When unset (default), Syd strips any such control message from message header before forwarding the call to Linux and logs the event. IP_RETOPTS installs IPv4 options for the outgoing packet, including the Loose Source and Record Route (LSRR) and Strict Source and Record Route (SSRR) source routing options. Linux kernel rewrites the packet's destination address to the first hop encoded in the LSRR or SSRR option, bypassing the address only access check that Syd performs on the syscall destination argument. 😭
trace/allow_unsafe_socket
- type:
boolean - static:
yes
Specify whether unsafe socket families should be allowed. When set, Syd does not drop the capability CAP_NET_RAW on startup for itself, but it is still dropped for the sandbox process. This allows:
- use of
RAWandPACKETsockets. - bind to any address for transparent proxying.
- make use of the
ping(1)command. 🏓
trace/allow_unsupp_socket
- type:
boolean - drop:
yes, off only - static:
yes
Specify whether unsupported socket families such as netlink sockets should be allowed access when network sandboxing is on. By default Syd allows sandboxed access to unix, ipv4 and ipv6 sockets. This option has no effect when network sandboxing is off.
As of version 3.52.0 Syd allows access to algorithm sockets with the trace/allow_unsafe_kcapi option rather than with this option. Algorithm sockets are used to interact with the Linux kernel cryptography API. 🔐
As of version 3.42.0, Transparent Inter-Process Communication (AF_TIPC) sockets at socketpair(2) boundary are only permitted if this option is set to true. move over, generic socket option, kcapi's got this now 💅
trace/allow_unsupp_cmsg
- type:
boolean - drop:
yes, off only
Specify whether unsupported control messages for sendmsg(2) and sendmmsg(2) are allowed to cross the network sandbox unchanged. When unset (default), Syd strips any such control message from message header before forwarding the call to Linux and logs the event. ✂️
trace/allow_unsafe_personality
- type:
boolean - static:
yes
Specify whether personality(2) restrictions should be lifted. Refer to the output of syd-ls personality for the list of allowlisted personality(2) personas. Refer to the "Personality Syscall Restrictions" section of the syd(7) manual page for more information. 🎭
trace/allow_unsafe_arch_prctl
- type:
boolean - static:
yes
okay so this one's about the shadow stack — whether arch_prctl(2) shadow stack restrictions should be lifted at all. When it's disabled (which is the default, bestie), Syd is totally fine with you enabling the shadow stack, but it flat-out denies disabling it, unlocking it, and enabling the writable ARCH_SHSTK_WRSS feature. Like, you can put the seatbelt on, you just can't take it off. 🎀
trace/allow_unsafe_prctl
- type:
boolean - static:
yes
Specifies whether prctl(2) restrictions should be lifted. Wanna know exactly which prctl requests get let through? Just vibe-check the output of syd-ls prctl and it'll tell you the whole allowed list. iykyk ✨
trace/allow_unsafe_prlimit
- type:
boolean - static:
yes
Specifies whether prlimit(2) restrictions should be lifted. That's it, that's the whole thing. 🦋
trace/allow_unsafe_shm
- type:
boolean - static:
yes
Specifies whether sysvipc(7) system calls should be allowed. Fr fr, go read the "Shared Memory Hardening" section of the syd(7) manual page for the whole lore — it's giving essential reading. And if you want the actual list of system calls this setting unlocks, run syd-ls shm. 👑
trace/allow_unsafe_perm_shm
- type:
boolean - static:
yes
Specifies whether unsafe permissions in the mode arguments of shmget(2), msgget(2), and semget(2) system calls — plus the IPC_SET operation of shmctl(2), msgctl(2), and semctl(2) — should be permitted. Peep the "Shared Memory Permissions Hardening" section of the syd(7) manual page for more. 💅
trace/allow_unsafe_msgqueue
- type:
boolean - static:
yes
Specifies whether mq_overview(7) system calls should be allowed. Again, the Shared Memory Hardening section of syd(7) is your bestie here. And syd-ls msgqueue shows you exactly which system calls this setting allows. 🌸
trace/allow_unsafe_perm_msgqueue
- type:
boolean - static:
yes
Specifies whether unsafe permissions in the mode argument of the mq_open(2) system call should be permitted. Yep, same drill: "Shared Memory Permissions Hardening" section of syd(7), go read it. 🫶
trace/allow_unsafe_rseq
- type:
boolean - static:
yes
Specifies whether unsafe Restartable Sequences via the rseq(2) system call should be permitted. The "Denying Restartable Sequences" section of the syd(7) manual page has the details, no cap. 🔥
trace/allow_unsafe_sud
- type:
boolean - static:
yes
A boolean specifying whether the PR_SET_SYSCALL_USER_DISPATCH prctl(2) operation should be permitted inside the sandbox. By default it's denied with errno(3) EINVAL ("Invalid argument"). Fun fact: the only things that actually use this operation are compatibility layers like Wine. 🍓
trace/allow_unsafe_sysinfo
- type:
boolean - static:
yes
Specifies whether the sysinfo(2) randomizer should be disabled at startup. If this option is set at startup, the sysinfo(2) system call becomes allowed and hands out identical info to the files /proc/loadavg and /proc/meminfo — which are disabled by default by common profiles such as the linux and user profiles. Notably, this mitigation is unset for the paludis profile, because leaking this side-channel is literally irrelevant for package builds. ✨
trace/allow_unsafe_syslog
- type:
boolean - static:
yes
Specifies whether the Linux capability CAP_SYSLOG should be retained. That lets the process perform privileged syslog(2) operations — super useful when you're sandboxing a service like syslogd. 🌈
trace/allow_unsafe_memfd
- type:
boolean - drop:
yes, off only - static:
yes
A boolean specifying whether executable memory file descriptors should be enabled. By default Syd strips the MFD_EXEC and adds the MFD_NOEXEC_SEAL flag to the memfd_create(2) flags argument — which means the memory file descriptor can never be made executable. Iconic behaviour, honestly. 🎀
Here's the catch though: the MFD_NOEXEC_SEAL flag requires Linux-6.3 or newer, so on older kernels you must enable this option to make memory file descriptors work at all.
⚠️ But please be aware, bestie: allowing encrypted memory file descriptors does allow an attacker to bypassExec,ForceandTPEsandboxing and execute denylisted code. That's not a vibe. 😭
trace/allow_unsafe_uname
- type:
boolean - static:
yes
A boolean specifying whether uname(2) hardening should be disabled. The "Hardened uname(2)" section of the syd(7) manual page has the full story. 🦋
trace/allow_unsafe_vmsplice
- type:
boolean - static:
yes
Specifies whether the vmsplice(2) system call should be allowed inside the sandbox. By default this system call is not permitted. Hit up the "Restricting vmsplice System Call" section of the syd(7) manual page for more. 💜
trace/deny_dotdot
- type:
boolean - drop:
yes, on only
Specifies whether .. components should be denied during path resolution for chdir(2) and open(2) family system calls. Lowkey this is so useful for mitigating path traversal attacks. More deets in the "Path Resolution Restriction For Chdir and Open Calls" section of the syd(7) manual page. 🔥
trace/deny_exec_elf32
- type:
boolean - static:
yes
Deny the execution of 32-bit ELF binaries. periodt. 💀
trace/deny_exec_elf_dynamic
- type:
boolean - static:
yes
Deny the execution of dynamically linked ELF binaries. 🦋
trace/deny_exec_elf_norelro
- type:
boolean - static:
yes
Deny the execution of ELF binaries without full RELRO — i.e. binaries lacking a PT_GNU_RELRO segment with immediate binding. And heads up: binaries without a dynamic section are denied as well. Not me being thorough 💅
trace/deny_exec_elf_static
- type:
boolean - static:
yes
Deny the execution of statically linked ELF binaries. 🌸
trace/deny_exec_script
- type:
boolean - static:
yes
Deny the execution of scripts — files with #!<interpreter> on the first line. 🎀
Okay so now the serious part, and I need you to actually read this. The execve(2) TOCTOU mitigations do not cover this option, which means this functionality is vulnerable to TOCTOU. That lets an attacker execute a script whose path is denylisted. This TOCTOU is limited to scripts and requires the interpreter binary to be allowlisted for exec — so no, this vulnerability does not allow an attacker to execute denylisted binaries. Still, your best move is to deny the respective interpreter binaries for execution instead, for a safe and secure approach. 🫶
On Linux-6.14 and newer, kernel-assisted executability provides a safe way to deny execution of scripts in cooperation with enlightened interpreters. See the "Securebits and Kernel-Assisted Executability" section of the syd(7) manual page for more. ⭐
trace/deny_passrights
- type:
boolean - static:
yes - This needs Linux-6.16 or newer btw. Setting this option on older kernels just fails with
ENOSYS("Function not implemented"). 💀 - The check happens at
sendmsg(2)time, and it's checked against the receiving socket — so the sender is the one who sees the error. iykyk. setsockopt(2)withSO_PASSRIGHTSis denied withEPERM("Operation not permitted") right at the seccomp-bpf level, so the sandbox process literally cannot re-enable fd reception. No take-backsies. 🚫- Lowkey important: this option denies reception outright in the kernel, whereas the
RecvFdsandboxing category restricts reception by file descriptor name. Different flavours, bestie. - Fr fr this breaks programs that rely on file descriptor passing, like Wayland, D-Bus, and PipeWire clients. 😭
- Wanna know the motivation behind the
SO_PASSRIGHTSsocket option? The link is right here:https://lwn.net/Articles/1023085/✨
trace/deny_tsc
- type:
boolean - static:
yes
Specify whether reading the timestamp counter should be denied. No accurate timer = so many timing attacks become way harder to pull off. 🕵️♀️
- Works on
aarch64,x32,x86, andx86_64only. Don't @ me. - It breaks time related calls in the vDSO, but honestly that can be trivially worked around by writing an
LD_PRELOADlibrary that calls the respective system calls directly. If you want a reference implementation, go look at"libsydtime",https://lib.rs/libsydtime. 🧚 - Heads up: this has a negative performance impact on programs that rely on
gettimeofday(2)being a vDSO call. Trade-offs, babes.
trace/deny_vdso
- type:
boolean - drop:
yes, on only
Specify whether access to vDSO should be prevented by overwriting the auxiliary vector members AT_SYSINFO and AT_SYSINFO_EHDR at exec(3) boundary. Cute detail: this option becomes a no-op if trace/allow_unsafe_exec_libc is set to true. 🙈
trace/exit_wait_all
- type:
boolean - static:
yes
Specify whether Syd should wait for all processes to exit before exiting. By default, Syd exits with the eldest process and any leftover processes chilling in the background get automatically killed. 💀 Periodt.
trace/force_cloexec
- type:
boolean - drop:
yes, on only
Specify whether the "O_CLOEXEC" flag should be enforced for all creat(2), open(2), openat(2), openat2(2), memfd_create(2), socket(2), accept(2), and accept4(2) system calls made by the sandbox process. When this feature is on, Syd makes sure every file descriptor opened by the sandbox process is automatically set with the "O_CLOEXEC" flag, which stops those file descriptors from being inherited by newly executed programs. It's giving security upgrade — closing file descriptors during exec(3) calls prevents the risk of file descriptor leakage, which could otherwise lead to unauthorized access to sensitive files or resources. 👑
And yes, the feature can be toggled at runtime using Syd's virtual stat(2) API, so you get flexible control over the confinement level of sandboxed processes. Slay. ✨
trace/force_rand_fd
- type:
boolean - drop:
yes, on only
Specify whether file descriptors returned by all creat(2), open(2), openat(2), openat2(2), memfd_create(2), socket(2), accept(2), and accept4(2) system calls made by the sandbox process should be randomized. When this feature is enabled, Syd specifies a random available slot (rather than the lowest-numbered one) to the SECCOMP_IOCTL_NOTIF_ADDFD operation which is used to install a file descriptor to the sandbox process. Randomizing file descriptor numbers makes it significantly harder for an attacker to predict or deliberately reuse critical descriptors, which raises the bar against file-descriptor reuse and collision attacks. 🔥
Ngl, enabling this may break programs which rely on the POSIX guarantee that open(2) returns the lowest available descriptor. You've been warned, bestie. ⚠️
This behavior can be toggled at runtime via Syd's virtual stat(2) API, so operators can enable or disable descriptor randomization without restarting or recompiling the sandboxed process. We're also cooperating with the HardenedBSD project to implement a similar feature in the BSD kernel — more info here: https://git.hardenedbsd.org/hardenedbsd/HardenedBSD/-/issues/117 🫶
This feature uses the kcmp(2) system call and requires a Linux kernel configured with the CONFIG_KCMP option. On a kernel without this option, all system calls that are part of this feature will return ENOSYS (Function not implemented). 😭
As of version 3.38.0, this option is enabled for the user profile. 🎀
trace/force_ro_open
- type:
boolean - drop:
yes, on only
Specify whether creating and writing open(2) family system calls should be denied regardless of the path argument. This option is restricted to creat(2), open(2), openat(2), and openat2(2) system calls and is provided for convenience. 💅
If you wanna stop all write-like access completely — including e.g. mkdir(2), truncate(2) etc. — use the readonly profile instead, which uses the rule "deny/wrset/***" to prevent all write-like access. Refer to the "PROFILES" section of the syd(5) manual page for more information.
trace/force_wx_open
- type:
boolean - drop:
yes, on only
Specify whether creating and writing open(2) family system calls for executable files should be denied regardless of the path argument. This option is restricted to creat(2), open(2), openat(2), and openat2(2) system calls. 🔒
You can combine it with the "trace/force_umask" option to confine the filesystem as W^X (Write XOR Execute). Refer to the "user" profile for an example, and to the "PROFILES" section of the syd(5) manual page for more information. iconic combo honestly.
trace/force_local_net
- type:
boolean - drop:
yes, on only
Force local networking — rewrites all addresses to loopback for bind/connect. 🏠
In addition, AF_INET and AF_INET6 sockets created at socket(2) boundary get a loopback-only classic BPF socket filter attached with SO_ATTACH_FILTER and locked with SO_LOCK_FILTER. The filter drops incoming packets whose originating network interface is not the loopback interface. It is inherited by accept(2)ed sockets together with the lock. And it can neither be detached, nor replaced, nor unlocked for the lifetime of the socket — even if the file descriptor leaks out of the sandbox. 💀🔐
Sockets created before this option was enabled do not have the filter attached. Timing matters, babes.
trace/force_no_symlinks
- type:
boolean - drop:
yes, on only
Specify whether path resolution for the open(2) family is forced to use the RESOLVE_NO_SYMLINKS resolve flag. This flag is forced during path canonicalization, so this mitigation applies to all hooked path system calls, not just the open(2) family. 🦋
When enabled, traversal of symbolic links is disallowed during lookup — all pathname components must be non-symlink entries. Note that this affects only the pathname resolution step and does not modify other flags or access checks. It's giving "no shortcuts allowed". 🚫
trace/force_no_magiclinks
- type:
boolean - drop:
yes, on only
Specify whether path resolution for the open(2) family is forced to use the RESOLVE_NO_MAGICLINKS resolve flag. This flag is forced during path canonicalization, so this mitigation applies to all hooked path system calls, not just the open(2) family. Same vibe as above, different flavour. ✨
When enabled, traversal of magic links (such as special proc(5) links that do not behave like regular symbolic links) is disallowed during lookup — all pathname components must be non-magiclink entries. This affects only the pathname resolution step and does not modify other flags or access checks. 🧚
Also! As of version 3.49.0, Syd honours this option when listing files under proc(5) filesystem, thereby hiding magic links when this option is enabled. Sneaky but cute. 🙈
trace/force_no_xdev
- type:
boolean - drop:
yes, on only
Bestie, this one's about forcing the RESOLVE_NO_XDEV resolve flag on path resolution for the open(2) family. And the plot twist? 🫶 This flag gets forced during path canonicalization, which means this mitigation isn't just for the open(2) family — it's lowkey for all hooked path system calls. Iconic reach, honestly.
When it's enabled, traversing mount points — including bind mounts — is disallowed during lookup. So the path has to live on the same mount as the directory referenced by dirfd (or, if dirfd == AT_FDCWD, your current working directory). And ngl, it's very much a "stay in your lane" situation:
- It only touches the pathname resolution step.
- It does not modify other flags.
- It does not modify access checks.
Periodt. 💅
🎀 trace/force_umask 🎀
- type —
octal - default —
7000 - drop —
yes, raise only
So this one lets you specify an umask mode to force for regular files. Want to unset a force umask you set earlier? Easy — use -1 or off as the value. ✨
Now for the lore, because there is a lot of it (iykyk):
- As of version 3.15.6, the
chmod(2)family of system calls also honours force umask, for extra hardening. 🛡️ - As of version 3.22.1, this setting does not apply to directory creation for the
mkdir(2)andmkdirat(2)system calls. - As of version 3.26.2, this setting does not apply to UNIX domain socket creation for the
bind(2)system calls, and it also doesn't apply to non-regular file creation for themknod(2)andmknodat(2)system calls. - As of version 3.54.0, the default value is
7000, which ignoressetuid/setgid/stickybits for thechmod(2)family of system calls. This is in consistency with OpenBSDpledge(2). 👑
💜 trace/memory_access 💜
- type —
integer - default —
2 - static —
yes
Okay this one sets the mode on cross memory attach and proc_pid_mem(5) usage, and it's genuinely important so pay attention bestie 😭
Cross memory attach is done using the system calls process_vm_readv(2) and process_vm_writev(2), which requires a Linux kernel configured with the CONFIG_CROSS_MEMORY_ATTACH option enabled. The supported modes are:
0: Use cross memory attach if available, useproc_pid_mem(5)otherwise.1: Use/proc/pid/mem(5)unconditionally.2: Use cross memory attach unconditionally.
Now the security tea ☕ From a security point of view, these two modes of access have an important distinction: cross memory attach honours page protections of the target process, however using /proc/pid/mem(5) does not. That makes direct proc_pid_mem(5) access dangerous — a Syd deputy process may be confused into corrupting or even controlling memory regions the sandbox process otherwise does not have direct access to. Yikes. 💀
This is the main reason why mode 2 has been added as of version 3.32.6, as a secure default alternative to the previous default mode 0, whose fallback behaviour can be unpredictable and is against the idea of secure defaults. So therefore, as of version 3.32.6, the user is asked to change the memory access mode explicitly if their Linux kernel is not configured with the CONFIG_CROSS_MEMORY_ATTACH option.
You may also use the environment variables SYD_NO_CROSS_MEMORY_ATTACH and SYD_PROC_PID_MEM_FALLBACK — refer to the ENVIRONMENT section of the syd(1) manual page for more information. 🧚
For further information about the security impact of proc_pid_mem(5) writes, refer to the following links:
https://lore.kernel.org/lkml/202403011451.C236A38@keescook/T/https://lwn.net/Articles/476947/https://issues.chromium.org/issues/40089045
And here's the receipts, in strace form, because a vibe check is always better with data 🌸
; strace -q -eprocess_vm_readv -fc -- syd -poff -pD -mtrace/memory_access:0 true
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- -----------------
100.00 0.000031 10 3 process_vm_readv
------ ----------- ----------- --------- --------- -----------------
100.00 0.000031 10 3 total
; strace -q -eprocess_vm_readv -fc -- syd -poff -pD -mtrace/memory_access:1 true
; strace -q -eprocess_vm_readv -fc -- syd -poff -pD -mtrace/memory_access:2 true
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- -----------------
100.00 0.000008 2 3 process_vm_readv
------ ----------- ----------- --------- --------- -----------------
100.00 0.000008 2 3 total
🦋 trace/sync_seccomp 🦋
- type —
boolean - static —
yes
Last one in this batch! This uses synchronous mode for seccomp_unotify(2), so each Syd syscall handler thread wakes up on the same CPU as the respective sandbox thread that executed the system call. 🧠✨
And the honest review: this option makes no functional difference and may or may not help with performance. Syd typically performs better with this option off, therefore it defaults to off. Not me telling you to just leave it alone fr fr 🙈
💅 PATTERN MATCHING 💅
okay so bestie, Syd uses shell-style pattern matching for path-based allowlists and filters, and for UNIX socket address patterns. The matcher is a derivative of the wildmatch matcher of rsync(1) with Syd-specific extensions, refer to the PATTERN MATCHING RULES section of the rsync(1) manual for background. Matching operates on raw bytes rather than characters: the matcher is 8-bit clean, case-sensitive, and independent of the locale, performing no Unicode normalization or code point interpretation. A pattern is a sequence of bytes in which the following constructs carry special meaning, any other byte matches itself:
*— matches any sequence of bytes, never matching the directory separator/.**— matches any sequence of bytes, including the directory separator/.?— matches exactly one byte, never matching the directory separator/.[...]— matches exactly one byte out of the given class, see below.\— escapes the next byte, stripping it of any special meaning.
Consistent with the filter rules of rsync(1), a trailing triple star matches the path itself in addition to everything below it: a pattern of the form P/*** is exactly equivalent to the pair of patterns P and P/**. For instance, /dev/*** matches both /dev and any file recursively under /dev, whereas /dev/** matches strictly below /dev. The triple star is only meaningful as a trailing component. In addition, Syd applies one extension to rsync(1) semantics, component elision: a ** between two separators also matches zero path components, e.g. /usr/**/bin/bash matches both /usr/bin/bash and /usr/local/bin/bash. A character class matches a single byte against a set:
[abc]— matches one of the given bytes. The byte]may be included by placing it first in the class, e.g.[]a].[!abc],[^abc]— negated class, matches any byte not in the given class, e.g.[^0-9]matches any byte which is not an ASCII digit.[a-f]— range, matches any byte between the given bounds inclusive. A-placed first or last is literal.[\x]— escapes the next byte inside a class.[[:name:]]— POSIX character class, where _name_ is one ofalnum,alpha,blank,cntrl,digit,graph,lower,print,punct,space,upper, orxdigit. Consistent with byte-wise matching, classes apply to ASCII range only.
Patterns are matched against canonicalized, absolute pathnames: symbolic links are resolved, and neither . (dot), nor .. (dotdot), nor consecutive separators occur in the matched text, hence patterns should be written in canonical form as well. A valid pattern starts with one of the characters / for pathnames, @ for abstract UNIX socket addresses, or ! for special addresses such as !unnamed (see ADDRESS MATCHING), or memory file descriptor names such as !memfd:*. Patterns may include environment variables which are expanded once at configure time. Finally, a pattern may be given hex-encoded, e.g. as produced by syd-hex(1), to express bytes which cannot be written in a configuration file, such as newlines: a pattern which is in its entirety a valid hexadecimal encoding is decoded before use. Decoding is unambiguous since canonical patterns start with one of /, @, or !, none of which is a hexadecimal digit. For efficient evaluation, Syd classifies each pattern at configure time into one of three match methods. This classification is an internal optimization with no semantic effect:
- Literal — a pattern including none of
*,?, or[is matched by plain byte equality. - Prefix — a pattern of the form
LITERAL/**orLITERAL/***is matched by a prefix comparison which honours component boundaries, e.g./dev/***matches/dev/nullbut not/devices. - Wildcard — any other pattern is matched with the full matcher, an iterative algorithm with constant additional memory based on Kirk Krauss's FastWildCompare, extended with dual backup points for the
*and**wildcards, and SIMD-accelerated literal scanning.
Syd gets patterns from multiple sources: a configuration file, a profile, the -m command line switch (see syd(1) and syd(5)), or a stat(2) call with the /dev/syd prefix. There is no precedence between different sources: all rules accumulate in order into a single list per sandboxing capability, and pattern matching during access control happens in a single step where the last matching pattern decides the outcome. In particular, precedence is positional rather than structural: a later, more general pattern overrides an earlier, more specific one. If no pattern matches, the default action of the respective sandboxing capability applies (see the default/ family of commands).
🦋 ADDRESS MATCHING 🦋
Syd has a simple address scheme to match network addresses. Addresses can either be glob(3p) patterns to match UNIX and abstract UNIX socket addresses, or IP CIDR followed by a port specification to match IPv4 and IPv6 addresses. Port specification can be a single port, a closed range in format port1-port2, or a comma-separated list of ports and/or ranges such as 80,443,8000-9000. The address and the port specification must be split by the character !. The precedence logic is same as PATTERN MATCHING where the last matching pattern decides the outcome. UNIX socket addresses are matched against glob(3p) patterns as follows:
/path— UNIX domain sockets are matched by their canonicalized socket path, hence the patterns always start with the character/, e.g./run/foo.sock, or/run/user/*/bus.@name— abstract UNIX sockets are prefixed with the character@before the access check, hence the patterns must start with the character@, e.g.@dbus-*matches abstract sockets whose name starts withdbus-. Abstract socket names are kernel-side identifiers, they're not filesystem paths and they're not canonicalized.!unnamed— unnamed UNIX sockets are matched with the dummy address!unnamed. This is used for thesocketpair(2)system call, and for socket operations on UNIX sockets without a pathname or an abstract name.
Some examples are given below:
allow/net/bind+/run/foo.sock— letsbind(2)go through to the UNIX socket/run/foo.sock.allow/net/connect+/run/user/*/bus— letsconnect(2)go through to user D-Bus sockets.allow/net/bind+@dbus-*— letsbind(2)go through to abstract sockets whose name starts withdbus-.allow/net/bind,net/connect+!unnamed— letssocketpair(2)through, plus any operations on unnamed UNIX sockets.
🦋 dial(3) strings, aka the glow-up era 🦋
okay so as of version 3.56.0, Syd eats dial(3) strings the way Plan9 and go do it, and these bad boys level up the address scheme with protocol filtering. A dial(3) string is shaped like this:
network!netaddr!service
and here's what each lil piece means, bestie:
- network — one of the predefined network names
tcp,tcp4,tcp6,mptcp,mptcp4,mptcp6,udp,udp4,udp6,udplite,udplite4,udplite6,sctp,sctp4,sctp6,dccp,dccp4,dccp6,icmp,icmp4,icmp6,net,net4,net6,unix,unixgram, orunixpacket. Network names are case-insensitive, slay. Thetcpnames match TCP sockets, themptcpnames match Multipath TCP sockets, theudpnames match UDP sockets, theudplitenames match UDP-Lite sockets, thesctpnames match SCTP sockets, thedccpnames match DCCP sockets, theicmpnames match ping sockets, and thenetnames match any protocol. Each protocol-qualified name matches only its own protocol, no cap. To cover a host across every protocol, use anetname or the legacy format. The4and6suffixes constrain the address family of netaddr. Heads up:icmp4andicmp6are distinct protocols.icmpunifies them by address family. The service field of anicmpname is the ICMP identifier. - netaddr — an IP address, an IP CIDR network, a network alias such as
loopback(peep below 👀), or the wildcard*which is an alias forany. Hostnames are not supported, sorry babe. - service — a port specification as described above, or the wildcard
*which matches all ports. Named services are not supported — the service must be numeric, periodt.
The protocol of a socket is determined at system call time using the SO_PROTOCOL socket option. The network names form a predefined set — there's no ndb(8) lookup, so don't go looking for one. Every field of a dial(3) string may be set with environment variables which are expanded at configure time, like tcp!${ADDR}!${PORT}, or ${PROTO}!127.0.0.1!80. Obsessed tbh.
For the network names unix, unixgram, and unixpacket, the service field is omitted and netaddr is a glob(3p) pattern matching UNIX and abstract UNIX socket addresses with the conventions described above: unix!/run/foo.sock is equivalent to /run/foo.sock, unix!@dbus-* is equivalent to @dbus-*, and unix!!unnamed is equivalent to !unnamed. The three UNIX network names are equivalent — the socket type is not used in matching, bestie. Some examples down here ✨:
tcp!127.0.0.1!80— TCP to127.0.0.1port80udp!9.9.9.9!53— UDP to9.9.9.9port53tcp!10.0.0.0/8!22,80,443— TCP to the10.0.0.0/8network on ports22,80, and443tcp6!*!443— TCP over IPv6 to any address on port443sctp!10.0.0.7!9899— SCTP to10.0.0.7port9899udp!loopback!*— UDP to the loopback addresses on any portnet!::1!8080— any protocol to::1port8080unix!/run/***— UNIX sockets under/rununix!@dbus-*— abstract UNIX sockets with the name prefixdbus-unix!!unnamed— unnamed UNIX sockets
dial(3) strings expanding to multiple networks add one rule per network, okay? So allow/net/connect+tcp!loopback!80 is equivalent to allow/net/connect+tcp!127.0.0.0/8!80 followed by allow/net/connect+tcp!::1/128!80. 💅
Addresses in the legacy format match any protocol, which means the legacy format is equivalent to the net network name, and existing configuration retains its behaviour — no glow-up required. Like, allow/net/connect+::1!8080 is equivalent to allow/net/connect+net!::1!8080, fr fr.
In addition there are some aliases, which may be used instead of specifying an address:
any— literally all of it, bestie:any4+any6🫶any4— the whole entire IPv4 address space:0.0.0.0/0any6— the whole entire IPv6 address space:::/0loopback—loopback4+loopback6(the "there's no place like home" combo 🏠)loopback4—127.0.0.0/8loopback6—::1/128linklocal—linklocal4+linklocal6linklocal4—169.254.0.0/16linklocal6—fe80::/10local—local4+local6, your cozy lil LAN bubble 🌸local4— loopback network and the RFC1918 private networks:127.0.0.0/8+10.0.0.0/8+172.16.0.0/12+192.168.0.0/16local6—::1/128+fe80::/10+fc00::/7+fec0::/10multicast—multicast4+multicast6multicast4— the IPv4 multicast address space:224.0.0.0/4multicast6— the IPv6 multicast address space:ff00::/8
🔒 SECURITY 🔒
okay so this one's important bestie, listen up 👂 the interface is only available if the sandbox lock is not set for the calling process. and like, same energy for parsing: command-line option parsing and configuration file parsing both stop the moment a lock:on clause is executed. no cap, it just slams the door shut 💅
✨ RETURN VALUE ✨
For stat(2) calls: on success, zero is returned. On error, -1 is returned and errno(3) gets set to tell you what went wrong 😭
For open(2) calls: on success you get the new file descriptor back — a nonnegative integer, and heads up, the file descriptor is randomized 🎲. On error, -1 is returned and errno(3) is set to indicate the error.
On a successful call, the stat(2) buffer has these fields masked 👇 — every other field is equivalent to the character device /dev/null:
- Inode is zero.
- The mode field represents the file type (character device) and permissions, with special bits set like this: sticky bit for
unshare/mount:1, SUID bit forunshare/user:1, SGID bit forunshare/net:1, user read/write/exec bits for Read, Write and Exec sandboxing, group read bit for Stat sandboxing, group write bit for Proxy sandboxing, group exec bit for TPE sandboxing, world read bit for Lock sandboxing, world write bit for Uring sandboxing, and world exec bit for Force sandboxing. - The nlink field represents the lower 32 bits of sandboxing capabilities, encoded as:
lock,walk,list,stat,read,write,exec,ioctl,create,delete,rename,readlink,symlink,truncate,chdir,readdir,mkdir,rmdir,chown,chgrp,chmod,chattr,chroot,notify,utime,mkbdev,mkcdev,mkfifo,mktemp,net/bind,net/connect, andsendfd. - The device type represents the
syd(2)API version as (major, minor). - Access, creation, and modification times are non-zero constants.
💀 ERRORS 💀
EINVAL— the sandbox command is syntactically incorrect. typo era 🙈ENOENT— the result of the given sandbox query is false (e.g.test -c /dev/syd/sandbox/stat?).ENOENT— the sandbox lock is on, so like, no commands are allowed. door's closed 🚪EPERM— you attempted to raise privileges inlock:dropmode. not today, bestie.EBUSY— you attempted to edit a setting at runtime that must be configured at startup.EEXIST— overwriting a rule is not permitted inlock:dropmode.EEXIST— you attempted to add a UID/GID transition for SafeSetID but a transition with the same source UID/GID already exists.ENODATA— SafeSetID is on but no UID/GID transits were defined for the current user/group.ENOSYS— attempt to enable Randomized File Descriptors on a system withoutkcmp(2)support 😭ENOSYS— attempt to enable Force sandboxing which was disabled at build time.EOPNOTSUPP— the hash algorithm is not supported for Force sandboxing.EOPNOTSUPP— the ioctl operation is not supported for Ioctl sandboxing.EOPNOTSUPP— the sandbox command is not supported forsyd-oci(1).
🦋 SEE ALSO 🦋
syd(1), syd(5), syd(7), syd-ls(1), open(2), stat(2), perf(1), pledge(2), ptrace(2), seccomp(2), strace(1), glob(3p), io_uring(7), gdb(1), valgrind(1)
- syd homepage: https://sydbox.exherbo.org
- libsyd homepage: https://libsyd.exherbo.org
- gosyd homepage: https://gosyd.exherbo.org
- plsyd homepage: https://plsyd.exherbo.org
- pysyd homepage: https://pysyd.exherbo.org
- rbsyd homepage: https://rbsyd.exherbo.org
- syd.el homepage: https://sydel.exherbo.org
- libsydtime homepage: https://lib.rs/libsydtime
- LandLock homepage: https://landlock.io/
- vDSO wiki: https://en.wikipedia.org/wiki/VDSO
- parse-size documentation: https://docs.rs/parse-size/1.0.0/parse_size/
- ipnetwork documentation: https://docs.rs/ipnetwork
👑 AUTHORS 👑
Maintained by Ali Polatel. Up-to-date sources can be found at https://gitlab.exherbo.org/sydbox/sydbox.git and on Radicle at rad:z38HCnbmcDegA2BMxuPaPRPMdp6wF. Bugs/patches can be submitted to https://gitlab.exherbo.org/groups/sydbox/-/issues. Discuss in #sydbox on Libera Chat or in #sydbox:mailstation.de on Matrix 💬
and that's a wrap bestie, we literally made it to the end of the whole entire page 🎉 no cap, go touch some grass, you've earned it 🌈💖
this was the syd(2) manual page, dated 2026-09-01 — yep, that date, that's the one 🎀 go be safe out there ✨🫶
📦 where this page comes from
- arch rolling: syd 3.59.0-1 · GPL-3.0-only
- 💖 girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms