SWP.ServerAgent API
HTTP API for managing Linux services, systemd units, Caddy reverse proxy routes, and server metrics. Used by SWP.ServerService to orchestrate multiple remote servers.
System
Returns current CPU, memory, and disk usage along with hostname and OS info. Memory and disk percentages are calculated server-side.
{
"totalMemoryMb": 8192,
"usedMemoryMb": 4096,
"totalDiskMb": 102400,
"usedDiskMb": 51200,
"cpuUsagePercent": 25.5,
"uptime": "5 days",
"hostname": "jarjarbinks",
"osVersion": "Ubuntu 24.04",
"memoryPercent": 50.0,
"diskPercent": 50.0
}
Time-series CPU and memory data for charting. Returns aggregated data points at the specified interval.
| Name | Type | Default | Description |
|---|---|---|---|
| hours | int | 24 | How many hours back |
| interval | int | 5 | Aggregation interval in minutes |
| aggregation | string | "max" | Aggregation function |
{
"dataPoints": [
{ "time": "08:00", "cpu": 10.0, "memory": 45.0 },
{ "time": "08:05", "cpu": 12.5, "memory": 46.2 }
]
}
{
"modelName": "AMD EPYC 7763",
"architecture": "x86_64",
"cpus": 4,
"coresPerSocket": 2,
"threadsPerCore": 2
}
[
{
"name": "sda1",
"size": "50G",
"type": "part",
"mountPoint": "/",
"fsType": "ext4"
}
]
Services
Returns both managed (provisioned) and systemd-discovered services. Managed services not yet deployed show activeState: "provisioned" and subState: "awaiting-deploy".
[
{
"id": 1,
"unitName": "my-api-test.service",
"displayName": "my-api",
"port": 5100,
"activeState": "active",
"subState": "running",
"memoryMb": 150,
"domain": "my-api-test.swphub.dev",
"isManaged": true,
"isProtected": false
},
{
"id": null,
"unitName": "caddy.service",
"displayName": "caddy",
"port": 0,
"activeState": "active",
"subState": "running",
"memoryMb": 42,
"domain": null,
"isManaged": false,
"isProtected": false
}
]
Returns everything about a service: status, PID, resource usage, port health check, unit file content, and journal logs.
{
"id": 1,
"unitName": "my-api-test.service",
"displayName": "my-api",
"port": 5100,
"activeState": "active",
"subState": "running",
"mainPid": 1234,
"activeSince": "2026-03-01T10:00:00Z",
"memoryBytes": 157286400,
"cpuTime": "1min 30s",
"domain": "my-api-test.swphub.dev",
"createdAt": "2026-02-25T12:00:00Z",
"isManaged": true,
"isProtected": false,
"isPortListening": true,
"unitFileContent": "[Unit]\nDescription=...",
"logs": "Mar 01 10:00:01 ..."
}
Unit not found and not in database.
{
"content": "[Unit]\nDescription=my-api TEST\nAfter=network.target\n\n[Service]\nWorkingDirectory=/opt/my-api-test/app\nExecStart=/opt/my-api-test/app/MyApi\n..."
}
| Name | Type | Default | Description |
|---|---|---|---|
| lines | int | 50 | Number of log lines |
{
"content": "Mar 01 10:00:01 jarjarbinks my-api[1234]: Application started\n..."
}
Per-service CPU and memory time-series data. Same query parameters and response shape as /api/metrics/history.
| Name | Type | Default | Description |
|---|---|---|---|
| hours | int | 24 | How many hours back |
| interval | int | 5 | Aggregation interval in minutes |
| aggregation | string | "max" | Aggregation function |
{
"dataPoints": [
{ "time": "08:00", "cpu": 5.2, "memory": 12.0 }
]
}
Generates a Forgejo Actions workflow YAML for the service. Includes deploy steps for all environments (test/prod) that share the same service name. Parses the unit file to extract WorkingDirectory and ExecStart paths.
id is the managed service database ID (int), not the unit name.application/x-yamlname: my-api-cicd on: push: branches: [ "master" ] workflow_dispatch: {} jobs: build_deploy: runs-on: dotnet10 steps: - uses: actions/checkout@v4 ...
Service not found.
Service Actions
Starts the systemd unit via systemctl start. No request body.
{ "success": true }
Stops the systemd unit via systemctl stop. No request body.
{ "success": true }
Restarts the systemd unit via systemctl restart. No request body.
{ "success": true }
Full deprovision: stops and disables the systemd unit, removes the Caddy route, deletes the app directory, releases allocated ports, and removes the database record. Protected services cannot be deleted.
id is the managed service database ID (int).{ "success": true }
{ "error": "Service X is protected and cannot be deleted" }
Provisioning
Creates a fully provisioned service: systemd unit file, app directory with correct ownership, Caddy reverse proxy route, port allocation, and database record. Can provision test and/or production environments in a single call.
Test:
{name}-test.{domain} (e.g. my-api-test.swphub.dev)Prod:
{name}.{domain} (e.g. my-api.swphub.dev) — no suffix
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Service name. Lowercase, numbers and hyphens only. |
| domain | string | required | Base domain for Caddy routes (e.g. swphub.dev). |
| testEnabled | bool | Provision a test environment. | |
| productionEnabled | bool | Provision a production environment. | |
| testPort | int | Port for the test environment. | |
| productionPort | int | Port for the production environment. | |
| template | string | Unit file template. Default: "kestrel". Options: kestrel, static, generic. | |
| executableName | string? | Executable/DLL name. Auto-generated from name if omitted (my-api → MyApi). | |
| environmentVariables | string? | KEY=VALUE pairs, one per line. Added to the systemd unit. | |
| restartPolicy | string | Default: "always". Options: always, on-failure, on-abnormal, on-abort, no. | |
| restartSec | int | Delay before restart in seconds. Default: 10. |
{
"name": "my-api",
"domain": "swphub.dev",
"testEnabled": true,
"productionEnabled": true,
"testPort": 5100,
"productionPort": 5105,
"template": "kestrel",
"restartPolicy": "always",
"restartSec": 10
}
{
"success": true,
"environments": ["test", "prod"]
}
{ "error": "Service name is required" }
{ "error": "At least one environment must be selected" }
Ports
Allocates and returns the next available port from the configured range. The port is reserved immediately.
{ "port": 5101 }
Suggests a test/production port pair spaced 5 apart, without allocating them. Used by the New Service form to pre-fill port fields.
{
"testPort": 5100,
"productionPort": 5105
}
Schedules
[
{
"name": "metrics-collector",
"description": "System & per-service CPU/memory metrics",
"schedule": "every 60s",
"retention": "7 days",
"type": "internal"
}
]
Error Responses
All endpoints return errors in the same shape. The HTTP status code indicates the error category.
{ "error": "Human-readable error message" }
| Status | Meaning |
|---|---|
| 400 | Bad request — validation failure (missing name, no environment selected) |
| 404 | Resource not found (service, unit name) |
| 500 | Internal error (systemd failure, shell command failure, database error) |