← BACK TO DASHBOARD
SKROBBE HUB API
All endpoints are behind Cloudflare Access (email OTP). Requests require valid session credentials.
Base URL: https://api.skrobbe.com
System
GET /api/status Health check
Response
{ "status": "ok" }
GET /api/system Pi hardware stats
Response
{
  "cpu_percent": 2.5,
  "mem_percent": 8.0,
  "mem_used": 319614976,
  "mem_total": 3980124160,
  "disk_percent": 2.1,
  "disk_used": 4964769792,
  "disk_total": 245681999872,
  "temp_c": 42.4,
  "uptime": "17h 35m"
}
Pi-hole
GET /api/pihole/stats DNS blocking statistics
Response
{
  "total_queries": 2163,
  "blocked": 5,
  "percent_blocked": 0.23
}
Laptop Control
GET /api/laptop/status Laptop power state via Shelly plug
Response
{
  "status": "offline | booting | online | idle | unreachable | not_configured",
  "plug_on": true,
  "power_w": 45.2,
  "voltage": 238.4,
  "plug_temp_c": 32.0,
  "idle_shutdown_sec": null
}
Status States
StatusMeaning
offlinePlug is off, laptop has no power
bootingPlug on, low power draw, within 3min boot grace period
onlinePlug on, power draw > 5W (laptop is running)
idlePlug on, power draw < 5W, past boot grace (auto-off countdown active)
unreachableCannot communicate with Shelly plug
A background monitor checks power every 30s. If power stays below 5W for 5 minutes (after a 3-minute boot grace), the plug auto-shuts off to prevent idle power waste.
POST /api/laptop/power-on Turn on Shelly plug (boot laptop)
Response
{
  "status": "powering_on",
  "message": "Plug turned on — laptop booting"
}
Errors
CodeDetail
409Laptop is already powered on
502Cannot reach Shelly plug
Blocked if plug is already on. The laptop boots via BIOS "power on AC restore" when the Shelly energizes the outlet.
POST /api/laptop/shutdown Turn off Shelly plug (cut laptop power)
Response
{
  "status": "powered_off",
  "message": "Plug turned off — laptop powered down"
}
Errors
CodeDetail
409Laptop is already powered off
502Cannot reach Shelly plug
Blocked if plug is already off. Hard power cut — ensure laptop is shut down via OS first if possible.
RustDesk
GET /api/rustdesk/config RustDesk server status & client config
Response
{
  "hbbs": "online",
  "hbbr": "online",
  "public_key": "...",
  "server": "skrobbe.com"
}
Fields
FieldDescription
hbbsSignal/ID server status (online | offline)
hbbrRelay server status (online | offline)
public_keyEd25519 public key for RustDesk client configuration
serverServer address to configure in RustDesk clients
Use the public_key and server values to configure RustDesk clients. Both hbbs and hbbr must be online for remote desktop to work.
About
GET /api/about Full hub architecture documentation
Response
{
  "documentation": "# Skrobbe Hub\n\n## Overview\n..."
}
Returns comprehensive markdown documentation of how the hub is built — architecture, services, security model, and more. Designed to be readable by both humans and AI systems.
File Manager
GET /api/files List directory contents
Parameters
NameTypeRequiredDescription
path string OPTIONAL Relative path within storage. Defaults to root.
Response
{
  "path": "",
  "entries": [
    {
      "name": "photos",
      "type": "dir",
      "size": null,
      "modified": 1711100000.0
    },
    {
      "name": "notes.txt",
      "type": "file",
      "size": 1024,
      "modified": 1711100000.0
    }
  ]
}
GET /api/files/download Download a file
Parameters
NameTypeRequiredDescription
path string REQUIRED Relative path to file within storage.
Response
Binary file download (Content-Disposition: attachment)
POST /api/files/upload Upload a file
Parameters
NameTypeRequiredDescription
path string OPTIONAL Target directory. Defaults to root.
file multipart/form-data REQUIRED The file to upload.
Response
{
  "uploaded": "notes.txt",
  "path": "notes.txt"
}
DELETE /api/files Delete a file or directory
Parameters
NameTypeRequiredDescription
path string REQUIRED Relative path to file or directory.
Response
{ "deleted": "notes.txt" }
Directories are deleted recursively. Cannot delete the storage root.
PUT /api/files/rename Rename a file or directory
Parameters
NameTypeRequiredDescription
path string REQUIRED Current relative path.
new_name string REQUIRED New filename (not a path, just the name).
Response
{
  "renamed": "old-name.txt",
  "new_name": "new-name.txt"
}