Repository navigation
Architecture
Krasnikov Maksim edited this page Oct 22, 2026
·
2 revisions
| Component | Role | Technology / approach |
|---|---|---|
| Control plane (panel) | UI, API, data model, Quadlet generation, change planning, backup scheduler, notifications | Rust (axum, sqlx); PostgreSQL for state; Solid SPA embedded in the binary; ConnectRPC API |
| Agent | Executes operations in the context of a workspace user: writes units, daemon-reload, start/stop, access to the Podman socket |
Rust, same binary as the control plane at Alpha; runs as root with a fixed operation whitelist and switches into the user via systemd-run --machine / D-Bus --machine; Podman REST API via /run/user/UID/podman/podman.sock
|
| systemd + Quadlet | Container lifecycle, restarts, dependencies, health, auto-update with rollback | Quadlet .container / .pod / .network / .volume / .build; templated units such as web@.container
|
| Podman (rootless) | Isolation, networks, in-workspace DNS, secrets, image builds | netavark + aardvark-dns; crun; podman secret; buildah |
| Edge proxy | Domains, TLS, routing to upstreams, graceful switch on deploy | Caddy driven via admin API; optional HAProxy on L4 in front |
| Registry | Stores built images by digest for dev → prod promotion | Local OCI registry (zot or distribution) |
| Logs | Centralised logs with rotation and rate limiting | journald (LogDriver=journald), read through the journal API |
| Notifier | Events and alerts | Telegram; subscribes to podman events and OnFailure= of units |
| Backup runner | Database dumps and upload to S3 on schedule | systemd timers + restic/rclone; pg_dump, clickhouse-backup
|
| MCP server | Single programmatic entry point for the agent, Telegram bot, Claude Code and external clients | Thin layer over the panel API with scoped tokens |
| LLM adapter | Any provider via the user's own key | Vercel AI SDK or LiteLLM; tool calling |
| Task tracker | Tasks, cycles, roles; widgets in the UI; agent as task author | Plane (self-hosted) shipped with the platform; Jira/Linear drivers |
| Telemetry | Per-workspace / per-service metrics, centralised logs, event feed | VictoriaMetrics + VictoriaLogs, Vector/Alloy, cgroups exporter; charts in the Maurice UI; Grafana optional |
User / Claude Code / Telegram
|
v
[UI + API + MCP] <-- tokens with scopes read / plan / apply
|
v
[Control plane] -- generates Quadlet units, change plans, Caddy config
| |
v v
[Agent] --machinectl--> [ws-<project>] rootless Podman + systemd --user
| |- web@.service (stateless, blue-green)
| |- postgres.service (stateful, restart)
| |- <project>.network (aardvark DNS: postgres, web, ...)
v
[Caddy admin API] <-- routes domain -> 127.0.0.1:<port>
|
[HAProxy L4, optional] <-- public IPs, PROXY protocol
- A workspace is a system user
ws-<project>:useradd --system,nologinshell, home on NVMe (e.g./srv/ws/<project>), explicitly allocated subuid/subgid (65536 per workspace, ranges tracked by the panel),loginctl enable-linger. - Services inside a workspace trust each other and share the user. A dedicated
user
ws-<project>-<service>is created only when the isolate flag is set (signers, parsers of untrusted input). - Nodes and ClickHouse that need full network performance run rootful with
Network=host; for them the panel only offers lifecycle management. - Deleting a workspace: stop units →
podman system resetas the user →disable-linger→userdel -r→ release the subuid range.
- Every workspace has its own network; service names resolve through the
built-in DNS. The same hostname
postgresin different workspaces does not clash. - Container names are unique per version (
web-a1b2c3); the service name is a stableNetworkAlias(web). Everything talks via the alias. - Publishing a port is an explicit action: host port, host IP (default
127.0.0.1for the proxy), optionally0.0.0.0. - Cross-workspace connectivity goes through an access link: the service is
published on an internal host address, the panel shows
host.containers.internal:<port>and can revoke the link. - Multiple public IPs separate zones (admin/SSH, public services, node P2P), not TLS.
- Start
web@<new>without the alias; withNotify=healthy,systemctl startreturns only after the health check passes. - Switch traffic: Caddy admin API changes the upstream; for internal traffic,
network connect --aliason the new container,disconnecton the old. Two containers never hold the same alias at once. - The old container gets
SIGTERMand drains withinStopTimeout. Graceful shutdown in applications is mandatory. - The previous image stays in the registry; rollback is the same process in reverse.
Maurice PaaS · Apache 2.0 · Repository · Task board