Relationships
#1706 swamp extracts embedded deno into $HOME → unexecutable under SELinux Enforcing; all user extensions fail to load
Opened by kneel · 8/18/2026· Shipped 8/18/2026
Bug: swamp extracts embedded deno into $HOME and can't exec it under SELinux Enforcing → all user extensions fail to load
Summary
On an SELinux Enforcing host, swamp extracts its embedded deno runtime to
$HOME/.swamp/deno/deno. Files created under a home directory get the
user_home_t SELinux type. The swamp process (running from /usr/local/bin/swamp,
labeled bin_t, launched by systemd) then tries to exec that user_home_t binary
as its "deno health check". The exec is denied by policy, the health check fails,
swamp re-extracts (producing another user_home_t file), fails again, and finally
"falls back to system deno" — which it also does not find/accept — so it loads
zero user model/vault/report extensions.
Net effect: every custom extension silently stops resolving
(Unknown model type: @kneel/...), which takes down any workflow, schedule, vault,
or report that depends on a user extension.
The embedded deno binary itself is fine — it runs --version, eval, run, and
--unstable-bundle correctly when invoked directly. The only problem is its
SELinux file context after extraction.
Environment
- swamp:
20260817.010424.0-sha.21133ea1(installed to/usr/local/bin/swamp,bin_t) - deno (extracted):
2.8.3 (stable, release, x86_64-unknown-linux-gnu) - OS: RHEL/Rocky 9 (el9), kernel
6.17.7-1.el9.elrepo.x86_64 - SELinux:
Enforcing - Run context: systemd unit
swamp-serve.service, userswamp, `HOME=/home/[REDACTED]
Impact
- Severity: high. All user extensions fail to load on the host. Scheduled
workflows that use custom model types fail at run time; CLI
model method run/model type describereturnUnknown model type. - Latent until restart. A long-running
swamp servekeeps the previously loaded binary mapped in memory, so it keeps working across an on-disk binary upgrade. The failure only surfaces on the next restart (or the next process that extracts fresh) — making it easy to ship a broken binary and not notice until an unrelated restart hours/days later.
Reproduction
- Host with SELinux
Enforcing; installswampto/usr/local/bin/swamp(bin_t). - Run any command that needs a user extension as the service user, e.g.
swamp model method run <instance> <method>where the instance's type is a local/pulled extension. - Observe repeated health-check failures and
Unknown model type.
[WRN] swamp·runtime·deno: Extracted deno at "/home/[REDACTED]/.swamp/deno/deno" failed health check — re-extracting
[WRN] swamp·runtime·deno: Embedded deno at "/home/[REDACTED]/.swamp/deno/deno" failed health check after extraction — falling back to system deno
[WRN] swamp·cli: Failed to load user model extensions: "Embedded deno at /home/[REDACTED]/.swamp/deno/deno failed health check and no system deno found in PATH. Try: xattr -c /home/[REDACTED]/.swamp/deno/deno"
[WRN] swamp·cli: Failed to load user vault extensions: "..."
[WRN] swamp·cli: Failed to load user report extensions: "..."
Error: Unknown model type: @kneel/route-plannerEvidence it's a labeling problem, not a broken deno
$ getenforce
Enforcing
$ ls -Z /home/[REDACTED]/.swamp/deno/deno
unconfined_u:object_r:user_home_t:s0 /home/[REDACTED]/.swamp/deno/deno
$ ls -Z /usr/local/bin/swamp
unconfined_u:object_r:bin_t:s0 /usr/local/bin/swamp
# The extracted deno runs fine when invoked directly as the service user:
$ sudo -u swamp env HOME=/home/[REDACTED] /home/[REDACTED]/.swamp/deno/deno --version
deno 2.8.3 (stable, release, x86_64-unknown-linux-gnu)
$ sudo -u swamp env HOME=/home/[REDACTED] /home/[REDACTED]/.swamp/deno/deno eval 'console.log(42)' # -> 42
$ sudo -u swamp env HOME=/home/[REDACTED] /home/[REDACTED]/.swamp/deno/deno eval --unstable-bundle 'console.log(42)' # -> 42A one-off chcon -t bin_t /home/[REDACTED]/.swamp/deno/deno makes the health check
pass — but swamp re-extracts on the next failure and resets the label, so a manual
relabel is not a durable fix (and shouldn't be required).
Root cause
swamp extracts a runtime it intends to execute into a location whose default
SELinux type (user_home_t, inherited from $HOME) is not execable by the swamp
domain on an Enforcing host. Extraction never restores an exec-appropriate context.
Why the existing fallbacks don't help
- Re-extraction reproduces the same bad label — infinite loop, then gives up.
- "Fall back to system deno" did not resolve it even with a working
denoonPATH(reported "no system deno found in PATH"); a PATH-provided deno at a non-bin_tpath would hit the same exec denial anyway. - The remediation hint
xattr -cis macOS quarantine-specific and misleading on Linux; it points users away from the actual (SELinux) cause.
Suggested fixes (swamp-side — this is the real fix)
- Restore an exec-appropriate context after extraction. After writing
~/.swamp/deno/deno, run the equivalent ofrestorecon/setbin_t(or extract into a directory whose default type is execable), so the runtime is execable under Enforcing SELinux without host intervention. - Don't clobber a healthy runtime. Health-check the existing extracted deno before re-extracting; skip extraction when it already passes. (This also lets an operator pin a correctly-labeled deno.)
- Support an operator-provided deno. Honor an env var / config
(e.g.
SWAMP_DENO_PATH) pointing at an external deno at a trusted (bin_t) path, and use it without extraction. Enables a clean host-managed runtime. - Fail loud, not silent. When no usable deno is found, surface a clear, actionable error instead of degrading to "zero user extensions loaded" with only WRN lines — a scheduled deployment can look healthy while loading nothing.
- Make the hint OS-aware. Replace the
xattr -chint on Linux with an SELinux-context hint (or detectgetenforce/ls -Z).
Workaround (interim, if a host-side stopgap is unavoidable)
Relabel the extracted deno to bin_t and prevent re-extraction from resetting
it (e.g. by pinning a correctly-labeled deno and relying on fix #2), or provide a
bin_t-labeled deno via fix #3. A bare chcon/restorecon alone is not durable
because swamp re-extracts on the next failed check.
Shipped
Click a lifecycle step above to view its details.
kneel commented 8/18/2026, 4:00:25 AM
CORRECTION — root cause found; the SELinux diagnosis in this report is WRONG.
The real failure: swamp's deno 'health check' fails when the swamp process's CWD is not accessible to the running user. strace shows the health-check child spawn does chdir(<cwd>) → -1 EACCES → exit_group(127); deno is NEVER exec'd. It was triggered by invoking swamp as a service user (sudo -u <svc>) from a shell whose CWD was another user's 0700 home dir — the spawned child can't chdir there, so the subprocess aborts before exec.
Two real bugs here:
- The error message is badly misleading — it blames the deno binary ('failed health check', 'no system deno found', 'Try: xattr -c') and points at SELinux/quarantine, when the actual cause is an inaccessible CWD (EACCES on chdir). The suggested
xattr -cis macOS-only and irrelevant on Linux. This masking cost hours of misdiagnosis (SELinux relabel, version rollback — all red herrings). - Suggest: surface the child's real failure (chdir EACCES / exit 127 / inaccessible cwd) in the error, and/or spawn deno with a guaranteed-accessible cwd (e.g. the swamp state dir) rather than inheriting an arbitrary one.
Not SELinux, not the swamp version (20260811 and 20260817 both affected identically), not disk/memory. Running the same command from a CWD the service user can access works immediately.
Sign in to post a ripple.