-
-
Notifications
You must be signed in to change notification settings - Fork 2
Forwarding
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.
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.
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" }
- The player connects. The proxy records their real IP from the TCP connection.
- 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.
- The player's VS
Identificationpacket reaches the backend normally. The backend sees the proxy's IP as the connection source. -
Nimbus.ServerModhooksPlayerJoin. It calls the registry to consume the reservation - another HMAC-signed call. On success it receives the full forwarding data. - 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)
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:
GetForwardedPlayeris populated during thePlayerJoinevent, 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 inPlayerJoinitself, use a short delay or a subsequent tick callback.
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.
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.
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. |