Hub API
All routes (except /api/health and /api/auth/*) require an authenticated session via cookie.
Base URL: /api
Authentication
Authentication is handled by Better Auth. All auth routes are prefixed with /api/auth.
POST /api/auth/sign-up/email
Create a new account.
Body:
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}Response: 200 — session information
POST /api/auth/sign-in/email
Sign in.
Body:
{
"email": "john@example.com",
"password": "password123"
}Response: 200 — session cookie
POST /api/auth/sign-out
Sign out. Invalidates the current session.
Vhosts
GET /api/vhosts
List all vhosts, sorted by domain.
Response: 200
[
{
"id": 1,
"domain": "app.example.com",
"config": " reverse_proxy http://backend:8080\n ...",
"enabled": true,
"upstream": "http://backend:8080",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}
]POST /api/vhosts
Create a new vhost.
Body:
{
"domain": "app.example.com",
"config": " reverse_proxy http://backend:8080\n log { ... }",
"upstream": "http://backend:8080"
}| Code | Description |
|---|---|
201 | Vhost created, Caddy reloaded |
400 | domain or config missing |
409 | Domain already exists |
502 | Caddy rejected the configuration (rollback performed) |
PUT /api/vhosts/:id
Update an existing vhost. All fields are optional.
Body:
{
"domain": "app.example.com",
"config": " reverse_proxy http://new-backend:8080\n ...",
"upstream": "http://new-backend:8080"
}| Code | Description |
|---|---|
200 | Vhost updated, Caddy reloaded |
404 | Vhost not found |
502 | Caddy rejected the configuration (rollback performed) |
DELETE /api/vhosts/:id
Delete a vhost.
| Code | Description |
|---|---|
200 | { "success": true } |
404 | Vhost not found |
502 | Caddy rejected the configuration (rollback performed) |
PATCH /api/vhosts/:id/toggle
Enable or disable a vhost (toggles current state).
| Code | Description |
|---|---|
200 | Vhost updated |
404 | Vhost not found |
502 | Caddy rejected the configuration (rollback performed) |
Logs
GET /api/logs
Retrieve Caddy access logs.
Query params:
| Param | Type | Default | Description |
|---|---|---|---|
domain | string | — | Filter by domain |
status | integer | — | Filter by HTTP code |
page | integer | 1 | Page number |
Response: 200
{
"logs": [
{
"timestamp": "2025-01-15T10:30:00.000Z",
"domain": "app.example.com",
"method": "GET",
"path": "/api/data",
"status": 200,
"latency": "12.3ms",
"ip": "192.168.1.1"
}
],
"total": 150,
"page": 1,
"pageSize": 50
}Changelog
GET /api/changelog
Retrieve the last 100 changelog entries.
Response: 200
[
{
"id": 1,
"userId": "abc123",
"userName": "John Doe",
"action": "create",
"domain": "app.example.com",
"diff": null,
"createdAt": "2025-01-15T10:30:00.000Z"
}
]Possible values for action: create, update, delete, toggle.
For update actions, the diff field contains a stringified JSON with old and new keys.
Health
GET /api/health
Health check. No authentication required.
Response: 200
{ "status": "ok" }