KRAKEN wiki

API reference

Every REST endpoint the Panel serves, generated from internal/panel/api/openapi.yaml — methods coloured by what the call can do to you, statuses by whose fault they are.

on this page

Everything the web UI does, it does through this surface. There is no private back channel: the bundle the Panel embeds is a client of the same routes below, which is why the reference is worth keeping honest — it is generated from internal/panel/api/openapi.yaml, the contract the Panel ships, rather than written beside it.

Every path below is relative to /api/v1 on the Panel's HTTP listener, which defaults to port 8080. The Agent's gRPC surface is a different thing entirely and is not documented here; the browser never reaches it.

How a caller authenticates

POST /auth/login returns a token, and every other route wants it as Authorization: Bearer <token>. The token is opaque — 32 bytes from crypto/rand — and the Panel stores only its SHA-256 digest, so the value you hold is the only copy there is.

Sessions expire. The lifetime is KRAKEN_SESSION_TTL, 24 hours by default, and POST /auth/logout ends one early. A revoked or expired session stops working everywhere at once, including on a download already granted a token (below).

Three marks on the rows below are worth reading before you skim them:

  • no auth on a route means it takes no session at all. There are two: login, and Agent enrollment, which authenticates with a one-time bootstrap token instead.
  • The whole /setup/* group answers only callers whose source address falls inside KRAKEN_SETUP_ALLOWED_CIDRS — loopback and the private ranges by default. Everything else gets a 403 whatever credentials it carries.
  • Permissions are checked per route on top of the session. The four built-in roles and what they hold are on the security page; a denial on a server you may not see is a 404, not a 403, so that another user's server is not revealed to exist by the refusal.

Downloading a file

The one flow on this page that does not look like the others. A file download cannot carry an Authorization header and still be a browser navigation, so it carries a token instead, and the token is deliberately not a session credential:

  1. POST /servers/{id}/files/download-token with the path set you want. It needs server.files.read, the same permission the raw route carries.
  2. The Panel mints a grant — single-use, 60 seconds, pinned to one server, one route kind and one exact canonicalised path set, and bound to the user and the session that asked — and hands back the token.
  3. GET /servers/{id}/files/raw?token=… or GET /servers/{id}/files/download?token=… redeems it. The browser streams to disk with its own progress, with Content-Length announced whenever the Agent knew the size.

With token present the Authorization header is not consulted at all, so a live session cannot rescue an invalid token; with it absent, both routes authenticate exactly as they always have. Redemption is rate limited at 30 a minute with a burst of 10, per client — the limit sits on the token branch only, so an ordinary session-authenticated download is never refused for sharing an address with somebody probing tokens.

The full argument for why a token in a URL is acceptable here, and the short list of what it deliberately does not protect against, is in SECURITY.md and summarised on Files and SFTP.

Errors

A refusal is JSON — {"error": "…"} — under the status code. Two shapes are worth knowing in advance:

  • A malformed id is a 404, not a 500. Ids are Postgres uuid columns, and a value that cannot be one is indistinguishable from a row that is not there.
  • A 429 carries Retry-After, and a refused request does not consume future capacity.

Auth

POST/auth/loginno auth

Log in and obtain a session token

Rate limited per source IP (20 a minute, burst 20) — generous enough that a whole team behind one NAT address can sign in normally, tight enough to put a ceiling on scripted guessing. A refusal is a 429 with Retry-After.

request bodyapplication/jsonrequired
LoginRequest
responses
  • 200AuthenticatedLoginResponse
  • 401Missing or invalid credentialsError
  • 429Too many login attempts from this address; retry after the Retry-After header says
POST/auth/logout

Invalidate the current session

responses
  • 200Logged out
GET/auth/me

Current user and role

responses
  • 200The authenticated user
    • user User
    • role object
POST/auth/change-password

Change the current user's password

Rotates the caller's password, clears the first-run must-change flag, and issues a fresh session token (the old bearer is invalidated).

request bodyapplication/jsonrequired
  • current_passwordrequired string · password
  • new_passwordrequired string · password
responses
  • 200Password changed; returns a rotated sessionLoginResponse
  • 400Invalid requestError
  • 401Current password incorrect

Servers

GET/servers

List servers

responses
  • 200Servers
POST/servers

Create (deploy) a server from a spec

request bodyapplication/jsonrequired
  • spec_idrequired string · uuid
  • namerequired string
  • variables object
  • memory_mb integerMemory to reserve. Omit to take the spec's own figure — its recommended memory, falling back to the minimum. An explicit value must be at least the spec's min_memory_mb: below that floor the game does not boot.
  • node_id string · uuidPin placement to one node. The scheduler still checks eligibility and reserves; an ineligible pin is a 409 naming the reason rather than a silent placement elsewhere.
  • install_bepinex booleanHonored only when the spec is bepinex_compatible.
  • pin_build booleanPin the server to the build this install pulls: no update pass runs before later starts. Default false — every operator-initiated start/restart re-runs the install script first so the game picks up depot updates.
  • steam_guard_code stringOne-time 2FA code for specs whose install needs an authenticated Steam login. Used for this install only, never persisted.
responses
GET/servers/{id}

Get a server

parameters
  • idpathrequired string · uuid
responses
DELETE/servers/{id}

Delete a retired server permanently

Only a retired server can be deleted: retire a live one first (POST /servers/{id}/retire), which is what removes its containers and world. The node the server was retired from is told to delete what is left of it and its backup archives — but only where they are its own: the zero-config node-local layout keeps each server's archives in &lt;backup_dir&gt;/&lt;server_id&gt;/, and those are deleted. A configured backup directory, a network share, SFTP and SMB keep every server's archives side by side, where an archive cannot be attributed to one server; those are kept, and note names where. An Agent too old to delete archives keeps them too, and note says so. Then the record and its schedules are deleted.

A node that cannot be reached does not block the delete: the removal, delete_backups included, is recorded on the node as a pending removal (see Node.pending_removals) and the node reconciler finishes it when the node answers; removal_pending is then true. A delete whose removal cannot be recorded is refused. Requires server.delete.

parameters
  • idpathrequired string · uuid
responses
  • 200Deleted (the node-side removal may still be pending)PermanentDeleteResult
  • 404Resource not foundError
  • 409code: server_not_retired — the server is not retired; retire it first. code: server_restoring or server_busy while a restore or a retire holds a live server, and server_busy while a revive holds a retired one or its retire is still letting go ("the retire is finishing — retry in a moment"). Nothing was sent to the node.Error
  • 500The node the server was retired from could not be read, or the removal could not be recorded on it; nothing was deleted.Error

POST/servers/{id}/retire

Retire a server — keep its id, config, schedules and backups; remove its containers and world

What the delete button does. Runs in the background and answers at once: 202 with the server in state retiring and a retire block (the state it came from, a phase moving stopping → backing_up → removing, and the final backup's outcome); poll GET /servers/{id} until the state is retired — or, for an abandoned retire, back to what it was. The retire stops the server, takes a final backup unless final_backup is false and waits for it to be ready (at most 30 minutes), then has the node remove the server's containers and data directory — never its archives — releases its memory and ports, deletes its DNS records and port forwards, and switches its schedules off (they are kept, flagged, and the first install that lands after the retire — the revive's, or a reinstall after a revive whose install failed — switches back on exactly those). The retired server is on no node: node_id is empty, retired_from_node_id names where its archives are, and retired_ports the ports it held.

While the node answers, nothing is removed unless the final backup that was asked for is READY. A final backup that fails, or is still being written after 30 minutes, abandons the retire: nothing is removed, the server goes back to the state it came from (offline if the retire had stopped it) and last_error and retire_note say why, starting "retire abandoned:". A backup that could not be tried (the stop or the backup did not reach the node) is tried again when the removal is due, if the node answers then; only a node unreachable at that moment gets its removal queued without a backup — as a pending removal holding the allocation (see Node.pending_removals) — and retire_note says so ("final backup skipped: node unreachable"). A stop the node answers with a refusal abandons the retire as well, and the reason says the way through: retire again with final_backup false, which does not depend on the stop's result: a refusal does not hold it up, and the removal force-removes the container. A retire whose removal cannot be recorded is abandoned too. While it runs, every writer answers 409 server_busy. Requires server.delete.

parameters
  • idpathrequired string · uuid
request bodyapplication/json
  • final_backup booleantake a backup after the stop and before the removal
responses
  • 202The retire started; the server is retiringServer
  • 400Invalid requestError
  • 404Resource not foundError
  • 409Nothing was started. code: server_retired — already retired. code: server_busy — installing, or a start, restart, reinstall, retire or revive holds the server. code: server_restoring — a backup restore is running.Error

POST/servers/{id}/revive

Revive a retired server — place it, install it, optionally restore a backup and start it

Places a retired server on a node again — node_id, else the node it was retired from, else (that node being gone) wherever the scheduler finds room — on the platform it ran on, with its old memory unless memory_mb says otherwise. Its old ports are asked for first and kept where they are free on that node; a port taken since falls back to the spec's default, then the lowest free one. Answers 202 with the server installing; the install pass then runs as a create's does (a failure lands install_failed). When it lands, the backup restore_backup_id is restored through the same job as POST /servers/{id}/backups/{backupId}/restore (restoring, then offline with restore_result), and — when start is true and the restore (if any) succeeded — the server is started as an operator start would start it; a start that is refused or fails lands offline with last_error. The schedules the retire switched off are switched back on once the install lands — by this install, or by a later reinstall when this one fails. Requires server.create.

parameters
  • idpathrequired string · uuid
request bodyapplication/json
  • node_id stringplace it on this node; default the node it was retired from
  • memory_mb integerdefault its old memory; must clear the spec's minimum
  • restore_backup_id stringa ready backup of this server on the node it lands on
  • start booleanstart it once the install (and the restore) succeeded
  • steam_guard_code stringone-time 2FA code for an authenticated Steam install
responses
  • 202Placed; the install is runningServer
  • 400Invalid requestError
  • 404No such server (or one this user may not reach), or code: node_not_found — the node_id asked for does not exist.Error
  • 409Nothing was changed. code: server_not_retired — only a retired server can be revived. code: server_busy — a permanent delete or another revive holds it, or its retire is still letting go ("the retire is finishing — retry in a moment"). code: removal_pending — the retire's removal of the old containers and world is still owed to a node, and would delete the revived world when it landed; revive once it has, or dismiss it. code: backup_not_found / backup_not_ready — the backup to restore is not a ready archive on that node. code: spec_missing / platform_missing — the spec, or the platform the server ran on, is gone. code: server_busy — another revive holds it. No code — no node can host it (the scheduler's reason, per node).Error
  • 503code: node_unreachable — the backup to restore could not be checked on the node.Error
POST/servers/{id}/power

Power action (start/stop/restart/kill)

Start and restart re-run the server's install script before launching, so the game picks up depot updates, unless one of these skips it: the server pins its build, the spec sets skip_update_on_start (per spec or per platform), the server's create or reinstall completed within the last 30 minutes (so the pass would only repeat it), or the spec needs an authenticated Steam login and the node has no stored credentials. GET /servers/{id}/settings reports which way the next start will go. That pass is asynchronous: the response is then 202 with state installing (the console streams the install log), and the server continues to running on its own. Where a failed pass lands depends on the phase: a stop (or agent connection) that fails before the install restores the previous state with last_error set and no reinstall gate, a failed install script lands in install_failed and refuses start until a reinstall, and a failed start over a good install lands in offline. Stop/kill, and a start that skips the pass, still answer 200 with the new state.

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
PowerRequest
responses
  • 200New lifecycle state
  • 202Update pass started; the server is installing and will start when it finishes
  • 409Start or restart refused: the server is installing, its install failed (reinstall first), it is retired (code: server_retired, for every action, stop and kill included — it is on no node), a retire or revive holds it (code: server_busy), a backup restore is running (code: server_restoring — stop and kill still reach the agent, and the state stays restoring until the restore ends), the game spec it was built from no longer exists (code: spec_missing; permanent, since a spec's id cannot be recreated), or a setting its spec marks required is empty (code: required_settings_missing, with the keys in missing_settings). Nothing was attempted and the server's state is unchanged. Stop and kill are never refused for any of these. The node's agent can also refuse a start or restart while an install pass for the server is running on it (code: install_running); retry once it ends.StartRefusal
  • 500The server's spec could not be read (a store error, not a missing spec), so start and restart were refused rather than run unchecked and nothing was attempted — or the node reported a failure it did not classify (code: node_error), with its message in error.Error
  • 503code: node_unreachable. Either the panel has no live connection to the hosting node's agent — then nothing was attempted and the server's state is unchanged — or the agent did not answer within the time allowed, and the action may still be completing on the node (a stop still inside its graceful-stop window, say): error says which.Error
POST/servers/{id}/reinstall

Re-run the install script once, now

Both the retry for a failed install and the explicit "update now" for a server whose start does not update it (a pinned build, or a spec that opted out of update-on-start). Valid from the stopped states — install_failed, offline, crashed — and refused while the server is installing, starting, running, stopping or restoring (the last with code: server_restoring). Runs asynchronously: the server flips to installing and the install log streams over the console socket.

parameters
  • idpathrequired string · uuid
request bodyapplication/json
  • steam_guard_code stringOne-time 2FA code for specs whose install needs an authenticated Steam login.
responses
  • 202Install started
  • 409The server is not in a stopped state
GET/servers/{id}/install-log

Read the retained output of the server's most recent install

The install runs on the Agent and is streamed to the Panel, which buffers it in memory: there is no container left to tail once the phase ends. The buffer is kept after the install finishes — success included, since an installer can exit 0 having produced a broken tree — until the server is retired or deleted. A new attempt (a reinstall, or the update pass before a start) does not discard the one it replaces: that attempt is answered as previous, exactly one back, so a reinstall retrying a failed pass keeps the output that says why it failed. The new attempt is opened before the request that starts it is answered, so a read made once the server shows installing already returns that attempt. It does NOT survive a Panel restart; retained is false when nothing is held, which is not the same as an install that printed nothing.

parameters
  • idpathrequired string · uuid
responses
  • 200Buffered install output
    • server_idrequired string
    • donerequired booleanthe attempt reached a verdict
    • retainedrequired booleanthis Panel process still holds a buffer
    • started_ms integer · int64
    • finished_ms integer · int64
    • linesrequired array of InstallLogLine
    • previousrequired objectThe attempt the current one replaced (an InstallAttempt), or null when there is none — never absent. Only one back is kept; a previous attempt is over, so done is always true, and finished_ms is absent when it was superseded before it reached a verdict.
GET/servers/{id}/settings

Get grouped game settings + current values

parameters
  • idpathrequired string · uuid
responses
  • 200Settings (groups + values), and what the next start does about updatesServerSettings
PUT/servers/{id}/settings

Update game settings values

Send only the settings you are changing. Every key in values is stored on the server verbatim — the panel never compares a value with the spec's default — so a key sent back unchanged becomes the server's own value. A client that GETs the settings and PUTs the whole values map back therefore freezes every value listed in from_spec: it stops following the spec's default and is no longer reported as from the spec. The web UI sends only the fields the operator edited. Sending a blank for a required field that has a spec default hands it back to that default.

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
  • values object
  • variables object
  • pin_build booleanThe server's build pin. Omit to leave it unchanged — an ordinary settings save must not unpin a server.
responses
  • 200Applied. The body carries values and from_spec as the GET returns them (a required field stored blank still yields to the spec's default, and a save of another field keeps it blank on the server), plus variables, applied, restart_needed, hot_reload, variables_changed and pin_build.
  • 400The body is invalid, or a value is outside what the spec allows (a read-only setting, a variable that is not editable) — or the settings were saved and the node refused a config file's path as one that can never work (code: bad_path).Error
  • 404No such server — or the settings were saved and the node reported a config file's folder missing (code: not_found).Error
  • 409The settings were saved, but the node refused the rendered config files: code is file_in_use, node_refused or already_exists, and error begins "settings saved but config apply failed:".Error
  • 500The settings could not be saved — or they were saved and applying the config failed on the Panel (code: config_apply_failed, a render error) or on the node (code: node_error). When the save itself succeeded, error begins "settings saved but config apply failed:".Error
  • 503The settings were saved, but applying them did not complete (code: node_unreachable): the node's agent could not be reached, or it did not answer within the time allowed — in which case the config may still have been written on the node.Error

Specs

GET/specs

List game specs

responses
  • 200Specs
POST/specs

Create a spec (accepts JSON or YAML body)

request bodyapplication/jsonrequired
Spec
request bodyapplication/yamlrequired
string
responses
GET/specs/{id}

Get a spec

parameters
  • idpathrequired string · uuid
responses
PUT/specs/{id}

Update a spec (JSON or YAML)

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
Spec
request bodyapplication/yamlrequired
string
responses
DELETE/specs/{id}

Delete a spec

parameters
  • idpathrequired string · uuid
responses
  • 204Deleted
  • 404Resource not foundError
  • 409Refused while any server is built from the spec: a server keeps its spec's id for life, and could never start again without it.
    • error string
    • code stringspec_in_use
    • servers integerhow many servers use the spec

Nodes

GET/nodes

List nodes

responses
  • 200Nodes
    • nodes array of Node
    • panel_version stringthe Panel's own build — an agent_version that differs is version skew
POST/nodes

Register a node

request bodyapplication/jsonrequired
NodeRegister
responses
GET/nodes/telemetry

Live host vitals for every node

Cpu, memory, disk, network and temperature for each node, as sampled by its Agent and cached by the Panel (refreshed every 5s). Intended to be polled for the node instrument bands.

A node appears only when the Panel holds a recent reading for it: nodes that are unreachable, or whose Agent predates the telemetry RPC, are absent from the map rather than present with zero values. Within a node, each metric group carries a *_known flag — false means the host could not supply that metric (a Windows host has no temperature source, for example) and the value must be rendered as "no data", never as 0.

responses
  • 200Telemetry keyed by node id
    • nodes object

GET/nodes/{id}

Get a node

parameters
  • idpathrequired string
responses
PATCH/nodes/{id}

Rename a node, or update its schedulable capacity (total memory, game-port range)

parameters
  • idpathrequired string
request bodyapplication/json
  • name stringDisplay name; servers reference a node by UUID, so renaming never disturbs what is running. Must not be blank.
  • total_memory_mb integerMust cover memory already reserved by servers on the node
  • port_start integerSet together with port_end; existing allocations are preserved
  • port_end integer
responses
  • 200Updated nodeNode
  • 400Invalid capacity values
DELETE/nodes/{id}

Deregister a node

parameters
  • idpathrequired string
responses
  • 204Deleted
POST/nodes/{id}/agent-update

Push this Panel's embedded agent binary to the node (admin)

Streams the agent build embedded in this Panel (always the Panel's own version) to the node over the mTLS gRPC channel. The agent verifies the checksum, swaps its binary transactionally (keeping the previous build for automatic rollback), and restarts. Dev Panel builds embed no agent binaries and return 503. Containerized agents refuse the push — pull the new image instead.

parameters
  • idpathrequired string
responses
  • 200Binary pushed; the agent is restarting into it
    • from_version string
    • to_version string
    • bytes integer
    • restarting boolean
  • 409Agent already at the Panel's version
  • 503Agent unreachable, or no embedded agent binary in this Panel build (reason verbatim)
GET/nodes/{id}/info

Live Agent info (pings the node over mTLS, brings it online)

The Agent's live NodeInfo, including running_servers, managed_containers (with each container's state) and containers_reported, read the same way as on the Node schema.

parameters
  • idpathrequired string
responses
  • 200Live node info
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/nodes/{id}/servers/{serverID}/power

Power action sent straight to one node's agent

A lower-level power path than POST /servers/{id}/power: it forwards the action to the named node's agent and does not run the update pass, re-push the spec or render config. Start and restart are refused on the same grounds as the server-scoped endpoint, with the same bodies.

parameters
  • idpathrequired string
  • serverIDpathrequired string · uuid
request bodyapplication/jsonrequired
PowerRequest
responses
  • 200The agent's reported state
    • state stringthe agent's enum name, e.g. SERVER_STATE_RUNNING
  • 400Invalid requestError
  • 404No such node (node not found). Or the server is not reachable through this node's path (code: not_found, "server not found on this node"): no such server, a server that lives on a different node, or one this user may not reach. Those three answer with the same body, so a caller probing other nodes' paths learns neither which servers exist nor where they live. Nothing was sent to any agent. A 404 can also be the agent's own not-found, passed through with code: not_found.Error
  • 409Start or restart refused: the server is installing, its install failed, a backup restore is running (code: server_restoring), its spec no longer exists (code: spec_missing), or a required setting is empty (code: required_settings_missing, with the keys in missing_settings). Nothing was sent to the agent. Or the agent refused a start or restart because an install pass for the server is running on the node (code: install_running); retry once it ends.StartRefusal
  • 500The server's spec could not be read, so start and restart were refused — or the node reported a failure it did not classify (code: node_error), with its message in error.Error
  • 503The panel could not reach the node's agent, or the agent did not answer within the time allowed (code: node_unreachable). A timed-out stop or restart may still be completing on the node.Error
DELETE/nodes/{id}/containers/{serverID}

Retire an untracked container (data untouched)

Stops and removes the container a node runs for a server id the Panel has no server on that node for — an entry of managed_containers that no server row matches — and has the Agent forget its spec so its watchdog never adopts it again. The server's data directory and backups are left exactly where they are. Requires server.delete and node.manage.

parameters
  • idpathrequired string
  • serverIDpathrequired stringthe server id from the container's kraken.server_id label, as listed in managed_containers
responses
  • 204Retired; the container is gone and the node's managed_containers no longer lists it
  • 400Malformed server id (code: invalid_server_id)CodedError
  • 403Missing server.delete or node.manage (code: forbidden)CodedError
  • 404Node not found (code: node_not_found)CodedError
  • 409Refused, nothing sent to the node. code: server_tracked — the Panel tracks a server with this id on this node; delete the server instead. code: removal_pending — a removal is already owed for this id (it may delete the data), so a retire that promised the data stays would be undone by the next replay; let it finish, or dismiss it first.CodedError
  • 500The node answered but could not remove the container, its reason verbatim — a container engine the Agent cannot reach included ("docker: cannot connect to the daemon: …") — code: node_error; or the Panel could not read its own records (code: internal).CodedError
  • 503code: node_unreachable. The node's Agent could not be reached — nothing was sent — or it did not answer within the time allowed, in which case the removal may still be completing on the node.CodedError
DELETE/nodes/{id}/removals/{serverID}

Dismiss a pending removal without delivering it

Forgets a removal the node owes — for a node that is gone for good, or a container cleared by hand — and releases the memory and ports it was holding. Nothing is sent to the node: if the container is in fact still running, retire it or remove it on the host. Requires server.delete and node.manage.

parameters
  • idpathrequired string
  • serverIDpathrequired stringthe server_id of an entry in the node's pending_removals
responses
  • 204Dismissed; the record is gone and its allocation released
  • 400Malformed server id (code: invalid_server_id)CodedError
  • 403Missing server.delete or node.manage (code: forbidden)CodedError
  • 404Node not found (code: node_not_found), or no removal is pending for this id (code: removal_not_found)CodedError
  • 500The Panel could not read or update the node record (code: internal). Nothing is sent to the node, so no Agent status applies.CodedError
GET/nodes/{id}/config

Get a node's backup/credential config (stored secrets are never echoed)

parameters
  • idpathrequired string
responses
PUT/nodes/{id}/config

Update a node's backup/credential config and push it to the Agent

Omitted fields keep their stored value; "" clears one. Saving pushes the config to an online Agent, which verifies the configured remote — the apply_* fields report that outcome, and a failure there is not a save failure.

parameters
  • idpathrequired string
request bodyapplication/json
NodeConfigUpdate
responses
  • 200Saved config plus the Agent apply resultNodeConfigView +
    • applied booleanthe config reached the Agent
    • apply_ok booleanthe configured target(s) verified
    • apply_detail string
  • 400Unknown backup_target, an unrecognized path token, or both replication mirrors enabled
  • 404Resource not foundError

Files

GET/servers/{id}/files

List files under a path

parameters
  • idpathrequired string · uuid
  • pathquery stringdirectory to list
responses
  • 200Directory listing
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
GET/servers/{id}/files/content

Read a single file's contents

parameters
  • idpathrequired string · uuid
  • pathqueryrequired string
responses
  • 200File content (capped)
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
GET/servers/{id}/files/raw

Download a single file (raw bytes)

Streams the file. Authenticate either with the ordinary session bearer, or with a one-time token from /files/download-token — the token form is what lets a plain download link carry the authorization, so the browser streams to disk instead of buffering the payload in memory. When token is present the Authorization header is not consulted at all, and path must be exactly the path the token was minted for.

The response carries Content-Length when the Agent announced the file's size (an older Agent does not, and then there is none), so a truncated transfer is detectable rather than arriving as a complete download. Accept-Ranges: none goes out either way: a download token is single-use, so a ranged second request could only be refused. Rate limited per source IP (30 a minute, burst 10).

parameters
  • idpathrequired string · uuid
  • pathquery stringThe file to stream. Required on the session-authenticated form; on the token form it may be omitted, since the token already names the path — supplied, it must match that path exactly or the request is refused.
  • tokenquery stringA one-time download token from POST /servers/{id}/files/download-token. Single-use, 60 seconds, bound to one server, one exact path set and the user who minted it. Present, it is the whole authority for the request and no Authorization header is read; absent, the route authenticates as it always has.
responses
  • 200Raw file stream
  • 400No path was supplied on the session-authenticated form, or the path can never work — it escapes the server's data directory, or names a folder (code: bad_path)
  • 401Unknown, expired, already-used or wrong-server/wrong-path download token — or the session that minted it is gone
  • 404No such server, or one this user may not reach (existence is not revealed) — or no such file on the node (code: not_found); the body is a JSON error, never a file
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 429Too many token redemptions from this address; retry after the Retry-After header says
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error

GET/servers/{id}/files/download

Download a token's path set as a zip

The GET twin of the zip route, for a plain download link. It carries no body: the paths come from the one-time token, which is also the only thing that authorizes it. Required — without a token there is nothing to stream.

parameters
  • idpathrequired string · uuid
  • tokenqueryrequired stringA one-time download token from POST /servers/{id}/files/download-token. Required here: it is both the authorization and the only source of the paths.
responses
  • 200Zip stream
  • 400No token was supplied
  • 401Unknown, expired, already-used or wrong-server download token — or the session that minted it is gone
  • 404No such server, or one this user may not reach (existence is not revealed) — or a path that does not exist on the node (code: not_found); the body is a JSON error, never a file
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 429Too many token redemptions from this address; retry after the Retry-After header says
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/download

Download selected paths as a zip

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
  • paths array of string
responses
  • 200Zip stream
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/download-token

Mint a one-time download token

Issues an opaque token that authorizes exactly one download of exactly one path set on this one server, for 60 seconds, on behalf of the calling user. It is redeemed on GET /files/raw or GET /files/download and deleted on first use; the user's permission is re-checked at redemption, so a revoked role takes effect immediately, and so is the minting session, so logging out or having that session revoked kills any token it minted. The token is held in the Panel's memory only, never persisted, and never a session credential. Requires server.files.read — the same permission the raw route carries.

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
  • path stringOne file, for a token redeemable on GET /files/raw.
  • paths array of stringA path set, for a token redeemable on GET /files/download.
responses
  • 201Token minted
    • url stringThe tokenised URL to navigate to.
    • token string
    • kind stringrawzip
    • expires_at string · date-time
    • expires_in_seconds integer
  • 400No path, both shapes at once, or a path that escapes the tree
  • 404No such server, or one this user may not reach (existence is not revealed)
POST/servers/{id}/files/mkdir

Create a directory

parameters
  • idpathrequired string · uuid
responses
  • 201Created
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/move

Move/rename a path

parameters
  • idpathrequired string · uuid
responses
  • 200Moved
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/copy

Copy a path

parameters
  • idpathrequired string · uuid
responses
  • 200Copied
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/write

Write/overwrite a file

parameters
  • idpathrequired string · uuid
responses
  • 201Written
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/upload

Upload files (multipart)

The request body is capped at 64 MiB plus a megabyte of multipart framing; anything larger is refused outright rather than spooled to the Panel's disk.

parameters
  • idpathrequired string · uuid
responses
  • 201Uploaded
  • 400The body is not a valid multipart form, names no files, or targets a path that can never work (code: bad_path)
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 413The upload is larger than the Panel accepts
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/files/delete

Delete paths

parameters
  • idpathrequired string · uuid
responses
  • 200Deleted
  • 400The path can never work: it escapes the server's data directory, or names the data root where a file or folder is required (code: bad_path).Error
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error

Backups

GET/servers/{id}/backups

List backups

Also answers for a retired server — keeping its archives listable is the point of retiring — from the node it was retired from. When that node no longer exists the answer is 404 not_found: its archives went with it.

parameters
  • idpathrequired string · uuid
responses
  • 200Backups
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/backups

Create a backup

parameters
  • idpathrequired string · uuid
request bodyapplication/json
  • name string
responses
  • 202Archiving started; poll the list for the backup to reach readyBackup
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
POST/servers/{id}/backups/{backupId}/restore

Restore a backup

Only valid while the server is stopped (state offline, crashed or install_failed). Runs asynchronously: the server enters restoring at once, and GET /servers/{id} carries a restore block (the archive, the state to return to, when it began, the phase and the compressed bytes read) until the restore ends. While it runs every writer of the server's tree answers 409 server_restoring — start, restart, reinstall, server delete, settings save, backup create and delete, and every file write, upload, move, copy, mkdir and delete (reads and downloads are not refused) — and scheduled restarts, backups, commands and replication are skipped with the reason in the schedule's last_error. When it ends the server goes back to the state it came from (restore.prev_state), and restore_result records the outcome; a failed restore's reason says whether the files were rolled back. last_error is never written by a restore.

parameters
  • idpathrequired string · uuid
  • backupIdpathrequired string
responses
  • 202Restore started; the server is restoringServer
  • 409The server is not stopped, a restore is already running for it (code: restore_in_progress), a start, restart or reinstall is in flight and still holds the server (code: server_busy — its row may read offline until the agent answers), a retire holds it (code: server_busy, state retiring), or it is retired (code: server_retired). Nothing was started.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error
DELETE/servers/{id}/backups/{backupId}

Delete a backup

parameters
  • idpathrequired string · uuid
  • backupIdpathrequired string
responses
  • 204Deleted
  • 404No such server, or one this user may not reach (existence is not revealed) — or the path or backup does not exist on the node (code: not_found).Error
  • 409The node refused the operation, and code says why: file_in_use (another process holds the file — usually the server's own game, still running), node_refused (the node's filesystem denied it) or already_exists. error carries the node's own words. On an operation that writes the server's files, server_restoring means a backup restore holds them and server_busy that a retire, revive or delete holds the server; nothing was sent to the node. On a retired server every file and backup route but the backup list answers server_retired (revive it first).Error
  • 500The node reported a failure it did not classify (code: node_error); error carries its message.Error
  • 503The panel could not reach the node's agent, or it did not answer in time (code: node_unreachable).Error

Schedules

GET/servers/{id}/schedules

List a server's scheduled tasks

parameters
  • idpathrequired string · uuid
responses
POST/servers/{id}/schedules

Create a scheduled task

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
ScheduleInput
responses
PUT/servers/{id}/schedules/{scheduleId}

Update a scheduled task

parameters
  • idpathrequired string · uuid
  • scheduleIdpathrequired string · uuid
request bodyapplication/jsonrequired
ScheduleInput
responses
DELETE/servers/{id}/schedules/{scheduleId}

Delete a scheduled task

parameters
  • idpathrequired string · uuid
  • scheduleIdpathrequired string · uuid
responses
  • 204Deleted

Users & Roles

GET/users

List users

responses
  • 200Users
POST/users

Create a user

responses
  • 201Created
PUT/users/{id}

Update a user

parameters
  • idpathrequired string · uuid
responses
  • 200Updated
DELETE/users/{id}

Delete a user

parameters
  • idpathrequired string · uuid
responses
  • 204Deleted
POST/users/{id}/password

Reset a user's password

parameters
  • idpathrequired string · uuid
responses
  • 200Reset
GET/roles

List roles

responses
  • 200Roles
GET/permissions

List known permissions

responses
  • 200Permissions

Enrollment

POST/agents/bootstrap-tokens

Issue a one-time Agent bootstrap token (admin)

request bodyapplication/jsonrequired
  • node_name stringaudit-log label only (default "remote-agent") — the node's real name comes from the agent at registration
  • ttl_seconds integertoken lifetime (default 900)
responses
  • 201Token issued
    • token string
    • node_name string
    • expires_at string · date-time
    • ca_fingerprint stringfull SHA-256 fingerprint of the Panel CA — embed in the agent's install command as --ca-fingerprint to pin the enrollment
  • 503CA signing not configured
GET/agents/enroll-status

Poll a bootstrap token's lifecycle (pending → redeemed)

Lets the setup wizard show live enrollment progress. Once redeemed, the response includes the enrolling source IP and the hosts the agent baked into its certificate (its reachable addresses) so node registration can be prefilled.

parameters
  • tokenqueryrequired string
responses
  • 200Token state
    • status stringpendingredeemedexpired
    • node_name string
    • ip string
    • hosts array of stringagent-advertised reachable addresses, IPs first
    • agent_port integeragent-reported gRPC port for the registration prefill
    • expires_at string · date-time
    • redeemed_at string · date-time
POST/agents/enrollno auth

Enroll an Agent — exchange a bootstrap token + CSR for a signed cert

Authenticated solely by the one-time bootstrap token (the Agent has no session yet). Returns the signed Agent certificate and the CA cert. Re-enrolling with a fresh token rotates the certificate.

request bodyapplication/jsonrequired
  • tokenrequired string
  • csrrequired stringPEM-encoded certificate signing request
  • agent_port integergRPC port the agent will serve on (default 9090) — used to prefill node registration
responses
  • 200Signed certificate + CA (PEM)
    • certificate string
    • ca string
  • 401Invalid/expired bootstrap token

Setup

GET/setup/status

First-run onboarding progress

The whole /setup/* surface answers only requests whose source IP falls inside KRAKEN_SETUP_ALLOWED_CIDRS (default: loopback + private ranges); external sources receive 403 regardless of credentials. setup_complete latches true permanently once onboarding finishes (computed complete once, or explicitly dismissed).

responses
  • 200Setup status
    • admin_must_change_password boolean
    • using_memory boolean
    • has_node_online boolean
    • has_spec boolean
    • has_server boolean
    • setup_complete boolean
  • 403Source IP outside the setup allowlist
POST/setup/dismiss

Mark first-run onboarding as finished (hides the Setup shortcut permanently)

responses
  • 200Dismissed
    • setup_complete boolean
  • 403Source IP outside the setup allowlist
GET/setup/database

Current datastore target (never returns the password)

responses
  • 200Datastore config
    • using_memory boolean
    • env_locked booleanmanaged by KRAKEN_DATABASE_URL
    • host string
    • port integer
    • user string
    • dbname string
    • sslmode string
POST/setup/database

Connect Postgres — create DB if needed, migrate, persist, restart

request bodyapplication/jsonrequired
DBConnectRequest
responses
  • 200Saved; the Panel will restart onto Postgres
    • restarting boolean
  • 400Invalid requestError
  • 409Database is env-managed (KRAKEN_DATABASE_URL)
  • 502Connection / create / migrate failed
POST/setup/database/test

Preflight a Postgres connection

request bodyapplication/jsonrequired
DBConnectRequest
responses
  • 200Reachable
    • ok boolean
    • db_exists boolean
    • can_create_db boolean
  • 400Invalid requestError
  • 502Connection failed
POST/setup/local-enrollno auth

Issue a bootstrap token for the co-located Agent (loopback only)

Mints a one-time enrollment token for the local single-host Agent. Gated on a loopback source IP; not reachable off-host.

responses
  • 201Token issuedBootstrapToken
  • 403Not called from the Panel host
  • 503Agent enrollment not configured
GET/catalog

List bundled starter game specs

responses
POST/catalog/{id}/import

Import a bundled catalog spec (one-click)

parameters
  • idpathrequired string
responses
  • 201ImportedSpec
  • 404Catalog item not found
  • 409A spec with that slug is already imported

Settings

GET/settings

Panel-global settings status (never returns secrets)

responses
PUT/settings

Update Panel-global settings (Cloudflare token, UniFi gateway)

request bodyapplication/jsonrequired
  • cloudflare_api_token stringScoped Cloudflare API token; empty string clears it
  • unifi_url stringUniFi controller base URL, e.g. https://192.168.1.1
  • unifi_api_key stringUniFi OS API key; empty string clears it
  • unifi_site stringUniFi site (default 'default')
responses
POST/settings/cloudflare/test

Verify the stored Cloudflare token by listing its zones

responses
  • 200Reachable zones
    • zones array of string
  • 400Cloudflare not configured
  • 502Cloudflare API error
POST/settings/unifi/test

Verify the stored UniFi credentials (lists forwards + WAN IP)

responses
  • 200Reachable
    • forward_count integer
    • wan_ip string
  • 400UniFi not configured
  • 502UniFi API error

DNS

GET/servers/{id}/dns

Current DNS assignment + target for a server

parameters
  • idpathrequired string · uuid
responses
  • 200DNS state
    • cloudflare_configured boolean
    • unifi_configured boolean
    • target_host stringexternal/WAN host for DNS + connect
    • lan_host stringnode LAN IP (port-forward target)
    • ports object
    • dns ServerDNS
    • forwards object
PUT/servers/{id}/dns

Assign a DNS name (creates A/CNAME + optional SRV in Cloudflare)

parameters
  • idpathrequired string · uuid
request bodyapplication/jsonrequired
  • namerequired stringFQDN, e.g. play.example.com
  • service stringSRV service label (e.g. minecraft); omit to skip SRV
  • port_name stringspec port to advertise; defaults to the primary port
responses
  • 200Records created
  • 400Invalid requestError
  • 409Refused, nothing changed: server_retired — the server is retired (revive it first); server_busy — a retire, revive or delete holds it; server_restoring — a backup restore holds it.Error
  • 502Cloudflare API error
  • 503Cloudflare not configured
DELETE/servers/{id}/dns

Remove a server's DNS records

parameters
  • idpathrequired string · uuid
responses
  • 200Removed
POST/servers/{id}/forwards/{portName}

Open (create/enable) or close (disable) a UniFi port forward for a server port

parameters
  • idpathrequired string · uuid
  • portNamepathrequired string
request bodyapplication/jsonrequired
  • openrequired boolean
responses
  • 200Forward updated
  • 400Invalid requestError
  • 409Refused, nothing changed: server_retired — the server is retired (revive it first); server_busy — a retire, revive or delete holds it; server_restoring — a backup restore holds it.Error
  • 502UniFi API error
  • 503UniFi not configured

Audit

GET/audit

List recent audit entries (newest first)

responses
  • 200Audit entries
    • entries array of AuditEntry
    • retention_days integerhow many days of audit log the Panel keeps (KRAKEN_AUDIT_RETENTION_DAYS); 0 means entries are never pruned

Meta

GET/version

Panel build version

The Panel's own build. Shown as the version stamp in the UI and compared against each node's agent_version to surface fleet version skew. Authenticated, so an unauthenticated caller can't fingerprint the build.

responses
  • 200Build metadata
    • version string
    • commit string
    • date string

Schemas

AuditEntry
  • id string · uuid
  • time string · date-time
  • actor string
  • actor_id string
  • action string
  • method string
  • path string
  • target_type string
  • target_id string
  • status integer
  • ip string
  • forwarded_for stringThe raw X-Forwarded-For chain as received. Present only when ip cannot identify anybody — a NAT gateway standing in for every caller, or an address exempted with KRAKEN_RATE_LIMIT_IP_SKIP. Written by the caller and therefore untrusted: forensics, never an input to a decision.
Backup
  • id string
  • name string
  • size integer
  • created_ms integer · int64
  • state stringpendingreadyfailedarchive lifecycle — backups run asynchronously
  • replication string""pendingdonefailedoff-node (SFTP) mirror status; empty when replication isn't configured
  • error stringwhy the backup failed (state=failed), or a degraded-capture note on a ready archive; omitted when clean
BootstrapToken
  • token string
  • node_name string
  • expires_at string · date-time
CatalogItem
  • id string
  • name string
  • slug string
  • description string
  • icon_url string
  • banner_url string
  • platforms array of string
  • already_imported boolean
CodedError
  • error stringthe reason, for a person
  • code stringa stable identifier for a client to branch on
DBConnectRequest
  • hostrequired string
  • port integer
  • userrequired string
  • password string · password
  • dbname string
  • sslmode string
Error
  • error stringThe human-readable reason, as the UI shows it.
  • code stringA stable, machine-readable reason, present on the failures a client may want to branch on — branch on this, never on error. A failed Agent operation carries one of node_unreachable, not_found, bad_path, file_in_use, node_refused, already_exists, install_running or node_error; a settings save whose config could not be rendered carries config_apply_failed. A backup restore's refusals carry server_restoring (a restore holds the server's files, so the write, start or delete was refused), restore_in_progress (a second restore) or server_busy (a start, restart or reinstall still holds the server). The retire model (#360) adds server_retired (the server is retired: nothing boots it, writes its files or backs it up — revive it first), server_not_retired (revive and permanent delete are only for a retired server), removal_pending (a removal for the server is still owed to a node) and server_busy for a retire or revive in progress.
InstallAttempt

One earlier install attempt, in the shape of the current one without the server-level fields.

  • done booleanthe attempt is over — always true for a previous attempt
  • started_ms integer · int64
  • finished_ms integer · int64absent when the attempt was superseded before it reached a verdict
  • lines array of InstallLogLine
InstallLogLine
  • ts integer · int64
  • stream string"install" or "error"
  • text string
LoginRequest
  • usernamerequired string
  • passwordrequired string · password
LoginResponse
  • token string
  • expires_at string · date-time
  • user User
Node
  • id string
  • name string
  • os stringlinuxwindows
  • wine_enabled boolean
  • status stringonlinepartialofflinecordonedonline = ready for work; partial = the Agent answers but cannot reach its container runtime (see runtime_error) and is not schedulable; offline = the Agent is unreachable; cordoned = reachable but excluded from new placements.
  • runtime_error stringwhy the container runtime is unreachable (set while status is partial)
  • agent_version stringAgent build seen on last contact; compare with panel_version on the list response
  • running_servers integerThe AGENT's own count of the kraken.managed containers it had running on last contact. Compared against the server rows the Panel placed on this node it is what surfaces a container the Panel has lost track of; it is not derived from those rows. Always the count of LIVE containers (running, paused or restarting — see managed_containers[].state), whatever managed_containers carries.
  • managed_containers array of
    • server_id stringfrom the container's own kraken.server_id label; what the Panel matches against its rows
    • container_name stringthe container's name on the host (e.g. kraken_&lt;server-id&gt;)
    • state stringDocker's state word as the Agent read it, lowercase and passed through: running, exited, created, dead, paused, restarting, removing. Read it by one rule — LIVE (holds memory and ports) = running, paused, restarting; NOT LIVE = created, exited, dead, removing; EMPTY = live (from an Agent that predates the field, which reported live containers only).
    Every kraken.managed game container the Agent reported, named, with Docker's state for each (the one-shot _install container is never listed). When containers_reported is true the list includes stopped containers, so a reader that means "running" must keep only the live ones by the rule on state. From an 0.54–0.58 Agent (containers_reported false) it holds only running containers and no state. Absent from an Agent older than 0.54.0, which reports only the count, and absent when empty — so without containers_reported a client must fall back to running_servers rather than treat a missing list as "nothing running".
  • containers_reported booleanTrue when the Agent's last report listed every managed container with its state, stopped ones included — even an empty list. It is what makes a missing managed_containers mean "no containers on this node" rather than "an Agent too old to say", and so what lets a client say an offline server has no container until it starts. Absent (false) from an Agent that predates it.
  • pending_removals array of
    • server_id string
    • delete_data booleanwhether the operator's delete also removes the world and config
    • delete_backups booleana permanent delete of a retired server: the node also deletes the server's archives, where they are its own
    • requested_at string · date-time
    • attempts integerfailed tries so far, the one made at delete time included
    • last_error stringthe most recent failure, verbatim
    • next_attempt string · date-timewhen the reconciler may try again — 20s after the first failure, doubling after each, an hour apart at most
    • memory_mb integerthe deleted server's memory, still allocated on this node until the removal lands
    • ports array of integerthe deleted server's host ports, still allocated on this node until the removal lands
    Server removals owed to this node: servers retired or deleted in the Panel whose removal the node never confirmed, because it was unreachable or its Agent reported a failure. A retired server's own removal is replayed; one for an id a live server row holds on this node waits. The Panel's node reconciler replays each one with the recorded delete_data intent, with backoff, while the node answers, and drops it once the Agent confirms — releasing the memory and ports it held. Absent when nothing is owed.
  • address string
  • public_host string
  • external_ip string
  • total_memory_mb integer
  • allocated_memory_mb integer
NodeConfigUpdate

Writable node config. Omit a field to leave it unchanged, send "" to clear it.

  • backup_target string""localsharesftpsmb
  • backup_dir string
  • sftp_host string
  • sftp_user string
  • sftp_password string · password
  • sftp_private_key string · passwordfull PEM, newlines included
  • sftp_base_path string
  • sftp_known_host_key string
  • replicate_to_sftp boolean
  • smb_host string
  • smb_share string
  • smb_user string
  • smb_password string · password
  • smb_domain string
  • smb_base_path string
  • replicate_to_smb boolean
  • steam_username string
  • steam_password string · password
NodeConfigView

Where a node stores backups, plus the credentials the Panel injects into installs. Secrets are write-only: a stored one surfaces as a *_configured flag, never as its value.

  • backup_target stringlocalsharesftpsmbshare = a network share mounted on the host; smb = an SMB server the Agent dials itself with the credentials below (no host mount, so it works from a service account)
  • backup_dir stringnode-local (or mounted-share) archive directory; supports {{SLUG}}
  • sftp_host stringhost:port (default port 22)
  • sftp_user string
  • sftp_password_configured boolean
  • sftp_key_configured boolean
  • sftp_base_path stringremote archive directory; supports {{SLUG}}
  • sftp_known_host_key stringpinned SSH host key in authorized_keys format; blank = trust-on-use
  • replicate_to_sftp boolean
  • smb_host stringhost[:port] (default port 445)
  • smb_share stringshare name only, e.g. games
  • smb_user string
  • smb_password_configured boolean
  • smb_domain stringoptional NTLM domain; blank for a NAS or standalone server
  • smb_base_path stringdirectory inside the share, share-relative; supports {{SLUG}}
  • replicate_to_smb booleanmutually exclusive with replicate_to_sftp
  • steam_username string
  • steam_configured boolean
NodeRegister
  • name stringblank = adopt the agent's self-reported node id (KRAKEN_NODE_ID)
  • os stringlinuxwindowsblank = adopt the agent's self-reported OS
  • wine_enabled booleanignored — derived from os (Wine ships in the game image, so every Linux node supports linux-wine)
  • addressrequired stringAgent gRPC host:port
  • public_host string
  • total_memory_mb integer
  • port_start integer
  • port_end integer
NodeTelemetry

One node's host vitals. Rate metrics (cpu, network) are measured by the Agent over its own fixed sampling interval, so they do not skew with how often this endpoint is polled.

  • ts_unix_ms integer · int64when the Agent sampled this reading
  • uptime_seconds integer · int64
  • cpu_percent number · double0-100 across all cores; meaningful only when cpu_known
  • cpu_cores integer
  • cpu_known booleanfalse until the Agent has taken two samples, or when /proc is unreadable
  • mem_total_mb integer · int64
  • mem_used_mb integer · int64physical memory in use — total minus available, so reclaimable cache does not count as used
  • mem_known boolean
  • disk_path stringthe filesystem measured — the one holding the Agent's data dir
  • disk_total_mb integer · int64
  • disk_used_mb integer · int64
  • disk_known boolean
  • net_rx_bps number · doublebytes per second across the host's physical interfaces
  • net_tx_bps number · double
  • net_known boolean
  • link_rtt_ms number · doublethe Panel's own timing of the gRPC round trip that fetched this reading — no *_known flag, because the entry existing is the proof of the round trip
PermanentDeleteResult
  • note stringEmpty when everything went. Otherwise: archives a shared backup target kept ("archives on a shared backup target were kept (the network share): …"), an Agent too old to delete archives, or a node that is owed the delete.
  • removal_pending booleanthe node could not be reached; it deletes what is left, archives included, when it answers
PowerRequest
  • actionrequired stringstartstoprestartkill
RestoreProgress

A running backup restore. Progress is compressed bytes read from the archive against its size; bytes_total is 0 when the size is unknown (an agent too old to report progress, or a target that cannot size the archive), and a client must then show progress as unknown, not as zero.

  • backup_id string
  • prev_state ServerState
  • phase stringopeningextractingapplyingdonerestoringrestoring = an old agent restoring without progress
  • bytes_done integer · int64
  • bytes_total integer · int64
  • started_at string · date-timeserver clock
RestoreResult

How the most recent backup restore ended. Kept on the server until the next restore replaces it.

  • backup_id string
  • ok boolean
  • error stringthe agent's reason, which says whether the files were rolled back; absent when ok
  • finished_at string · date-timeserver clock
ScheduleInput
  • name string
  • actionrequired stringrestartbackupcommandreplicatewhat the task does on each run; restart runs only on a server that is running, starting or crashed (it is skipped on offline, stopping, installing and install_failed, and while a required setting is empty or the spec is gone), never runs the update pass, and records any skip in last_error; replicate mirrors the server's existing backups to the node's configured off-node target (SFTP or SMB)
  • cronrequired string5-field cron expression (min hour dom month dow)
  • command stringrequired when action=command
  • enabled boolean
ScheduledTask
ScheduleInput +
  • id string · uuid
  • server_id string · uuid
  • last_run_at string · date-time
  • next_run_at string · date-time
  • last_error string
  • disabled_by_retire booleanswitched off by its server's retire; the first install that lands after the retire (the revive's, or a reinstall after a revive whose install failed) switches it back on. An operator's own enable or disable clears it
  • created_at string · date-time
Server
  • id string · uuid
  • name string
  • spec_id string · uuid
  • node_id string
  • kind stringlinux-nativelinux-winewindows-native
  • state ServerState
  • vars object
  • settings object
  • ports object
  • memory_mb integer
  • dns ServerDNS
  • bepinex booleandeployed with BepInEx mod support
  • pin_build booleanPinned to the build on disk — the panel skips the install/update pass it otherwise runs before every start or restart.
  • last_error stringwhy the most recent provisioning attempt failed (a backup restore never writes it)
  • restorePresent only while a backup restore is running (state restoring). backup_id, prev_state and started_at are stored on the server; the phase and byte counts are the running job's live reading.
  • restore_resultHow the most recent backup restore ended.
  • retire
    • phase stringstoppingbacking_upremoving
    • prev_state ServerState
    • final_backup stringoffrequestedreadyfailedskippedthe final backup so far; off = not asked for
    • final_backup_note stringwhy it failed or was skipped
    • final_backup_id stringthe archive, once the node has one
    • started_at string · date-timeserver clock
    Present only while a retire runs (state retiring).
  • retired_at string · date-timewhen the server was retired; only on a retired server
  • retire_note stringWhat the retire could not do — a final backup skipped ("final backup skipped: node unreachable") or failed, a removal queued for a node that did not answer — or, starting "retire abandoned:", why a retire did not happen. Cleared by the next retire and by a revive.
  • retired_from_node_id stringthe node a retired server left: where its archives are, and where a revive places it by default. node_id is empty while it is retired
  • retired_ports objectthe host ports a retired server held (spec port name → port), asked for again by a revive
  • provisioned_at string · date-timeWhen the most recent create or reinstall install pass completed. A start within 30 minutes of it skips the update pass, which would only repeat an install that just ran.
  • created_at string · date-time
ServerDNS
  • name string
  • zone_id string
  • service string
  • port_name string
  • record_ids array of string
ServerSettings
  • groups array of objectthe spec's grouped settings schema
  • values objectThis server's effective values: what the start gate judges and the config files render from. A field the server has no stored value for takes the spec's current default, and so does a required field stored blank (empty or only whitespace) when the spec has a non-blank default for it.
  • from_spec array of stringThe setting keys whose value in values is the spec's current default rather than one stored on this server: a field the spec added after the server was created, or a required field stored blank that yields to the spec's default. Every other value is the server's own. Always present, in declared order. The Settings tab marks a required field listed here as coming from the spec.
  • variables array of objectthe spec's launch variables with this server's values
  • hot_reload boolean
  • pin_build boolean
  • updates_on_start booleanFalse when the spec opted out of update-on-start (per spec or per platform), so the build pin is moot. About the spec, not the next start — see next_start_updates.
  • next_start_updates booleanWhether the next operator start or restart through the panel would run the update pass right now. Scheduled restarts and the node-scoped power endpoint never run it, and it means nothing while the server is installing or install_failed, where a start is refused.
  • update_skip_reason stringspecpinnedfresh_installsteam_loginWhy next_start_updates is false; absent when it is true.
ServerState
stringinstallinginstall_failedofflinestartingrunningstoppingcrashedrestoringretiringretired
SettingsView
  • cloudflare_configured boolean
  • unifi_configured boolean
  • unifi_url string
  • unifi_site string
Spec

Game specification (the "egg" equivalent). Large; key fields shown.

  • id string · uuid
  • name string
  • slug string
  • version integer
  • platforms array of
    • kind string
    • image string
  • startup
    • command string
    • ready_regex string
    • stop
      • type stringsignalcommand
      • value string
    • restart
      • on_crash boolean
      • max_retries integer
  • ports array of
    • name string
    • protocol stringtcpudp
    • default integer
    • required boolean
  • backup
    • include array of string
    • exclude array of string
    What a backup of this game captures — a backup is the SAVE DATA, not the reinstallable install tree. Patterns are doublestar globs (** spans any number of path segments) matched against data-dir-relative POSIX paths ("savegame/world.db"). include selects (omit for everything); exclude then filters, so an exclude wins on a conflict. Omit the whole block and the Panel applies its built-in policy: the whole data dir minus an ephemeral-only exclude list. Rejected at save time if a pattern does not compile.
StartRefusal
  • error stringthe sentence to show the operator
  • code stringrequired_settings_missingspec_missinginstall_runningserver_restoringserver_retiredserver_busyabsent for the install-state refusals; install_running when the node's agent refused because an install pass is running there; server_restoring while a backup restore holds the server; server_retired on a retired server (revive it first); server_busy while a retire or revive holds it (state retiring)
  • missing_settings array of stringrequired_settings_missing only: the empty fields' keys
User
  • id string · uuid
  • username string
  • email string
  • role_id string
  • disabled boolean
  • must_change_password boolean