diff --git a/README.md b/README.md
index cd838b3..d97f1b1 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,37 @@ dependencies can instead be placed in separate or grouped layers so
application-only changes retain the same dependency-layer digests for
registry- and fleet-wide cache reuse.
+## How it fits together
+
+```mermaid
+flowchart
+ binary["java_binary or scala_binary"]
+ runtime["Runtime JARs
JavaRuntimeClasspathInfo"]
+ data["Binary runfiles
and explicit data"]
+ lock["Optional Maven lock file"]
+ rule["jvm_jar_layers"]
+
+ binary --> runtime --> rule
+ binary --> data --> rule
+ lock -.->|artifact routing| rule
+
+ rule --> maven["Maven dependency layers
/app/lib/*.jar"]
+ rule --> fallback["Fallback layer
app JARs + unmatched JARs"]
+ rule --> runfiles["Data layer
/app/<repo>/..."]
+ rule --> classpath["/app/lib/classpath"]
+ classpath --> fallback
+
+ base["Base JVM image"] --> image["OCI image rule"]
+ maven --> image
+ fallback --> image
+ runfiles --> image
+
+ image --> output["OCI image"]
+ output --> entrypoint["Entrypoint
java -cp @/app/lib/classpath MainClass"]
+ entrypoint -.->|loads intact JARs| maven
+ entrypoint -.->|loads app and fallback JARs| fallback
+```
+
## Install
Until the module is published to a registry, add a pinned override to the
@@ -25,10 +56,13 @@ client's `MODULE.bazel`:
```starlark
bazel_dep(name = "jvm_image", version = "0.1.0")
-git_override(
+archive_override(
module_name = "jvm_image",
- commit = "",
- remote = "https://github.com/stackb/jvm_image.git",
+ integrity = "sha256-FcnG0wSlMCAIGnLykKyQIshz8jauYHi8KEdDX5nNG+A=",
+ strip_prefix = "jvm_image-0.1.0",
+ urls = [
+ "https://github.com/stackb/jvm_image/releases/download/v0.1.0/jvm_image-v0.1.0.tar.gz",
+ ],
)
```
@@ -83,6 +117,38 @@ runfiles layer under `/app`. Workspace files use
the application uses Bazel's runfiles lookup conventions. Host JDK files,
binary launchers, and runtime JARs are excluded from this data layer.
+The `example/hello` oci_tarball has the following structure:
+
+```
+--- sha256:c4fd6f20ece135d1caf7d46368138b7aa58603e775f01c24479912075ec8c490 ---
+Mode Size Name
+-rw-r--r-- 34 B app/_main/greeting.txt
+-rw-r--r-- 36 B app/_main/runtime/native.marker
+
+--- sha256:19730c7f427ef030cf5b9eb1b610fc5d01855a9100e0ea08bdd5da06640fca7f ---
+-rw-r--r-- 1.2 kB app/lib/hello.jar
+-rw-r--r-- 2.3 kB app/lib/processed_listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
+-rw-r--r-- 364 B app/lib/classpath
+
+--- sha256:3abdf128e65e2e1437f3ac7346c0e9fabdf3f9842a99cbcbef42207181b64065 ---
+-rw-r--r-- 21 kB app/lib/processed_jsr305-3.0.2.jar
+
+--- sha256:1cbbdab98aee4c1ea5436f305f2cd68169443dfa4f0931becb4800d73371706f ---
+-rw-r--r-- 19 kB app/lib/processed_error_prone_annotations-2.36.0.jar
+
+--- sha256:58c28ed69316ce659037cb1df9c51533878a956c5e8bbe3bca7be9ef5bb428f7 ---
+-rw-r--r-- 4.8 kB app/lib/processed_failureaccess-1.0.2.jar
+
+--- sha256:a6ef98beaef5ab8b409ca6cec41b3d5be38804bcec418b3c3138cd470e5704e3 ---
+-rw-r--r-- 3.1 MB app/lib/processed_guava-33.4.0-jre.jar
+
+--- sha256:8d650354e3037e3908f78628d47b637d0ec1922988c5f24a75b2a15626502c8b ---
+-rw-r--r-- 12 kB app/lib/processed_j2objc-annotations-3.0.0.jar
+
+--- sha256:05fd8c687c7112da76548914db47d51d43f023ce2ac3cde4dc91c1a8aad92bd0 ---
+-rw-r--r-- 232 kB app/lib/processed_checker-qual-3.43.0.jar
+```
+
## Configuration notes
- `maven_lock_file` is optional. When omitted, every runtime JAR goes to the
diff --git a/example/hello/MODULE.bazel b/example/hello/MODULE.bazel
index 9523fef..8f9c3fa 100644
--- a/example/hello/MODULE.bazel
+++ b/example/hello/MODULE.bazel
@@ -57,6 +57,7 @@ pull = use_repo_rule("@rules_img//img:pull.bzl", "pull")
pull(
name = "distroless_java",
digest = "sha256:4eadd00d3bff73e6a7491dd36653c1d318ac93fb1fb2cd5eef768fd2b4238408",
+ layer_handling = "lazy",
registry = "gcr.io",
repository = "distroless/java25-debian13",
tag = "nonroot",