Hi π Is there anyway to use clojure.core/evalwithout leaking memory (due to the Class metadata ending up in the Metaspace leading to a java.lang.OutOfMemoryError: Metaspace at some point)? At the moment I'm using https://github.com/babashka/sci for that reason, but the generated Clojure Code is trusted (our code generates it) so using SCI feels like an unnecessary step for my use case.
Yes
Hi Steven, I never tried using clojure.core/eval in production, since a conversation with Claude Code informed me that this problem exists.
Okay so you've never actually hit the issue yourself or witnessed/reproduced it?
Hi Max. I'm curious, before you switched to babashka, how long was your JVM running and handling eval jobs before this error happens?
I'm really curious as to how do you even end up using eval enough for this to be a problem.
Leaving that aside, to my understanding eval always compiles a fn and loads it via its classloader, but potentially (IDK) it may get GC'd.
Over the years I noticed that you can make some complex processes simpler by generating a little piece of code that performs it. Thereby you turn a bunch of side-effects into a read (returns the generated code). Our video rendering process for example needs to: β’ take a screenshot with transparent background of each layer. β’ Then it invokes https://www.mltframework.org/ to generate the video. β’ Then ffmpeg to extract a preview picture. β’ It uploads the video to Google Cloud Storage β’ And does a Datomic transaction so that the video shows up in our UI In the old version, it would probably take a couple of hours to extract and understand this workflow out of our large code base. The new solution generates the script and it is pretty straight-forward to read, even if you see it for the first time.
I don't expect SCi to add much overhead in this scenario. The function calls that call external processes dominate the time that interpretation time adds. Itβs nice to be able to ditch a dependency or course. You could build your own interpreter to bypass eval.
@borkdude thanks a lot for creating SCI and babashka, they are awesome π€© I'm fine with keeping the dependency. And as you mentioned stuff like ffmpeg make the compiled vs. interpreted detail completely irrelevant. However, if the (trusted) scripts have access to "dangerous" things anyway, is there a yolo mode? π Meaning something in SCI to just whitelist everything?
Hmm, not currently.
Are you aware of sci/copy-ns?
No, I just have looked at sci/copy-ns source code the first time. I'm not entirely sure how to use it? Currently, I'm doing:
(defn eval!
[form]
(let [ctx (sci/init
{:namespaces
(into {}
(map (fn [ns]
[ns (ns-publics ns)]))
['clojure.core
'clojure.string
'
'babashka.process
'app.effect
'app.blob.store
'storrito.effect])
:classes {'java.lang.Throwable Throwable
'java.io.File java.io.File}})]
(sci/eval-form ctx
form))) copy-ns is a bit like this but more advanced, it also copied macros properly
Ah okay, cool
clojure.core and clojure.sring should probably not be necessary since they come with SCI
unless you're missing some core functions
that are new
anyway, if this works, it seems like a minimal amount of work to maintain :)
Yeah, totally fine π I just thought about using clojure.core/eval since is yet another detail you have to explain your colleagues or the agent. Claude Code for example today just used clojure.core/eval since it wasn't aware of my SCI helper namespace, but I will add it to the claude.md
For other use cases: how secure is it to rely on SCI as a sandbox? Looking at things like meltdown and spectre. Could they be mitigated by not providing a way to receive an exact timestamp. Should it be wrapped into something like gvisor or a microvm?
SCI sandboxing should be safe in the sense that it doesn't let you execute code you don't give it access to. But there is one main caveat: it doesn't give you any control over how long your code is running
SCI runs directly in your host and doesn't do anything special with regards to system resources, it doesn't limit RAM or the time it's running
Okay, so an endless loop, adding objects to an array to fill the heap would be a problem. Sorry, for hijacking this thread for further questions π As the creator of all these great projects like Squint, Cherry, SCI, babashka, nbb, ... what would you use as a sandbox to execute Clojure code that has been generated by an AI agent in response to a user prompt? I'm considering to rely on something like https://e2b.dev/ but having something more lightweight would of course be preferable. Especially if the Clojure Code for example only assembles a few Hiccup pages or so.
Currently I'm just reviewing the code and confirming it by hand
claude also has a --sandbox flag which will use macOS's sandboxing feature
Yeah, I allow Claude Code to even eval code via nrepl without asking anymore π But if you build an AI feature into your web app, then any user could prompt the AI to write malicious code.
or you could use a webbrowser as a sandbox if it's just pure clojure functions
yeah, that sounds like something you want to run in a container
Yes, but the container would also only be kind of safe with something like https://gvisor.dev/
But I also already thought about using a web browser as sandbox, like a headless chrome on the server. Probably, one of the most secure sandboxes that are available for free.
However, I would love to see AI generate more Clojure code instead of JS clojure-spin I think it would even be beneficial for our customers, since Clojure is faster to learn than JS, if someone like to go down this rabbit hole.