Tiny Clojure utility for embedding git provenance metadata into a JAR at build time, and reading it back at runtime.
When called from a tools.build build.clj immediately before the b/jar step, this lib emits two small parallel resource files containing the same provenance data:
META-INF/<group>/<artifact>/build-provenance.edn ← canonical Clojure shape (kebab-case keys)
META-INF/<group>/<artifact>/build-provenance.json ← for non-Clojure consumers (camelCase keys)
Both contain seven fields:
;; build-provenance.edn
{:artifact "com.dcj/build-provenance"
:version "0.1.0"
:commit "0d84c2df7a50c8783d895ad0ca12c1ff018b23c6"
:commit-abbreviated "0d84c2d"
:branch "main"
:describe "v0.1.0-2-g0d84c2d"
:dirty false}// build-provenance.json
{
"artifact": "com.dcj/build-provenance",
"version": "0.1.0",
"commit": "0d84c2df7a50c8783d895ad0ca12c1ff018b23c6",
"commitAbbreviated": "0d84c2d",
"branch": "main",
"describe": "v0.1.0-2-g0d84c2d",
"dirty": false
}A consumer can read this back at runtime to identify exactly which build of the lib is loaded — useful for diagnostics, deployment audits, and provenance round-tripping into SBOM-adjacent contexts. Clojure consumers read the EDN; non-Clojure tooling (jq, CI bash, supply-chain scanners) reads the JSON.
deps.edn:
{:deps {com.dcj/build-provenance {:mvn/version "0.2.0"}}}(ns build
(:require [clojure.tools.build.api :as b]
[build-provenance.write :as bp]))
(def lib 'com.example/my-library)
(def version "1.0.0")
(def class-dir "target/classes")
(defn ci [opts]
(b/copy-dir {:src-dirs ["src" "resources"] :target-dir class-dir})
(bp/write! {:lib lib :version version :class-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file (format "target/%s-%s.jar" (name lib) version)}))(require '[build-provenance.read :as bp])
(bp/read 'com.example/my-library)
;; => {:artifact "com.example/my-library" :version "1.0.0" :commit "..." ...}
;; or nil if no build-provenance.edn is on the classpath for that artifactWhen you don't want to enumerate lib coords up front — e.g. a service /health endpoint that lists every build identity in the running JVM — use discover:
(require '[build-provenance.read :as bp])
(bp/discover)
;; => [{:artifact "com.dcj/build-provenance" :version "0.2.0" :commit "..." ...}
;; {:artifact "com.example/my-library" :version "1.0.0" :commit "..." ...}
;; ...]discover walks the classpath, finds every META-INF/<group>/<artifact>/build-provenance.edn resource, and returns the parsed maps sorted by :artifact. Returns an empty vector if no instrumented JARs are present. Useful for diagnostics, deployment audits, and SBOM-adjacent reporting.
The runtime reader is EDN-only — Clojure consumers don't need a JSON parser. Non-Clojure consumers can read the parallel .json resource directly:
unzip -p target/foo.jar META-INF/com.example/my-library/build-provenance.json | jq .A Babashka-based CLI ships in this repo for shell-side inspection of provenance metadata in JARs and exploded class-dirs — no JVM startup required. The same entry point also runs under the JVM (clj -M -m build-provenance.cli ...).
Via bbin:
bbin install io.github.dcj/build-provenance --as bpOr clone and run via the bb task:
git clone https://github.com/dcj/build-provenance && cd build-provenance
bb run bp --helpPrint provenance from each JAR or directory. Default output is EDN sorted by :artifact; --json emits a JSON array of the canonical .json shapes (camelCase keys) read directly out of each JAR — no EDN→JSON conversion in the CLI, so the bytes are bit-identical to what is embedded.
bp inspect target/build-provenance-0.2.0.jar
bp inspect ~/.m2/repository/com/example/foo/1.0.0/foo-1.0.0.jar
bp inspect --json target/*.jar | jq 'sort_by(.artifact)'Resolve a deps.edn to a classpath via clojure -Spath, then list every instrumented artifact in the resolved set.
bp discover deps.edn
bp discover --json deps.edn | jq 'map({artifact, version, commit})'For an arbitrary classpath without writing a deps.edn:
clojure -Spath | tr ':' '\n' | xargs bp inspectIf your library embeds build-provenance, advertise it in your README so downstream consumers know bp inspect and (build-provenance.read/discover) will surface it.
Drop this snippet near the top of your README:
[](https://github.com/dcj/build-provenance)
This JAR embeds [build-provenance](https://github.com/dcj/build-provenance) metadata at `META-INF/<group>/<artifact>/build-provenance.{edn,json}`. Inspect with `bp inspect path/to.jar` from a shell, or `(build-provenance.read/read 'group/artifact)` from a Clojure REPL.Use the badge URL verbatim — the convention only works if every consumer's badge links back to this repo, so a clicker lands on documentation explaining what the metadata is for.
Best-effort. If git is unavailable on PATH, the project is not a git working tree, or HEAD is detached:
- Unknowable string fields (
:commit,:commit-abbreviated,:branch,:describe) are emitted asnil. :dirtyis conservatively set totrue(we cannot prove the tree is clean).- The EDN is still written; the writer never throws on a git failure.
The writer DOES throw on programmer errors:
:libmust be a qualified symbol like'group/artifact.:versionmust be a string.:class-dir(forwrite!) must be a string.
The two formats are sourced from a single collect call inside write!, so they cannot drift. Each format optimizes for its audience:
- EDN — canonical for Clojure tooling. Native (
edn/read-string), kebab-case keys, no parser dependency on the reader. - JSON — canonical for everything else. camelCase keys per JSON convention. Hand-rolled writer (no JSON dependency in this library) since the schema is a flat seven-field map of strings/booleans/nils.
META-INF/<group>/<artifact>/build-provenance.{edn,json} mirrors Maven's convention of META-INF/maven/<group>/<artifact>/pom.xml. Per-artifact pathing matters: when multiple build-provenance-instrumented JARs sit on the same classpath, each artifact's metadata is independently addressable.
MIT License — Copyright (c) 2026 Clark Communications Corporation