Skip to content

Forwarding

Tsu edited this page Jun 10, 2026 · 1 revision

Nimbus Forwarding is the equivalent of Velocity's Modern Forwarding. It gives backends a trusted, cryptographically-verified player identity - real IP address, player UID, player name, and source server.

Why it matters

When a player connects to the proxy, the backend server only sees a TCP connection from the proxy's IP - not the player's actual IP address. Without forwarding, you lose:

  • The ability to IP-ban players at the backend level
  • Accurate connection logging
  • Trust that the player UID you see actually came from an authenticated source

Nimbus Forwarding restores all of this, and the trust is cryptographic - not reliant on network topology or trust flags.

How it works

Client (1.2.3.4)  -->  Nimbus Proxy  -->  Backend (VS server)
                         |
                    Mints reservation:
                    { uid, name, realIp: "1.2.3.4", ... }
                    Signs with HMAC
                         |
                    Backend mod consumes reservation:
                    POST /api/reservations/consume-by-uid
                    (HMAC-signed request)
                         |
                    Receives full forwarding data:
                    { uid, name, realIp: "1.2.3.4", source: "hub" }
  1. The player connects. The proxy records their real IP from the TCP connection.
  2. Before routing to the backend, the proxy calls the registry to mint a reservation - a short-lived single-use token containing: player UID, player name, real client IP and port, source server ID, and expiry timestamp. The API call is HMAC-signed.
  3. The player's VS Identification packet reaches the backend normally. The backend sees the proxy's IP as the connection source.
  4. Nimbus.ServerMod hooks PlayerJoin. It calls the registry to consume the reservation - another HMAC-signed call. On success it receives the full forwarding data.
  5. The mod stores this per player and exposes it via GetForwardedPlayer(uid).

The trust properties match Velocity Modern Forwarding:

  • Only the proxy can mint reservations (the HMAC shared secret is unknown to players or backends)
  • Backends verify by consuming via a signed API call - they cannot forge or replay it
  • The real IP is the proxy's observed TCP source - not a header the player can spoof
  • Reservations are single-use and expire (default 60 seconds)

Accessing forwarding data from a backend mod

using Vintagestory.API.Server;
using Nimbus.ServerMod;
using Nimbus.Shared.Models;

public class MyMod : ModSystem
{
    public override void StartServerSide(ICoreServerAPI api)
    {
        api.Event.PlayerJoin += player =>
        {
            var nimbus = api.ModLoader.GetModSystem<NimbusServerModSystem>();
            TransferReservation? forwarded = nimbus?.GetForwardedPlayer(player.PlayerUID);

            if (forwarded != null)
            {
                // Player arrived via the proxy - data is trusted
                string realIp   = forwarded.RealRemoteIp;
                int    realPort = forwarded.RealRemotePort;
                string uid      = forwarded.PlayerUid;
                string source   = forwarded.SourceServerId ?? "initial join";
                api.Logger.Notification($"[MyMod] {player.PlayerName} from {realIp} via Nimbus (source: {source})");
            }
            else
            {
                // No forwarding data - direct connect or registry not configured
                api.Logger.Notification($"[MyMod] {player.PlayerName} connected without forwarding data");
            }
        };
    }
}

Timing note: GetForwardedPlayer is populated during the PlayerJoin event, which fires after the player entity is placed in the world. The forwarding check runs asynchronously on a background thread. If you need the data in PlayerJoin itself, use a short delay or a subsequent tick callback.

Enforcement with ReservationRequired

Set ReservationRequired = true in nimbus-server.json (this is the default) to block any player who does not arrive via the proxy. When enabled:

  • Players with a valid reservation join normally - forwarding data is stored.
  • Players without a reservation are kicked immediately with: Direct connections are not permitted. Please connect via the Nimbus proxy.
  • If the registry is unreachable when a player joins, the check is skipped and the player is allowed through. A registry outage never locks players out.

PROXY Protocol v2

For backend software that supports PROXY Protocol v2 natively (not the VS ServerMod), add it to proxy_protocol_servers in the proxy config:

proxy_protocol_servers = ["my-backend"]

The proxy emits a PROXY v2 header before the first upstream byte, carrying the client's real IP as the connection source address. PROXY v2 and Nimbus Forwarding are independent - you can use both, either, or neither depending on your setup.

For VS backends running Nimbus.ServerMod, use Nimbus Forwarding (reservation-based). PROXY v2 is for other software.

TransferReservation fields

The TransferReservation object returned by GetForwardedPlayer contains:

Field Type Description
PlayerUid string Player UID as parsed from the VS Identification packet.
PlayerName string Player name at the time of the initial proxy connection.
RealRemoteIp string Player's actual IP address as seen by the proxy.
RealRemotePort int Player's actual source port.
SourceServerId string? Backend they transferred from, or null for an initial join.
TargetServerId string This backend's server ID (what the reservation was minted for).
Reason string Audit reason string set when the reservation was minted.
ExpiresAtUnix long Unix timestamp when this reservation expires. Already consumed by the time you read it.

Clone this wiki locally