Skip to content

System

Host-level system operations: LVM snapshots, file backups, disk and resource info.

All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.

Endpoint count: 17

Endpoints

Method Path Handler Guards
DELETE /api/system/backup/:filename deleteBackup RequireSuperuser()
GET /api/system/backup/download/:filename downloadBackup RequireSuperuser()
POST /api/system/backup/pull-s3 pullBackupsFromS3 RequireSuperuser()
POST /api/system/backup/restore/:filename restoreBackup RequireSuperuser()
POST /api/system/backup/upload uploadBackup RequireSuperuser()
GET /api/system/ips getServerIPs AuthWithDB(cfg.JWTSecret, db)
POST /api/system/lvm/extend middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/system/portcheck netprobePortCheck AuthWithDB(cfg.JWTSecret, db)
GET /api/system/processes middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/system/processes/:pid/kill middleware AuthWithDB(cfg.JWTSecret, db)
GET /api/system/public-ip getPublicIP AuthWithDB(cfg.JWTSecret, db)
POST /api/system/rewrite-all middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/system/selfupdate middleware AuthWithDB(cfg.JWTSecret, db)
DELETE /api/system/snapshots/:name deleteSnapshot RequireSuperuser()
POST /api/system/snapshots/:name/restore restoreSnapshot RequireSuperuser()
GET /api/system/updates middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/system/updates middleware AuthWithDB(cfg.JWTSecret, db)

Source

Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:

{ "data": { ... } }

On error:

{ "error": { "code": "string", "message": "string" } }

Reference

DELETE /api/system/backup/:filename

Deletes a single stored backup file from the panel's backup directory.

Guards: RequireSuperuser()

Path parameters

Name Type Description
filename string Name of the backup file to delete.

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 delete_failed backup.Delete failed (e.g. file not found / not removable); the underlying error message is returned.

Side effects

  • Removes the backup file from the backup directory via backup.Delete(filename).

Example

curl -X DELETE https://panel.example.com:2087/api/system/backup/backup-2026-06-17.tar.gz \
  -H "Authorization: Bearer $TOKEN"

GET /api/system/backup/download/:filename

Streams a stored backup file back to the client as a file download.

Guards: RequireSuperuser()

Path parameters

Name Type Description
filename string Name of the backup file to download.

Response - 200 OK

Returns the raw file contents (not the JSON envelope). The handler sets Content-Disposition: attachment; filename=<filename> and serves the file at backup.FilePath(filename) via c.File(path). If the file does not exist, gin responds with 404.

Example

curl -X GET https://panel.example.com:2087/api/system/backup/download/backup-2026-06-17.tar.gz \
  -H "Authorization: Bearer $TOKEN" \
  -o backup-2026-06-17.tar.gz

POST /api/system/backup/pull-s3

Pulls backup archives that exist in the configured S3 bucket down into the local backup directory.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "count": 3, "success": true } }

count is the number of backups pulled from S3.

Errors

Status Code Meaning
500 pull_failed backup.PullFromS3(db) failed (e.g. S3 not configured or credentials invalid).

Side effects

  • Downloads backup files from S3 into the local backup directory via backup.PullFromS3(db).

Example

curl -X POST https://panel.example.com:2087/api/system/backup/pull-s3 \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/backup/restore/:filename

Restores the panel from a previously created backup file, then restarts the panel service.

Guards: RequireSuperuser()

Path parameters

Name Type Description
filename string Name of the backup file to restore. A .gpg suffix marks an encrypted backup.

Request body

The body is optional. A passphrase is only used for encrypted (.gpg) backups.

{ "passphrase": "string (optional)" }
Field Type Required Description
passphrase string no Manual passphrase for decrypting an encrypted (.gpg) backup. If empty, or if the file is not .gpg, the default restore path is used.

Response - 200 OK

{
  "data": {
    "success": true,
    "pre_restore_file": "pre-restore-2026-06-17.tar.gz",
    "message": "Restored successfully. Panel will restart in 3 seconds..."
  }
}

pre_restore_file is a safety backup created before the restore overwrote current state.

Errors

Status Code Meaning
500 restore_failed backup.Restore / backup.RestoreWithPassphrase failed.

Side effects

  • Overwrites panel state from the backup; creates a pre-restore safety backup.
  • Schedules a panel restart 3 seconds after responding: systemctl restart starkone.

Example

curl -X POST https://panel.example.com:2087/api/system/backup/restore/backup-2026-06-17.tar.gz.gpg \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"passphrase": "my-secret-passphrase"}'

POST /api/system/backup/upload

Uploads a backup file (multipart form upload) into the panel's backup directory.

Guards: RequireSuperuser()

Request body

Sent as multipart/form-data, not JSON. The file is read from the file form field.

Field Type Required Description
file file (multipart) yes The backup file to upload. Only .db, .tar.gz, and .gpg extensions are accepted.

Response - 200 OK

Flat (non-enveloped) body:

{ "success": true, "filename": "backup-2026-06-17.tar.gz", "type": "full" }

type is "db" for .db uploads and "full" for .tar.gz / .gpg uploads.

Errors

Status Code Meaning
400 bad_request No file uploaded, or the filename does not end in .db, .tar.gz, or .gpg.
500 save_error Saving the uploaded file to the backup directory failed.

Side effects

  • Writes the uploaded file to backup.FilePath(filename).

Example

curl -X POST https://panel.example.com:2087/api/system/backup/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]"

GET /api/system/ips

Lists the server's local IPv4 addresses (excluding loopback).

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "ips": ["203.0.113.10", "203.0.113.11"] } }

IPs are parsed from ip -4 addr show; 127.0.0.1 is filtered out. If the command fails, ips may be empty/null.

Example

curl -X GET https://panel.example.com:2087/api/system/ips \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/lvm/extend

Extends the root LVM logical volume by the requested size and grows its filesystem.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Request body

{ "size": "string (required)" }
Field Type Required Description
size string yes Amount to extend. Use 100%FREE to consume all free extents, or a fixed size such as +5G / 5G (passed to lvextend -L).

Response - 200 OK

{ "success": true }

Note: flat (non-enveloped) success body.

Errors

Status Code Meaning
400 bad_request Body binding failed (size missing/invalid).
400 no_lvm No LVM volume group found (vgs returned empty).
400 no_lv Root logical volume could not be located within the volume group.
500 extend_failed lvextend failed; the command output is returned.
500 resize_failed Both resize2fs and xfs_growfs / failed to grow the filesystem.

Side effects

  • Runs vgs / lvs to discover the volume group and root LV.
  • Runs lvextend -l +100%FREE <lv> (for 100%FREE) or lvextend -L <size> <lv>.
  • Grows the filesystem via resize2fs <lv>, falling back to xfs_growfs /.

Example

curl -X POST https://panel.example.com:2087/api/system/lvm/extend \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"size": "100%FREE"}'

POST /api/system/portcheck

Probes whether a TCP or UDP port on a given host is reachable from the server, reporting connection latency.

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "host": "string (required)", "port": 443, "protocol": "string (optional)" }
Field Type Required Description
host string yes Target hostname or IP. Rejected if it contains shell metacharacters.
port int yes Target port; must be > 0. Values above 65535 are clamped to 65535.
protocol string no tcp (default) or udp.

Response - 200 OK

{ "data": { "open": true, "message": "TCP connection to example.com:443 successful", "time_ms": "12.3" } }

open is a boolean; a failed/closed probe still returns 200 with open: false and an empty time_ms. For UDP, an open result can mean "open or filtered" (no ICMP unreachable received).

Errors

Status Code Meaning
400 bad_request Body binding failed, host empty, or port <= 0.
400 bad_request host contains forbidden shell metacharacters.

Example

curl -X POST https://panel.example.com:2087/api/system/portcheck \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"host": "example.com", "port": 443, "protocol": "tcp"}'

GET /api/system/processes

Lists the top running processes (up to 50) sorted by CPU, memory, or PID.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Query parameters

Name Type Optional Description
sort string yes Sort order. One of cpu (default), mem, or pid. Maps to ps --sort=-pcpu / -pmem / -pid.

Response - 200 OK

{
  "data": {
    "processes": [
      { "pid": 1234, "user": "user1", "cpu": 3.2, "mem": 1.5, "rss_mb": 42.0, "command": "/usr/sbin/nginx" }
    ]
  }
}

rss_mb is resident memory in MB (RSS in KB divided by 1024). At most 50 processes are returned.

Errors

Status Code Meaning
500 ps_error The ps aux command failed.

Example

curl -X GET "https://panel.example.com:2087/api/system/processes?sort=mem" \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/processes/:pid/kill

Force-kills a process by PID with kill -9, with guards against killing PID 1 or the panel itself.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Path parameters

Name Type Description
pid int PID of the process to kill.

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 bad_request PID is not a valid positive integer.
400 forbidden Attempt to kill PID 1, or the panel's own process.
500 kill_failed The kill -9 <pid> command failed.

Side effects

  • Runs kill -9 <pid>, terminating the target process.

Example

curl -X POST https://panel.example.com:2087/api/system/processes/1234/kill \
  -H "Authorization: Bearer $TOKEN"

GET /api/system/public-ip

Resolves the server's public IPv4/IPv6 addresses (via external lookup services) plus the connecting client's IP.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{
  "data": {
    "server": "203.0.113.10",
    "server_ipv4": "203.0.113.10",
    "server_ipv6": "2001:db8::1",
    "server_info": "US ยท host.example.com",
    "client": "203.0.113.50"
  }
}

server is a legacy field preferring IPv4. Any field may be empty if the corresponding external lookup fails (5s timeout each). client is c.ClientIP() of the requester.

Side effects

  • Makes outbound HTTPS calls to api.ipify.org, api6.ipify.org, and ifconfig.me/all.json.

Example

curl -X GET https://panel.example.com:2087/api/system/public-ip \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/rewrite-all

Regenerates all managed service configuration files by invoking the panel CLI's rewrite_all command.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Response - 200 OK

{ "data": { "success": true, "output": "...command output..." } }

Errors

Status Code Meaning
500 rewrite_failed starkone-cli rewrite_all exited non-zero; combined output is returned.

Side effects

  • Runs /usr/sbin/starkone-cli rewrite_all, which rewrites managed config files (and typically reloads affected services).

Example

curl -X POST https://panel.example.com:2087/api/system/rewrite-all \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/selfupdate

Triggers a self-update of the panel in a detached background process so it survives the panel service restart.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Response - 200 OK

Flat (non-enveloped) body:

{ "status": "ok", "message": "Update started in background" }

Errors

Status Code Meaning
500 (none) { "error": "<message>" } - failed to start the detached update process.

Side effects

  • Starts systemd-run --scope --quiet /usr/sbin/starkone-cli selfupdate and returns immediately without waiting.

Example

curl -X POST https://panel.example.com:2087/api/system/selfupdate \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/system/snapshots/:name

Removes an LVM snapshot.

Guards: RequireSuperuser()

Path parameters

Name Type Description
name string Name of the LVM snapshot to delete.

Response - 200 OK

Flat (non-enveloped) body:

{ "success": true }

Errors

Status Code Meaning
400 bad_request Snapshot name missing.
400 no_lvm No LVM volume group found (vgs returned empty).
500 delete_failed lvremove -f <vg>/<name> failed; command output is returned.

Side effects

  • Runs vgs to find the volume group, then lvremove -f <vg>/<name> to delete the snapshot.

Example

curl -X DELETE https://panel.example.com:2087/api/system/snapshots/snap-2026-06-17 \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/snapshots/:name/restore

Merges (restores) an LVM snapshot into its origin volume and schedules a server reboot to complete the merge.

Guards: RequireSuperuser()

Path parameters

Name Type Description
name string Name of the LVM snapshot to restore.

Response - 200 OK

Flat (non-enveloped) body:

{ "success": true, "message": "Snapshot merge scheduled. Server will reboot." }

Errors

Status Code Meaning
400 bad_request Snapshot name missing.
400 no_lvm No LVM volume group found (vgs returned empty).
400 not_snapshot The named volume is not a snapshot or was not found (lvs -o origin empty).
500 restore_failed lvconvert --merge failed; command output is returned.

Side effects

  • Runs vgs and lvs -o origin to validate the snapshot.
  • Runs lvconvert --merge <vg>/<name>.
  • Schedules a reboot ~2 seconds after responding to complete the merge.

Example

curl -X POST https://panel.example.com:2087/api/system/snapshots/snap-2026-06-17/restore \
  -H "Authorization: Bearer $TOKEN"

GET /api/system/updates

Checks for available OS package updates and reports the detected OS and upgradable packages.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Response - 200 OK

Flat (non-enveloped) body:

{
  "count": 2,
  "os": "Ubuntu 24.04 LTS",
  "packages": [
    { "name": "openssl", "current": "3.0.1", "available": "3.0.2" }
  ]
}

On Debian/Ubuntu, each package includes name, current, and available. On RHEL-family systems (dnf), packages include name and available only.

Side effects

  • On Debian/Ubuntu: runs apt-get update -qq then apt list --upgradable.
  • On RHEL-family: runs dnf check-update -q.

Example

curl -X GET https://panel.example.com:2087/api/system/updates \
  -H "Authorization: Bearer $TOKEN"

POST /api/system/updates

Applies all available OS package updates.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also enforces RequireSuperuser())

Response - 200 OK

Flat (non-enveloped) body:

{ "status": "ok", "output": "...package manager output..." }

Errors

Status Code Meaning
500 (none) { "error": "<message>", "output": "<command output>" } - the upgrade command exited non-zero.

Side effects

  • On Debian/Ubuntu: runs apt-get upgrade -y.
  • On RHEL-family: runs dnf update -y.

Example

curl -X POST https://panel.example.com:2087/api/system/updates \
  -H "Authorization: Bearer $TOKEN"