344b1701dd
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
111 lines
5.6 KiB
Bash
111 lines
5.6 KiB
Bash
# Every variable the application actually reads, so a rebuilt secrets file cannot silently drop one.
|
|
# On the server this is /etc/threadcount/secrets.env; locally it is .env.local.
|
|
#
|
|
# The variables beginning NEXT_PUBLIC_ are compiled into the browser bundle at build time, not read
|
|
# at runtime — set them before `next build`, and never put anything secret in one.
|
|
|
|
# ---- required. instrumentation.ts refuses to start production without these ----
|
|
DATABASE_URL=postgresql://user:pass@localhost:5432/threadcount
|
|
# Signs every session cookie. Generate one per environment (`openssl rand -base64 48`) and never
|
|
# ship the placeholder — production refuses to start while it still says change-me.
|
|
SESSION_SECRET=change-me
|
|
# Cloudflare Turnstile. Protects sign-in, sign-up, password reset, the contact form and the update
|
|
# list. Missing in production means either an unprotected front door or an authentication outage,
|
|
# so the server refuses to start without both.
|
|
TURNSTILE_SECRET=
|
|
NEXT_PUBLIC_TURNSTILE_SITEKEY=
|
|
|
|
# ---- transactional mail ----
|
|
# With these unset nothing is sent: a request is still raised, and the screens say plainly that
|
|
# nobody was emailed rather than claiming otherwise.
|
|
SMTP_HOST=
|
|
SMTP_PORT=587
|
|
SMTP_USER=
|
|
SMTP_PASS=
|
|
SMTP_FROM="ThreadCount <no-reply@threadcount.tech>"
|
|
# Where the website's contact form is delivered.
|
|
CONTACT_TO=
|
|
|
|
# ---- addresses and storage ----
|
|
# Absolute base URL, used for links in emails (approval links, password resets).
|
|
NEXT_PUBLIC_SITE_URL=https://threadcount.tech
|
|
# Where uploaded photographs are written. Must be on a volume that is backed up with the database.
|
|
PHOTO_DIR=/var/lib/threadcount/photos
|
|
|
|
# ---- Android app links ----
|
|
# The SHA-256 signing-certificate fingerprints served from /.well-known/assetlinks.json. Several may
|
|
# be listed per app, comma-separated. ANDROID_APP_FINGERPRINTS is the fallback for both.
|
|
ANDROID_APP_FINGERPRINTS_COUNTER=
|
|
ANDROID_APP_FINGERPRINTS_STAFF=
|
|
|
|
# ---- operations console (ops.threadcount.tech) ----
|
|
# Signs the operator session cookie (tc_ops). Its OWN secret, never SESSION_SECRET: that one also
|
|
# derives the key encrypting every customer's TOTP secret, so sharing it would mean a compromised
|
|
# operator credential forces a rotation that destroys every facility's second factor. Generate it
|
|
# the same way (`openssl rand -base64 48`). Only the console reads it; the product starts without it.
|
|
OPS_SESSION_SECRET=
|
|
# The console reads customer data through restricted Postgres roles, never the app role:
|
|
# ops_ro — SELECT on control-plane columns only (counts, dates, configuration).
|
|
# ops_reveal — SELECT on a facility's id and three coordinator contact columns, nothing else.
|
|
# Same host and database as DATABASE_URL, different user. Create the roles by hand as postgres;
|
|
# the grants are in prisma/migrations (ops_ro_grants, ops_reveal_grants).
|
|
OPS_DATABASE_URL=
|
|
OPS_REVEAL_DATABASE_URL=
|
|
# Single sign-on for FACILITIES (coordinators and, if a facility allows it, staff): the self-hosted
|
|
# BoxyHQ Jackson broker at sso.threadcount.tech holds each facility's IdP metadata. With either
|
|
# unset there is no SSO anywhere in the product; the key authenticates the management API only.
|
|
JACKSON_URL=
|
|
JACKSON_API_KEY=
|
|
# Single sign-on for the console: ops.threadcount.tech sits behind a Cloudflare Access application
|
|
# (Authentik + one-time-PIN). With BOTH set, the app verifies the Access assertion (RS256, fail
|
|
# closed) and mints the operator session from the verified email; an email Access admits but the
|
|
# console does not know falls back to the password door. With either unset there is no SSO.
|
|
CF_ACCESS_TEAM_DOMAIN=
|
|
CF_ACCESS_AUD=
|
|
# Every contact reveal emails a notice here (who, which facility, why, until when — never the
|
|
# contacts). Defaults to the revealing operator's own address.
|
|
OPS_ALERT_TO=
|
|
|
|
# ---- optional switches ----
|
|
# 1 hides the create-account form and refuses the signup endpoint.
|
|
SIGNUPS_DISABLED=
|
|
# 1 takes the public demo facility out of service.
|
|
DEMO_DISABLED=
|
|
# Bearer token the demo facility's scheduled reset presents.
|
|
DEMO_RESET_TOKEN=
|
|
# Local production-mode smoke tests ONLY: 1 makes Turnstile advisory so `next start` and the e2e
|
|
# runners work on a box with no Cloudflare keys. NEVER set this in /etc/threadcount/secrets.env.
|
|
TURNSTILE_OPTIONAL=
|
|
|
|
# ---- optional: analytics, errors, update list ----
|
|
# All three default to ThreadCount's own self-hosted infrastructure; set them only to point
|
|
# somewhere else.
|
|
NEXT_PUBLIC_UMAMI_SITE_ID=
|
|
NEXT_PUBLIC_UMAMI_APP_ID=
|
|
NEXT_PUBLIC_GLITCHTIP_DSN=
|
|
# Stamped on error reports so a fault can be tied to a deploy.
|
|
NEXT_PUBLIC_RELEASE=
|
|
LISTMONK_URL=
|
|
LISTMONK_LIST_UUID=
|
|
|
|
# ---- local development only ----
|
|
# 1 is required by the embedded `prisma dev` server, which cannot handle concurrent queries.
|
|
# Never set it in production: it serialises every database call in the app.
|
|
DB_POOL_MAX=
|
|
|
|
# Plans. 1 forces plans live on this box whatever the console switch says: new sign-ups start on
|
|
# Hosted Small (60 staff records) and Settings shows the Plan tab. Leave unset; flip it from the
|
|
# console once the notice to existing rooms has run.
|
|
# PLANS_LIVE=1
|
|
|
|
# ---- edition ----
|
|
# "community" = the self-hosted edition (Dockerfile / docker-compose.yml): every feature, no plans,
|
|
# no staff ceiling, no demo, no operations console, no error reports or usage statistics sent
|
|
# anywhere, and Turnstile optional. Leave unset on threadcount.tech.
|
|
EDITION=
|
|
# Only docker-compose.yml reads this: the password of the bundled Postgres.
|
|
POSTGRES_PASSWORD=
|
|
# Only docker-compose.yml reads this: the host port the app is published on (the container always
|
|
# listens on 3000). Point your reverse proxy at it.
|
|
APP_PORT=3000
|