feat(geyser): bedrock entry point and floodgate delivery - #223
Merged
Conversation
Three pieces of the Bedrock path, all pinned to an exact build with a verified checksum so a version bump without a matching digest fails the build rather than shipping something unverified. geyser — Geyser Standalone (2.11.1 build 1219). Terminates the RakNet/UDP session a Bedrock client opens and connects to Velocity as an ordinary Java client. The config is mounted at /config and copied into the working directory by start.sh, because Geyser rewrites config.yml on startup and cannot do that against a read-only mount — it exits with "Resource busy", reported as a config error. plugin-floodgate — the Floodgate Velocity JAR (2.2.5 build 140) at /jar/plugin.jar, which is the plugin-velocity-jar chart's contract. Floodgate is what lets a single online-mode proxy accept Bedrock players: it verifies the player data Geyser signs and assigns a UUID derived from the XUID, shaped so it can never collide with a real Java UUID. An offline-mode proxy would derive UUIDs from the username instead, which means a Bedrock player and a Java player sharing a name share an identity. velocity — start.sh learns VELOCITY_FORCE_KEY_AUTHENTICATION. Floodgate needs that setting off, since Bedrock players have no Mojang profile key and are kicked at login while it is enforced. Making it an env var is what lets only the Bedrock proxy relax it while the Java proxies keep enforcing. Both images are registered in all three hardcoded matrices — CI, build-push and release-please. A container missing from one of them is not an error; the jobs simply skip and the run goes green having built nothing.
Floodgate reads its key from its own plugin data directory and also creates a config.yml there on first start, so mounting the Secret straight onto plugins/floodgate would make that directory read-only and the plugin would fail to initialise — the same trap Geyser has with its config. FLOODGATE_KEY_FILE points at a neutral mount and the key is copied in, leaving the directory writable. Unset is a no-op, so this changes nothing for the Java proxies.
start.sh gained two load-testing knobs on main while this branch was open. Both sides kept, with the Bedrock block placed FIRST on purpose: the offline-mode block also forces force-key-authentication off, and it has to be able to override an explicit `true` from VELOCITY_FORCE_KEY_AUTHENTICATION. An offline player carries no Mojang-signed key either, so enforcing it there would reject them one step later and look like an unrelated failure.
This was referenced Aug 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three pieces of the Bedrock path. Both new images are pinned to an exact build with a verified sha256, so a version bump without a matching digest fails the build rather than shipping something unverified.
geyserGeyser Standalone 2.11.1 build 1219. Terminates the RakNet/UDP session a Bedrock client opens and connects to Velocity as an ordinary Java client — nothing downstream of the proxy learns the player came from Bedrock.
The config is mounted at
/configand copied into the working directory bystart.sh, because Geyser rewritesconfig.ymlon startup and cannot do that against a read-only mount. It exits withFileSystemException: ... Resource busy, reported as a config error. Matching chart change: groundsgg/charts#160.No
HEALTHCHECK: Geyser speaks RakNet over UDP and exposes no HTTP endpoint, and a UDP port probe cannot tell "listening" from "nothing there" — it would report healthy for a dead process.plugin-floodgateThe Floodgate Velocity JAR 2.2.5 build 140 at
/jar/plugin.jar, which is theplugin-velocity-jarchart's contract. Nothing in the image is ever executed.Floodgate is what lets a single online-mode proxy accept Bedrock players: it verifies the player data Geyser signs with a shared key and assigns a UUID derived from the XUID, shaped
00000000-0000-0000-XXXX-XXXXXXXXXXXXso it can never collide with a real Java UUID. The alternative — an offline-mode proxy — derives UUIDs from the username, so a Bedrock player and a Java player sharing a name would share an identity. With ratings keyed on(player, mode)that is not a cosmetic difference.The zeroed high bits are also how the rest of the network can recognise a Bedrock player (
uuid.mostSignificantBits == 0) without taking a dependency on Floodgate.velocitystart.shlearnsVELOCITY_FORCE_KEY_AUTHENTICATION. Floodgate needs that setting off — Bedrock players have no Mojang profile key and are kicked at login while it is enforced. Making it an env var is what lets only the Bedrock proxy relax it while the Java proxies keep enforcing chat signatures;velocity.tomlis baked into the image, so otherwise this would need a second image.Unset keeps the image default, so this is a no-op for every existing release.
Registered in all three matrices
CI, build-push and release-please each carry a hardcoded container list. A container missing from one is not an error — the jobs skip and the run goes green having built nothing. (That exact trap cost a cycle in groundsgg/charts#158.)
Verified locally
Both images built with
docker build, so the checksum verification actually ran:plugin-floodgate—/jar/plugin.jar, 6.9 MB, valid ZIP headergeyser— runs as uid 100, Java 25,/opt/geyser/Geyser.jar29.7 MBand
geyserrun against the chart's rendered config:One operational note
Geyser downloads a Minecraft JAR at startup to extract assets (
Downloading Minecraft JAR to extract required files...). The pod needs egress for that. Worth checking againstpulumi/core/src/platform/network-policies.tsbefore this runs anywhere locked down.