Skip to content

Webapps & Websites

Manage WordPress sites, custom web apps, domain aliases, SSL, cron, backups, SSH keys, rate limits, performance, Cloudflare integration, Varnish, DNS, and per-website ACL.

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

Endpoint count: 92

Endpoints

Method Path Handler Guards
DELETE /api/webapps/:id deleteWebsite RequirePermission(db, "websites_administration")
GET /api/webapps/:id getWebsite RequirePermission(db, "websites_management")
PATCH /api/webapps/:id updateWebsite RequirePermission(db, "websites_administration")
GET /api/webapps/:id/access listWebsiteAccess RequirePermission(db, "websites_management")
POST /api/webapps/:id/access grantWebsiteAccess RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/access/:uid revokeWebsiteAccess RequirePermission(db, "websites_administration")
GET /api/webapps/:id/acl listWebsiteACLRulesets RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
DELETE /api/webapps/:id/acl/:ruleset_id unassignACLFromWebsite RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
POST /api/webapps/:id/acl/assign assignACLToWebsite RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
GET /api/webapps/:id/acl/available listAvailableACLRulesets RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
DELETE /api/webapps/:id/aliases/:alias removeAlias RequirePermission(db, "websites_administration")
GET /api/webapps/:id/analytics middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/webapps/:id/backup-sets createWebsiteBackupSet RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/backup-sets/:set deleteWebsiteBackupSet RequirePermission(db, "websites_administration")
GET /api/webapps/:id/backups listWebsiteBackups RequirePermission(db, "websites_administration")
POST /api/webapps/:id/backups createWebsiteBackup RequirePermission(db, "websites_administration")
GET /api/webapps/:id/backups/progress getBackupProgress RequirePermission(db, "websites_administration")
POST /api/webapps/:id/backups/restore restoreWebsiteBackup RequirePermission(db, "websites_administration")
POST /api/webapps/:id/change-domain changeWebsiteDomain RequirePermission(db, "websites_administration")
POST /api/webapps/:id/cloudflare/attack-mode cfAttackMode RequirePermission(db, "websites_administration")
POST /api/webapps/:id/cloudflare/cache/mode cfCacheMode RequirePermission(db, "websites_administration")
POST /api/webapps/:id/cloudflare/cache/purge cfPurgeCache RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/cloudflare/connect cfDisconnect RequirePermission(db, "websites_administration")
POST /api/webapps/:id/cloudflare/connect cfConnect RequirePermission(db, "websites_administration")
GET /api/webapps/:id/cloudflare/dns cfGetDNS RequirePermission(db, "websites_administration")
POST /api/webapps/:id/cloudflare/dns cfCreateDNS RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/cloudflare/dns/:record_id cfDeleteDNS RequirePermission(db, "websites_administration")
PATCH /api/webapps/:id/cloudflare/dns/:record_id cfUpdateDNS RequirePermission(db, "websites_administration")
GET /api/webapps/:id/cloudflare/status cfStatus RequirePermission(db, "websites_administration")
GET /api/webapps/:id/crontab getWebsiteCrontab RequirePermission(db, "websites_administration")
POST /api/webapps/:id/crontab addCrontabEntry RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/crontab/:index deleteCrontabEntry RequirePermission(db, "websites_administration")
PUT /api/webapps/:id/crontab/:index updateCrontabEntry RequirePermission(db, "websites_administration")
GET /api/webapps/:id/cust-nginx getCustNginxConfig RequirePermission(db, "websites_administration")
POST /api/webapps/:id/cust-nginx saveCustNginxConfig RequirePermission(db, "websites_administration")
GET /api/webapps/:id/databases listDatabases RequirePermission(db, "websites_management")
GET /api/webapps/:id/databases/mongo listMongoDatabases RequirePermission(db, "websites_management")
GET /api/webapps/:id/databases/pg listPgDatabases RequirePermission(db, "websites_management")
POST /api/webapps/:id/databases/sync syncDatabasesFromServer RequirePermission(db, "websites_management")
GET /api/webapps/:id/detected-apps middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/webapps/:id/dns/reset resetDNSZone RequirePermission(db, "websites_administration")
POST /api/webapps/:id/duplicate duplicateWebsite RequirePermission(db, "websites_administration")
POST /api/webapps/:id/files/search searchWebsiteFiles RequirePermission(db, "websites_management")
POST /api/webapps/:id/fix-permissions fixWebsitePermissions RequirePermission(db, "websites_administration")
POST /api/webapps/:id/install/minio middleware RequirePermission(db, "websites_administration")
POST /api/webapps/:id/install/nextcloud installNextcloud RequirePermission(db, "websites_administration")
POST /api/webapps/:id/install/starkstudio middleware RequirePermission(db, "websites_administration")
POST /api/webapps/:id/install/wordpress installWordPress RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/limits removeWebappLimits RequirePermission(db, "administrative_operations")
GET /api/webapps/:id/limits getWebappLimits RequirePermission(db, "administrative_operations")
POST /api/webapps/:id/limits setWebappLimits RequirePermission(db, "administrative_operations")
GET /api/webapps/:id/limits/logs getWebappLimitsLogs RequirePermission(db, "administrative_operations")
GET /api/webapps/:id/limits/stats getWebappLimitsStats RequirePermission(db, "administrative_operations")
POST /api/webapps/:id/nextcloud-autologin middleware AuthWithDB(cfg.JWTSecret, db)
GET /api/webapps/:id/pm2 middleware RequirePermission(db, "websites_management")
POST /api/webapps/:id/pm2 middleware RequirePermission(db, "websites_management")
POST /api/webapps/:id/pm2/:app/:action middleware RequirePermission(db, "websites_management")
GET /api/webapps/:id/pm2/:app/info middleware RequirePermission(db, "websites_management")
GET /api/webapps/:id/pm2/:app/logs middleware RequirePermission(db, "websites_management")
GET /api/webapps/:id/pm2/dirs middleware RequirePermission(db, "websites_management")
POST /api/webapps/:id/pm2/save middleware RequirePermission(db, "websites_management")
GET /api/webapps/:id/pm2/scripts middleware RequirePermission(db, "websites_management")
POST /api/webapps/:id/pm2/startup middleware RequirePermission(db, "websites_management")
POST /api/webapps/:id/refresh-disk refreshWebsiteDiskUsage RequirePermission(db, "websites_administration")
GET /api/webapps/:id/remote-log getWebsiteRemoteLog RequirePermission(db, "websites_administration")
POST /api/webapps/:id/remote-log setWebsiteRemoteLog RequirePermission(db, "websites_administration")
GET /api/webapps/:id/replications getWebsiteReplications RequirePermission(db, "websites_administration")
POST /api/webapps/:id/replications createWebsiteReplication RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/replications/:replication_id deleteWebsiteReplication RequirePermission(db, "websites_administration")
POST /api/webapps/:id/replications/:replication_id/initial-sync triggerInitialSync RequirePermission(db, "websites_administration")
POST /api/webapps/:id/replications/:replication_id/sync triggerReplicationSync RequirePermission(db, "websites_administration")
GET /api/webapps/:id/replications/:replication_id/watch-folders getReplicationWatchFolders RequirePermission(db, "websites_administration")
POST /api/webapps/:id/replications/:replication_id/watch-folders addReplicationWatchFolder RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/replications/:replication_id/watch-folders/:folder_id deleteReplicationWatchFolder RequirePermission(db, "websites_administration")
POST /api/webapps/:id/replications/:replication_id/watch-folders/:folder_id/toggle toggleReplicationWatchFolder RequirePermission(db, "websites_administration")
POST /api/webapps/:id/rewrite-config rewriteWebsiteConfig RequirePermission(db, "websites_administration")
GET /api/webapps/:id/ssh-keys listSSHKeys RequirePermission(db, "websites_administration")
POST /api/webapps/:id/ssh-keys addSSHKey RequirePermission(db, "websites_administration")
GET /api/webapps/:id/ssh-private-keys listSSHPrivateKeys RequirePermission(db, "websites_administration")
POST /api/webapps/:id/ssh-private-keys generateSSHPrivateKey RequirePermission(db, "websites_administration")
DELETE /api/webapps/:id/ssh-private-keys/:name deleteSSHPrivateKey RequirePermission(db, "websites_administration")
GET /api/webapps/:id/ssh-private-keys/:name/download downloadSSHPrivateKey RequirePermission(db, "websites_administration")
POST /api/webapps/:id/ssl/issue websiteSSLIssueLetsEncrypt RequirePermission(db, "websites_administration")
GET /api/webapps/:id/ssl/status websiteSSLStatus RequirePermission(db, "websites_administration")
GET /api/webapps/:id/stats middleware AuthWithDB(cfg.JWTSecret, db)
GET /api/webapps/:id/terminal websiteTerminal -
POST /api/webapps/:id/varnish/purge purgeWebsiteVarnish RequirePermission(db, "websites_management")
POST /api/webapps/:id/wp-autologin middleware AuthWithDB(cfg.JWTSecret, db)
POST /api/webapps/import-orphans importOrphanUsers RequirePermission(db, "websites_administration")
GET /api/webapps/scan-orphans scanOrphanUsers RequirePermission(db, "websites_administration")
DELETE /api/webapps/ssh-keys/:keyid deleteSSHKey RequirePermission(db, "websites_administration")
POST /api/webapps/system-user createSystemWebapp RequirePermission(db, "websites_administration")

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/webapps/:id

Deletes a website and all of its associated resources via the websites.Delete package routine.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request id is not a valid integer.
500 delete_failed The underlying delete operation failed.

Side effects

  • Removes the website's system user, files, vhost, and DB rows (handled inside websites.Delete).

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42 \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id

Returns the full record for a single website.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "website": { "id": 42, "domain": "example.com", "username": "user1", "php_ver": "8.2" } } }

Errors

Status Code Meaning
400 bad_request id is not a valid integer.
404 not_found Website does not exist.
500 server_error Lookup failed.

Example

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

PATCH /api/webapps/:id

Updates per-website settings (PHP version, HSTS, IP mode, proxy, email limit, backup skip, Varnish, port, custom docroot); only supplied fields change.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{
  "php_ver": "string (optional)",
  "is_hsts": "bool (optional)",
  "ip_mode": "string (optional)",
  "proxy_pass": "int (optional)",
  "proxy_url": "string (optional)",
  "proxy_ssl_verify": "bool (optional)",
  "email_limit": "int (optional)",
  "skip_backup": "bool (optional)",
  "varnish_enabled": "bool (optional)",
  "port": "int (optional)",
  "custom_docroot": "string (optional)"
}
Field Type Required Description
php_ver string no PHP version; if changed, updates .bashrc and rewrites configs.
is_hsts bool no Toggle HSTS.
ip_mode string no One of ipv4, ipv6, both.
proxy_pass int no Proxy-pass flag.
proxy_url string no Upstream proxy URL.
proxy_ssl_verify bool no Verify upstream TLS.
email_limit int no Per-website mail send limit.
skip_backup bool no Exclude site from backups.
varnish_enabled bool no Toggle Varnish caching.
port int no Backend port.
custom_docroot string no Custom document root path.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id, bad JSON, or invalid ip_mode.
500 server_error Update query failed.

Side effects

  • Updates the webapps row.
  • If proxy/varnish/port/docroot or PHP version changed, asynchronously runs starkone-cli rewrite_user_confs.
  • On PHP version change, rewrites the user's .bashrc.

Example

curl -X PATCH https://panel.example.com:2087/api/webapps/42 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"php_ver": "8.3", "varnish_enabled": true}'

GET /api/webapps/:id/access

Lists the panel users granted explicit access to a website.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "access": [ { "id": 1, "user_id": 7, "username": "user1", "permission_type": "view" } ] } }

Errors

Status Code Meaning
400 bad_request Invalid id.
500 server_error Query failed.

Example

curl https://panel.example.com:2087/api/webapps/42/access \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/access

Grants (or replaces) a panel user's access level to a website.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "user_id": 7, "permission_type": "view" }
Field Type Required Description
user_id int yes Panel user to grant access to.
permission_type string yes One of admin, admin-limited, view.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id, bad JSON, or invalid permission_type.
500 server_error Insert failed.

Side effects

  • Deletes any existing access row for the user/website, then inserts the new one into user_websites_access.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/access \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id": 7, "permission_type": "view"}'

DELETE /api/webapps/:id/access/:uid

Revokes a panel user's access to a website.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
uid integer Panel user ID whose access is revoked.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id or uid.

Side effects

  • Deletes the matching row from user_websites_access.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/access/7 \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/acl

Lists the nginx ACL rulesets currently assigned to a website (read from the user's ACL JSON).

Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "id": "rs1", "name": "Block bots", "description": "", "is_public": false, "enabled": true, "match_mode": "any", "action_type": "deny", "action_value": "", "rule_count": 3 } ] }

Errors

Status Code Meaning
404 not_found Website/username could not be resolved.
500 read_error Could not read the ACL JSON config.

Example

curl https://panel.example.com:2087/api/webapps/42/acl \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/webapps/:id/acl/:ruleset_id

Unassigns an ACL ruleset from a website by removing it from the user's ACL config.

Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id integer Numeric website ID.
ruleset_id string ID of the ruleset to remove.

Response - 200 OK

{ "data": { "message": "unassigned" } }

Errors

Status Code Meaning
404 not_found Website/username could not be resolved.
500 read_error / write_error Failed to read or write the ACL config.

Side effects

  • Rewrites the user's ACL JSON config file (file-locked).

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/acl/rs1 \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/acl/assign

Copies a public ACL ruleset into a website's ACL config.

Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "ruleset_id": "rs1" }
Field Type Required Description
ruleset_id string yes ID of a public ruleset to assign.

Response - 201 Created

{ "data": { "message": "assigned" } }

If the ruleset is already present, returns 200 OK with { "data": { "message": "already assigned" } }.

Errors

Status Code Meaning
400 bad_request ruleset_id missing.
404 not_found Website/username unresolved, or public ruleset not found.
500 read_error / write_error Failed to read or write the ACL config.

Side effects

  • Appends the ruleset to the user's ACL JSON config file (file-locked).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/acl/assign \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ruleset_id": "rs1"}'

GET /api/webapps/:id/acl/available

Lists public ACL rulesets that are not yet assigned to the website.

Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "id": "rs2", "name": "Country block", "description": "", "is_public": true, "match_mode": "any", "action_type": "deny", "action_value": "", "rule_count": 5 } ] }

Errors

Status Code Meaning
404 not_found Website/username could not be resolved.

Example

curl https://panel.example.com:2087/api/webapps/42/acl/available \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/webapps/:id/aliases/:alias

Removes a domain alias from a website via starkone-cli alias_remove.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
alias string The alias domain to remove.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id or empty alias.
404 not_found Website not found.
500 cli_error The CLI alias-remove command failed.

Side effects

  • Runs /usr/sbin/starkone-cli alias_remove <domain> <alias> (rewrites webserver config).
  • Deletes the row from domain_aliases.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/aliases/www.example.com \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/analytics

Returns an HTML GoAccess analytics report for a single website's access log.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

Returns text/html (a rendered GoAccess report), not the JSON envelope.

Errors

Status Code Meaning
400 bad_request Invalid id.
404 not_found Website not found.
404 no_logs No access log found for the website.
500 goaccess_error GoAccess failed to generate the report.

Side effects

  • Runs goaccess against the site's access log (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/analytics \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/backup-sets

Creates a named backup set directory for a website.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "name": "weekly" }
Field Type Required Description
name string yes Set name; lowercased and stripped to [a-z0-9-], max 32 chars.

Response - 201 Created

{ "success": true, "name": "weekly" }

Errors

Status Code Meaning
400 bad_request Missing/invalid set name.
404 not_found Website not found.
500 server_error Directory creation failed.

Side effects

  • Creates /mnt/starkone-backups/<user>/<domain>/<name> (mode 0750, owned root:root).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/backup-sets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "weekly"}'

DELETE /api/webapps/:id/backup-sets/:set

Deletes a named backup set directory (cannot delete default).

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
set string Backup set name; sanitized to [a-z0-9-].

Response - 200 OK

{ "success": true }

Errors

Status Code Meaning
400 bad_request Set is default or name invalid.
404 not_found Website or backup set not found.
500 server_error Directory removal failed.

Side effects

  • Recursively removes /mnt/starkone-backups/<user>/<domain>/<set>.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/backup-sets/weekly \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/backups

Lists backup chains for a website via starkone-cli backup_status, merged with the available backup sets.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Query parameters

Name Type Optional Description
set string yes Backup set name; defaults to default.

Response - 200 OK

{ "chains": [], "status": "ok", "sets": ["default", "weekly"] }

When no backups exist yet, status is no_backups.

Side effects

  • Runs starkone-cli backup_status (read-only) and scans the backup directory for sets.

Example

curl "https://panel.example.com:2087/api/webapps/42/backups?set=default" \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/backups

Starts an asynchronous website backup (files and/or databases), blocked when the root disk is near full.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "backup_type": "INCREMENTAL", "set": "default", "include_files": true, "include_db": true }
Field Type Required Description
backup_type string no FULL or INCREMENTAL (default INCREMENTAL).
set string no Backup set name (default default).
include_files bool no Back up files. Defaults to true if neither flag set.
include_db bool no Back up databases. Defaults to true if neither flag set.

Response - 202 Accepted

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

Errors

Status Code Meaning
503 disk_full Root disk usage is at/above the 95% threshold; backup blocked.

Side effects

  • Spawns a background starkone-cli backup_website job.
  • On disk-full, sends a critical notification to root.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/backups \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"backup_type": "FULL", "include_files": true, "include_db": true}'

GET /api/webapps/:id/backups/progress

Returns the progress of a running backup job, or the last progress log if none is running.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "running": true, "output": "...", "error": "" }

When idle: { "running": false } (optionally with output from the last log).

Example

curl https://panel.example.com:2087/api/webapps/42/backups/progress \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/backups/restore

Starts an asynchronous restore of a website backup, optionally into a sandbox subdomain.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{
  "time_mark": "1h",
  "restore_path": "",
  "restore_files": true,
  "restore_database": true,
  "delete_before_restore": false,
  "sandbox_restore": false,
  "backup_set": "default",
  "passphrase": ""
}
Field Type Required Description
time_mark string no Restore point (e.g. 30M, 1h, 2024-01-01).
restore_path string no Target path; defaults to /home/<user>.
restore_files bool no Restore files.
restore_database bool no Restore databases.
delete_before_restore bool no Wipe files before restoring.
sandbox_restore bool no Restore into a new rp-<label>.<domain> sandbox site.
backup_set string no Backup set name (default default).
passphrase string no Manual passphrase for encrypted backups.

Response - 202 Accepted

{ "data": { "status": "started", "message": "Restore started in background", "restore_path": "/home/user1", "time_mark": "1h", "restore_files": true, "restore_database": true } }

Errors

Status Code Meaning
404 not_found Website not found.
500 sandbox_error Sandbox site creation failed.

Side effects

  • Spawns a background starkone-cli restore_website job.
  • With sandbox_restore, creates a new website (rp-<label>.<domain>).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/backups/restore \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"time_mark": "1h", "restore_files": true, "restore_database": true}'

POST /api/webapps/:id/change-domain

Renames a website's primary domain via starkone-cli change_domain.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "new_domain": "newexample.com" }
Field Type Required Description
new_domain string yes The new primary domain.

Response - 200 OK

{ "data": { "status": "ok", "old_domain": "example.com", "new_domain": "newexample.com" } }

If the CLI returns valid JSON, that JSON is returned under data instead.

Errors

Status Code Meaning
400 bad_request new_domain missing.
500 change_failed The CLI command failed.

Side effects

  • Runs starkone-cli change_domain (renames vhost, files, DB references).
  • Logs a change_domain action.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/change-domain \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"new_domain": "newexample.com"}'

POST /api/webapps/:id/cloudflare/attack-mode

Enables or disables Cloudflare "Under Attack" mode for the website's zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "enabled": true }
Field Type Required Description
enabled bool no Whether to enable Under Attack mode.

Response - 200 OK

{ "data": { "success": true, "enabled": true } }

Errors

Status Code Meaning
400 bad_request Invalid id or bad JSON.
400 not_connected Cloudflare not connected for this website.
502 cf_error Cloudflare API call failed.

Side effects

  • Calls the Cloudflare API to set the security level.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/attack-mode \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

POST /api/webapps/:id/cloudflare/cache/mode

Toggles Cloudflare development mode (cache bypass) for the website's zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "mode": "development" }
Field Type Required Description
mode string yes development or normal.

Response - 200 OK

{ "data": { "success": true, "mode": "development" } }

Errors

Status Code Meaning
400 bad_request Invalid id, bad JSON, or mode not development/normal.
400 not_connected Cloudflare not connected.
502 cf_error Cloudflare API call failed.

Side effects

  • Calls the Cloudflare API to set development mode.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/cache/mode \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode": "development"}'

POST /api/webapps/:id/cloudflare/cache/purge

Purges the entire Cloudflare cache for the website's zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id.
400 not_connected Cloudflare not connected.
502 cf_error Cloudflare API call failed.

Side effects

  • Calls the Cloudflare purge-cache API.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/cache/purge \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/webapps/:id/cloudflare/connect

Removes stored Cloudflare credentials for a website.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id.
500 server_error Failed to clear credentials.

Side effects

  • Clears the website's stored Cloudflare credentials in the DB.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/cloudflare/connect \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/cloudflare/connect

Stores (encrypted) Cloudflare API credentials for a website after verifying them.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "api_token": "cf_token", "zone_id": "zone123" }
Field Type Required Description
api_token string yes Cloudflare API token.
zone_id string yes Cloudflare zone ID.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id or bad JSON.
400 cf_invalid Credentials failed verification against Cloudflare.
500 server_error Failed to persist credentials.

Side effects

  • Verifies the token via the Cloudflare API, then stores encrypted credentials in the DB.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/connect \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"api_token": "cf_token", "zone_id": "zone123"}'

GET /api/webapps/:id/cloudflare/dns

Returns all DNS records for the website's Cloudflare zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "records": [ { "id": "rec1", "type": "A", "name": "example.com", "content": "203.0.113.10", "proxied": true } ] } }

Errors

Status Code Meaning
400 bad_request Invalid id.
400 not_connected Cloudflare not connected.
502 cf_error Cloudflare API call failed.

Example

curl https://panel.example.com:2087/api/webapps/42/cloudflare/dns \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/cloudflare/dns

Creates a DNS record in the website's Cloudflare zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "type": "A", "name": "www.example.com", "content": "203.0.113.10", "proxied": true }
Field Type Required Description
type string yes Record type (A, CNAME, TXT, ...).
name string yes Record name.
content string yes Record content.
proxied bool no Whether the record is proxied through Cloudflare.

Response - 201 Created

{ "data": { "id": "rec2", "type": "A", "name": "www.example.com", "content": "203.0.113.10", "proxied": true } }

Errors

Status Code Meaning
400 bad_request Invalid id or bad JSON.
400 not_connected Cloudflare not connected.
502 cf_error Cloudflare API call failed.

Side effects

  • Creates the DNS record via the Cloudflare API.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/dns \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "A", "name": "www.example.com", "content": "203.0.113.10", "proxied": true}'

DELETE /api/webapps/:id/cloudflare/dns/:record_id

Deletes a DNS record from the website's Cloudflare zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
record_id string Cloudflare DNS record ID.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id.
400 not_connected Cloudflare not connected.
502 cf_error Cloudflare API call failed.

Side effects

  • Deletes the DNS record via the Cloudflare API.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/cloudflare/dns/rec2 \
  -H "Authorization: Bearer $TOKEN"

PATCH /api/webapps/:id/cloudflare/dns/:record_id

Updates a DNS record in the website's Cloudflare zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
record_id string Cloudflare DNS record ID.

Request body

{ "type": "A", "name": "www.example.com", "content": "203.0.113.20", "proxied": false }
Field Type Required Description
type string yes Record type.
name string yes Record name.
content string yes Record content.
proxied bool no Whether the record is proxied.

Response - 200 OK

{ "data": { "id": "rec2", "type": "A", "name": "www.example.com", "content": "203.0.113.20", "proxied": false } }

Errors

Status Code Meaning
400 bad_request Invalid id or bad JSON.
400 not_connected Cloudflare not connected.
502 cf_error Cloudflare API call failed.

Side effects

  • Updates the DNS record via the Cloudflare API.

Example

curl -X PATCH https://panel.example.com:2087/api/webapps/42/cloudflare/dns/rec2 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "A", "name": "www.example.com", "content": "203.0.113.20", "proxied": false}'

GET /api/webapps/:id/cloudflare/status

Returns whether Cloudflare is connected for the website, with zone info; attempts auto-connect using a global token if available.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "connected": true, "zone": { "id": "zone123", "name": "example.com" } } }

When not connected: { "data": { "connected": false } }.

Errors

Status Code Meaning
400 bad_request Invalid id.
500 server_error Failed to load the Cloudflare client.
502 cf_error Cloudflare zone lookup failed.

Side effects

  • May auto-connect the website to a Cloudflare zone (writes credentials) when a global token matches the domain.

Example

curl https://panel.example.com:2087/api/webapps/42/cloudflare/status \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/crontab

Returns the parsed crontab entries for the website's system user.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "schedule": "0 3 * * *", "command": "/usr/bin/php cron.php" } ] }

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Runs crontab -l -u <username> (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/crontab \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/crontab

Appends a crontab entry for the website's system user.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "schedule": "0 3 * * *", "command": "/usr/bin/php cron.php" }
Field Type Required Description
schedule string no Cron schedule expression.
command string no Command to run.

Response - 201 Created

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

Errors

Status Code Meaning
400 (message only) Bad JSON body.
404 (message only) Website not found.
500 (message only) Failed to update crontab.

Side effects

  • Reinstalls the user's crontab with the new entry appended.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/crontab \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schedule": "0 3 * * *", "command": "/usr/bin/php cron.php"}'

DELETE /api/webapps/:id/crontab/:index

Removes the crontab entry at the given zero-based index for the website's user.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
index integer Zero-based index of the entry among non-comment lines.

Response - 200 OK

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

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Reinstalls the user's crontab without the removed entry.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/crontab/0 \
  -H "Authorization: Bearer $TOKEN"

PUT /api/webapps/:id/crontab/:index

Replaces the crontab entry at the given index for the website's user.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
index integer Zero-based index of the entry to replace.

Request body

{ "schedule": "*/5 * * * *", "command": "/usr/bin/php cron.php" }
Field Type Required Description
schedule string no New cron schedule.
command string no New command.

Response - 200 OK

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

Errors

Status Code Meaning
400 (message only) Bad JSON body.
404 (message only) Website not found.
500 (message only) Failed to update crontab.

Side effects

  • Reinstalls the user's crontab with the entry replaced.

Example

curl -X PUT https://panel.example.com:2087/api/webapps/42/crontab/0 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schedule": "*/5 * * * *", "command": "/usr/bin/php cron.php"}'

GET /api/webapps/:id/cust-nginx

Returns the website's custom nginx config snippet (empty if none exists).

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "content": "location /api { ... }", "exists": true }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
400 bad_request Invalid website id.
404 not_found Website not found.

Side effects

  • Reads /etc/starkone/users_data/<user>/cust_nginx.conf (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/cust-nginx \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/cust-nginx

Saves a custom nginx config snippet for the website, validates the full nginx config, and reloads if valid.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "content": "location /api { proxy_pass http://127.0.0.1:3000; }" }
Field Type Required Description
content string no The custom nginx config snippet.

Response - 200 OK

{ "success": true }

If nginx -t fails the file is still saved but not applied:

{ "success": false, "nginx_error": "...", "message": "Nginx configuration test failed. File was saved but NOT applied." }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
400 bad_request Invalid website id or bad JSON.
404 not_found Website not found.
500 write_error Failed to create directory or write the config.

Side effects

  • Writes /etc/starkone/users_data/<user>/cust_nginx.conf.
  • Runs starkone-cli rewrite_user_confs, tests with nginx -t, and on success runs systemctl reload nginx.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/cust-nginx \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "location /api { proxy_pass http://127.0.0.1:3000; }"}'

GET /api/webapps/:id/databases

Lists the MySQL databases tracked in the panel for a website.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "databases": [ { "id": 1, "name": "user1_app", "website_id": 42, "users": [] } ] } }

Errors

Status Code Meaning
400 bad_request Invalid website id.
500 server_error Lookup failed.

Example

curl https://panel.example.com:2087/api/webapps/42/databases \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/databases/mongo

Lists the MongoDB databases tracked for a website; returns an empty list when the Mongo plugin is disabled.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "databases": [] } }

When the plugin is disabled: { "data": { "databases": [], "plugin_enabled": false } }.

Errors

Status Code Meaning
400 bad_request Invalid website id.
500 server_error Lookup failed.

Example

curl https://panel.example.com:2087/api/webapps/42/databases/mongo \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/databases/pg

Lists the PostgreSQL databases tracked for a website; returns an empty list when the PG plugin is disabled.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "databases": [] } }

When the plugin is disabled: { "data": { "databases": [], "plugin_enabled": false } }.

Errors

Status Code Meaning
400 bad_request Invalid website id.
500 server_error Lookup failed.

Example

curl https://panel.example.com:2087/api/webapps/42/databases/pg \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/databases/sync

Reconciles the panel's database/user records with the actual MySQL server state for a website (by username_% prefix).

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "synced": 2, "users_synced": 3, "total_on_server": 5 } }

Errors

Status Code Meaning
404 not_found Website not found.
500 mysql_error Could not list databases from MySQL.
500 db_error Panel DB query failed.

Side effects

  • Runs mysql queries against the server, then inserts missing rows into the databases and databases_users tables.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/databases/sync \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/detected-apps

Detects installed applications (WordPress, Nextcloud) in the website's document root and reports versions.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "wordpress": { "detected": true, "version": "6.5" } } }

Errors

Status Code Meaning
400 bad_request Invalid id.
404 not_found Website not found.

Side effects

  • Reads wp-config.php / version.php from the docroot (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/detected-apps \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/dns/reset

Resets the website's DNS zone to the default template via starkone-cli dns_reset_zone.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "status": "ok", "message": "DNS zone reset to default" } }

Errors

Status Code Meaning
404 not_found Website not found.
500 dns_error The CLI reset command failed.

Side effects

  • Runs starkone-cli dns_reset_zone <domain> (rewrites the DNS zone).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/dns/reset \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/duplicate

Clones a website to a new domain via starkone-cli duplicate_website, optionally on a custom port.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Source website ID.

Request body

{ "new_domain": "copy.example.com", "port": 8090 }
Field Type Required Description
new_domain string yes Domain for the clone.
port int no Custom backend port for the clone.

Response - 200 OK

{ "data": { } }

The data value is the raw JSON returned by the CLI.

Errors

Status Code Meaning
400 bad_request new_domain missing.
500 duplicate_failed The CLI command failed.

Side effects

  • Runs starkone-cli duplicate_website. With a custom port, updates the new row, rewrites configs, and reloads nginx.
  • Logs a duplicate_website action.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/duplicate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"new_domain": "copy.example.com"}'

POST /api/webapps/:id/files/search

Searches a website's home directory for files matching extensions, up to a max depth.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "pattern": "", "max_depth": 3, "extensions": ["php", "js"] }
Field Type Required Description
pattern string no Reserved (not used for matching in the current implementation).
max_depth int no Max directory depth (default 3).
extensions string[] no File extensions to include (without the dot).

Response - 200 OK

{ "data": { "files": [ { "path": "/home/user1/public_html/index.php", "name": "index.php", "size": 1024 } ] } }

Errors

Status Code Meaning
404 not_found Website not found.

Side effects

  • Walks /home/<user> (read-only).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/files/search \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"max_depth": 3, "extensions": ["php"]}'

POST /api/webapps/:id/fix-permissions

Repairs file ownership/permissions for a website via starkone-cli fix_permissions.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

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

Errors

Status Code Meaning
500 server_error The CLI command failed.

Side effects

  • Runs starkone-cli fix_permissions <id> (chowns/chmods website files).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/fix-permissions \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/install/minio

Installs and starts a per-website MinIO S3 server under PM2 with generated credentials.

Guards: RequirePermission(db, "websites_administration") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "access_key": "minioXXXXXXXX", "secret_key": "...", "api_port": 9042, "console_port": 9043, "api_url": "http://SERVER_IP:9042", "console_url": "http://SERVER_IP:9043", "message": "MinIO S3 installed successfully!" } }

Errors

Status Code Meaning
404 (message only, {"error": "Website not found"}) Website not found.
500 (message only) MinIO download or start failed.

Side effects

  • Creates /home/<user>/apps/minio-s3, downloads the MinIO binary, writes a PM2 ecosystem file, and starts/saves it under PM2.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/install/minio \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/install/nextcloud

Downloads and installs Nextcloud into the website's docroot, creating a database, admin user, nginx/PHP-FPM tweaks, and a background cron job.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "success": true, "database": "user1_nextcloud", "db_user": "user1_nextcloud", "admin_user": "admin", "admin_password": "...", "data_dir": "/home/user1/nextcloud-data", "message": "Nextcloud installed successfully!" } }

Errors

Status Code Meaning
404 not_found Website not found.
500 db_error Database creation failed.
500 download_error Nextcloud download failed.
500 install_error occ maintenance:install failed.

Side effects

  • Creates a MySQL database, downloads Nextcloud, runs occ install, writes custom nginx + PHP-FPM configs, rewrites vhost, installs the autologin app, and adds a cron entry.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/install/nextcloud \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/install/starkstudio

Installs and starts StarkStudio under PM2, proxied at /starkstudio/ via custom nginx config.

Guards: RequirePermission(db, "websites_administration") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "message": "StarkStudio installed successfully!", "url": "https://example.com/starkstudio/", "admin_user": "admin", "admin_pass": "...", "port": 8542, "app_dir": "/home/user1/apps/stark-studio" }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
404 (message only) Website not found.
500 (message only) Directory creation, download, config write, or PM2 start failed.

Side effects

  • Creates /home/<user>/apps/stark-studio, downloads the binary, writes a PM2 ecosystem file, starts/saves it under PM2, appends an nginx proxy block, and rewrites the vhost.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/install/starkstudio \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/install/wordpress

Downloads WordPress core into the website's docroot and creates a database and wp-config.php.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "success": true, "database": "user1_wp", "db_user": "user1_wp", "message": "WordPress downloaded. Visit your site to complete installation." } }

Errors

Status Code Meaning
404 not_found Website not found.
500 db_error Database creation failed.
500 wp_error wp core download or wp config create failed.

Side effects

  • Creates a MySQL database, runs wp core download, wp config create, and wp config shuffle-salts as the website user.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/install/wordpress \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/webapps/:id/limits

Removes resource limits (cgroup slice, PHP-FPM isolation, network bandwidth) for a website's user.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "status": "ok" }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
404 (message only) Website not found.
500 (message only) OS user lookup failed.

Side effects

  • Restores the main PHP-FPM pool, removes tc/iptables bandwidth rules, stops and deletes the user's systemd slice, and runs systemctl daemon-reload.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/limits \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/limits

Returns the configured cgroup resource limits and live stats for a website's user.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "supported": true, "active": true, "config": { "cpuPercent": "50", "memoryValue": "512", "memoryUnit": "MB" }, "stats": { "memUsage": "120 MB", "cpuLimit": "50%" } }

When cgroups v2 is unavailable: { "supported": false }. Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
404 (message only) Website not found.
500 (message only) Could not resolve the user slice.

Side effects

  • Reads the user's systemd slice file and cgroup stats (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/limits \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/limits

Sets cgroup resource limits (CPU, memory, I/O, network bandwidth) for a website's user and moves its processes into the slice.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "cpuPercent": 50, "memoryValue": 512, "memoryUnit": "MB", "ioWriteSpeed": 50, "ioReadSpeed": 50, "networkBandwidth": 100 }
Field Type Required Description
cpuPercent int no CPU quota percent (1-100).
memoryValue float no Memory limit value.
memoryUnit string no percent, MB, or GB.
ioWriteSpeed int no Max write bandwidth (MB/s).
ioReadSpeed int no Max read bandwidth (MB/s).
networkBandwidth int no Network bandwidth limit (Mbit/s); 0/nil removes it.

Response - 200 OK

{ "status": "ok", "processes_moved": 3, "php_fpm_isolated": true }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
400 (message only) Bad JSON or cgroups v2 unsupported.
404 (message only) Website not found.
500 (message only) User lookup or slice file write failed.

Side effects

  • Writes a /etc/systemd/system/user-<uid>.slice file, reloads systemd, moves the user's processes into the cgroup, optionally sets up an isolated PHP-FPM service, and configures/removes tc + iptables bandwidth rules.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/limits \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cpuPercent": 50, "memoryValue": 512, "memoryUnit": "MB"}'

GET /api/webapps/:id/limits/logs

Returns systemctl status output for the user's resource-limit slice.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "logs": "● user-1042.slice - Resource limits ..." }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
404 (message only) Website not found.
500 (message only) OS user lookup failed.

Side effects

  • Runs systemctl status user-<uid>.slice (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/limits/logs \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/limits/stats

Returns live cgroup usage stats (memory, CPU, I/O) for the website's user.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "stats": { "memUsage": "120 MB", "memLimit": "512 MB", "cpuLimit": "50%", "ioWriteLimit": "Unlimited" } }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Reads the user's cgroup stat files (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/limits/stats \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/nextcloud-autologin

Generates a one-click Nextcloud login (autologin token or admin password reset).

Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_administration"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "url": "https://example.com/apps/starkone_autologin/token/...", "method": "token", "message": "Opening Nextcloud..." } }

If the autologin app is absent, falls back to resetting the admin password and returns url, username, password.

Errors

Status Code Meaning
400 not_nextcloud Nextcloud is not installed on this site.
404 not_found Website not found.
500 reset_failed Password reset failed.

Side effects

  • Runs occ to create a login token or reset the admin password.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/nextcloud-autologin \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/pm2

Lists the PM2 applications running under the website's system user.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "pm_id": 0, "name": "app", "status": "online", "cpu": 0.5, "memory": 52428800, "port": "3000" } ], "pm2_installed": true }

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Runs pm2 jlist as the website user (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/pm2 \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/pm2

Creates and starts a new PM2 application for the website user.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "name": "app", "script": "server.js", "interpreter": "node", "cwd": "/home/user1/app", "args": "", "instances": 1, "watch": false, "max_memory": "500M", "env": { "NODE_ENV": "production" }, "port": 3000 }
Field Type Required Description
name string yes PM2 app name.
script string yes Entry script (resolved within the user's home).
interpreter string no Interpreter (default node; none for binaries).
cwd string no Working directory; must be within the user's home.
args string no Script arguments.
instances int no Instance count (default 1).
watch bool no Enable watch mode.
max_memory string no Memory restart threshold (e.g. 500M).
env object no Environment variables.
port int no Sets PORT env var.

Response - 200 OK

{ "data": { "message": "App created and started", "output": "..." } }

Errors

Status Code Meaning
400 (message only) Bad JSON, or cwd outside the user's home.
404 (message only) Website not found.
500 (message only) Failed to write ecosystem file or start the app.

Side effects

  • Writes a PM2 ecosystem .js file in the user's home, runs pm2 start and pm2 save as the user.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/pm2 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "app", "script": "server.js", "port": 3000}'

POST /api/webapps/:id/pm2/:app/:action

Performs a PM2 lifecycle action (start/stop/restart/reload/delete) on an app.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.
app string PM2 app name or ID.
action string One of start, stop, restart, reload, delete.

Response - 200 OK

{ "data": { "message": "Action completed", "output": "..." } }

Errors

Status Code Meaning
400 (message only) Invalid action.
404 (message only) Website not found.
500 (message only) PM2 command failed.

Side effects

  • Runs pm2 <action> <app> as the website user.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/pm2/app/restart \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/pm2/:app/info

Returns detailed PM2 info for a specific app (raw pm2 show --format json).

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.
app string PM2 app name or ID.

Response - 200 OK

Returns the raw JSON output of pm2 show <app> --format json (not the data envelope).

Errors

Status Code Meaning
404 (message only) Website not found, or app not found.

Side effects

  • Runs pm2 show as the website user (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/pm2/app/info \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/pm2/:app/logs

Returns the last N lines of a PM2 app's combined logs.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.
app string PM2 app name or ID.

Query parameters

Name Type Optional Description
lines integer yes Number of log lines (default 100).

Response - 200 OK

{ "data": { "logs": "..." } }

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Runs pm2 logs <app> --nostream as the website user (read-only).

Example

curl "https://panel.example.com:2087/api/webapps/42/pm2/app/logs?lines=200" \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/pm2/dirs

Lists directories (up to 5 levels deep) under the website user's home for app selection.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": ["/home/user1", "/home/user1/app"] }

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Walks /home/<user> (read-only), skipping hidden/node_modules/vendor/cache dirs.

Example

curl https://panel.example.com:2087/api/webapps/42/pm2/dirs \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/pm2/save

Saves the current PM2 process list for the website user.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "message": "State saved" } }

Errors

Status Code Meaning
404 (message only) Website not found.
500 (message only) pm2 save failed.

Side effects

  • Runs pm2 save as the website user.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/pm2/save \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/pm2/scripts

Lists candidate script files in a directory for PM2 app selection.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Query parameters

Name Type Optional Description
dir string no Directory to scan; must be within the user's home.

Response - 200 OK

{ "data": ["server.js", "app.py"] }

Errors

Status Code Meaning
400 (message only) dir outside the user's home.
404 (message only) Website not found.

Side effects

  • Reads the given directory (read-only).

Example

curl "https://panel.example.com:2087/api/webapps/42/pm2/scripts?dir=/home/user1/app" \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/pm2/startup

Enables PM2 startup-on-boot for the website user and saves current state.

Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "message": "Startup enabled", "command": "sudo env PATH=... pm2 startup ..." } }

Errors

Status Code Meaning
404 (message only) Website not found.

Side effects

  • Runs pm2 startup as the user, executes the resulting sudo command as root, and runs pm2 save.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/pm2/startup \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/refresh-disk

Recomputes and stores the website's disk usage.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "disk_usage": 10485760 } }

Errors

Status Code Meaning
400 bad_request Invalid website id.
500 server_error Disk usage computation failed.

Side effects

  • Recomputes disk usage and updates the webapps row.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/refresh-disk \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/remote-log

Returns whether per-website remote (syslog) logging is enabled.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "remote_log_enabled": true } }

Errors

Status Code Meaning
400 bad_request Invalid id.
404 not_found Website not found.

Side effects

  • Checks for /etc/starkone/users_data/<user>/remote_log.conf (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/remote-log \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/remote-log

Enables or disables remote syslog logging for the website's nginx access/error logs.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "enabled": true, "server": "203.0.113.50", "port": 514, "protocol": "udp" }
Field Type Required Description
enabled bool no Enable or disable remote logging.
server string no Syslog server; falls back to global setting.
port int no Syslog port; falls back to global setting or 514.
protocol string no udp or tcp; defaults to udp.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request Invalid id, bad JSON, or no server configured.
404 not_found Website not found.
500 server_error Failed to write the config file.

Side effects

  • Writes or removes /etc/starkone/users_data/<user>/remote_log.conf and runs starkone-cli rewrite_user_confs.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/remote-log \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "server": "203.0.113.50", "port": 514, "protocol": "udp"}'

GET /api/webapps/:id/replications

Lists the configured replications for a website, with target server details and watch-folder counts.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "id": 1, "server_id": 2, "server_name": "dr-site", "server_host": "203.0.113.60", "remote_username": "user1", "status": "active", "last_sync": "2026-06-17 02:00:00", "sync_interval": 3600, "watch_count": 2 } ] }

Example

curl https://panel.example.com:2087/api/webapps/42/replications \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/replications

Sets up replication of a website to a remote StarkOne server (creates the remote site, exchanges SSH keys, schedules rsync).

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "server_id": 2, "force": false }
Field Type Required Description
server_id int yes Target replication server ID.
force bool no Proceed even if the user already exists remotely.

Response - 201 Created

{ "data": { "id": 1, "status": "active", "remote_username": "user1" } }

Errors

Status Code Meaning
400 (message only) Bad JSON, cannot connect to remote, or remote create failed.
404 (message only) Website or server not found.
409 user_exists User already exists remotely and force is false.
500 (message only) SSH key read or DB insert failed.

Side effects

  • Calls the remote server's API to create the site, generates an SSH replication keypair, installs the public key remotely, adds an hourly rsync crontab entry, and inserts a website_replications row.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/replications \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"server_id": 2}'

DELETE /api/webapps/:id/replications/:replication_id

Removes a replication, deleting its rsync crontab entry and DB record.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.

Response - 200 OK

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

Errors

Status Code Meaning
404 (message only) Replication not found.

Side effects

  • Removes the matching rsync line from the user's crontab and deletes the website_replications row.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/replications/1 \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/replications/:replication_id/initial-sync

Runs a full initial rsync of the website to the remote replication target (creating the remote directory first).

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.

Response - 200 OK

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

Errors

Status Code Meaning
404 (message only) Replication not found.
500 (message only) rsync failed.

Side effects

  • Runs ssh mkdir -p then rsync -avz --delete as the website user; updates last_sync.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/initial-sync \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/replications/:replication_id/sync

Triggers an immediate rsync of the website to the remote replication target.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.

Response - 200 OK

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

Errors

Status Code Meaning
404 (message only) Replication not found.
500 (message only) rsync failed.

Side effects

  • Runs rsync -avz --delete as the website user; updates last_sync.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/sync \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/replications/:replication_id/watch-folders

Lists the real-time watch folders configured for a replication.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.

Response - 200 OK

{ "data": [ { "id": 1, "path": "/home/user1/domains/example.com/public_html", "enabled": true } ] }

Example

curl https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/replications/:replication_id/watch-folders

Adds a real-time watch folder to a replication and restarts the watcher.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.

Request body

{ "path": "/home/user1/domains/example.com/public_html" }
Field Type Required Description
path string yes Folder to watch; must exist on disk.

Response - 201 Created

{ "data": { "id": 1 } }

Errors

Status Code Meaning
400 (message only) Bad JSON or folder does not exist.
500 (message only) DB insert failed.

Side effects

  • Inserts a replication_watch_folders row, updates the user's sync-folders file, and restarts the watcher service.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path": "/home/user1/domains/example.com/public_html"}'

DELETE /api/webapps/:id/replications/:replication_id/watch-folders/:folder_id

Removes a watch folder from a replication and restarts the watcher.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.
folder_id integer Watch-folder ID.

Response - 200 OK

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

Side effects

  • Deletes the replication_watch_folders row, updates the sync-folders file, and restarts the watcher.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders/3 \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/replications/:replication_id/watch-folders/:folder_id/toggle

Toggles the enabled state of a watch folder and restarts the watcher.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
replication_id integer Replication record ID.
folder_id integer Watch-folder ID.

Response - 200 OK

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

Side effects

  • Flips the enabled flag, updates the sync-folders file, and restarts the watcher.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders/3/toggle \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/rewrite-config

Rewrites a website's webserver vhost config via the CLI and reloads nginx/Apache.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

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

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
400 bad_request Invalid website id.
404 not_found Website not found.
500 rewrite_error The CLI rewrite failed.

Side effects

  • Runs starkone-cli rewrite_user_confs <domain> and reloads nginx, apache2, and httpd.

Example

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

GET /api/webapps/:id/ssh-keys

Lists authorized SSH public keys registered for the website's system user.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "id": 1, "username": "user1", "key_type": "ssh-ed25519", "public_key": "ssh-ed25519 AAAA...", "fingerprint": "SHA256:...", "label": "laptop", "expires_at": "", "created_at": "2026-06-17 10:00:00" } ] }

Errors

Status Code Meaning
400 bad_request Invalid website id.
404 not_found Website not found.
500 db_error Query failed.

Example

curl https://panel.example.com:2087/api/webapps/42/ssh-keys \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/ssh-keys

Adds an SSH public key for the website's user (DB + authorized_keys).

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "public_key": "ssh-ed25519 AAAA...", "label": "laptop", "expires_at": null }
Field Type Required Description
public_key string yes The SSH public key.
label string no Friendly label.
expires_at string no Optional expiry timestamp.

Response - 200 OK

{ "id": 1, "username": "user1", "key_type": "ssh-ed25519", "fingerprint": "SHA256:...", "label": "laptop", "expires_at": "" }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
400 bad_request Invalid id, missing/invalid public key.
404 not_found Website not found.
500 db_error / file_error DB insert or authorized_keys write failed.

Side effects

  • Inserts into ssh_keys and appends the key to /home/<user>/.ssh/authorized_keys (creating/chmod-ing .ssh).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/ssh-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"public_key": "ssh-ed25519 AAAA...", "label": "laptop"}'

GET /api/webapps/:id/ssh-private-keys

Lists SSH key pairs stored in the website user's .ssh directory.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": [ { "name": "id_ed25519", "type": "ssh-ed25519", "modified": "2026-06-17 10:00", "public_key": "ssh-ed25519 AAAA..." } ] }

Errors

Status Code Meaning
404 not_found Website not found.

Side effects

  • Reads /home/<user>/.ssh (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/ssh-private-keys \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/ssh-private-keys

Generates a new SSH key pair in the website user's .ssh directory and returns the private key once.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "name": "deploy", "type": "ed25519" }
Field Type Required Description
name string yes Key file name (sanitized).
type string no Key type (default ed25519; rsa uses 4096 bits).

Response - 200 OK

{ "data": { "name": "deploy", "type": "ed25519", "private_key": "-----BEGIN ...", "public_key": "ssh-ed25519 AAAA...", "public_key_path": "/home/user1/.ssh/deploy.pub" } }

Errors

Status Code Meaning
400 bad_request Missing name.
400 exists A key with that name already exists.
404 not_found Website not found.
500 ssh_dir_failed / keygen_failed .ssh setup or ssh-keygen failed.

Side effects

  • Runs ssh-keygen, writing the key pair to /home/<user>/.ssh and fixing ownership/mode.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/ssh-private-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "deploy", "type": "ed25519"}'

DELETE /api/webapps/:id/ssh-private-keys/:name

Deletes an SSH key pair (private and public) from the website user's .ssh directory.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
name string Key file name (sanitized).

Response - 200 OK

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

Errors

Status Code Meaning
404 not_found Website not found.

Side effects

  • Removes /home/<user>/.ssh/<name> and <name>.pub.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/42/ssh-private-keys/deploy \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/ssh-private-keys/:name/download

Returns the contents of a private key file from the website user's .ssh directory.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.
name string Key file name (sanitized).

Response - 200 OK

{ "data": { "content": "-----BEGIN ...", "name": "deploy" } }

Errors

Status Code Meaning
404 not_found Website or key file not found.

Side effects

  • Reads the key file (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/ssh-private-keys/deploy/download \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/ssl/issue

Issues a Let's Encrypt certificate for the website via starkone-cli issue_letsencrypt.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Request body

{ "email": "[email protected]" }
Field Type Required Description
email string yes Contact email for the certificate.

Response - 200 OK

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

Errors

Status Code Meaning
400 bad_request email missing.
500 ssl_error The CLI issuance failed.

Side effects

  • Runs starkone-cli issue_letsencrypt <id> <email> (obtains and installs a certificate).

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/ssl/issue \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}'

GET /api/webapps/:id/ssl/status

Returns the SSL certificate status for the website (raw CLI JSON).

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

Returns the raw JSON output of starkone-cli ssl_status (not wrapped in the data envelope).

Errors

Status Code Meaning
500 ssl_error The CLI command failed.

Side effects

  • Runs starkone-cli ssl_status <id> (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/ssl/status \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/stats

Returns usage stats for a website (emails sent, disk usage, logs size, domain aliases).

Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "emails_sent": 12, "disk_usage": 10485760, "logs_size": 524288, "aliases": ["www.example.com"] } }

Errors

Status Code Meaning
400 bad_request Invalid id.
404 not_found Website not found.

Side effects

  • Runs wc, du, and starkone-cli alias_list (read-only).

Example

curl https://panel.example.com:2087/api/webapps/42/stats \
  -H "Authorization: Bearer $TOKEN"

GET /api/webapps/:id/terminal

Opens a WebSocket PTY terminal running as the website's OS user.

Guards: none in the route table (-); the handler authenticates via a JWT token query parameter.

Path parameters

Name Type Description
id integer Numeric website ID.

Query parameters

Name Type Optional Description
token string no JWT used to authenticate the WebSocket upgrade.

Response

Upgrades to a WebSocket connection (no JSON envelope). Errors before the upgrade return JSON.

Errors

Status Code Meaning
400 bad_request Invalid id.
401 unauthorized Missing or invalid token.
403 forbidden Caller is not superuser and does not own the website.
404 not_found Website not found.

Side effects

  • Spawns an interactive shell as the website's OS user; logs a terminal_website action.

Example

# WebSocket connection (illustrative)
wscat -c "wss://panel.example.com:2087/api/webapps/42/terminal?token=$TOKEN"

POST /api/webapps/:id/varnish/purge

Purges Varnish cache entries for the website's domain.

Guards: RequirePermission(db, "websites_management")

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "success": true, "message": "Cache purged for example.com" }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
404 not_found Website not found.
500 purge_failed The varnishadm ban command failed.

Side effects

  • Runs varnishadm ban req.http.host ~ <domain>.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/varnish/purge \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/:id/wp-autologin

Generates a one-time WordPress admin login link by dropping a temporary login script into the docroot.

Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))

Path parameters

Name Type Description
id integer Numeric website ID.

Response - 200 OK

{ "data": { "url": "https://example.com/starkone-autologin.php?token=..." } }

Errors

Status Code Meaning
400 bad_request Invalid id, or WordPress not detected.
404 not_found Website not found.
500 server_error Failed to write the login script.

Side effects

  • Writes starkone-autologin.php into the docroot (owned by the website user); the script self-deletes after a successful login.

Example

curl -X POST https://panel.example.com:2087/api/webapps/42/wp-autologin \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/import-orphans

Imports selected orphan system users/domains found on disk into the panel database.

Guards: RequirePermission(db, "websites_administration")

Request body

{ "users": [ { "username": "user1", "domains": ["example.com"] } ] }
Field Type Required Description
users array no Users to import.
users[].username string no System username.
users[].domains string[] no Domains to register for the user.

Response - 200 OK

{ "data": { "imported": 1 } }

Errors

Status Code Meaning
400 (message only) Bad JSON body.

Side effects

  • Inserts missing rows into the users and webapps tables.

Example

curl -X POST https://panel.example.com:2087/api/webapps/import-orphans \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"users": [{"username": "user1", "domains": ["example.com"]}]}'

GET /api/webapps/scan-orphans

Scans /home for system users/domains that are not tracked in the panel database.

Guards: RequirePermission(db, "websites_administration")

Response - 200 OK

{ "data": [ { "username": "user1", "domains": ["example.com"] } ] }

Errors

Status Code Meaning
500 (message only) DB query or /home read failed.

Side effects

  • Reads /home and its domains/ subdirectories (read-only).

Example

curl https://panel.example.com:2087/api/webapps/scan-orphans \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/webapps/ssh-keys/:keyid

Deletes an SSH key by its DB ID from both the database and the user's authorized_keys.

Guards: RequirePermission(db, "websites_administration")

Path parameters

Name Type Description
keyid integer The ssh_keys row ID.

Response - 200 OK

{ "ok": true }

Note: this endpoint does not use the data envelope.

Errors

Status Code Meaning
400 bad_request Invalid key id.
404 not_found SSH key not found.
500 file_error / db_error Failed to update authorized_keys or the DB.

Side effects

  • Removes the marked line from /home/<user>/.ssh/authorized_keys and deletes the ssh_keys row.

Example

curl -X DELETE https://panel.example.com:2087/api/webapps/ssh-keys/1 \
  -H "Authorization: Bearer $TOKEN"

POST /api/webapps/system-user

Creates a minimal hidden webapp record for an existing system user (used for SSH-only/system accounts).

Guards: RequirePermission(db, "websites_administration")

Request body

{ "username": "user1" }
Field Type Required Description
username string yes The system username.

Response - 201 Created

{ "data": { "domain": "user1.system", "username": "user1" } }

If a record already exists, returns 200 OK with { "data": { "message": "already exists" } }.

Errors

Status Code Meaning
400 bad_request username missing.
500 db_error Insert failed.

Side effects

  • Inserts a webapps row (domain <username>.system) and creates /home/<user>/.ssh.

Example

curl -X POST https://panel.example.com:2087/api/webapps/system-user \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username": "user1"}'