Files
horizon/docs/api/server-reference.md
T

16 KiB

Server reference

Everything horizon-server offers a host or a tool: its commands, every server.toml key and the control API. How to host: hosting.md; the design: server.md; stability and versions: stability.md.

Commands

horizon-server help prints:

horizon-server: host a Horizon (Minecraft Java Edition 26.3) server

Usage: horizon-server [--dir DIR] <command>

  init        Set up a server here: the EULA, the world, the port, who may join (asks, or takes flags:
              --eula --world NAME --seed SEED --port N --whitelist/--no-whitelist --player NAME... --op NAME
              --offline --yes)
  run         Run the server in the foreground; type console commands, Ctrl-C stops it cleanly. It
              restarts the game if it crashes.
  start       Run the server in the background, or resume a stopped supervisor (output goes to logs/supervisor.log)
  stop, restart
  status [--json]
              Up or not, TPS, MSPT, players, RAM and CPU
  cmd COMMAND...
              Run a console command on the running server and print what it answers
  report [SECONDS]
              Record a performance report for SECONDS (default 60): where the ticks' time goes,
              entities, chunks, network, memory; saved in reports/ (JSON and text, no player data)
  logs [-f] [-n N]
              The last N lines of the log (default 50); -f follows it
  console     Attach to the running server: its log as it happens, TPS, MSPT, players and RAM at the
              top, and a command line for console commands; Ctrl-D detaches
  mcp         An MCP server on stdin and stdout, for an AI agent to run and moderate the server
              (players, kick, ban, whitelist, logs, events, backups, settings, restart...)
  backup [NAME]
              Zip the world and the settings into backups/ (a running server saves first and stays up)
  backups [--json]
              List the backups
  restore BACKUP [--dimension ID] [--yes]
              Put a backup's world in place (the current one is kept in backups/); a running
              server restarts with it. --dimension (like minecraft:the_nether) brings back only
              that dimension and leaves the rest of the world as it is
  service install|uninstall|print [--system]
              Start the server at boot or login: a systemd unit on Linux (--system: for every user,
              with sudo), a launchd agent on macOS, a task at logon on Windows
  panel       A one-time login link for the web panel ([panel] in server.toml; 127.0.0.1:8130 by
              default)
  update [--check [--json]] [--version TAG] [--timeout SECONDS]
              Install the newest release (checked against GitHub's SHA-256), back up the world and
              restart on it; goes back by itself if the game isn't up within the timeout (300 s).
              --archive FILE --version NAME installs an archive you have
  rollback    Back to the release used before the update
  config [--json]
              The settings in server.toml
  config set KEY=VALUE... [--json]
              Change settings (for example world.difficulty=hard server.max_players=10), checked first;
              a running server takes the difficulty, default game mode and whitelist at once, the rest
              at its next start
  import FILE Write server.toml from a vanilla server.properties
  import-resources DIR
              Copy game files prepared elsewhere (by the launcher or horizon-prepare) into place, after
              checking they are complete and for this Minecraft version; the old copy stays on failure
  op NAME, deop NAME
  whitelist on|off|list, whitelist add|remove NAME
              Who may join (on the running server, or in the files when it is stopped)
  players [--json]
              Who is online, the operators, the whitelist and the bans
  kick NAME [REASON], ban NAME [REASON], pardon NAME, ban-ip ADDRESS|NAME [REASON], pardon-ip ADDRESS
              Moderate the running server, or edit ban files while it is stopped

  --dir DIR   The server folder (default: the current folder)

Settings live in server.toml; the server writes server.properties from it before every start.

server.toml

Horizon server settings (docs/server.md). The server writes them into server.properties before every start: edit this file, not that one. Unknown keys are refused. horizon-server init writes this file with these defaults; horizon-server config set SECTION.KEY=VALUE changes it.

[server]

Key Default In server.properties What it does
motd "A Minecraft Server" motd the line under the server's name in the server list
port 25565 server-port the port players connect to
bind "" server-ip listen on every address, or only on this one (server-ip)
max_players 20 max-players how many players can be online at once
online_mode true online-mode check players' Minecraft accounts (false: anyone joins under any name)
whitelist true white-list only whitelisted players and operators can join
enforce_whitelist false enforce-whitelist kick online players taken off the whitelist
view_distance 10 view-distance chunks sent around each player
simulation_distance 10 simulation-distance chunks around each player where mobs, crops and redstone tick
max_view_distance 0 horizon-max-view-distance past view_distance, saved chunks only, up to this (never generated; 0: off)
admin_feedback_to_ops true horizon-admin-feedback-to-ops operators see each other's commands ([name: ...], vanilla); false: only the log

[world]

Key Default In server.properties What it does
name "world" level-name the world folder
seed "" level-seed empty: a random seed on the first start
game_mode "survival" gamemode survival, creative, adventure or spectator
difficulty "easy" difficulty peaceful, easy, normal or hard
hardcore false hardcore one life: a player who dies becomes a spectator

[properties]

Any other server.properties key, spelled as vanilla spells it, for example: spawn-protection = 16

[engine]

Key Default In server.properties What it does
path "" the game program; empty: the one installed beside horizon-server
resources "" the prepared game files; empty: resources/ here, else the usual places
restart_on_crash true start the game again when it crashes
low_memory false true: no transparent huge pages, ~60 MB less idle for ~1 % more tick time

[backups]

Key Default In server.properties What it does
every "6h" automatic backups while the server runs: 30m, 6h, 1d...; "0" turns them off
keep 12 automatic backups kept (backups made by hand are never deleted)

[updates]

Key Default In server.properties What it does
keep 1 other releases kept by updates, besides the one in use and the one rollback goes to

[panel]

Key Default In server.properties What it does
bind "127.0.0.1:8130" the web panel: this computer only, or a tailnet address (100.x); "" turns it off
allow_public false true lets bind be any address; never put the panel on the open internet

[api]

Key Default In server.properties What it does
bind "" the control API: "" a free port on 127.0.0.1; or a fixed one, like 127.0.0.1:8131 or a tailnet address
token "" its token (16 characters or more); "" a new random one at every start
allow_public false true lets bind be any address; the token is then all that guards the server

[transfer]

A proxy handing players over between servers (docs/server.md). Not vanilla's accepts-transfers (the transfer packet).

Key Default In server.properties What it does
enabled false true: take a proxy's player-* requests; needs Velocity forwarding (forwarding.secret)
expect_seconds 10 how long a player the proxy announced is waited for, when it names no time
max_expect_seconds 60 the longest the proxy may ask for
max_expected 64 players waited for at once
max_freeze_seconds 30 the longest a player stays frozen while it is handed over
max_hold_seconds 60 the longest a player arriving from another server is held before it is dropped

[discord]

Post the chat, joins, leaves and deaths to a Discord channel, drawn like the in-game chat (docs/hosting.md)

Key Default In server.properties What it does
webhook "" the channel's webhook URL (a secret: anyone with it can post there); "" off
events ["chat", "join", "leave", "death"] what is posted; "kick" too if you like
enabled true false pauses it and keeps the webhook

Control API (/api/v1)

HTTP and JSON on 127.0.0.1. A running server writes its address and a random token to .horizon/api.json, readable only by its own user; every request sends Authorization: Bearer <token> and a Host that is that address. Errors answer {"error": "..."} with 400, 401, 403, 404, 405 or 409. The web panel uses the same routes with its session.

Method Path Takes Answers What it does
GET /api/v1/status nothing phase, version, pid, uptime_seconds, engine_pid, engine_uptime_seconds, crashes, last_crash, sample (TPS, MSPT, players, RAM, CPU), update How the server does
GET /api/v1/metrics ?since=T (seconds since 1970) {"samples": [...]}: one a second, the last 10 minutes The samples taken after T, for graphs
GET /api/v1/logs ?since=N {"next": N, "lines": [...]} The log lines after the Nth (the last 2000 are kept)
GET /api/v1/events ?since=N (or Last-Event-ID), ?types=join,leave,kick,chat,chat_filtered,death,log,phase (all by default) with Accept: text/event-stream, server-sent events as they happen (id, event: the type, data: JSON); otherwise {"next": N, "events": [...]} Log lines, the supervisor's phase, and the game's joins, leaves, kicks, chat, messages the chat filter refused (chat_filtered, without their text) and deaths (the last 2000 kept)
POST /api/v1/command {"line": "<one command>"} the lines the command logged Run a console command on the running game
POST /api/v1/stop nothing {"ok": true} Stop the game and the supervisor
POST /api/v1/start nothing {"ok": true} Start the game while keeping the supervisor and API running
POST /api/v1/restart nothing {"ok": true} Restart the game
GET /api/v1/backups nothing {"backups": [{"name", "bytes"}]} List the backups
POST /api/v1/backups {"name": "..."} (optional) {"name": "<file>"} Back up now (a running game saves first and stays up)
DELETE /api/v1/backups ?name=<file> (the full name backups lists) {"name": "<file>"} Delete a backup
GET /api/v1/backups/download ?name=<file> the zip Download a backup
GET /api/v1/crash-reports nothing {"reports": [{"name", "bytes"}]}, newest first (the .txt files in crash-reports/) List the game's crash reports
GET /api/v1/crash-reports/read ?name=<file> (a name crash-reports lists) the report's text (text/plain) Read one crash report
POST /api/v1/restore {"name": "<backup>", "dimension": "minecraft:the_nether"} (dimension optional) {"name": "<backup>", "dimension": "<id>" or null} Restart the game with that backup's world, or only one dimension of it (what it replaces is kept in backups/)
POST /api/v1/update {"tag": "...", "timeout": seconds} (both optional) {"ok": true}; status says how it went (update) Restart on that installed release, or download the newest first; goes back if the game isn't up within the timeout
GET /api/v1/metrics/prometheus nothing the latest sample as Prometheus text: horizon_up, horizon_tps, horizon_mspt{stat}, horizon_players_online, horizon_memory_rss_bytes, horizon_loaded_chunks{dimension} and horizon_entities{dimension} (players and block entities apart)... Metrics for a monitoring tool to scrape
GET /api/v1/players nothing online (name, uuid, address, ping_ms, dimension, position, op_level), max_players, ops, whitelist (enabled, players), bans, ip_bans (with reason, source, created, expires) Who is online, and vanilla's lists
GET /api/v1/mods nothing engine (found, target, base, horizon, hooks) and native (id, version, sha256), from the native-mods.json the launcher wrote beside the game program The native mods the server's game program was built with
POST /api/v1/players/kick {"name", "reason"} (reason optional) {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Kick an online player
POST /api/v1/players/ban {"name", "reason"} (reason optional) {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Ban a player (kicked if online)
POST /api/v1/players/pardon {"name"} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Lift a player's ban
POST /api/v1/players/ban-ip {"ip", "reason"}, or {"name"} of an online player {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Ban an address (its players are kicked)
POST /api/v1/players/pardon-ip {"ip"} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Lift an address's ban
POST /api/v1/players/op {"name"} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Make a player an operator (op-permission-level)
POST /api/v1/players/deop {"name"} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Make a player no longer an operator
POST /api/v1/whitelist {"enabled": bool} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Turn the whitelist on or off
POST /api/v1/whitelist/add {"name"} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Whitelist a player
POST /api/v1/whitelist/remove {"name"} {"changed": bool, "output": [...]}: whether the list (or who is online) changed, and the console's lines Take a player off the whitelist (kicked if enforce-whitelist)
GET /api/v1/config nothing {"config": {...}}: server.toml's settings by section (api.token hidden) The settings
PATCH /api/v1/config {"changes": {"world.difficulty": "hard", ...}} saved, applied (taken by the running game at once), restart (from the next start), running Change settings as config set does; not engine.*, panel.*, api.*, nor RCON, query or management keys
POST /api/v1/panel/login nothing {"url": "..."} A one-time login link for the web panel (with the token only, not from the panel)