Skip to content

Settings

Server-wide settings: PHP versions, mail relay, timezone, MySQL remote access, backups, Cloudflare, web server choice.

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

Endpoint count: 37

Endpoints

Method Path Handler Guards
POST /api/settings/backup/disable-encryption disableBackupEncryption RequireSuperuser()
POST /api/settings/backup/generate-key generateBackupKey RequireSuperuser()
POST /api/settings/backup/get-key getBackupKey RequireSuperuser()
POST /api/settings/backup/test-s3 testBackupS3 RequireSuperuser()
POST /api/settings/backup/webapp-restore restoreWebappBackup RequireSuperuser()
POST /api/settings/backup/webapp-restore-all restoreAllWebappBackups RequireSuperuser()
GET /api/settings/backup/webapp-restore-progress getWebappRestoreProgress RequireSuperuser()
GET /api/settings/backup/webapp-scan scanWebappBackups RequireSuperuser()
GET /api/settings/mail getMailSettings RequireSuperuser()
POST /api/settings/mail setMailSettings RequireSuperuser()
GET /api/settings/mysql getMySQLSettings RequireSuperuser()
POST /api/settings/mysql/max-connections setMySQLMaxConnections RequireSuperuser()
POST /api/settings/mysql/port setMySQLPort RequireSuperuser()
POST /api/settings/mysql/read-only setMySQLReadOnly RequireSuperuser()
POST /api/settings/mysql/remote setMySQLRemote RequireSuperuser()
POST /api/settings/mysql/replication/restart restartReplication RequireSuperuser()
POST /api/settings/mysql/replication/skip-error skipReplicationError RequireSuperuser()
POST /api/settings/mysql/slave-skip-errors setSlaveSkipErrors RequireSuperuser()
GET /api/settings/php-security getPHPSecuritySettings RequireSuperuser()
POST /api/settings/php-security setPHPSecuritySettings RequireSuperuser()
GET /api/settings/php/:version/config getPHPConfig RequireSuperuser()
POST /api/settings/php/:version/config setPHPConfig RequireSuperuser()
GET /api/settings/php/available getAvailablePHPVersions RequireSuperuser()
POST /api/settings/php/install installPHPVersion RequireSuperuser()
POST /api/settings/php/remove removePHPVersion RequireSuperuser()
POST /api/settings/remote-logging saveRemoteLogging RequireSuperuser()
GET /api/settings/server-name getServerName AuthWithDB(cfg.JWTSecret, db)
POST /api/settings/server-name middleware AuthWithDB(cfg.JWTSecret, db)
GET /api/settings/server-timezone getServerTimezone RequireSuperuser()
POST /api/settings/server-timezone setServerTimezone RequireSuperuser()
POST /api/settings/ssh-audit/forward setSSHAuditForward RequireSuperuser()
GET /api/settings/ssh-audit/logs getSSHAuditLogs RequireSuperuser()
POST /api/settings/ssh-audit/rewrite rewriteSSHAudit RequireSuperuser()
GET /api/settings/ssh-audit/status getSSHAuditStatus RequireSuperuser()
POST /api/settings/ssh-audit/toggle toggleSSHAudit RequireSuperuser()
POST /api/settings/webserver/switch switchWebServer RequireSuperuser()
POST /api/settings/website-creation-lock setWebsiteCreationLock RequireSuperuser()

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

POST /api/settings/backup/disable-encryption

Clears the stored backup encryption passphrase and disables encryption for both local and remote backups.

Guards: RequireSuperuser()

Response - 200 OK

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

Side effects

  • Sets the backup_encryption_passphrase, backup_encryption_key_set, backup_encrypt_local, and backup_encrypt_remote settings to empty/0 in the settings store.

Example

curl -X POST https://panel.example.com:2087/api/settings/backup/disable-encryption \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/backup/generate-key

Generates a new 32-character random backup encryption passphrase, stores it encrypted, and returns the plaintext passphrase once.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "passphrase": "string (32 chars)" } }

Errors

Status Code Meaning
500 server_error Random generation, encryption, or settings write failed.

Side effects

  • Stores the encrypted passphrase under setting backup_encryption_passphrase and sets backup_encryption_key_set to 1.

Example

curl -X POST https://panel.example.com:2087/api/settings/backup/generate-key \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/backup/get-key

Returns the currently stored backup encryption passphrase in plaintext (decrypted from the settings store).

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "passphrase": "string" } }

Errors

Status Code Meaning
404 not_found No encryption key is set.
500 server_error Decryption of the stored passphrase failed.

Example

curl -X POST https://panel.example.com:2087/api/settings/backup/get-key \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/backup/test-s3

Tests S3 backup connectivity by calling head_bucket with the supplied credentials, then measures latency to the endpoint host with ping.

Guards: RequireSuperuser()

Request body

{
  "s3_endpoint": "string (required)",
  "s3_bucket": "string (required)",
  "s3_access_key": "string (required)",
  "s3_secret_key": "string (required)",
  "s3_region": "string (optional)",
  "s3_prefix": "string (optional)"
}
Field Type Required Description
s3_endpoint string yes S3 endpoint URL.
s3_bucket string yes Bucket name.
s3_access_key string yes Access key ID.
s3_secret_key string yes Secret access key.
s3_region string no Region; defaults to us-east-1 if empty.
s3_prefix string no Key prefix.

All five string fields are validated to reject shell metacharacters (;|&$`\"'(){}[]!<> and newlines).

Response - 200 OK

{ "data": { "success": true, "message": "Connection successful", "ping_ms": 12.3, "ping_host": "s3.example.com" } }

ping_ms is -1 when the ping probe fails or could not be parsed.

Errors

Status Code Meaning
400 bad_request Body did not bind, a required field was empty, or input contained invalid characters.
400 s3_error The boto3 head_bucket call failed (bad credentials, wrong bucket, unreachable endpoint).

Side effects

  • Runs python3 -c <boto3 script> to call head_bucket, and ping -c 3 against the endpoint host. No persistent changes.

Example

curl -X POST https://panel.example.com:2087/api/settings/backup/test-s3 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"s3_endpoint":"https://s3.example.com","s3_bucket":"backups","s3_access_key":"AKIAEXAMPLE","s3_secret_key":"secret","s3_region":"us-east-1"}'

POST /api/settings/backup/webapp-restore

Restores a single webapp's backup (files and/or database), pulling from S3 first if requested; the actual restore runs in a background goroutine.

Guards: RequireSuperuser()

Request body

{
  "username": "string (required)",
  "domain": "string (required)",
  "set": "string (optional)",
  "time_mark": "string (optional)",
  "source": "string (optional)",
  "restore_files": "bool (optional)",
  "restore_database": "bool (optional)",
  "passphrase": "string (optional)"
}
Field Type Required Description
username string yes System user owning the webapp.
domain string yes Webapp domain to restore.
set string no Backup set name; defaults to default.
time_mark string no Duplicity time mark to restore to (empty = latest).
source string no local or s3; defaults to local.
restore_files bool no Restore site files. If both file/db flags are false, both are restored.
restore_database bool no Restore the database.
passphrase string no Required when the backup is GPG-encrypted.

Response - 202 Accepted

{ "data": { "status": "started", "message": "Restore started in background" } }

Errors

Status Code Meaning
400 bad_request Body failed to bind (e.g. missing username/domain).
400 s3_error Pulling the backup from S3 failed.
400 passphrase_required Backup is encrypted but no passphrase was supplied.

Side effects

  • For source: s3, downloads backup files into /mnt/starkone-backups/<user>/<domain>/<set> before restoring.
  • Background restore may create the website (websites.Create) if missing and runs the restore_website CLI command, restoring files under /home/<username> and/or the database.

Example

curl -X POST https://panel.example.com:2087/api/settings/backup/webapp-restore \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username":"user1","domain":"example.com","source":"local","restore_files":true,"restore_database":true}'

POST /api/settings/backup/webapp-restore-all

Scans all local webapp backups and starts a background bulk restore (one set per site, files + database), tracking progress globally.

Guards: RequireSuperuser()

Request body

{ "source": "string (optional)", "passphrase": "string (optional)" }
Field Type Required Description
source string no local or s3; defaults to local.
passphrase string no Passphrase applied to every encrypted backup.

Response - 202 Accepted

{ "data": { "status": "started", "message": "Bulk restore started for 3 sites", "total": 3 } }

When no backups are found the handler returns 200 OK with an error envelope (code: no_backups).

Errors

Status Code Meaning
409 already_running A bulk restore is already in progress.
200 no_backups No webapp backups were found under /mnt/starkone-backups (returned with HTTP 200).

Side effects

  • Scans /mnt/starkone-backups for duplicity files and restores each unique user/domain in the background via the restore_website CLI command (may create missing websites). Updates the shared restore-progress state.

Example

curl -X POST https://panel.example.com:2087/api/settings/backup/webapp-restore-all \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"source":"local"}'

GET /api/settings/backup/webapp-restore-progress

Returns the current state of the in-progress (or last) bulk webapp restore.

Guards: RequireSuperuser()

Response - 200 OK

{ "running": false, "total": 3, "completed": 3, "current": "example.com", "errors": [] }

Note: this endpoint returns the fields at the top level, not wrapped in a data envelope.

Example

curl https://panel.example.com:2087/api/settings/backup/webapp-restore-progress \
  -H "Authorization: Bearer $TOKEN"

GET /api/settings/backup/webapp-scan

Scans local /mnt/starkone-backups and (if remote backups are enabled) S3 for webapp backup sets, merging the two sources and flagging which sites still exist on the server.

Guards: RequireSuperuser()

Response - 200 OK

{
  "backups": [
    {
      "username": "user1",
      "domain": "example.com",
      "sets": [
        { "name": "default", "latest_date": "2026-06-01 03:00", "size_mb": "12.4 MB", "chains": 1, "source": "local", "encrypted": false }
      ],
      "exists_on_server": true,
      "source": "local"
    }
  ]
}

Note: results are returned at the top level under backups, not in a data envelope. source per entry can be local, s3, or both. Results are sorted with sites missing from the server first.

Side effects

  • Read-only on disk; when remote backups are enabled, runs a python3 boto3 script to list S3 objects. No writes.

Example

curl https://panel.example.com:2087/api/settings/backup/webapp-scan \
  -H "Authorization: Bearer $TOKEN"

GET /api/settings/mail

Returns the outbound mail relay state by inspecting the exim4 service and config files.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "smart_route": "", "config_type": "internet", "running": true, "default_from": "[email protected]" } }
Field Type Description
smart_route string Configured smarthost (dc_smarthost) or empty.
config_type string Exim config type (dc_eximconfig_configtype), e.g. internet or smarthost.
running bool Whether the exim4 service is active.
default_from string Default From address from /etc/starkone/mail.conf.

Side effects

  • Read-only; runs systemctl is-active exim4 and reads exim/panel config files.

Example

curl https://panel.example.com:2087/api/settings/mail \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/mail

Configures the outbound mail smarthost and default From address, then rebuilds the exim config and restarts exim4.

Guards: RequireSuperuser()

Request body

{ "smart_route": "string (optional)", "default_from": "string (optional)" }
Field Type Required Description
smart_route string no Smarthost host::port. Non-empty switches config type to smarthost; empty switches to internet.
default_from string no Default From address, saved to /etc/starkone/mail.conf.

Response - 200 OK

{ "data": { "status": "ok" } }

Errors

Status Code Meaning
400 bad_request Body failed to bind.
500 server_error Could not read or write the exim config file.

Side effects

  • Writes /etc/starkone/mail.conf and rewrites /etc/exim4/update-exim4.conf.conf (config type, dc_local_interfaces, dc_smarthost).
  • Runs update-exim4.conf and systemctl restart exim4.

Example

curl -X POST https://panel.example.com:2087/api/settings/mail \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"smart_route":"smtp.example.com::587","default_from":"[email protected]"}'

GET /api/settings/mysql

Returns MySQL/MariaDB server settings parsed from the active config file plus runtime values (max connections, firewall port state).

Guards: RequireSuperuser()

Response - 200 OK

{
  "data": {
    "remote_enabled": false,
    "port_open": false,
    "bind_address": "127.0.0.1",
    "config_file": "/etc/mysql/mysql.conf.d/mysqld.cnf",
    "slave_skip_errors": false,
    "read_only": false,
    "max_connections": 151
  }
}
Field Type Description
remote_enabled bool True when bind-address is 0.0.0.0, ::, or *.
port_open bool Whether port 3306 appears open in firewalld/csf/iptables.
bind_address string Parsed bind-address.
config_file string Detected config file path.
slave_skip_errors bool Whether slave-skip-errors is set (uncommented).
read_only bool Whether read_only is set to on.
max_connections int Runtime @@max_connections.

Side effects

  • Read-only; runs mysql, firewall-cmd, csf, and/or iptables to read state.

Example

curl https://panel.example.com:2087/api/settings/mysql \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/mysql/max-connections

Sets the MySQL max_connections value both in the config file and at runtime.

Guards: RequireSuperuser()

Request body

{ "value": "int (required, 10-10000)" }
Field Type Required Description
value int yes New max connections; must be between 10 and 10000 (binding:"required,min=10,max=10000").

Response - 200 OK

{ "status": "ok" }

Note: this response is not wrapped in a data envelope.

Errors

Status Code Meaning
400 - Body failed to bind / value out of range (error.message only, no code).
500 - Config file not found, or read/write failed (error.message only).

Side effects

  • Rewrites max_connections in the detected MySQL config file under [mysqld].
  • Runs mysql ... -e "SET GLOBAL max_connections = <value>" to apply immediately.

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/max-connections \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value":500}'

POST /api/settings/mysql/port

Opens or closes TCP port 3306 in the active firewall (firewalld, csf, or ufw).

Guards: RequireSuperuser()

Request body

{ "open": "bool (optional)" }
Field Type Required Description
open bool no true to open port 3306, false to close it.

Response - 200 OK

{ "data": { "status": "ok" } }

Errors

Status Code Meaning
400 bad_request Body failed to bind.

Side effects

  • Runs firewall commands: firewall-cmd --add-port/--remove-port=3306/tcp --permanent + reload, or csf -a/-d 3306, or ufw allow/deny 3306/tcp, depending on which is installed.

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/port \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"open":true}'

POST /api/settings/mysql/read-only

Toggles the MySQL read_only global, persisting it in config and applying it live.

Guards: RequireSuperuser()

Request body

{ "enable": "bool (optional)" }
Field Type Required Description
enable bool no true sets read_only = ON, false sets read_only = OFF.

Response - 200 OK

{ "data": { "status": "ok" } }

Errors

Status Code Meaning
400 - Body failed to bind (error.message only).
500 - Config file not found or read/write failed (error.message only).

Side effects

  • Writes read_only = ON/OFF into the [mysqld] section of the detected config file.
  • Runs mysql ... -e "SET GLOBAL read_only = ON/OFF".

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/read-only \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enable":true}'

POST /api/settings/mysql/remote

Enables or disables remote MySQL access by setting bind-address and restarting the database service.

Guards: RequireSuperuser()

Request body

{ "enabled": "bool (optional)" }
Field Type Required Description
enabled bool no true sets bind-address = 0.0.0.0, false sets 127.0.0.1.

Response - 200 OK

{ "data": { "status": "ok" } }

Errors

Status Code Meaning
400 bad_request Body failed to bind.
500 server_error Config file not found or read/write failed.

Side effects

  • Rewrites bind-address in the detected MySQL config file.
  • Restarts the DB service via systemctl restart mysql, mysqld, and mariadb.

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/remote \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

POST /api/settings/mysql/replication/restart

Restarts MySQL replication by stopping and starting the slave, then reports IO/SQL thread status.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "io_running": true, "sql_running": true } }

Side effects

  • Runs mysql ... -e "STOP SLAVE", then START SLAVE, then reads SHOW SLAVE STATUS.

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/replication/restart \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/mysql/replication/skip-error

Skips a number of replication errors via SQL_SLAVE_SKIP_COUNTER, then reports whether the SQL thread is running.

Guards: RequireSuperuser()

Request body

{ "count": "int (optional)" }
Field Type Required Description
count int no Number of events to skip. Defaults to 1 if absent/invalid; clamped to 1-100.

Response - 200 OK

{ "data": { "skipped": 1, "sql_running": true } }

Errors

Status Code Meaning
500 - The SET GLOBAL SQL_SLAVE_SKIP_COUNTER query failed (error.message only).

Side effects

  • Runs mysql ... -e "STOP SLAVE", SET GLOBAL SQL_SLAVE_SKIP_COUNTER = <count>, START SLAVE, then SHOW SLAVE STATUS.

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/replication/skip-error \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"count":1}'

POST /api/settings/mysql/slave-skip-errors

Persists the slave-skip-errors = 1032,1062,1452 directive in the MySQL config (or removes it) and restarts the database to apply.

Guards: RequireSuperuser()

Request body

{ "enable": "bool (optional)" }
Field Type Required Description
enable bool no true adds the slave-skip-errors directive; false removes it.

Response - 200 OK

{ "data": { "status": "ok" } }

Errors

Status Code Meaning
400 - Body failed to bind (error.message only).
500 - Config file not found or read/write failed (error.message only).

Side effects

  • Edits the [mysqld] section of the detected config file.
  • Restarts the DB via systemctl restart mariadb (falling back to mysql).

Example

curl -X POST https://panel.example.com:2087/api/settings/mysql/slave-skip-errors \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enable":true}'

GET /api/settings/php-security

Returns, per detected PHP version, whether StarkOne's hardened disable_functions list is applied in 99-starkone.ini.

Guards: RequireSuperuser()

Response - 200 OK

{ "settings": { "8.2": true, "8.3": false } }

Note: keys are PHP versions (checked across 7.4, 8.0-8.4); value is true when the FPM 99-starkone.ini contains a disable_functions line including exec and shell_exec. Not wrapped in a data envelope.

Side effects

  • Read-only; reads /etc/php/<ver>/fpm/conf.d/99-starkone.ini for each version.

Example

curl https://panel.example.com:2087/api/settings/php-security \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/php-security

Enables or disables the hardened PHP disable_functions list for one PHP version (FPM and CLI) and restarts PHP-FPM.

Guards: RequireSuperuser()

Request body

{ "version": "string (required)", "secure": "bool (optional)" }
Field Type Required Description
version string yes PHP version, e.g. 8.2.
secure bool no true writes the full hardened disable_functions list; false clears it.

Response - 200 OK

{ "success": true }

Note: not wrapped in a data envelope.

Errors

Status Code Meaning
400 bad_request Body failed to bind.
404 not_found The PHP version's FPM conf.d directory does not exist.
500 server_error Writing the FPM ini file failed.

Side effects

  • Writes disable_functions into /etc/php/<ver>/fpm/conf.d/99-starkone.ini and the matching CLI 99-starkone.ini, preserving other existing lines.
  • Runs systemctl restart php<ver>-fpm.

Example

curl -X POST https://panel.example.com:2087/api/settings/php-security \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"version":"8.2","secure":true}'

GET /api/settings/php/:version/config

Returns the StarkOne-managed PHP INI settings for a version, read only from its 99-starkone.ini file.

Guards: RequireSuperuser()

Path parameters

Name Type Description
version string PHP version, e.g. 8.2.

Response - 200 OK

{ "memory_limit": "256M", "upload_max_filesize": "64M", "custom": "true" }

Note: returns a flat map of INI keys (dots replaced with underscores) plus a custom field ("true"/"false") indicating whether the file exists. Not wrapped in a data envelope.

Side effects

  • Read-only; reads /etc/php/<version>/fpm/conf.d/99-starkone.ini.

Example

curl https://panel.example.com:2087/api/settings/php/8.2/config \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/php/:version/config

Writes a StarkOne-managed PHP INI file (99-starkone.ini) for the version, for both FPM and CLI.

Guards: RequireSuperuser()

Path parameters

Name Type Description
version string PHP version, e.g. 8.2.

Request body

JSON object of setting keys. Only the following keys are recognized; non-empty values are written:

{
  "memory_limit": "256M",
  "upload_max_filesize": "64M",
  "post_max_size": "64M",
  "max_execution_time": "120",
  "max_input_time": "60",
  "max_input_vars": "3000",
  "display_errors": "Off",
  "error_reporting": "E_ALL",
  "log_errors": "On",
  "date_timezone": "UTC",
  "session_gc_maxlifetime": "1440",
  "opcache_enable": "1",
  "opcache_memory_consumption": "128",
  "realpath_cache_size": "4096k",
  "allow_url_fopen": "On"
}
Field Type Required Description
(any of the keys above) string/number no Mapped to the corresponding INI directive (e.g. date_timezone -> date.timezone). Unknown keys are ignored.

Response - 200 OK

{ "success": true, "message": "PHP 8.2 configuration saved" }

Note: not wrapped in a data envelope.

Errors

Status Code Meaning
400 bad_request Body failed to bind as JSON.
500 write_error Writing the FPM ini file failed.

Side effects

  • Writes /etc/php/<version>/fpm/conf.d/99-starkone.ini and the CLI 99-starkone.ini. Does not restart PHP-FPM.

Example

curl -X POST https://panel.example.com:2087/api/settings/php/8.2/config \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"memory_limit":"256M","upload_max_filesize":"64M"}'

GET /api/settings/php/available

Lists PHP versions installable on the host, querying the package manager and adding the ondrej PPA / remi repo if too few versions are found.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "versions": ["8.3", "8.2", "8.1"] } }

Versions are sorted descending.

Side effects

  • Runs apt-cache search (Debian) or dnf search (RHEL). If 0-1 versions are found, may run add-apt-repository ppa:ondrej/php + apt-get update, or install the remi-release RPM, then re-search.

Example

curl https://panel.example.com:2087/api/settings/php/available \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/php/install

Installs a PHP version via the install_php CLI command.

Guards: RequireSuperuser()

Request body

{ "version": "string (required)" }
Field Type Required Description
version string yes PHP version in X.Y format (validated by regex).

Response - 200 OK

{ "success": true, "message": "PHP 8.3 installed" }

Note: not wrapped in a data envelope.

Errors

Status Code Meaning
400 bad_request Body failed to bind.
400 invalid_version Version is not in X.Y format.
500 install_failed The install_php <ver> add CLI command failed (includes output).

Side effects

  • Runs the install_php <version> add CLI command, which installs PHP packages.

Example

curl -X POST https://panel.example.com:2087/api/settings/php/install \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"version":"8.3"}'

POST /api/settings/php/remove

Removes a PHP version via the install_php CLI command.

Guards: RequireSuperuser()

Request body

{ "version": "string (required)" }
Field Type Required Description
version string yes PHP version to remove.

Response - 200 OK

{ "success": true, "message": "PHP 8.3 removed" }

Note: not wrapped in a data envelope.

Errors

Status Code Meaning
400 bad_request Body failed to bind.
500 remove_failed The install_php <ver> remove CLI command failed (includes output).

Side effects

  • Runs the install_php <version> remove CLI command, which uninstalls PHP packages.

Example

curl -X POST https://panel.example.com:2087/api/settings/php/remove \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"version":"8.3"}'

POST /api/settings/remote-logging

Configures remote log forwarding via rsyslog and journald, persisting the settings to the database.

Guards: RequireSuperuser()

Request body

{
  "server": "string (optional)",
  "port": "int (optional)",
  "protocol": "string (optional)",
  "syslog": "bool (optional)",
  "journald": "bool (optional)",
  "http": "bool (optional)"
}
Field Type Required Description
server string no Remote log server address. Rejected if it contains shell metacharacters/spaces or is a loopback address.
port int no Defaults to 514 if <= 0; clamped to <= 65535.
protocol string no tcp or udp; anything else becomes udp.
syslog bool no Enable rsyslog forwarding.
journald bool no Set journald ForwardToSyslog.
http bool no Stored as remote_log_http; no rsyslog effect in this handler.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Body failed to bind, server had invalid characters, or server was a loopback address.
500 server_error Writing the rsyslog config file failed.

Side effects

  • Persists remote_log_* settings to general_settings.
  • Writes or removes /etc/rsyslog.d/starkone-remote.conf and restarts rsyslog.
  • Edits /etc/systemd/journald.conf ForwardToSyslog and restarts systemd-journald when changed.

Example

curl -X POST https://panel.example.com:2087/api/settings/remote-logging \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"server":"203.0.113.10","port":514,"protocol":"udp","syslog":true,"journald":true}'

GET /api/settings/server-name

Returns the configured server name, falling back to the system hostname when unset.

Guards: AuthWithDB(cfg.JWTSecret, db)

Response - 200 OK

{ "data": { "server_name": "panel.example.com" } }

Side effects

  • Read-only; runs hostname only when no server_name setting exists.

Example

curl https://panel.example.com:2087/api/settings/server-name \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/server-name

Sets the panel's server name (handler setServerName; the route also applies RequireSuperuser()).

Guards: AuthWithDB(cfg.JWTSecret, db)

Request body

{ "server_name": "string (optional)" }
Field Type Required Description
server_name string no New server name to persist.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Body failed to bind.
500 db_error Upserting the server_name setting failed.

Side effects

  • Upserts server_name into general_settings.

Example

curl -X POST https://panel.example.com:2087/api/settings/server-name \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"server_name":"panel.example.com"}'

GET /api/settings/server-timezone

Returns the server's timezone via timedatectl, falling back to /etc/timezone then UTC.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "timezone": "UTC" } }

When detection fails entirely, returns { "data": { "timezone": "UTC", "error": "Could not detect server timezone" } }.

Side effects

  • Read-only; runs timedatectl show -p Timezone.

Example

curl https://panel.example.com:2087/api/settings/server-timezone \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/server-timezone

Sets the server timezone using timedatectl set-timezone.

Guards: RequireSuperuser()

Request body

{ "timezone": "string (required)" }
Field Type Required Description
timezone string yes IANA timezone name, e.g. UTC or America/New_York.

Response - 200 OK

{ "data": { "success": true, "timezone": "America/New_York" } }

Errors

Status Code Meaning
400 invalid_request Body failed to bind (missing timezone).
500 set_timezone_failed timedatectl set-timezone failed.

Side effects

  • Runs timedatectl set-timezone <timezone>.

Example

curl -X POST https://panel.example.com:2087/api/settings/server-timezone \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"timezone":"America/New_York"}'

POST /api/settings/ssh-audit/forward

Enables or disables forwarding of SSH command-audit log entries to the configured remote log server via rsyslog.

Guards: RequireSuperuser()

Request body

{ "enabled": "bool (optional)" }
Field Type Required Description
enabled bool no true writes the rsyslog forward rule; false removes it.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Body failed to bind.
400 no_server Enabling requested but no remote log server is configured.

Side effects

  • Persists ssh_audit_forward to general_settings.
  • Writes or removes /etc/rsyslog.d/starkone-audit-forward.conf and restarts rsyslog.

Example

curl -X POST https://panel.example.com:2087/api/settings/ssh-audit/forward \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

GET /api/settings/ssh-audit/logs

Returns parsed SSH command-audit log entries from /var/log/starkone/ssh-audit.log, with optional filtering and pagination (newest first).

Guards: RequireSuperuser()

Query parameters

Name Type Optional Description
user string yes Filter entries to this user (exact match).
search string yes Case-insensitive substring filter on the command.
limit int yes Page size; defaults to 100, clamped to 1-1000.
offset int yes Pagination offset; defaults to 0.

Response - 200 OK

{
  "data": {
    "logs": [
      { "timestamp": "2026-06-17T10:00:00Z", "user": "user1", "command": "ls -la", "exit_code": 0, "tty": "pts/0", "ip": "203.0.113.5" }
    ],
    "total": 1
  }
}

total reflects the count after filtering but before pagination. If the log file is missing, returns empty logs with total: 0.

Example

curl "https://panel.example.com:2087/api/settings/ssh-audit/logs?user=user1&limit=50" \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/ssh-audit/rewrite

Re-applies the SSH audit hooks across users by running the audit_rewrite CLI command.

Guards: RequireSuperuser()

Response - 200 OK

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

Errors

Status Code Meaning
500 server_error The audit_rewrite CLI command failed.

Side effects

  • Runs the audit_rewrite CLI command, which rewrites the audit hooks in user shell profiles.

Example

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

GET /api/settings/ssh-audit/status

Returns SSH command-audit status: whether auditing is enabled, log size/entry count, monitored user count, and remote-forward state.

Guards: RequireSuperuser()

Response - 200 OK

{ "data": { "enabled": true, "log_size": 10240, "entry_count": 42, "users_count": 3, "forward_remote": false } }

Side effects

  • Read-only; reads the audit log file and scans /root/.bashrc and /home/*/.bashrc for the audit marker.

Example

curl https://panel.example.com:2087/api/settings/ssh-audit/status \
  -H "Authorization: Bearer $TOKEN"

POST /api/settings/ssh-audit/toggle

Enables or disables SSH command auditing via the audit_enable / audit_disable CLI commands.

Guards: RequireSuperuser()

Request body

{ "enabled": "bool (optional)" }
Field Type Required Description
enabled bool no true runs audit_enable; false runs audit_disable.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Body failed to bind.
500 server_error The audit_enable/audit_disable CLI command failed.

Side effects

  • Runs the audit_enable or audit_disable CLI command, which installs/removes audit hooks in user shell profiles.

Example

curl -X POST https://panel.example.com:2087/api/settings/ssh-audit/toggle \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

POST /api/settings/webserver/switch

Switches the active web server between nginx and apache: stops/disables all web servers, installs and enables the target, rewrites all site configs, and starts the target.

Guards: RequireSuperuser()

Request body

{ "target": "string (required)" }
Field Type Required Description
target string yes nginx or apache (case-insensitive).

Response - 200 OK

{ "data": { "status": "ok", "message": "Switched to nginx" } }

Errors

Status Code Meaning
400 bad_request Body failed to bind, or target is not nginx/apache.

Side effects

  • Stops and disables nginx, apache2, and httpd via systemctl.
  • Installs the target web server (apt-get/dnf) if missing, enables it, and starts it.
  • Updates the webserver setting in general_settings.
  • Runs the rewrite_all CLI command to regenerate all website configs.

Example

curl -X POST https://panel.example.com:2087/api/settings/webserver/switch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target":"nginx"}'

POST /api/settings/website-creation-lock

Locks or unlocks website creation panel-wide by toggling the website_creation_locked setting.

Guards: RequireSuperuser()

Request body

{ "locked": "bool (optional)" }
Field Type Required Description
locked bool no true locks website creation; false unlocks it.

Response - 200 OK

{ "data": { "locked": true } }

Errors

Status Code Meaning
400 - Body failed to bind (error.message only).

Side effects

  • Upserts website_creation_locked (1/0) into general_settings.

Example

curl -X POST https://panel.example.com:2087/api/settings/website-creation-lock \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"locked":true}'