Environment variables
Copy .env.example to .env and fill in the values before running the dev server or deploying. All variables listed here are read at runtime by the Netlify function — none are baked into the static build.
GitHub
Section titled “GitHub”| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN | Yes | Fine-grained personal access token with read/write access to the client repo’s Contents, plus the mandatory read-only Metadata permission. Scoped to the single client repository. |
GITHUB_OWNER | Yes | GitHub organization or username that owns the client repo. |
GITHUB_REPO | Yes | Repository name (without the owner prefix). |
Auth core
Section titled “Auth core”| Variable | Required | Description |
|---|---|---|
SESSION_SECRET | Yes | Random string used to sign session cookies. Must be at least 32 characters. Generate one: |
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"| Variable | Required | Description |
|---|---|---|
AUTH_PROVIDER | No | password (default) or supabase. Omit to use password-only auth. |
Password mode
Section titled “Password mode”Used when AUTH_PROVIDER=password (or AUTH_PROVIDER is unset). All *_PASSWORD values are bcrypt hashes, not plaintext.
| Variable | Required | Description |
|---|---|---|
ADMIN_PASSWORD | Yes (password mode) | Bcrypt hash for the site owner account. |
EDITOR_PASSWORD | Yes (password mode) | Bcrypt hash for editor accounts. |
VIEWER_<NAME>_PASSWORD | No | Bcrypt hash for a named viewer audience. <NAME> becomes the audience name (e.g. VIEWER_INTERNAL_PASSWORD). Optional — add one per gated viewer audience. |
VIEWER_<NAME>_COLOR | No | Hex color for the named audience badge in the editor UI (e.g. VIEWER_INTERNAL_COLOR=#10b981). |
Generating a bcrypt hash
Section titled “Generating a bcrypt hash”node -e "console.log(require('bcryptjs').hashSync('your-password', 10))"Critical — escape every $ in the hash with a backslash. Vite runs dotenv-expand, which interprets $name as a variable reference and silently strips it, corrupting the hash. Quoting the value does NOT prevent this; only \$ does.
# bcryptjs gives you:$2b$10$n1JHs0z5qYC.ISZG...
# Write in .env as:ADMIN_PASSWORD=\$2b\$10\$n1JHs0z5qYC.ISZG...Second gotcha: no leading whitespace before the key name. dotenv silently skips indented lines, so every sign-in attempt will fail with “Invalid password” — with no error in the logs.
Supabase mode
Section titled “Supabase mode”Used when AUTH_PROVIDER=supabase.
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL | Yes (supabase mode) | Your Supabase project URL, e.g. https://your-project.supabase.co. |
SUPABASE_ANON_KEY | Yes (supabase mode) | Supabase anon (public) key. Safe to include in the build. |
SUPABASE_SERVICE_ROLE_KEY | Yes (supabase mode) | Supabase service role key. Server-only — never expose to the client. |
The optional SUPABASE_ACCOUNT_TOKEN and SUPABASE_PROJECT_REF variables are only needed when running Supabase migrations via the CLI (pnpm db:push) — they are not required at runtime.
Deploy
Section titled “Deploy”| Variable | Required | Description |
|---|---|---|
SITE | Yes (Supabase mode, production) | The canonical origin of the site, e.g. https://acme.drawn.guide. Required in Supabase mode — used by invite and password-reset emails. Not needed in password-only mode. Set in the Netlify dashboard, not in .env. |
SITE must be pinned to the custom domain — not the *.netlify.app subdomain. It is used by invite and password-reset emails. Netlify’s built-in URL environment variable can resolve to the Netlify subdomain instead of the custom domain, so an explicit SITE is required.
This variable is set by the provisioner when creating the site. For existing sites built before this was introduced, set it manually in the Netlify dashboard and trigger a redeploy.
For the exhaustive list of every variable and its validation rules, see the Environment variable reference.