How SpipCP is built
The technology stack behind SpipCP β the frameworks, database, and tooling the panel, worker, and agent are built on, and the principles that shaped the choices.
This page is for the curious and the contributor: what SpipCP itself is made of. The stack is kept small and simple where it matters β one database, no extra services, typed end to end.
At a glance
| Layer | Technology |
|---|---|
| Web UI | React 19 + TanStack Start (full-stack React) + TanStack Router |
| Styling | Tailwind CSS v4 + Base UI, with a custom graphite/white design language |
| API | oRPC + Zod β typed RPC and an OpenAPI/REST surface from one definition |
| Auth | better-auth (password + TOTP, sessions, invites) |
| Database | PostgreSQL + Drizzle ORM β the single source of state |
| Jobs | pg-boss β a durable queue inside Postgres (no Redis, no broker) |
| Fleet agent | A small static Go binary on each node, over a secure WebSocket |
| Reverse proxy | Caddy (TLS termination, routing) |
| Packaging | Docker Compose Β· pnpm monorepo |
| Docs | This site β Fumadocs + TanStack Start |
The shape of it
Fleet agent (Go) βWSSβ Worker (panel) βPostgresβ Web (panel, React)The panel is one application with two faces: the React web UI and a background worker that runs the job queue and talks to the agents. The agent is a small Go binary installed on each node; it connects out over a WebSocket, so nodes need no inbound ports open. All durable state β jobs, fleet state, audit β lives in one PostgreSQL database.
Principles behind the choices
| Principle | What it means in practice |
|---|---|
| One stateful service | Postgres holds everything. The job queue is a table, so it survives restarts β no Redis, no broker to operate. |
| Typed end to end | One validated definition serves the web UI, the REST/OpenAPI surface, and the agent contract, so the pieces can't drift apart. |
| The panel decides, the agent acts | The panel never SSHes a node to run things; it computes desired state and the agent applies it. |
| API-first | Every capability is on the API before the UI β the web app is just one client of /api/v1. |
| Self-hosted, no lock-in | Open-source components, self-hosted infrastructure, direct connections between panel and fleet. |
The repository
SpipCP is a pnpm monorepo. To build or contribute, Project layout maps every package and Development setup gets it running locally.
Versions and releases
SpipCP's version is an authored semver published in the changelog β not a build counter, and not the deployed image tag. What the number means, and why an internal phase number is never a product version: Versioning. To consume the releases as JSON: Changelog API.
β Security architecture: Security. Β· What can be installed with it: Installable services.