Prerequisites
Make sure you have the following installed:
- Rust (latest stable) — rustup.rs
- Topcoat CLI —
cargo install topcoat-cli --version 0.9.0 --locked - Bun — bun.sh (for Tailwind CSS)
- Docker — for PostgreSQL and Mailpit
Quick Start
Clone the repository
cd templates/teams-topcoatBootstrap your env file
just bootstrapThis copies .env.example to .env and generates a fresh SESSION_SECRET for you.
The defaults sign you in by email code (Mailpit catches the mail) with no identity provider to set up. Edit .env if you need to change:
DATABASE_URL— PostgreSQL connection string (default:postgres://oxidt_teams:oxidt_teams@localhost:5496/oxidt_teams)BASE_URL— Your app's base URL (default:http://localhost:8080)COMING_SOON— off by default; settrueto enable the pre-launch waitlist
Start infrastructure
docker compose up -dThis starts:
- PostgreSQL 18 on port
5496 - Mailpit with SMTP on
1096and web UI on8096, where the sign-in codes arrive
Schema migrations in migrations/ are embedded in the binary and applied automatically on first boot.
FerrisKey is optional. just ferriskey starts one on port 8090 and creates the realm and client the app expects; see Authentication.
Install frontend dependencies
bun installRun the development server
just serveThe app will be available at http://localhost:8080. just serve builds Tailwind CSS, then topcoat dev rebuilds, bundles assets and restarts on code changes.
Project Commands
just ferriskey is the one optional extra: it starts FerrisKey from the ferriskey compose profile and runs scripts/ferriskey-bootstrap.sh against it.
| Command | Description |
|---|---|
just serve |
Start the development server with reload |
cargo build --release && topcoat asset bundle --release |
Production build |
bunx @tailwindcss/cli -i tailwind.css -o ./assets/tailwind.css |
Build Tailwind CSS manually |
cargo fmt --all -- --check |
Check formatting |
cargo clippy --workspace --all-targets -- -D warnings |
Lint |
cargo machete |
Detect unused dependencies |
Run just tw after changing classes while topcoat dev is running; just serve builds Tailwind CSS once at start.
Environment Variables
Core Settings
DATABASE_URL— PostgreSQL connection stringBASE_URL— Public URL of your applicationSESSION_SECRET— Hex-encoded session signing key (64+ bytes)SECURE_COOKIES— Set totruein production (HTTPS)COMING_SOON—truereplaces the landing page with a coming-soon page and waitlist (see the Waitlist accordion)
Registration & Waitlist
OPEN_REGISTRATION—truelets any address sign up; see Authentication for the allowlist alternativeCAPTCHA_URL/CAPTCHA_SITE_KEY/CAPTCHA_SECRET_KEY— Bollwark captcha in front of registration and the waitlist formCOMING_SOON— whiletrue,/is a coming-soon page whose form writes to thewaitlisttable (SELECT count(*) FROM waitlistto see how many are waiting); login, docs and the dashboard stay reachable
Authentication
AUTH_MODE—local(default: email OTP + passkeys, nothing else to run) orferriskeyTOS_VERSION— terms version users must accept on login; unset disables the stepTRUST_PROXY_HEADERS— Set totruewhen behind a reverse proxy- FerrisKey mode only:
FERRISKEY_URL(local instance:http://localhost:8090/api),FERRISKEY_ISSUER_URL(optional),FERRISKEY_REALM,FERRISKEY_CLIENT_ID,FERRISKEY_CLIENT_SECRET—just ferriskeyproduces all of these
Email (SMTP)
SMTP_HOST— SMTP server hostSMTP_PORT— SMTP port (default:1025for Mailpit)SMTP_USER— SMTP username (optional for Mailpit)SMTP_PASSWORD— SMTP password (optional for Mailpit)SMTP_FROM— Sender email address
Billing (Polar)
POLAR_ACCESS_TOKEN— Polar API access tokenPOLAR_WEBHOOK_SECRET— Webhook verification secret
Sentry (optional)
Requires building with --features sentry.
SENTRY_DSN— Sentry project DSNENVIRONMENT— environment tag (e.g.,development,production)
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:
docker compose up -d
cargo sqlx prepare -- --no-default-features --features server
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.
Renaming the project
After cloning, give the project its own name:
just rename my-new-projectThis rewrites the package name, Postgres database/user name, tracing filter, Dockerfile binary path, and docs in one shot. Run cargo build afterwards to regenerate Cargo.lock.