Skip to content

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).

See also