Start, 5 minute read

Overview

Contents

A Minecraft Java Edition proxy written in Rust with embedded LuaJIT. Route hostnames through one port, authenticate players, switch backends, and manage configuration through Lua or the bundled web dashboard. No Java or separate Lua installation is needed to run Rift.

Rift can also supervise local Minecraft servers: start them on demand, stop empty servers, and operate their lifecycle from the CLI or web dashboard. Service groups can maintain minimum and spare capacity, scale from occupancy or queue pressure, and recover failed processes with bounded retries. Define local templates from server jars, plugins, configs and maps, then create instances with allocated ports and live backend registration. Persistent worlds retain files after removal; disposable game instances delete their generated files on removal. Both retain files across stop/start. See the managed-server guide, managed-server example, service groups and provisioning templates. The dashboard provides live server logs and commands, structured group/template editing, configuration deployment history and rollback, scoped operator tokens, and retained audit records. See the HTTP operator guide. Managed Minecraft processes need their own Java runtime and explicit EULA acceptance.

Quick start

Download a native archive from Releases, extract it, then run:

./rift init
# Edit listener and backend addresses in rift.lua.
./rift check
./rift

On Windows, use rift.exe. The default listener is 0.0.0.0:25565 and the backend is 127.0.0.1:25566. rift init refuses to overwrite existing files. With no arguments, Rift loads rift.lua from the current working directory if present or uses those default addresses. rift check and rift --check validate that same file by default. Use rift --config path/to/custom.lua to start with another configuration file. For a quick local run without a configuration file:

./rift 0.0.0.0:25565 127.0.0.1:25566

Configure a local backend in server.properties:

server-ip=127.0.0.1
server-port=25566
online-mode=false
prevent-proxy-connections=false

The default configuration uses unauthenticated offline identities. Keep backend ports private and enable authentication for public Paper networks as shown below. Offline backends should have forwarding disabled and enforce-secure-profile=false.

Authentication

Start from examples/online.lua, which configures an authenticated lobby/survival network. Enable both fields in your Lua script:

rift.config.authentication = { online_mode = true, timeout_ms = 10000 }
rift.config.forwarding = { mode = "velocity", secret_env = "RIFT_FORWARDING_SECRET" }

Set RIFT_FORWARDING_SECRET in Rift's environment. On each Paper backend, keep online-mode=false in server.properties and settings.bungeecord=false in spigot.yml, then configure config/paper-global.yml:

proxies:
  velocity:
    enabled: true
    online-mode: true
    secret: "the same secret as RIFT_FORWARDING_SECRET"

Restart Paper after changing these settings. Rift verifies accounts with Mojang, handles client encryption, and forwards player identities to Paper. Failed verification never falls back to offline authentication.

Routing and configuration

Write an ordinary Lua script using rift.config; no outer return table is needed:

local config = require("rift.config")
config.listeners.public = "0.0.0.0:25565"
config.backends.lobby = "127.0.0.1:25566"
config.routes.public = "lobby"

-- Import lua/config/services.lua:
-- require("config.services")
-- Load plugins/greeting/init.lua and its lua/ modules:
-- rift.plugin("greeting")

rift.on(event, callback) composes callbacks in registration order, and rift.command(name, definition) registers authenticated commands. Local modules and folder-based plugins are captured on load/reload, with paths relative to the configuration file. Existing return { ... } configurations remain supported. See the Lua API and modular example.

Generate a commented starter with rift init, or use the network example for hostname routing, fallback, health checks, rate limits and metrics. Hostname routing also works from the CLI:

./rift 0.0.0.0:25565 \
  --route survival.example.com=127.0.0.1:25566 \
  --route '*.games.example.com=127.0.0.1:25567' \
  --default 127.0.0.1:25566

Routes prefer exact names, then the longest wildcard suffix, then the default. Listeners require an IP and port; backends accept IPs or DNS names with ports.

Validate edits with rift check rift.lua. Reload with SIGHUP on Unix, Ctrl-Break on Windows, or rift admin reload when administration is configured. Invalid reloads retain the working configuration; existing sessions keep their routes. Gameplay listener changes require a restart. Ctrl-C drains active sessions for up to 30 seconds by default; a second Ctrl-C closes them immediately.

Authenticated extensions

Lua extensions provide login and transfer decisions, lifecycle events, commands with UUID permission checks, and FIFO server queues. API v2 adds durable namespaced state, recurring jobs, configured HTTP integrations and permissions that update during a session; v1 remains supported. The extension example demonstrates these APIs with a survival queue and staff-only server. See the extension API contract for ordering, deadlines, permissions and reload behavior. Extensions require online authentication and Java 1.19.3–26.3 (the online authentication range).

Compatibility

Minecraft Java 1.8.9 through 26.3, including all intervening releases, supports login, /server, /hub, backend switching and crash recovery. All 66 releases have checksum-pinned official server fixtures for joining, world/chunk delivery, chat, switching, ban rollback and recovery. Older clients use Join Game/Respawn world resets; 1.20.2+ uses the configuration phase. See the protocol matrix and test scope.

Authenticated acceptance has a separate Paper test matrix for signed chat and commands, resource-pack acceptance/refusal and transfer cleanup, plus pinned LuckPerms/EssentialsX and ViaVersion/ViaBackwards combinations. CI runs their startup and rejection preflights; full acceptance requires a signed-in 1.21.11 client and records Paper observations alongside operator confirmations.

Clients and backends must use the same protocol version. Rift does not translate between Minecraft versions. Online authentication, Velocity modern forwarding and authenticated extensions require 1.19.3 or newer; earlier versions use offline backends with forwarding disabled. Bedrock/UDP, unlisted snapshots and legacy pre-1.7 pings are unsupported.

Set rift.config.network = { bungeecord = true } for existing backend plugins to request transfers and query players/servers over the BungeeCord plugin channel. See the supported subchannels and configuration.

Development

Building requires Rust (pinned in rust-toolchain.toml) and a C toolchain, including MSVC on Windows. Python harnesses use the standard library.

cargo build --release --locked
cargo fmt --all --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked --all-targets -- --test-threads=1
python3 -m unittest discover -s tests -p 'test_*.py'
node src/web_assets/tests.js

To preview the documentation site, run npm ci and npm run serve in site/ (Node 24 or newer), then open http://localhost:4321.

CI also runs release tests, wire-protocol checks, real-server integration tests, benchmarks and packaging smoke tests. See the CI workflow and manual online-mode procedure for those checks.

On Linux, benchmark peers negotiate a 1460-byte TCP MSS to avoid loopback window stalls. Benchmark and pilot JSON reports record this cap; compare results with matching socket settings.

BSD-2-Clause. Run rift --license for the embedded license and third-party notices, or rift --help for CLI usage.