Publishing an API

One rule, and it is the one that catches people out.

Why it happens

Relocation is what makes shading safe: your copy of the framework cannot collide with anyone else's, because yours lives under your own package. The consequence is that every relocated type has a different name inside your jar than it had when you compiled against it.

That is invisible while everything is inside your plugin. It becomes visible the moment a separate jar, an addon someone else wrote, compiles against your API artifact, which still names the original package.

What to do instead

Re-declare the concept in your own package and delegate.

  • Sigil does this with SigilScheduler: its own interface, in its own package, backed by Keystone's scheduler internally.
  • HoloPanels does it with its Registration handle.

Types used purely inside your plugin need no such wrapper. The rule is about the surface, not about usage.

Text across the boundary

Text is the case where this bites hardest, because Component is the type you most want to expose.

Two workable answers:

Cross the boundary as MiniMessage strings. Sigil's choice. sigil-api references only Bukkit types and its own, so there is no relocated type on the surface at all, and an addon needs nothing but the Bukkit API to compile.

Take the Paper-only build. HoloPanels' choice. Adventure is not relocated, so the Component on your API surface is the server's own and lines up for everyone. See Adventure.

Which one is right follows from who your addon authors are. If they are writing against Paper anyway, the second is more pleasant. If your plugin runs on Spigot too, the first is the only one available.

A checklist before you publish

  1. Does your API artifact import anything under dev.bwmp.keystone? If so, wrap it.
  2. Does it expose net.kyori.adventure.* while you are on the shaded build? Either take the Paper-only build or switch to MiniMessage strings.
  3. Does it expose any of your shaded third-party dependencies, such as a YAML library or a HTTP client? Same rule, same fix.
  4. Compile a throwaway addon against the published artifact and run it against your shipped jar. This is the only check that catches all three at once.

Also inspect generic parameters, record components, exceptions, annotations and callback method signatures; shaded types can leak through any of them, not only return values. Keep the API artifact free of Keystone dependencies where possible, and test against the installed plugin jar rather than only running unit tests in the reactor.