Prerequisites
- Node.js >= 22.14 and pnpm.
- PostgreSQL — bring your own, or use the one in the repo’s
docker-compose.ymlfor local development.
Local development
The repo’sdocker-compose.yml provisions only Postgres (and Adminer, a DB UI) — not the
app. Bring the database up, then run the server and dashboard from source.
Configure the server
DATABASE_URL already matches the compose Postgres. It also sets a
dev-only BETTER_AUTH_SECRET — fine locally, but generate a real one for anything else.Migrate and seed
ASTILBA_SEED_ALLOW_REMOTE=1.Environment variables
The server validates its environment on start and fails fast with a clear message if anything is missing or malformed.| Variable | Required | Default | Purpose |
|---|---|---|---|
DATABASE_URL | yes | — | Postgres connection string |
BETTER_AUTH_SECRET | yes | — | Session-signing key; must be at least 32 characters |
BETTER_AUTH_URL | no | request origin | The server’s public URL (cookies/redirects) |
PORT | no | 3000 | Port the Node server listens on |
ASTILBA_STATIC_DIR | no | — | Path to the built dashboard, for one-origin serving (see below) |
db:migrate and db:seed load apps/server/.env; start reads the environment directly
(no .env). For production, either provide a .env with your production DATABASE_URL
before migrating, or run the migrate script with the variables set in the environment.Production (manual)
There’s no image yet, so production is a manual, from-source deploy. The shape:Provision Postgres and secrets
Point
DATABASE_URL at your database, set a real BETTER_AUTH_SECRET (>= 32 chars), and
set BETTER_AUTH_URL to your public URL.Serve both on one origin
Set The server serves the dashboard’s assets and falls back to
ASTILBA_STATIC_DIR to the built dashboard and start the server:index.html for client-routed
paths, while /api/* is handled first — so a static file can never shadow the API, and the
dashboard and API share one origin. Run it behind a TLS-terminating reverse proxy.First user and organization
There is no sign-up page. To create the first account:- Locally,
db:seeddoes it for you and prints the credentials. - Otherwise, use Better Auth’s endpoints under
/api/auth/*: register a user, create an organization, then set it as the session’s active organization (Better Auth’s set-active-organization endpoint) — or just sign in again once the membership exists. The active org is chosen when a session is created, so a session made before the org existed has none, and/api/projects//api/tokensanswerNo active organization(403) until you set it. With an active org, mint an API token and you’re ready topush/pull.