ServiceAgent
Two values, then you are running
Everything else this app needs — your Jobber connection, your model API key, photo retention, email — is configured in Settings once you are signed in. These two have to be environment variables, because neither can bootstrap from the database it protects.
A PostgreSQL database
Create a free database at neon.com (or Supabase), US East. Copy the connection string into your Hostinger environment as DATABASE_URL.
DATABASE_URL=postgres://user:pass@host/db?sslmode=require
It has to be PostgreSQL, not MySQL. Row-level security is what keeps one company's customers off another company's screen at the database level, and holds even if the app has a bug. MySQL has no equivalent — the protection would have to be app code someone promises is correct.
not setAn encryption key for your settings
Generate one and set it as SETTINGS_ENC_KEY. Every credential you later type into Settings is encrypted at rest with it.
openssl rand -base64 48
Back this up
Run the migrations
In GitHub, add repository secrets ADMIN_DATABASE_URL (your database owner connection string) and APP_DB_PASSWORD (any 16+ character password). Then run the Migrate database workflow.
The admin connection string lives in GitHub secrets and never in this app's environment, so a compromised app cannot drop a table or disable a policy. The workflow also creates the restricted role the app connects as, and refuses to report success unless that role genuinely cannot bypass row-level security.
Or run it locally:
ADMIN_DATABASE_URL=... npm run migrate ADMIN_DATABASE_URL=... APP_DB_PASSWORD=... npm run provision-rolenone applied
Check tenant isolation is actually enforced
Waiting on the database.
Once all four are green this page hands you straight to sign-in. You can re-check any time at /api/health.