Java Plugin API

The jhelm-plugin-api module lets you extend jhelm by implementing a small Java interface — no WebAssembly to compile, no subprocess to spawn. Your plugin runs in-process and is discovered off the classpath, either as a JDK ServiceLoader service (any application) or as a Spring bean (the jhelm CLI/REST/MCP apps, or your own Spring app embedding jhelm).

This is one of three plugin surfaces. See also Helm plugin compatibility (native Helm plugins as subprocesses) and the WASM .jhp system. Java plugins are the simplest way to extend jhelm from Java code.

1. Dependency


    org.alexmond
    jhelm-plugin-api
    1.5.0

2. Extension points

All interfaces live in org.alexmond.jhelm.pluginapi and extend JhelmPlugin (which provides a name() for listings/logs).

Post-renderer

JhelmPostRenderer — transform the rendered manifest (install, upgrade, template).

public class BannerPostRenderer implements JhelmPostRenderer {
    public String postRender(String manifest) {
        return "# rendered via my plugin\n" + manifest;
    }
}
Chart downloader

JhelmChartDownloader — fetch charts for a custom URL scheme (s3://, gs://, …). supports(scheme) selects it; download(url) returns the chart archive bytes.

Lifecycle listener

JhelmLifecycleListener — react to release events. onEvent(JhelmReleaseEvent) receives the phase (PRE_INSTALL, POST_INSTALL, PRE_UPGRADE, POST_UPGRADE, PRE_ROLLBACK, POST_ROLLBACK, PRE_UNINSTALL, POST_UNINSTALL), release name, namespace, and metadata. A throwing listener is logged and ignored.

Template functions

JhelmTemplateFunctionProvider — contribute Go-template functions callable from any chart.

public class MyFunctions implements JhelmTemplateFunctionProvider {
    public Map<String, JhelmTemplateFunction> functions() {
        return Map.of("my_greet", args -> "hello, " + args[0]);
    }
}

Name your functions distinctively — a name that collides with a built-in overrides it (the cluster-backed lookup is the one exception, always preserved).

Any method may throw JhelmPluginException; jhelm surfaces the message and aborts the operation.

3. Registering a plugin

ServiceLoader (any application)

add a file under META-INF/services/ named for the interface, listing your implementation class. For a post-renderer:

META-INF/services/org.alexmond.jhelm.pluginapi.JhelmPostRenderer
com.example.BannerPostRenderer
Spring bean (jhelm CLI/REST/MCP, or your Spring app)

expose the plugin as a bean:

@Bean
JhelmPostRenderer bannerPostRenderer() {
    return new BannerPostRenderer();
}

jhelm unions both sources and de-duplicates by implementation class (a Spring bean that is also a declared service is registered once, preferring the bean instance).

4. Loading external plugin JARs

The two mechanisms above discover plugins already on the application classpath. To load a plugin without rebuilding jhelm — dropping a JAR into a directory — point jhelm.plugins.path at one or more directories:

jhelm:
  plugins:
    path: /opt/jhelm-plugins        # or a comma-separated list of directories

Every *.jar in each directory is loaded in its own class loader (parented on jhelm-core, so the plugin resolves the jhelm-plugin-api types; isolating each jar tolerates conflicting transitive dependencies between plugins), and its declared ServiceLoader services are discovered and merged with the classpath and Spring-bean plugins. This works on every surface — the CLI, the REST and MCP servers, and an embedded library — and is independent of how the application is launched. Empty by default, so nothing is scanned until a directory is configured.

On the CLI, set it with the global --plugin-dir flag, the JHELM_PLUGINS_PATH environment variable, or the property directly:

jhelm --plugin-dir /opt/jhelm-plugins template my-release ./chart

4.1. Packaging a plugin as a self-contained JAR

Build an ordinary JAR that (1) compiles against jhelm-plugin-api (scope provided — jhelm supplies it at runtime) and (2) contains a META-INF/services/ file for each extension point it implements, as in Registering a plugin. Bundle any third-party libraries your plugin needs into the JAR (a shaded/uber JAR), since only that JAR is added to the plugin’s class loader. Drop the finished JAR into a jhelm.plugins.path directory.

This is jhelm’s own Java plugin store, distinct from the native-Helm plugin store ($HELM_PLUGINS) and the WASM .jhp store. A JAR here is in-process Java code — the same trust model as a classpath plugin — so, like the other Java plugins, it is not gated on the FULL security posture; the directory you point at is the trust boundary.

5. Worked examples

The jhelm-plugin-api-sample module in the source tree implements one plugin of each kind (post-renderer, downloader, lifecycle listener, template functions) with ServiceLoader registrations, and an integration test that runs them through jhelm-core end to end.

6. Stability

The plugin API follows the project’s semantic versioning: interfaces are additive within a minor line. New extension points and enum constants may be added; existing method signatures are kept stable within a major version.

7. Relation to the native-Helm and WASM plugin systems

Java plugins run in-process, so — unlike native Helm plugins (arbitrary executables) — they are not gated on the FULL security posture; they are code you already put on the classpath. Use a Java plugin when you extend jhelm from Java, a Helm plugin to reuse an existing Helm plugin, and the WASM .jhp system for a sandboxed, language-agnostic plugin.