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"}}}
build.clj(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 artifact
When 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 bp
Or clone and run via the bb task:
git clone https://github.com/dcj/build-provenance && cd build-provenance
bb run bp --help
bp inspect [--json] FILE...Print 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)'
bp discover [--json] DEPS_EDNResolve 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 inspect
If 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:
:commit, :commit-abbreviated, :branch, :describe) are emitted as nil.:dirty is conservatively set to true (we cannot prove the tree is clean).The writer DOES throw on programmer errors:
:lib must be a qualified symbol like 'group/artifact.:version must be a string.:class-dir (for write!) 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/read-string), kebab-case keys, no parser dependency on the reader.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.
The full API reference is embedded in the JAR as Markdown via codox-md, and is discoverable at runtime through clj-doc-browse:
(require '[doc.browse :as docs])
(docs/print-doc "build-provenance.write")
(docs/print-doc "build-provenance.read")
MIT License — Copyright (c) 2026 Clark Communications Corporation
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |