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:
On error:
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
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
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.
| 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:
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
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
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
| 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
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/lvsto discover the volume group and root LV. - Runs
lvextend -l +100%FREE <lv>(for100%FREE) orlvextend -L <size> <lv>. - Grows the filesystem via
resize2fs <lv>, falling back toxfs_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
| 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
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, andifconfig.me/all.json.
Example
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
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:
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 selfupdateand 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:
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
vgsto find the volume group, thenlvremove -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:
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
vgsandlvs -o originto 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 -qqthenapt list --upgradable. - On RHEL-family: runs
dnf check-update -q.
Example
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:
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