Engine

Setup

From clone to a seeded tenant.

Local development

# Install dependencies
npm install

# Start local infrastructure (Postgres & Redis)
docker-compose up -d

# One-shot bootstrap
npm run setup

# Start the backend
npm run start:dev

npm run setup creates .env with generated JWT secrets, runs the migrations, and interactively seeds a root admin plus a first tenant company.

It is idempotent — re-running it skips .env creation if the file already exists, and skips seeding any root user or tenant account that is already there. You can run it again after pulling without wiping your data.

Environment

Copy the example file and fill in your secrets:

cp .env.example .env

Joi validates these at boot. Anything marked Required fails the app fast at startup if missing.

Core

VariableRequiredDefaultDescription
NODE_ENVNodevelopmentdevelopment | production | test
PORTYesPort the Fastify server listens on
API_BASE_URLYesPublic base URL of this API — builds OAuth callbacks and asset links
FRONTEND_URLYesBase URL of the frontend — used in email links
DATABASE_URLYesPostgres connection string
MIGRATIONS_AUTO_APPLYNofalseRun Drizzle migrations automatically on boot

Connection pooling

Schema-per-tenant means one pool per schema. These three control that, and they are the settings most worth tuning under load.

VariableRequiredDescription
DB_POOL_MAXYesMax connections for the template (public) schema pool
TENANT_POOL_MAXYesMax connections per per-tenant schema pool
TENANT_POOL_IDLE_TTL_MSYesIdle time before an unused tenant pool is evicted

The eviction TTL matters: without it, a system with many tenants accumulates idle pools until Postgres refuses new connections.

Queues, tokens and mail

VariableRequiredDescription
REDIS_HOST / REDIS_PORTYesRedis connection for queues
JWT_ACCESS_SECRETYesSigning secret for access tokens
JWT_REFRESH_SECRETYesSigning secret for refresh tokens
JWT_ACCESS_EXPIRES_INYesAccess token lifetime, e.g. 15m
JWT_REFRESH_EXPIRES_INYesRefresh token lifetime, e.g. 30d
SMTP_HOSTYesSMTP host for transactional email
SMTP_PORTYesSMTP port (465 = implicit TLS)
SMTP_USER / SMTP_PASSNoSMTP auth credentials
MAIL_FROMYesFrom address on outgoing email

SSO providers

Optional as a group — fill in only the providers you enable. An unset provider simply leaves that strategy unconfigured.

VariableProvider
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETGoogle
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETGitHub
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRETMicrosoft (Azure AD)
DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRETDiscord
APPLE_CLIENT_ID / APPLE_TEAM_ID / APPLE_KEY_ID / APPLE_PRIVATE_KEYSign in with Apple