kargo.lock
kargo.lock is written by Kargo after resolving dependencies. Commit it for applications and libraries alike: it makes builds reproducible and detects tampered downloads.
What it records
Section titled “What it records”For every resolution (one per target and classpath, such as jvm/runtime):
- a fingerprint of the inputs (requested dependencies, BOMs, repositories, and the version of Kargo’s resolver). The repositories are the ones
kargo.tomldeclares, so a mirror or--vendoredkeeps the lock, - the root modules, each once,
- every resolved module with its version and the dependencies it asked for,
- the repository path and SHA-256 of every downloaded file.
kargo bench pins the libraries of the benchmarks under jvm/bench (the dependencies, dev-dependencies, kotlinx-benchmark and JMH) and JMH’s harness generator under jvm/bench-tools.
The JS target’s resolutions are js/main and js/test, like the Wasm target’s wasm/main and wasm/test.
The web resources of Wasm libraries from a repository are pinned under wasm/web-resources (and wasm/web-resources-test for the tests): one entry per library that ships them, with its -web-resources.zip. A project whose libraries ship none has no such entry.
A [[generate]] step that runs a tool, or the protobuf built-in, pins what it downloads under generate/<name>: the tool and its dependencies, or protoc and the gRPC plugins.
Build tools
Section titled “Build tools”What the build downloads and runs is pinned too, under tools/, so a changed or tampered compiler or plugin fails the build like a changed dependency does:
| Key | What it pins |
|---|---|
tools/kotlin-compiler |
The Kotlin compiler (kotlin-compiler-embeddable) and the Build Tools API that incremental compilation uses, with their dependencies. Packages built together share one Kotlin version, so a workspace has one entry |
tools/<artifact> of a compiler plugin |
The compiler plugin of a framework, such as tools/kotlin-serialization-compiler-plugin-embeddable |
tools/junit-platform-console-standalone |
The JUnit console launcher that runs the JVM tests |
tools/ksp |
The KSP tool and its dependencies, when a ksp dependency needs it |
tools/dokka-cli, tools/dokka-plugins |
Dokka and its plugins, once kargo doc ran |
tools/ktlint-cli, tools/detekt-cli |
ktlint and detekt, once kargo fmt or kargo lint ran |
Each entry is recorded and checked like a dependency resolution, and --locked fails when one is missing or out of date. kargo fetch locks the tools of the enabled targets (--tools adds Dokka, ktlint and detekt), kargo update locks again the ones the lockfile had, and a build locks any other the first time it runs it. A Compose Desktop jar for other platforms (kargo package --platforms) pins the Skiko natives it adds the same way, as tools/skiko-awt-runtime-<platform>. In a workspace, every entry but the compiler’s is keyed with its package’s name, like the resolutions.
A profile with dependencies of its own has resolutions of its own, named after it: jvm/runtime@staging is the runtime classpath of the staging profile. The profiles without such dependencies all share jvm/runtime.
# This file is automatically generated by Kargo.# It is not intended for manual editing.version = 1
[resolution."jvm/runtime"]inputs = "4f9c..."roots = ["org.jetbrains.kotlinx:kotlinx-datetime"]
[[resolution."jvm/runtime".package]]module = "org.jetbrains.kotlinx:kotlinx-datetime-jvm"version = "0.7.1"dependencies = ["org.jetbrains.kotlin:kotlin-stdlib:2.1.20"]
[[resolution."jvm/runtime".package.files]]path = "org/jetbrains/kotlinx/kotlinx-datetime-jvm/0.7.1/kotlinx-datetime-jvm-0.7.1.jar"sha256 = "b3a1..."A module is locked once, at one version, and lists all of its files. A module declared with a classifier, such as a native library, lists its main jar and the classifier jar:
[[resolution."jvm/runtime".package]]module = "org.lwjgl:lwjgl"version = "3.3.6"
[[resolution."jvm/runtime".package.files]]path = "org/lwjgl/lwjgl/3.3.6/lwjgl-3.3.6.jar"sha256 = "5c7e..."
[[resolution."jvm/runtime".package.files]]path = "org/lwjgl/lwjgl/3.3.6/lwjgl-3.3.6-natives-windows.jar"sha256 = "91d0..."How it’s used
Section titled “How it’s used”- If the inputs of a resolution haven’t changed, Kargo reuses the locked result without touching the network for metadata.
- Every artifact is checked against its locked SHA-256 before it is used. Missing files are downloaded and checked. A cached file in
~/.kargo/cache/maventhat doesn’t match is an error: Kargo doesn’t use it, and doesn’t replace it either. - Hashing a large dependency tree on every build is slow, so Kargo keeps each cached file’s hash in a
.stampfile next to it, and reuses that hash while the file’s size and modification time are unchanged. A copy whose content was replaced without changing its size or modification time is therefore only caught by--locked, which hashes every cached file. - When
kargo.lockis rewritten (kargo add,kargo update, a manifest change), cached artifacts are checked against the checksum their repository publishes, in parallel. A file that is unchanged since it was last checked (a.verifiedstamp next to it records its size and modification time) or whose SHA-256 equals its entry in the previous lockfile is not checked again, so adding a dependency does not re-verify the whole tree over the network. - Downloads are also checked against the checksum the repository publishes next to them: the
.sha256file when there is one (Maven 3.9 and Gradle publish it), else the.sha1. A repository on disk that has neither vouches for its own copy. A file without any published checksum is accepted, and-vprintsUnverifiedfor it;--require-checksumsmakes that an error. - When the lockfile is written (the first build, or
kargo update), a file that was already in~/.kargo/cache/mavenis checked against the repository’s checksum too. A corrupt cached file (for example, left by an interrupted download) is downloaded again, so a bad file is never locked. - The cache keeps the files of each repository apart. Files from Maven Central and Google (or their mirrors) are stored by their plain Maven path,
~/.kargo/cache/maven/<group>/<artifact>/<version>/, since a release there never changes. Everything else, including every SNAPSHOT, is stored in a directory of its repository,~/.kargo/cache/maven/@<name>-<hash of the URL>/<group>/.... So a private or forked artifact that reuses the coordinates of a public one never replaces it, and one repository’s copy is never used for another.kargo.locklists the plain Maven path without the repository directory, and a locked build uses the cached copy that matches the lock. Files cached by an older Kargo that are not from Central or Google are downloaded again once. - When you change dependencies in
kargo.toml, only the affected resolutions are recomputed. - The file is only rewritten when its content changes.
SNAPSHOT versions
Section titled “SNAPSHOT versions”A SNAPSHOT keeps its version (1.0-SNAPSHOT), but the files it locks have the timestamped names the repository stores, so the lockfile records which build you use:
[[resolution."jvm/runtime".package]]module = "com.acme:lib"version = "1.0-SNAPSHOT"
[[resolution."jvm/runtime".package.files]]path = "com/acme/lib/1.0-SNAPSHOT/lib-1.0-20240101.120000-1.jar"sha256 = "9d2e..."The build stays locked until kargo update (which prints Updating com.acme:lib v1.0-SNAPSHOT (20240101.120000-1) -> v1.0-SNAPSHOT (20240102.080000-2)) or --refresh picks a newer one. A selective kargo update <group:artifact> keeps every other SNAPSHOT on its locked build. See SNAPSHOT versions.
Workspaces and path dependencies
Section titled “Workspaces and path dependencies”Packages built together share one kargo.lock: the workspace’s, at its root, or the consuming package’s when it has path dependencies outside a workspace. Each package’s resolutions are keyed with its name, such as mathlib/jvm/runtime. Outside a workspace, the consuming package keeps the plain keys (jvm/runtime). kargo update re-resolves every package of the project.
Checksum mismatches
Section titled “Checksum mismatches”When a file doesn’t match kargo.lock, the error is checksum mismatch for .... For a cached file, it names the file to delete. Kargo never uses such a file, so deleting it is the way to get a fresh copy: the next build downloads it again and checks it against the lockfile. If the download is also a mismatch, the error says which side is suspect:
- If the downloaded file matches the checksum the repository publishes, the lockfile entry is likely wrong, for example because it was recorded from a corrupt cached file. If you trust the repository, run
kargo updateto re-lock it. - If the repository publishes no checksum, Kargo can’t tell: the artifact may have changed upstream or been tampered with, or the lockfile entry may be wrong. Investigate before running
kargo update.
Requiring published checksums
Section titled “Requiring published checksums”A repository that publishes no checksum for a file gives Kargo nothing to check the download against before it is locked. --require-checksums (or KARGO_REQUIRE_CHECKSUMS=1) makes such a download an error, which suits CI:
kargo build --locked --require-checksumsA file that kargo.lock already pins is checked against the lock instead, so a locked project builds even from a repository without checksums. Offline, a cached file that was never checked against its repository can’t be locked with --require-checksums; run once online first.
Commands
Section titled “Commands”| Command | Effect |
|---|---|
kargo update |
Re-resolve everything and rewrite the lockfile (picks up new versions within ranges and the newest build of each SNAPSHOT) |
kargo update <group:artifact>... |
Re-resolve only these modules; every other module keeps its locked version |
kargo update <group:artifact> --precise <version> |
Lock one module at exactly this version. It stays locked until kargo.toml changes or you update that module again |
kargo update --dry-run |
Print what would change (Updating, Adding, Removing) without writing the lockfile |
--locked |
Fail if the lockfile would change, and hash every cached artifact instead of trusting its .stamp. Use it in CI. |
--refresh |
Ignore the lockfile and cached metadata for this run, and re-verify cached artifacts against the repository (re-downloading corrupt ones) |
If the lockfile is corrupt or from an unsupported version, delete it; Kargo regenerates it on the next build.
A Kargo release that changes how dependencies resolve changes the inputs fingerprint, so the affected resolutions are computed again the first time you build with it. With --locked that is an error: after upgrading Kargo, build once without --locked and commit the updated kargo.lock.