Files
minecraft_protocol/proxy-forwarding
claude-timemachine cf6aa978d0 verify pass 3: FML token correction + BungeeGuard/cmd-providers from live source
archive.org Wayback was network-blocked; pivoted to live GitHub. Key fix: FML
host token is \0FML\0 (not \0FML2/3\0 — those are FMLNETVERSION ints on the
fml:handshake channel); modern Forge uses \0FORGE/\0FORGEn. Confirmed BungeeGuard
backend token check (lucko source), 4 cmd suggestion providers, 26.2 interval_select
worldgen-only, corrected 1.14 villager metadata layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 19:26:37 +02:00
..

Proxy Forwarding Modes

How a Minecraft proxy moves a player between backend servers — and how it securely tells each backend who the player is.

This is the most load-bearing section of the reference. Get forwarding wrong and you either (a) break logins, or (b) open your network to anyone connecting as anyone.

What a proxy does

A Minecraft proxy (BungeeCord, Waterfall, Velocity) sits in front of N backend servers (lobby, survival, minigames, modded, …). The player's client connects once to the proxy. The proxy speaks the full client-facing protocol — handshake, login, encryption, online-mode auth against Mojang — and then opens its own connection to a backend server and pumps packets between the two. When the player runs /server survival, the proxy silently tears down the backend connection and opens a new one to a different backend, all without the client reconnecting.

sequenceDiagram
    autonumber
    actor C as "Client (player)"
    participant P as "Proxy (Velocity/BungeeCord)"
    participant L as "Backend: lobby (offline-mode)"
    participant S as "Backend: survival (offline-mode)"
    C->>P: "connect once (online-mode auth here)"
    P->>L: "open backend conn + forward identity"
    L-->>C: "play (via proxy relay)"
    Note over C,S: "player runs /server survival"
    P->>S: "open new backend conn + forward identity"
    S-->>C: "play (via proxy relay)"

Why forwarding exists

The proxy already authenticated the player against Mojang. The backends must not repeat that — they run in offline mode (online-mode=false), because:

  • A backend in online mode would demand its own Mojang session handshake from the connecting party (the proxy), which the proxy can't satisfy on the player's behalf.
  • Running backends offline lets the proxy own a single client connection and freely re-point it.

But an offline-mode server, by itself, knows nothing real about who connected. It only sees a username string in LoginStart, and it derives an offline UUID from that name (see online-offline-modes.md). So the proxy must forward the player's real identity to the backend:

  • real client IP (so the backend sees the player, not the proxy)
  • real UUID (the Mojang account UUID for premium players)
  • username
  • game-profile properties — the textures property (skin/cape) and, on newer versions, the player's signature/public key

And — critically — the backend must be able to trust that this forwarded identity actually came from the proxy and not from an attacker who found the backend's port. That trust mechanism is what distinguishes the modes.

The menu of modes

Mode Doc One-line
none online-offline-modes.md No forwarding. Backend uses the raw LoginStart name → offline UUID. Only safe if the backend is the edge.
legacy / bungeecord bungeecord-legacy.md Proxy stuffs ip\0uuid\0properties into the handshake address field. No crypto.
bungeeguard bungeeguard.md Legacy + a shared secret token smuggled in the forwarded properties. Third-party.
modern / velocity velocity-modern.md Backend requests identity via a Login Plugin message; proxy replies with an HMAC-SHA256-signed payload. Gold standard.

Comparison

Mode Crypto? Backend support needed Spoofable if backend port exposed? Forge-friendly
none none (default offline behavior) Yes — anyone can claim any name n/a
legacy (BungeeCord) None Spigot/Paper settings.bungeecord: true (or Fabric equiv.) Yes — forge the \0-delimited string Yes — \0FML\0 marker rides in the handshake (see forge-fml.md)
bungeeguard Shared secret (plaintext token, compared by the backend plugin) Legacy forwarding + BungeeGuard plugin on the backend No (attacker lacks the token), but the token travels in plaintext inside the connection Yes — same handshake-field transport as legacy
modern (Velocity) HMAC-SHA256 over the payload, keyed by a shared secret Paper velocity.enabled+velocity.secret (or Fabric/Forge mod that implements it) No — attacker can't produce a valid HMAC without the secret Needs care — see forge-fml.md (the FML handshake competes with the login-plugin channel)

Rule of thumb: use modern/Velocity when every backend supports it; fall back to bungeeguard when a backend only speaks legacy; use bare legacy only inside a network where the backend ports are firewalled so only the proxy can reach them; use none only when the server is itself the internet edge in online mode.

The "exposed port" risk is the whole point. With legacy and none, your only defense is the firewall — bind backends to localhost / a private interface and let only the proxy connect. With bungeeguard and modern, the protocol itself rejects forged identities. See online-offline-modes.md for why offline backends are open by default.

Mapping to the automc platform

In the automc stack, each backend Minecraft server runs with a Velocity sidecar managed by mc-wrapper (the wrapper runs the proxy alongside the MC server in the same pod). mc-wrapper's PROXY_MODE concept selects which of these forwarding modes the sidecar↔server pair uses — practically that means modern/Velocity forwarding (the velocity-modern.md path) with a shared secret, since proxy and server are co-located and the wrapper provisions the secret. This doc set is the protocol-level reference behind that knob; the automc-specific wiring lives in the mc-wrapper repo, not here.