Skip to content

110 - Adopt plugin versioning scheme#110

Open
robobario wants to merge 1 commit into
kroxylicious:mainfrom
robobario:explicit-plugin-versioning
Open

110 - Adopt plugin versioning scheme#110
robobario wants to merge 1 commit into
kroxylicious:mainfrom
robobario:explicit-plugin-versioning

Conversation

@robobario
Copy link
Copy Markdown
Member

@robobario robobario commented May 14, 2026

This is a smaller idea for adopting plugin versioning without tackling all of the other ideas like contract-first Configuration based on JSON schemas, kubernetes sympathy by decomposing into multiple configuration files which are covered in #96 . It only makes versioning explicit and adopts k8s CRD versioning conventions to try and make our stability guarantees explicit.

Proposal generated with the help of Claude Sonnet 4.5

@robobario robobario requested a review from a team as a code owner May 14, 2026 05:11
Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>
@robobario
Copy link
Copy Markdown
Member Author

Prototype implementation kroxylicious/kroxylicious#3952

@robobario robobario changed the title Adopt plugin versioning scheme 110 - Adopt plugin versioning scheme May 14, 2026
@robobario robobario mentioned this pull request May 14, 2026
For proxy users, configuration syntax extends from `type: RecordEncryption` to support `type: RecordEncryption/v1alpha1`.
When a version is present in the type string, the proxy uses it both for validation and for name resolution.
A plugin named RecordEncryption annotated with `@Version("v1alpha1")` can be referenced as either `RecordEncryption/v1alpha1` (explicit version) or `RecordEncryption` (implicit, triggers warning).
If multiple RecordEncryption implementations exist with different versions (v1alpha1 and v1beta1), users disambiguate by including the version rather than switching to fully qualified class names.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that should say can disambiguate it's optional and they could still disambiguate using fully qualified name, backwards compat is preserved in phase 1.0.0

Kroxylicious is approaching its 1.0.0 release, which will establish backward compatibility guarantees for public APIs.
However, different plugins may have different maturity levels and API stability guarantees.
Coupling all plugin APIs to a single project version creates tension between shipping experimental features and maintaining stability.
The Record Encryption filter, for example, may need multiple major revisions before its API stabilizes, but tying those revisions to the project version would either delay 1.0.0 or force premature API commitments.
Copy link
Copy Markdown
Member Author

@robobario robobario May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude has picked our most stable plugin for the example 🤷


Phase 2 begins with the 1.0.0 release.

- The enforcement policy changes from warning to error when a plugin declares a version but the configuration omits it.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a bullet to say that the annotation on the plugin becomes mandatory too

Copy link
Copy Markdown
Member

@tombentley tombentley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @robobario. I really appreciate that someone is trying to see if there's a simpler alternative to #96 which might satisfy some of the things we agreed in #82 are important to have for Kroxylicious 1.0.

I'll admit that 96 doesn't do a great job of calling out all of its goals, but let me try to compare the two:

Goal #96 #100
Explicit plugin config version Yes Yes
Support config schema evolution Yes Yes
Plugin identification Yes No
Deeper runtime understanding of nested plugins Yes No
Fine grained dependency analysys Yes No
Better documentation of schema configs Yes No
External validation of schema configs Yes No
Editor support for writing configs Yes No
Globally shared interpretation of proxy config Yes No
Synergy with K8S Stronger Weaker

It's clear that there's a very different level of ambition between the two proposals. My fear is that, out of a desire to see 1.0 happen sooner, we make decisions which undermine our ability to address some of the concerns in the left hand column.

I asked Claude to look had how compatible there two proposals are and this is the result. Reading that I think it's difficult to see 110 as an outright good move that's compatible with the ideas in 96 as a solution of those goals. Instead while there are those structural differences between the two (like the annotation, the YAML syntax and the difference in terms of simple vs FQCNs) 110 looks more like a temporary thing which would cause disruption to plugin developers.

So I think it would be helpful to hear if you think any of those goals are not actually worth striving for. If they are worth solving then I'd appreciate your review on 96: is it a good way of solving them (maybe there are better ways)? In the light of that we can figure out if 110 is the best way of doing something that's "good enough for 1.0", or if we can tweak 110 to be more compatible with 96.

I also think it's worth considering that kroxylicious/kroxylicious#3691, which is a POC of the changes proposed in 96, is "only" a +7k change. Sure that's a lot, and it needs work on the tests and so on, so would likely grow a bit. But in the light of some of the recent big things we've merged, I don't think we should immediately be thinking that 96 is a huge piece of work that would take many months to implement.

For proxy users, configuration syntax extends from `type: RecordEncryption` to support `type: RecordEncryption/v1alpha1`.
When a version is present in the type string, the proxy uses it both for validation and for name resolution.
A plugin named RecordEncryption annotated with `@Version("v1alpha1")` can be referenced as either `RecordEncryption/v1alpha1` (explicit version) or `RecordEncryption` (implicit, triggers warning).
If multiple RecordEncryption implementations exist with different versions (v1alpha1 and v1beta1), users disambiguate by including the version rather than switching to fully qualified class names.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If two unrelated plugins share a simple class name then they can be disambiguated via version number, but logically that's not the right way to fix it: The correct fix is to use an FQ name.

It's nasty and surprising to have two different disambiguation mechanisms.

When a plugin reaches stability, annotating it with `@Version("v1")` communicates that the API will remain backward compatible within the v1 series.

Version-based disambiguation enables new packaging strategies.
A plugin author maintaining both alpha and beta implementations can ship `io.kroxylicious.filter.encryption.alpha.RecordEncryption` annotated with `@Version("v1alpha1")` and `io.kroxylicious.filter.encryption.beta.RecordEncryption` annotated with `@Version("v1beta1")` in the same JAR.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This again highlights the weirdness of having two different ways of referring to the same thing. You need the alpha/beta in the FQ class name, but you also have it in the annotation.

This can make for a poor UX: It's easy to imagine a user meaning to update from alpha (specified as io.kroxylicious.filter.encryption.alpha.RecordEncryption/v1alpha1) to beta, but not really paying attention to the alpha in the package name and being confused when they change it to io.kroxylicious.filter.encryption.alpha.RecordEncryption/v1beta1.

The reality of this situation is there there are two different plugins masquerading as being one plugin which accepts two different configuration formats, and it's only when you use simple class naming that that pretence works. To my mind, this is pushing people towards using simple class names. However, I think simple class names are a mistake, because it means the interpretation of a configuration is relative to the plugins available to the runtime. Ultimately a configuration is about communicating intent. A configuration schema which results in configurations that are open to interpretation is a poor configuration schema. This is why #96 adopts FQ names.

An environment variable `KROXYLICIOUS_REQUIRE_PLUGIN_VERSIONS` allows operators to opt into strict enforcement before 1.0.0 for testing purposes.

For filter authors and plugin developers, the `@Version` annotation becomes part of the public API contract.
Placing `@Version("v1alpha1")` on a filter implementation signals to users that the API is experimental and subject to breaking changes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very odd that the thing that the version is intended to describe (the schema for the configuration) ends up in an annotation on the plugin itself.

@tombentley
Copy link
Copy Markdown
Member

if we can tweak 110 to be more compatible with 96.

I asked Claude about this and it's the second file in the gist

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants