Learn the modern isolation patterns for running risky or untrusted work around Clojure systems, and why OS, process, container, or service isolation is usually stronger than in-process JVM sandboxing.
Sandboxing: Running code or data-processing work inside a constrained environment so that compromise or misbehavior has a much smaller blast radius.
The old intuition for JVM sandboxing was often “use the Security Manager and a library wrapper.” That guidance is no longer a solid default. OpenJDK deprecated the Security Manager for removal in JEP 411 and later moved to permanently disable it in JEP 486, which means modern Clojure systems should usually think about isolation at the process, container, service, or VM boundary instead of assuming strong in-process sandboxing is available.
If the workload is genuinely untrusted or high risk, stronger patterns usually include:
That is a better default than letting untrusted logic run inside the same privileged JVM as the main application.
The diagram below shows the safer architecture shape: the main service never gives the untrusted execution environment ambient access to everything.
flowchart TD
User["Untrusted input or code"] --> Broker["Main Clojure service"]
Broker --> Policy["Validate request and assign narrow execution policy"]
Policy --> Worker["Isolated worker or container"]
Worker --> Result["Bounded result or rejected execution"]
Worker --> Limits["CPU, memory, time, network, filesystem limits"]
Historically, tools such as Clojail were interesting because they tried to constrain evaluation inside the JVM. That idea is still useful to understand conceptually, but it is not the modern first-choice security boundary for hostile workloads.
Why?
That does not mean all in-process restrictions are worthless. It means you should not confuse them with strong sandboxing.
Even with a container or worker boundary, think about what the task is allowed to do:
Isolation is strongest when combined with least privilege rather than treated as a magic wall.
These patterns are especially relevant for:
The question is always the same: what if this workload is malicious, buggy, or simply much more expensive than expected?
A sandbox is not only where the code runs. It is also how the operator regains control:
Without those, the “sandbox” may still become an opaque denial-of-service amplifier.
They may help with policy, but they are not the same as OS or service isolation.
A container that still has the main service’s secrets, network reach, and writable filesystem is only partially isolated.
Untrusted or semi-trusted workloads often fail through exhaustion before they fail through direct privilege abuse.
If the system cannot stop runaway work quickly, the isolation story is incomplete.
When code or input is truly untrusted, isolate it outside the main Clojure runtime whenever possible. Use process, container, service, or VM boundaries plus explicit limits on resources and reachable capabilities. Treat historical in-JVM sandboxing techniques as narrow tools, not as the default security answer for modern JDKs. In Clojure, strong sandboxing starts with architecture, not with a clever evaluation wrapper.