Reference¶
Quick-lookup tables for the identifiers, formats, and paths Brewlet uses.
Normative sources include the
capability-label contract,
core/internal/artifact/,
and SPECIFICATION.
Labels & annotations¶
Node labels (set by the provisioner; drive scheduling)¶
The
canonical capability-label contract
defines the stable runtime, JDK, launcher, and architecture keys, their token
grammar, and compatibility guarantees. JDK and launcher capabilities are
boolean-presence labels: admission matches their keys with Operator: Exists,
not by requiring the current value true.
Provisioning selects nodes through NodeProfile.spec.nodePool and requires
managed ownership. See
Capability labels and autoscaling for
the provisioning, affinity, Cluster Autoscaler, and Karpenter workflows.
Node annotations¶
| Key | Example | Meaning |
|---|---|---|
brewlet.sh/jdks |
temurin-21,microsoft-25 |
Current compact JDK inventory for compatibility admission, node-ready events, and CLI status/profile inspection. Contains distribution/feature tokens, not detailed runtime metadata. |
brewlet.sh/jdks-info |
[{"distribution":"temurin","vendor":"Eclipse Adoptium","feature":21,"version":"21.0.5","arch":"amd64"}] |
Structured diagnostic inventory used by brewlet k8s jdk list and doctor's JDK check. vendor/version/arch are read from the installed JDK itself. A root whose java cannot be run is omitted. |
brewlet.sh/launchers |
java,jaz |
Advertised launcher layers. Always includes the implicit java. |
brewlet.sh/profile |
default |
metadata.name of the NodeProfile that last provisioned this node. |
brewlet.sh/profile-generation |
4 |
That profile's metadata.generation, so a stale node can be told from an up-to-date one. |
brewlet.sh/provision-state |
Provisioning | Ready | Failed |
The operator's view of the node's lifecycle (distinct from the provisioner-owned runtime=ready label). |
brewlet.sh/provision-error |
launcher-jaz-not-executable |
Stable reason code for a provisioner failure — safe to parse and alert on. Enumerated in SPECIFICATION §14.2. The operator uses it for ProvisionFailed events and clears it after a successful run. |
brewlet.sh/provision-error-message |
launcher jaz binary at /opt/brewlet/… is not executable |
Human detail for the code above. Not a contract — wording may change at any time, so branch on the code instead. |
These annotations do not drive scheduler affinity; the per-capability labels do. Compact inventory is still used by compatibility admission's fleet check. Detailed inventory never falls back to compact tokens: missing/blank/empty structured data is omitted, and malformed nonblank data fails explicitly. See JDK management for empty-output behavior and operator guidance, and Specification §14.1 for the retained contracts and support boundaries.
Pod annotations¶
| Key | Example | Set by | Meaning |
|---|---|---|---|
brewlet.sh/jdk |
21 or temurin-21 |
you | Request a JDK feature (any distro) or an exact <dist>-<feature>. Validated + scheduled by the webhook. |
brewlet.sh/launcher |
jaz |
you | Request a launcher. Empty / java = vanilla OpenJDK launcher. |
brewlet.sh/arch |
amd64 or amd64,arm64 |
you (or the JavaApplication controller from spec.arch) |
Optional architecture constraint for non-portable JARs bundling JNI natives. Injects kubernetes.io/arch nodeAffinity; if no ready node of a required arch exists → admitted with a warning, stays Pending. Omit for arch-neutral bytecode. |
brewlet.sh/artifact-container |
app |
you + webhook | Selects which regular container's image is mirrored into Pod-wide compatibility hints. The webhook normalizes the value to the selected container name; other tasks ignore those hints and remain bound to their own CRI images. |
brewlet.sh/artifact-ref |
repo:tag |
webhook | Compatibility hint mirrored from the selected Pod image. |
brewlet.sh/artifact-digest |
sha256:… |
webhook | Compatibility hint mirroring the selected Pod image digest. The shim resolves executable content from containerd metadata, not from this annotation. |
Event reasons¶
Recorded by the operator / admission webhook (see Troubleshooting):
| Reason | Emitted when |
|---|---|
Provisioning |
The operator has requested provisioning for a node. |
NodeReady |
A node is provisioned and advertising the brewlet runtime. |
ProvisionFailed |
The provisioner reports a validation/reconfiguration error, or its pod is failing (for example CrashLoopBackOff). |
NoCompatibleJDK |
A pod requested a JDK no ready node or valid NodeProfile provides → admission denied. |
NoCompatibleLauncher |
A pod requested a launcher no ready node or valid NodeProfile provides → admission denied. |
NoCompatibleArch |
Reserved; no longer emitted. An arch with no ready node is admitted with a warning. |
Well-known names¶
| Name | Value | Notes |
|---|---|---|
| RuntimeClass / containerd handler | brewlet |
|
| Provisioner DaemonSet | brewlet-node-provisioner |
Managed by the operator. |
| Default namespace | brewlet |
Created by the Helm chart. |
| containerd runtime type | io.containerd.brewlet.v2 |
Registered in the effective containerd config through an imported drop-in or the primary file. |
| Vanilla launcher name | java |
Provided by every JDK; needs no layer. |
OCI media types¶
The Java application ships either as a runnable image (the default for
brewlet push and the Maven plugin — a standard, kubelet-pullable OCI image; see
runnable image) or as a native OCI artifact with the
custom media types below. Neither carries an OS layer or a JVM. The native types
are used for local OCI-layout / CLI / prepare-bundle workflows.
| Component | Media type | Contents |
|---|---|---|
| Artifact type | application/vnd.brewlet.app.v1+json |
Manifest artifactType. |
| Config blob | application/vnd.brewlet.jvm.config.v1+json |
The launch config (below). |
| Payload layer | application/vnd.brewlet.jar.layer.v1+jar |
The raw self-executable JAR. |
| Optional layer | application/vnd.brewlet.classpath.layer.v1+tar |
Extra JARs (dependency layers) unpacked to /app/lib; see layered classpath deployment. |
| Optional layer | application/vnd.brewlet.modulepath.layer.v1+tar |
Library modules for a modular (JPMS) app, unpacked to /app/mods and fed to --module-path; see JPMS support. |
| Optional layer | application/vnd.brewlet.cds.layer.v1+jsa |
A single Application Class-Data Sharing archive (.jsa), mounted read-only at /app/<archive> and consumed with -Xshare:auto -XX:SharedArchiveFile; best-effort startup accelerator, see AppCDS. |
Push with oras using exactly these types — see
Building & publishing.
Layered-deployment options (CLI & Maven)¶
Opt-in flags for layered (thin JAR) deployment —
a thin app JAR plus one or more classpath.layer.v1+tar dependency layers unpacked to
/app/lib. The fat JAR (entry.mode: jar) remains the default.
| Option | Tool | Default | Meaning |
|---|---|---|---|
push --classpath-layer TAR |
CLI | (none) | Attach a pre-built tar of dependency JARs as a classpath.layer.v1+tar layer, unpacked to /app/lib. Repeatable, in stable → volatile order. |
push --module-layer TAR |
CLI | (none) | Attach a pre-built tar of library module JARs as a modulepath.layer.v1+tar layer, unpacked to /app/mods and fed to --module-path. Repeatable; see JPMS support. |
<layered> / -Dbrewlet.layered |
Maven | false |
Ship a thin app JAR plus the resolved transitive POM dependency tree packed into reproducible OCI layers. In classpath mode this produces classpath.layer.v1+tar layers and sets entry.classPath=[mainJar, "lib/*"]; in module mode the dependency modules are packed into a single modulepath.layer.v1+tar layer (unpacked to /app/mods) and entry.modulePath=[mainJar, "mods"] is set. Forces entry.mode=classpath only when the JAR is not modular. |
<splitSnapshotLayers> / -Dbrewlet.splitSnapshotLayers |
Maven | true |
When layered, pack released deps and -SNAPSHOT deps into separate deps / snapshot-deps layers (stable → volatile) for finer dedup. |
Full flag reference: CLI reference and the Maven plugin README.
Launch config schema (config blob)¶
{
"schemaVersion": 1,
"mainJar": "app.jar",
"entry": { "mode": "jar" },
"enablePreview": true,
"addOpens": ["java.base/java.lang=ALL-UNNAMED"],
"systemProperties": { "spring.aot.enabled": "true" },
"cds": { "archive": "app.jsa", "mode": "dynamic" },
"arch": ["amd64"],
"env": []
}
| Field | Type | Notes |
|---|---|---|
schemaVersion |
int | 1. |
mainJar |
string | Physical filename of the single primary JAR, mounted read-only at /app/<mainJar>. This is not the entrypoint (the mode selects that via the manifest Main-Class, mainClass, or module); it only names the file that classPath/modulePath entries reference by name. Must be a bare filename — path separators, wildcards and parent references (..) are rejected, because the name is resolved against the node's staging directory and the result becomes a read-only bind-mount source. Defaults to app.jar. |
entry.mode |
jar | classpath | module |
jar → java -jar; classpath → java -cp <jar> <mainClass>; module → java [-cp <classPath>] -p <modulePath> -m <module>[/<mainClass>] (JPMS, optionally with a supplementary class path — the mixed form; see JPMS support and layered deployment §8). |
entry.mainClass |
string | Required iff entry.mode == classpath; optional in module mode (selects <module>/<mainClass>). |
entry.classPath |
array | Optional; ordered /app-relative class-path entries (e.g. ["app.jar","lib/*"]) for layered deployment. Used in classpath mode and, optionally, in module mode as a supplementary class path alongside the module path (the mixed form). |
entry.module |
string | Required iff entry.mode == module; the root module name for java -m. |
entry.modulePath |
array | Optional; ordered /app-relative module-path entries (e.g. ["orders.jar","mods"]) fed to java -p. Only in module mode; defaults to mainJar. |
enablePreview |
boolean | Optional; expands to --enable-preview for preview-feature code. |
addModules |
array | Optional; expands to --add-modules <comma-joined>. |
addOpens |
array | Optional; each token expands to --add-opens <module>/<package>=<target>. |
addExports |
array | Optional; each token expands to --add-exports <module>/<package>=<target>. |
systemProperties |
object | Optional string map expanded, sorted by key, as -D<key>=<value>. |
cds |
object | Optional Application Class-Data Sharing hint: {archive, mode}. archive is a bare filename (e.g. app.jsa) shipped as a cds.layer.v1+jsa layer, mounted read-only at /app/<archive>; launch prepends -Xshare:auto -XX:SharedArchiveFile=/app/<archive>. mode (dynamic|static, informational) records how it was produced. Best-effort accelerator: a build/version/classpath mismatch falls back to base CDS, never fails. See AppCDS. |
arch |
array | Optional architecture constraint (amd64, arm64). Omit for arch-neutral bytecode (the default — runs on any provisioned arch). Set only for non-portable JARs that bundle JNI native libraries or arch-specific deps; steers scheduling to matching-arch nodes via kubernetes.io/arch nodeAffinity, and, when no ready node of a required arch exists, admits the pod with a warning so it waits Pending for capacity. The CLI (brewlet push) and Maven plugin auto-detect bundled natives and default this accordingly. |
env |
array | {name, value}. |
Process credentials are not artifact fields. A config containing user is
rejected; Kubernetes identity comes from Pod securityContext, while standalone
bundles use the trusted brewlet bundle --uid/--gid flags.
Artifact launch knobs expand first in this order: -Xshare:auto
-XX:SharedArchiveFile (when cds is set), --enable-preview, --add-modules,
--add-opens, --add-exports, sorted -D flags. Descriptor jvm.args follows
for deployment tuning/escape-hatch flags, then the entrypoint. Because the JVM
resolves conflicting options last-wins, descriptor jvm.args override
app-embedded flags.
Descriptor jvm.args are delivered as argv, carried on the pod as the
brewlet.sh/jvm-args annotation (a JSON array of strings). Brewlet does not set
JDK_JAVA_OPTIONS/JAVA_TOOL_OPTIONS, which the launcher would prepend and so
invert that precedence. Args that select the entrypoint — -jar, -cp /
-classpath / --class-path, -p / --module-path, -m / --module, and
@argfile — are rejected: the artifact owns the entrypoint.
JDK feature/distribution and launcher are not part of this artifact config. They
are specified in the deployment descriptor (spec.jvm.version /
spec.jvm.distribution / spec.jvm.launcher) or raw pod annotations
(brewlet.sh/jdk / brewlet.sh/launcher).
Mode owns its fields (validated). Each entry.mode uses a fixed set of
fields; fields foreign to the selected mode are rejected, not silently
ignored. jar mode must not set mainClass or classPath (the manifest
Main-Class is authoritative); classpath mode requires mainClass and
forbids module/modulePath; module mode requires module and additionally
permits classPath for the mixed form (a supplementary -cp alongside the
module path — see layered deployment §8).
Unknown
modes and foreign-mode fields are errors, caught by the Maven plugin at build
time (mvn brewlet:config/build/push) and by the launch core at publish and
run time. Unknown JSON fields (e.g. a typo like maimJar) are additionally
rejected by the launch core — the CLI and shim parse configs with strict field
checking — at publish and run time.
Top-level JAR references are cross-checked. Dependency layers unpack under
/app/lib (class path) or /app/mods (module path); the single primary JAR is
the only file at the /app top level. So a bare <name>.jar entry (no /, no
*) in classPath/modulePath can only resolve to the primary JAR. When
mainJar is set, any such entry must equal it, or validation fails with a
dangling-reference error — this catches a mainJar/path-entry filename mismatch
before deploy time. Nested entries like lib/classpath-helper.jar and wildcards like
lib/* are unaffected.
Full field semantics: Building & publishing.
Well-known host paths (on a provisioned node)¶
| Path | Contents |
|---|---|
/opt/brewlet/bin/containerd-shim-brewlet-v2 |
The shim binary (also linked into /usr/local/bin). |
/opt/brewlet/jdks/<dist>-<feature>/bin/java |
A shared, read-only JDK runtime root. |
/opt/brewlet/launchers/<name>/bin/<name> |
A shared, read-only launcher layer. |
/etc/containerd/config.toml |
Primary containerd config; patched only when imported drop-ins are unavailable. |
/etc/containerd/config.toml.d/99-brewlet.toml |
Preferred Brewlet-managed runtime drop-in when the primary config imports the directory. |
/app/<mainJar> |
The JAR, mounted read-only inside the sandbox. |
Override the /opt/brewlet prefix with BREWLET_PREFIX
(Configuration).
containerd runtime registration¶
The provisioner renders this runtime block into
/etc/containerd/config.toml.d/99-brewlet.toml when the primary config imports
that directory, otherwise it appends the block to a backed-up
/etc/containerd/config.toml:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.brewlet]
runtime_type = "io.containerd.brewlet.v2"
In default validated mode, the provisioner checks the effective config with
containerd config dump, restarts the host service when activation is needed,
verifies the live runtime handler, and automatically rolls back failed changes.
Glossary¶
| Term | Definition |
|---|---|
| OCI Artifact | Non-image content stored/distributed via an OCI registry using custom media types (OCI Image Spec ≥ 1.1). |
| containerd Runtime v2 shim | Pluggable per-runtime process containerd talks to (TTRPC) to manage a container/task — the integration seam SpinKube/runwasi use. |
| RuntimeClass | Kubernetes object selecting which node runtime/handler executes a pod. |
| JDK runtime root | A minimal, read-only Linux userland + JDK installed on the node and overlay-mounted into every JVM sandbox. |
| Launcher | The java-compatible program that fronts the entrypoint (java, or jaz). |
| Overlay rootfs | The sandbox filesystem: shared RO JDK lower + per-container upper/work, JAR at /app. |
| AppCDS | Application Class-Data Sharing — a class archive that cuts startup (ships as a cds.layer). |