Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.process-jail

OS-level process CONTAINMENT — the 'jail' — that wraps the shell executors' argv so an allowed child is physically confined to the session workspace roots and, when network is off, cannot open a socket. This is real containment, a real containment boundary — not a cooperative name/argv check, which can be walked around since argv[0] is bash and the real binary hides inside the -lc string; the jail constrains what the child can DO once it runs, regardless of what a script inside it tries (curl, python -c, /dev/tcp — all hit the same wall).

POLICY, NOT GUARDS. The jail is driven by a declarative policy compiled from vis.yml + the LIVE session roots, not by hand-written guard functions. The policy is a plain VALUE passed per spawn (never a process-global singleton, so many concurrent sessions in one gateway never stomp each other). Its shape:

{:roots-fn (fn [] [root-strings]) ; live session RW roots, re-read/spawn :net-enabled? <bool> ; whole shell-child network on/off :allow-read-write [<path> …] ; concise full read+write grant :allow-write [<path> …] ; legacy writable paths (also readable) :deny-write [<path> …] ; protect within writable (deny wins) :allow-read [<path> …] ; additional read-only paths :deny-read [<path> …] ; protect a read region (deny wins) :inbound-ports [<int> …]} ; extra local ports a child may ACCEPT on ; (bind is local-only; accept is port-gated)

The filesystem model mirrors Anthropic's sandbox-runtime:

  • WRITE is allow-only: denied everywhere except the session roots + tmp + :allow-read-write + :allow-write; :deny-write wins.
  • READ is default-deny here (workspace-focused, stronger than srt's read-everywhere default): system code/config + RW paths + :allow-read are readable; :deny-read wins.

wrap-argv compiles the policy, per spawn, into the OS enforcement primitive:

  • macOS : a Seatbelt profile handed to the system sandbox-exec -p — ships with the OS, ZERO install. IMPLEMENTED + verified.
  • Linux : bubblewrap (bwrap) mount + network namespaces. IMPLEMENTED (fs confinement + net-off wall); filtered egress via the proxy still needs seccomp, so a proxy-restricted net is denied ENTIRELY (safe) rather than left open. supported? is true only when bwrap is installed; otherwise unenforceable-reason explains the gap so a requested jail fails LOUD instead of silently passing the child.
  • other : unsupported — callers keep the cooperative gate as the floor.

Two locks learned from the kernel, baked in here:

  1. sandbox-exec matches RESOLVED real paths, so every root is realpath'd before templating (/tmp -> /private/tmp, else the rule never matches).
  2. a default-deny profile MUST (import "system.sb") or dyld/sysctl startup reads are denied and every binary aborts before main.
OS-level process CONTAINMENT — the 'jail' — that wraps the shell executors'
argv so an allowed child is physically confined to the session workspace roots
and, when network is off, cannot open a socket. This is real containment, a
real containment boundary — not a cooperative name/argv check, which can be
walked around since argv[0] is `bash` and the real binary hides inside the
`-lc` string;
the jail constrains what the child can DO once it runs, regardless of what a
script inside it tries (curl, python -c, /dev/tcp — all hit the same wall).

POLICY, NOT GUARDS. The jail is driven by a declarative *policy* compiled from
vis.yml + the LIVE session roots, not by hand-written guard functions. The
policy is a plain VALUE passed per spawn (never a process-global singleton, so
many concurrent sessions in one gateway never stomp each other). Its shape:

  {:roots-fn     (fn [] [root-strings])  ; live session RW roots, re-read/spawn
   :net-enabled? <bool>                  ; whole shell-child network on/off
   :allow-read-write [<path> …]           ; concise full read+write grant
   :allow-write      [<path> …]           ; legacy writable paths (also readable)
   :deny-write       [<path> …]           ; protect within writable (deny wins)
   :allow-read       [<path> …]           ; additional read-only paths
   :deny-read        [<path> …]           ; protect a read region (deny wins)
   :inbound-ports    [<int> …]}           ; extra local ports a child may ACCEPT on
                                          ; (bind is local-only; accept is port-gated)

The filesystem model mirrors Anthropic's sandbox-runtime:
  - WRITE is allow-only: denied everywhere except the session roots + tmp +
    `:allow-read-write` + `:allow-write`; `:deny-write` wins.
  - READ is default-deny here (workspace-focused, stronger than srt's
    read-everywhere default): system code/config + RW paths + `:allow-read`
    are readable; `:deny-read` wins.

`wrap-argv` compiles the policy, per spawn, into the OS enforcement primitive:

  - macOS  : a Seatbelt profile handed to the system `sandbox-exec -p` — ships
             with the OS, ZERO install. IMPLEMENTED + verified.
  - Linux  : bubblewrap (`bwrap`) mount + network namespaces. IMPLEMENTED (fs
             confinement + net-off wall); filtered egress via the proxy still
             needs seccomp, so a proxy-restricted net is denied ENTIRELY (safe)
             rather than left open. `supported?` is true only when `bwrap` is
             installed; otherwise `unenforceable-reason` explains the gap so a
             requested jail fails LOUD instead of silently passing the child.
  - other  : unsupported — callers keep the cooperative gate as the floor.

Two locks learned from the kernel, baked in here:
  1. sandbox-exec matches RESOLVED real paths, so every root is realpath'd
     before templating (`/tmp` -> `/private/tmp`, else the rule never matches).
  2. a default-deny profile MUST `(import "system.sb")` or dyld/sysctl startup
     reads are denied and every binary aborts before `main`.
raw docstring

compile-policyclj

(compile-policy {:keys [roots-fn net-enabled? allow-read-write allow-write
                        allow-read deny-write deny-read deny-exec proxy-port
                        loopback-port inbound-ports]})

Resolve a raw jail policy VALUE into the canonical map macos-profile consumes: read the LIVE session roots via :roots-fn, add the always-writable temp dirs, fold in the environment-snapshotted :allow-read-write/:allow-write/ :allow-read/:deny-write/:deny-read paths (home-expanded + realpath'd). Called per spawn, so each child gets the CURRENT live roots without re-reading model-writable project config. :allow-read-write is the concise equivalent of granting the same path through both legacy allow lists.

Resolve a raw jail policy VALUE into the canonical map `macos-profile` consumes:
read the LIVE session roots via `:roots-fn`, add the always-writable temp dirs,
fold in the environment-snapshotted `:allow-read-write`/`:allow-write`/
`:allow-read`/`:deny-write`/`:deny-read` paths (home-expanded + realpath'd).
Called per spawn, so each child gets the CURRENT live roots without re-reading
model-writable project config. `:allow-read-write` is the concise equivalent of
granting the same path through both legacy allow lists.
sourceraw docstring

jailed-child-envclj

(jailed-child-env policy)

The COMPLETE environment for a confined child. Only an allowlist of safe, non-secret operator vars (PATH/HOME/LANG/…) plus any jail.env opt-ins are inherited; every API key / token / credential in the operator environment is DROPPED. This session's proxy + CA variables are then added. Returns nil when the policy is not enforcing — the caller keeps the parent environment as-is (unjailed platforms/sandbox: false), so non-confined behavior is unchanged.

The COMPLETE environment for a confined child. Only an allowlist of safe,
non-secret operator vars (PATH/HOME/LANG/…) plus any `jail.env` opt-ins are
inherited; every API key / token / credential in the operator environment is
DROPPED. This session's proxy + CA variables are then added. Returns nil when
the policy is not enforcing — the caller keeps the parent environment as-is
(unjailed platforms/`sandbox: false`), so non-confined behavior is unchanged.
sourceraw docstring

language-process-policyclj

(language-process-policy base loopback-port)

Derive a managed-language jail policy from a session's base policy. It keeps filesystem confinement, adds read-only runtime installations plus Vis-owned log directories, and replaces the shell proxy endpoint with this session's attributed language-process endpoint.

Dependency caches enter through the shared workspace.filesystem catalog and are already present on the base policy's read/write roots; this pass only adds the read-only language runtimes/toolchains plus Vis-owned log directories.

Direct network access is disabled. CA-aware runtimes receive the combined PEM bundle, while JVM children also receive an ephemeral PKCS12 truststore. loopback-port permits only the managed nREPL's selected listener port.

Derive a managed-language jail policy from a session's base policy. It keeps
filesystem confinement, adds read-only runtime installations plus Vis-owned log
directories, and replaces the shell proxy endpoint with this session's
attributed language-process endpoint.

Dependency caches enter through the shared `workspace.filesystem` catalog and
are already present on the base policy's read/write roots; this pass only adds
the read-only language runtimes/toolchains plus Vis-owned log directories.

Direct network access is disabled. CA-aware runtimes receive the combined PEM
bundle, while JVM children also receive an ephemeral PKCS12 truststore.
`loopback-port` permits only the managed nREPL's selected listener port.
sourceraw docstring

linux-bwrap-argsclj

(linux-bwrap-args {:keys [rw ro deny-write deny-read deny-exec net-enabled?
                          proxy-port loopback-port]})

Compile the bubblewrap flag vector (ending in --) from a RESOLVED policy map (the same shape macos-profile consumes). Filesystem: read-only bind the system toolchain roots + :ro, read-write bind :rw (session roots + tmp), then re-bind :deny-write read-only and mask :deny-read (empty tmpfs for dirs, /dev/null for files) -- later binds win, so the denies override the allows. Only bound paths exist inside the child, so everything else on the host (e.g. ~/.ssh) is simply absent. :deny-exec binaries are masked with /dev/null (a char device), so execve fails EACCES -- the Linux equivalent of macOS's (deny process-exec*); it is bound AFTER the allow binds so a denied binary inside an allowed :ro root is still blocked. Network: for FILTERED egress (:proxy-port set) pasta wraps the bwrap child, giving it a private net namespace that reaches ONLY the host gateway proxy port (see linux-pasta) -- exact macOS parity; bwrap then SHARES that namespace (no --unshare-net). Without pasta, filtered egress degrades to --unshare-net (no egress at all, safe). Net-off also --unshare-net; an explicitly-open network (net-enabled? with no proxy, e.g. a managed nREPL) shares the host network namespace.

Compile the bubblewrap flag vector (ending in `--`) from a RESOLVED policy map
(the same shape `macos-profile` consumes). Filesystem: read-only bind the system
toolchain roots + `:ro`, read-write bind `:rw` (session roots + tmp), then
re-bind `:deny-write` read-only and mask `:deny-read` (empty tmpfs for dirs,
/dev/null for files) -- later binds win, so the denies override the allows. Only
bound paths exist inside the child, so everything else on the host (e.g. `~/.ssh`)
is simply absent. `:deny-exec` binaries are masked with /dev/null (a char device),
so `execve` fails EACCES -- the Linux equivalent of macOS's `(deny process-exec*)`;
it is bound AFTER the allow binds so a denied binary inside an allowed `:ro` root
is still blocked. Network: for FILTERED egress (`:proxy-port` set) pasta wraps
the bwrap child, giving it a private net namespace that reaches ONLY the host
gateway proxy port (see `linux-pasta`) -- exact macOS parity; bwrap then SHARES
that namespace (no `--unshare-net`). Without pasta, filtered egress degrades to
`--unshare-net` (no egress at all, safe). Net-off also `--unshare-net`; an
explicitly-open network (`net-enabled?` with no proxy, e.g. a managed nREPL)
shares the host network namespace.
sourceraw docstring

macos-profileclj

(macos-profile {:keys [rw ro deny-write deny-read deny-exec net-enabled?
                       proxy-port loopback-port inbound-ports]})

Compile a Seatbelt (SBPL) profile string from a RESOLVED policy map {:rw [..] :ro [..] :deny-write [..] :deny-read [..] :net-enabled? <bool>} (all paths already canonical). Rules are emitted in Seatbelt's LAST-match-wins order: allow reads (system + rw + ro), allow writes (rw), then the deny carve- outs so :deny-write/:deny-read win over the allows. One-line string for sandbox-exec -p.

Compile a Seatbelt (SBPL) profile string from a RESOLVED policy map
`{:rw [..] :ro [..] :deny-write [..] :deny-read [..] :net-enabled? <bool>}`
(all paths already canonical). Rules are emitted in Seatbelt's LAST-match-wins
order: allow reads (system + rw + ro), allow writes (rw), then the deny carve-
outs so `:deny-write`/`:deny-read` win over the allows. One-line string for
`sandbox-exec -p`.
sourceraw docstring

prepare-session-jail!clj

(prepare-session-jail! {:keys [session-id jail-policy-fn]})

Bind the language surface's live session env to the managed-process contract. Missing session identity or policy fails closed before a language handler can start/restart a REPL or project test process. Safe and idempotent per dispatch.

Bind the language surface's live session env to the managed-process contract.
Missing session identity or policy fails closed before a language handler can
start/restart a REPL or project test process. Safe and idempotent per dispatch.
sourceraw docstring

proxy-envclj

(proxy-env policy)

Environment additions for a confined child. VIS_SEATBELT_ACTIVE records that the kernel policy is already inherited, preventing invalid nested sandbox-exec calls. When a gateway proxy endpoint is present, common proxy and CA variables cover curl/git/Python/Bun/etc.; managed JVM children additionally receive proxy + ephemeral truststore properties through JAVA_TOOL_OPTIONS.

Environment additions for a confined child. `VIS_SEATBELT_ACTIVE` records that
the kernel policy is already inherited, preventing invalid nested
`sandbox-exec` calls. When a gateway proxy endpoint is present, common proxy and
CA variables cover curl/git/Python/Bun/etc.; managed JVM children additionally
receive proxy + ephemeral truststore properties through JAVA_TOOL_OPTIONS.
sourceraw docstring

register-session-jail!clj

(register-session-jail! session-id policy-fn)

Register (or replace) this session's live base jail-policy function.

Register (or replace) this session's live base jail-policy function.
sourceraw docstring

repl-policyclj

(repl-policy base loopback-port)

Derive the managed-nREPL variant for its selected loopback listener port.

Derive the managed-nREPL variant for its selected loopback listener port.
sourceraw docstring

session-process-launchclj

(session-process-launch session-id argv)
(session-process-launch session-id argv {:keys [loopback-port]})

THE language-surface launch contract. Atomically resolve session-id's policy and return {:argv ... :env ...} for one managed language subprocess.

:loopback-port is reserved for a managed nREPL's preselected local listener. Unknown, nil, disposed, or failing sessions are denied before spawn; the contract never silently returns a naked argv. On an OS without a supported enforcer, :argv remains unchanged (the platform capability gap is explicit), while the policy lookup still must succeed.

THE language-surface launch contract. Atomically resolve `session-id`'s policy
and return `{:argv ... :env ...}` for one managed language subprocess.

`:loopback-port` is reserved for a managed nREPL's preselected local listener.
Unknown, nil, disposed, or failing sessions are denied before spawn; the
contract never silently returns a naked argv. On an OS without a supported
enforcer, `:argv` remains unchanged (the platform capability gap is explicit),
while the policy lookup still must succeed.
sourceraw docstring

supported?clj

(supported?)

True when the current OS can ENFORCE a jail. macOS: Seatbelt via the system sandbox-exec. Linux: bubblewrap (bwrap) namespaces. Windows/other: not supported -- callers keep the cooperative admission gate as the floor and unenforceable-reason explains why so sandbox: true never silently no-ops.

True when the current OS can ENFORCE a jail. macOS: Seatbelt via the system
`sandbox-exec`. Linux: bubblewrap (`bwrap`) namespaces. Windows/other: not
supported -- callers keep the cooperative admission gate as the floor and
`unenforceable-reason` explains why so `sandbox: true` never silently no-ops.
sourceraw docstring

unenforceable-reasonclj

(unenforceable-reason)

Nil when supported?, else a human string explaining why the jail CANNOT be enforced on this host -- so a requested sandbox: true fails LOUD instead of passing the child through unconfined. Distinguishes 'wrong OS' from 'right OS, enforcer binary missing' (the actionable case: install the tool).

Nil when `supported?`, else a human string explaining why the jail CANNOT be
enforced on this host -- so a requested `sandbox: true` fails LOUD instead of
passing the child through unconfined. Distinguishes 'wrong OS' from 'right OS,
enforcer binary missing' (the actionable case: install the tool).
sourceraw docstring

unregister-session-jail!clj

(unregister-session-jail! session-id)

Drop this session's registered jail policy (loop dispose).

Drop this session's registered jail policy (loop dispose).
sourceraw docstring

wrap-argvclj

(wrap-argv argv policy)

Given the base executor argv and a jail POLICY value, return the argv to spawn. macOS wraps the first managed process with sandbox-exec (descendants inherit the kernel policy and are left unwrapped, since Seatbelt rejects a second application). Linux wraps with bubblewrap (bwrap) namespaces. When a jail is requested but the host cannot enforce it, the child is passed through UNWRAPPED but a loud one-time warning fires -- sandbox: true never silently no-ops.

Given the base executor argv and a jail POLICY value, return the argv to spawn.
macOS wraps the first managed process with `sandbox-exec` (descendants inherit
the kernel policy and are left unwrapped, since Seatbelt rejects a second
application). Linux wraps with bubblewrap (`bwrap`) namespaces. When a jail is
requested but the host cannot enforce it, the child is passed through UNWRAPPED
but a loud one-time warning fires -- `sandbox: true` never silently no-ops.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close