Prerequisites
- Rust (latest stable) — rustup.rs
- Dioxus CLI —
curl -sSL http://dioxus.dev/install.sh | sh - Bun — bun.sh, for Tailwind CSS
- Docker — for PostgreSQL and Mailpit
- just —
cargo install just
Quick start
This page builds Thermite from source, which is what you want to work on it. To just run it, pull the image instead: Self-hosting.
Clone the repository
git clone https://github.com/hauju/thermite-rs.git
cd thermite-rsBootstrap your env file
just bootstrapCopies .env.example to .env and fills in a freshly generated SESSION_SECRET. It refuses
to overwrite an existing .env.
The defaults work as-is for local development. Set THERMITE_ADMIN_EMAIL and
THERMITE_ADMIN_PASSWORD and that is your sign-in — no identity provider to stand up. An
external one is optional; see Authentication.
Start the infrastructure
just initBrings up PostgreSQL 18 on 5433 and Mailpit with SMTP on 1028 and its web UI on
8028. The ports are offset off the defaults so the stack runs alongside other projects'
containers, and every one is bound to 127.0.0.1 — a dev database with default credentials is
never reachable from the network.
Migrations live in migrations/, are embedded in the binary, and apply automatically on first
boot. There is no separate migrate step.
Install frontend dependencies
bun installRun the development server
just serveThermite comes up on http://localhost:8080, hot-reloading on change and compiling Tailwind
as it goes.
Signing in
Thermite signs people in itself by default. Set the operator credential and there is nothing else to run:
THERMITE_ADMIN_EMAIL=you@example.com
THERMITE_ADMIN_PASSWORD=a-long-random-oneThe password is hashed once at boot and never stored — the value in the environment is the only copy. Your user row is created on the first successful sign-in, and once you are in you can enroll a passkey and stop typing it. With SMTP configured, the login page also offers an emailed code, which is a way in on its own, so an instance may set SMTP and no admin credential.
Setting FERRISKEY_URL switches authentication to FerrisKey OIDC
instead, for a deployment that already runs an identity provider — see
Authentication. That variable is the whole switch; there is no
mode to set.
An instance with none of these refuses to boot, naming what to set. A server nobody can log into is a misconfiguration, not a deployment.
The first account owns everything. With no allowlist configured, only the first account may register on a fresh instance, and registration closes as soon as any user exists. Register yours before you expose the instance to anything.
Send it a first event
Create a project in the dashboard, copy its DSN from Project settings → DSN keys, and point any Sentry SDK at it. Thermite speaks Sentry's wire protocol, so there is nothing to install beyond the SDK you would have used anyway — see the SDKs guide.
Project commands
| Command | Description |
|---|---|
just serve |
Development server with hot reload |
just init |
Start Postgres and Mailpit |
just check |
Everything CI runs: fmt, clippy, test |
just test |
Workspace tests, plus the app crate under its server feature |
just clippy |
cargo clippy --workspace --all-targets -- -D warnings |
just prepare |
Regenerate the committed sqlx query metadata |
just tw |
Build Tailwind CSS manually |
just icons |
Re-render the favicon and PWA icons from their SVG sources |
The app crate's default feature builds the wasm client, so plain cargo test does not test the
server. Anything server-side needs --no-default-features --features server, which is what
just test does for you.
Environment variables
Changing the database schema
Queries use sqlx's compile-time-checked macros, so column names and types are verified against
the schema when you build. The metadata lives in .sqlx/ and is committed, which is what lets a
fresh clone build with no database running.
After editing any SQL or adding a migration, regenerate the metadata:
just init # a database must be running
just prepare
Editing a query without re-preparing fails the next build. Editing a migration without re-preparing does not — cached entries are keyed by the query text, so untouched queries keep matching now-stale metadata and compile fine, failing only at runtime. CI guards this by building against a real database.
Migrations are immutable once applied: sqlx records a checksum, so editing an applied file — even a comment — fails the next boot. Add a new numbered migration instead.