Technical Architecture
Detailed Schema
Browser
|
| HTTPS
v
+-----+------+ +------------------+
| Frontend | | Backend |
| React +------>| Hono API |
| (Nginx) | /api | SQLite (WAL) |
+------------+ +--------+---------+
|
HTTP POST | /caddy/reload
HTTP GET | /logs
(Bearer) |
v
+-----------+---------+
| Agent |
| Hono API |
+----+----------+-----+
| |
POST /load | | read file
v v
+----+----+ +---+--------+
| Caddy | | access.log |
| Admin | | (JSON) |
| API | +------------+
+---------+Tech Stack
| Layer | Technology |
|---|---|
| Frontend | React 19, TanStack Router, TanStack Query, Tailwind CSS |
| Backend | Hono.js, Better Auth, Drizzle ORM |
| Database | SQLite (better-sqlite3, WAL mode) |
| Agent | Hono.js (lightweight service, no DB) |
| Runtime | Node.js 22+ |
| Package manager | pnpm 9.15 (monorepo workspaces) |
| Containerization | Docker multi-stage, Nginx (frontend) |
Data Model
users
| Column | Type | Description |
|---|---|---|
id | TEXT PK | Unique identifier |
name | TEXT | User name |
email | TEXT UNIQUE | Email address |
role | TEXT | admin or user |
banned | BOOLEAN | Banned account |
createdAt | TIMESTAMP | Creation date |
vhosts
| Column | Type | Description |
|---|---|---|
id | INTEGER PK | Auto-increment |
domain | TEXT UNIQUE | Domain name |
config | TEXT | Caddyfile configuration |
enabled | BOOLEAN | Vhost active or not |
upstream | TEXT | Upstream URL (optional) |
createdAt | TIMESTAMP | Creation date |
updatedAt | TIMESTAMP | Last modification |
changelog
| Column | Type | Description |
|---|---|---|
id | INTEGER PK | Auto-increment |
userId | TEXT FK | Reference to users.id |
action | TEXT | create, update, delete, toggle |
domain | TEXT | Affected domain |
diff | TEXT | JSON old/new config |
createdAt | TIMESTAMP | Modification date |
Hub / Agent Communication
Communication uses HTTP with Authorization: Bearer <AGENT_TOKEN> header:
| Operation | Method | Endpoint | Description |
|---|---|---|---|
| Reload | POST | /caddy/reload | Sends the complete Caddyfile |
| Logs | GET | /logs | Reads access logs |
| Health check | GET | /health | No authentication required |
Caddyfile Generation
- Retrieve active vhosts (
enabled = true) - Build the global block (ACME email, Caddy logs)
- Concatenate blocks:
domain { config } - Send to Caddy admin API (
POST /load) - On failure: rollback the database modification
Example generated Caddyfile:
nginx
{
email admin@example.com
log {
output file /var/log/caddy/caddy.log
format json
}
}
app.example.com {
reverse_proxy http://backend:8080
log {
output file /var/log/caddy/access.log {
roll_size 100mb
roll_keep 5
}
format json
}
}