clojure 2026-06-29

What are the best tools for running security analysis on Clojure codebases? Main thing I found for SAST is clj-watson, but the last update was 4 years ago. I'm interested in both libraries and SaaS offerings I could use.

Four years? You're looking at the wrong thing. It was updated last month with a major new release: https://github.com/clj-holmes/clj-watson

clj-watson is what we use at work, which is why I'm one of the maintainers 🙂

@seancorfield my mistake, I meant clj-holmes. Looking for SAST.

Ah, source analysis. I don't know of anything for Clojure (and wouldn't recommend clj-holmes, TBH -- we've found it problematic to use, even with clj-watson maintenance).

Mythic will send you 400 issues no problem

AOT compilation in libraries: Is there ever a case for it? I know the stdlib is aot'd and direct linked, but other than that (bonus question: How feasible is it to have the stdlib not aot'd. I have a reason for it i think)

A big one is environments that can't compile code at runtime like graalvm and android (is that still the case there?)

but that isn't a reason to aot libraries, that is a reason to aot programs

misread, shame on me

the big issue with aot'iong libraries is transitive compilation (in a coupld of ways), in general because clojure itself doesn't depend on other libraries (including clojure itself as a library) it doesn't have those issues when aot compiled

so it is aot compiled so it loads faster

people do distribute aot compiled clojure libraries though (I think the datomic client stuff may be the big example of this?)

the transitive compilation problems come in two forms basically: 1. dependencies are also aot compiled and the code is generate for those so if you are not careful you will end up distributing your dependencies code in your library build which causes problems for consumers understanding what versions of code they are running 2. your dependencies are part of your compilation environment, and can influence how the code is compiled, and result in the code being compiled in a fragile way that won't work with other versions. (this is the more subtle issue that people run afoul of when aot'ing their library)

I'll give a little context on why I am asking this, in case its helpful: I am working on a maven central replacement where a constraint is that artifacts must be JVM modules. I have reasons for that, but its a whole tangent. If a clojure library is packaged up with a dummy module-info it works perfectly fine. It is just resources. But when they are AOT'd I encounter two issues 1. There is something going on where classes get defined twice (genuinely don't have a root cause yet, but while clojure works, as soon as I pull in clojure.spec something breaks) 2. Namespaces are translated like so (ns ) -> class (ns demo.stuff) -> class demo.stuff Which bumps into the one relevant restriction of modules: a given package can only be owned by one module. This is fine until it isn't (one wacko direction i've thought of is changing clojure so that the translation is (ns ) -> class demo.app.$ (ns demo.stuff) -> class demo.stuff.$ but that gets annoying instantly)

Alex Miller (Clojure team) 2026-06-29T17:24:53.125739Z

we do build and publish a "slim" classifier source-only version of clojure too, btw

I will try the -slim. I did not know about that

maybe you need let rec?

❓ 1

(module reference joke)

Alex Miller (Clojure team) 2026-06-29T17:30:34.258579Z

RIP Joe 😢

Fascinating thread

but going to try out the slim artifact with my scheme. If it works i'll get excited. I don't give much thought to kotlin and scala in my schemes, but I want to make sure clojure works

Syntax error (ClassNotFoundException) compiling at (clojure/repl.clj:285:4).
sun.misc.Signal
Using the slim artifact on a newer JDK

Alex Miller (Clojure team) 2026-06-29T19:38:43.932419Z

that class is still available in the most recent JDKs and afaik they do not have a plan to remove it yet

it is due to enabling modules

Alex Miller (Clojure team) 2026-06-29T19:43:22.026919Z

$ clj
Clojure 1.12.5
user=> (System/getProperty "java.version")
"26.0.1"
user=> (require '[clojure.repl :as repl])
nil
user=> (repl/set-break-handler! #(println "hi" %))
#object[clojure.repl.proxy$java.lang.Object$SignalHandler$d8c00ec7 0x5e39850 "clojure.repl.proxy$java.lang.Object$SignalHandler$d8c00ec7@5e39850"]
user=> hi -- caught signal SIGINT

user=>
^^ test on Java 26 for example

It is due to removing unused modules - I am adding a synthetic module descriptor to org.clojure

so i didn't include jdk.unsupported initially

Does AOT make sense if you don't want to deliver the source code?

jlink --add-modules ALL-MODULE-PATH -p jars --output jdk --launcher clojure=org.clojure/clojure.main

right now the issue I identified is that clojure (when it is a module) won't find resources in other modules...

jdk/bin/clojure
Clojure 1.12.5
user=> (require '[ring.util.response])
Execution error (FileNotFoundException) at user/eval8844 (REPL:1).
Could not locate ring/util/response__init.class, ring/util/response.clj or ring/util/response.cljc on classpath.
user=> (require '[clojure.core.specs.alpha])
nil
user=>

Executable jar files. The main function must be AOT.

> Executable jar files. The main function must be AOT That makes sense for an AOT compiled app. Less so for libraries unless they want to play double duty

emccue@Ethans-MacBook-Pro linkedclj % jdk/bin/clojure
Clojure 1.12.5
user=> (java.lang.ModuleLayer/boot)
#object[java.lang.ModuleLayer 0x71ce19ee "org.clojure.core.specs.alpha, java.logging, java.xml, java.desktop, java.transaction.xa, java.datatransfer, jdk.unsupported, org.clojure.spec.alpha, java.prefs, , java.base, org.clojure, java.sql"]
user=> ^D
emccue@Ethans-MacBook-Pro linkedclj % jdk/bin/java --list-modules
java.base@25.0.1
java.datatransfer@25.0.1
java.desktop@25.0.1
java.logging@25.0.1
java.prefs@25.0.1
java.sql@25.0.1
java.transaction.xa@25.0.1
java.xml@25.0.1
jdk.internal.vm.ci@25.0.1
jdk.unsupported@25.0.1
org.clojure@1.12.5 open
org.clojure.core.specs.alpha@0.5.81 open
org.clojure.spec.alpha@0.6.249 open
ring.core@1.15.5 open
So interestingly ModuleLayer.boot() doesn't have all the modules that come with the JDK. I need to look into clojure.main

--add-modules=ALL-SYSTEM
And it works