Sandbox¶
zoryn runs scripts from .gear/up.d/ and .gear/merge-up.d/ in a sandbox to protect sensitive files (~/.ssh, ~/.gnupg, ~/.config) from untrusted repository scripts.
Setup¶
Install bubblewrap for lightweight sandboxing:
For hybrid mode (bwrap + hasher chroot with the package's build dependencies):
Runner modes¶
| Runner | Description |
|---|---|
hybrid | bwrap + hasher chroot — uses the package's build dependencies (default when both tools are installed) |
bwrap | bubblewrap only — lightweight namespace isolation with host system paths |
direct | No isolation — full filesystem access (not recommended) |
The runner is auto-detected: hybrid if both bwrap and hsh are available, bwrap if only bwrap, otherwise direct.
If the configured runner is unavailable, zoryn prompts for confirmation before running hooks without isolation. In non-interactive mode, execution is refused. Use --no-sandbox to skip the prompt and run without sandboxing.
What every sandbox mode shares¶
For both hybrid and bwrap (i.e. anything other than direct):
| Aspect | Behaviour |
|---|---|
| Project directory | The git root is bind-mounted read-write at the same path. Hooks see and can modify the repository. |
| Working directory | chdir to the git root before exec. |
Home ($HOME) | Replaced with an empty tmpfs. ~/.ssh, ~/.gnupg, ~/.config, history files, agent sockets — none of these are visible. |
~/.gitconfig | Bind-mounted read-only if it exists, so git inside the hook keeps your user identity. |
| Environment | Cleared with --clearenv. Only PATH, HOME, and TAG/COMMIT/SHORTCOMMIT (merge hooks only) are re-injected. |
PATH | /usr/bin:/bin by default (configurable via [sandbox] path = "…"). |
/proc | Mounted as a fresh procfs. |
/dev | Bind-mounted from the host (--dev-bind /dev /dev) for full device access. |
| Process namespace | --unshare-all — fresh PID, IPC, UTS, cgroup namespaces. |
| Network | Allowed (--share-net). Hooks can curl, go mod download, cargo fetch, etc. |
| Lifetime | --die-with-parent — the sandbox dies if zoryn dies. |
What differs between modes is where /usr, /lib, /bin, /sbin, /etc come from:
bwrap¶
System directories (/usr, /lib, /lib64, /etc, /bin, /sbin) are bind-mounted read-only from the host. The hook sees exactly the tools you have installed in your host distro.
- Pros: zero setup cost — bwrap launch is fast (no chroot to build).
- Cons: build dependencies declared in the spec aren't guaranteed to be there; the host's library versions are what the hook gets.
Use when hooks don't need any package beyond what's already on your machine (typical for sed/awk glue scripts).
hybrid¶
hsh first builds a hasher chroot populated with the package's build dependencies (parsed from BuildRequires:), then bwrap is launched with the chroot's /usr, /lib, /lib64, /etc, /bin, /sbin, /var bind-mounted read-only instead of the host's (with a writable /var/tmp tmpfs on top for scratch space). Project directory, /proc, /dev, network, and home isolation are identical to bwrap.
/etc/resolv.conf is bind-mounted from the host so DNS works inside the chroot.
- Pros: hooks see exactly the toolchain that will be used to build the package — reproducible across machines.
- Cons: first run pays the chroot-preparation cost (seconds to a minute, cached afterwards in
~/hasher).
Use when hooks need build tooling (go mod vendor, cargo vendor, meson subprojects download, etc.).
Additional packages on top of the spec's BuildRequires: can be listed in .gear/version-up:
These are installed via hsh-install after the chroot is prepared.
To skip the package's BuildRequires: entirely, set specbr = false. The chroot is then initialised bare (hsh --initroot-only, no src.rpm build) — only the explicitly configured packages (git, plus any [sandbox.chroot] packages from ~/.zoryn and the packages listed above) are installed, not the spec's build dependencies. Useful when those dependencies are irrelevant to the hooks and the src.rpm build is just overhead:
specbr defaults to true (build the src.rpm and use its BuildRequires:). When specbr = false, a custom [sandbox.chroot] prepare command (from ~/.zoryn) is ignored in favour of the bare init — zoryn prints a warning when this happens.
direct¶
No isolation at all. The hook script is executed in the current process's environment, with full access to your filesystem, ~/.ssh, the network, environment variables, and so on. Equivalent to running the hook by hand.
Only use this when you have audited the hooks and trust the repository.
Configuration¶
In ~/.zoryn:
[sandbox]
runner = "hybrid"
path = "/usr/bin:/bin"
[sandbox.chroot]
prepare = "gear-hsh --commit -- --mountpoints=/proc,/dev/pts,/dev/kvm --lazy-cleanup --build-srpm-only"
[sandbox.hasher]
workdir = "~/hasher"
Per-project (~/.config/zoryn/projects.d/<project>.toml):
[sandbox]
runner = "hybrid"
[sandbox.chroot]
prepare = "gear-hsh --commit -- --mountpoints=/proc,/dev/pts,/dev/kvm --lazy-cleanup --build-srpm-only"
Additional packages¶
In .gear/version-up:
The git package is always installed. Packages listed here are installed after chroot preparation via hsh-install, on top of the spec's BuildRequires:. Add specbr = false to skip those BuildRequires: and the src.rpm build (see hybrid above).
CLI flags¶
zoryn up --sandbox=hybrid # use hybrid (default)
zoryn up --sandbox=bwrap # use bubblewrap with host paths
zoryn up --no-sandbox # disable sandbox entirely
How hybrid works¶
In hybrid mode, hasher prepares a chroot populated with the package's build dependencies (parsed from the spec file), and bwrap runs scripts using paths from that chroot. This gives both filesystem isolation and access to required tools without needing setuid privileges at runtime.