Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublicTeam
Assigneesstack72

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, user swamp, `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 describe return Unknown model type.
  • Latent until restart. A long-running swamp serve keeps 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

  1. Host with SELinux Enforcing; install swamp to /usr/local/bin/swamp (bin_t).
  2. 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.
  3. 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-planner

Evidence 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)'  # -> 42

A 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 deno on PATH (reported "no system deno found in PATH"); a PATH-provided deno at a non-bin_t path would hit the same exec denial anyway.
  • The remediation hint xattr -c is 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)

  1. Restore an exec-appropriate context after extraction. After writing ~/.swamp/deno/deno, run the equivalent of restorecon/set bin_t (or extract into a directory whose default type is execable), so the runtime is execable under Enforcing SELinux without host intervention.
  2. 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.)
  3. 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.
  4. 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.
  5. Make the hint OS-aware. Replace the xattr -c hint on Linux with an SELinux-context hint (or detect getenforce/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.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 10 MOREREVIEW+ 4 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

8/18/2026, 7:57:14 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
kneel moved this from the private collective Elowish to public9/1/2026, 2:59:51 AM
stack72 assigned stack728/18/2026, 2:56:57 PM
Editable. Press Enter to edit.

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 EACCESexit_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:

  1. 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 -c is macOS-only and irrelevant on Linux. This masking cost hours of misdiagnosis (SELinux relabel, version rollback — all red herrings).
  2. 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.

stack72 commented 8/18/2026, 7:57:27 PM

Thanks @kneel for reporting this! The fix has been merged and a release is on its way. We appreciate your contribution to swamp.

Sign in to post a ripple.