# Example environment file for apparel-designer. # # Copy this to `.env` and adjust values as needed. Compose reads # `.env` automatically for variable substitution in docker-compose.yml # AND passes the values as runtime environment to the running # container, so a single file covers both build-time and runtime. # # ────────────────────────────────────────────────────────────────── # Runtime — values the server reads at startup # ────────────────────────────────────────────────────────────────── # HTTP port the Express server binds to. Matches the host-side port # in docker-compose.yml's `ports:` mapping. PORT=3001 # `production` enables prod-only behavior in server.js (production # stickers path under dist/, production-mode logger). Use # `development` locally to read stickers from public/ instead. NODE_ENV=development # Pino log level. Comment out for `info` (default). Useful values: # trace, debug, info, warn, error, fatal # Set to `debug` when diagnosing a problem; revert before deploy. # LOG_LEVEL=info # CORS — uncomment and set when the frontend is served from a # different origin than this API. Single origin or comma-separated. # CORS_ORIGIN=https://your-domain.com # Upload / export TTL and sweep interval, in milliseconds. # Defaults (24h TTL, 1h sweep) work for most cases. Lower the TTL # if disk space is tight; lower the interval if uploads churn fast # and you want stale files cleaned sooner. # FILE_TTL_MS=86400000 # 24 hours # CLEANUP_INTERVAL_MS=3600000 # 1 hour # ================================================================== # auth-manager (v0.8) -- runtime # ================================================================== # # auth-manager mounts customer magic-link + admin Google SSO + the # account shell. If DATABASE_URL is NOT set, auth-manager is disabled # gracefully and the rest of the host still boots -- useful when # working on goods-editor / template-manager features without # provisioning Postgres locally. # Postgres connection string. Required to enable auth-manager. # Examples: # Local Docker: postgres://postgres:postgres@localhost:5432/apparel # Dokploy: postgres://:@:5432/ # DATABASE_URL=postgres://postgres:postgres@localhost:5432/apparel # Fully-qualified base used to build: # - the magic-link URL we email to customers (and print to console) # - the Google OAuth redirect URI for admin SSO # Must match the redirect URI registered in Google Cloud Console. # In dev, set to http://localhost:3001. In prod, your storefront URL. # Falls back to http://localhost:${PORT} if unset (dev only -- won't # work behind a reverse proxy). # PUBLIC_BASE_URL=http://localhost:3001 # Comma-separated allowlist of Google accounts that can sign in via # admin SSO. Two or more entries strongly recommended for break-glass # recovery (locked decision #4 in v08_Auth_v1.md). # ADMIN_EMAILS=you@gmail.com,backup@gmail.com # Google OAuth 2.0 Web client credentials. Required for admin SSO. # Create in Google Cloud Console -> APIs & Services -> Credentials -> # Create credentials -> OAuth client ID -> Web application. Authorized # redirect URI must be ${PUBLIC_BASE_URL}/auth/admin/google/callback. # GOOGLE_CLIENT_ID=...apps.googleusercontent.com # GOOGLE_CLIENT_SECRET=... # Optional: SMTP relay for magic-link emails. If unset, magic links # ONLY print to the server's stdout (still fully functional for dev # -- just copy/paste the link from your terminal). # # Recommended dev setup: Mailpit (https://mailpit.axllent.org). # docker run -d -p 1025:1025 -p 8025:8025 axllent/mailpit # then visit http://localhost:8025 to read sent mail. # # Resend integration is deferred to v2 -- see v08_Auth_v1.md # "Email delivery in v1" section for rationale. # SMTP_HOST=localhost # SMTP_PORT=1025 # SMTP_USER= # SMTP_PASS= # SMTP_FROM=no-reply@localhost # Optional: cookie domain for cross-subdomain session sharing # (e.g. '.yourdomain.com' so checkout.yourdomain.com sees the same # session as yourdomain.com). Usually leave unset -- SureCart's WP # sidecar uses its own customer identity (passed via metadata at # checkout creation), not auth-manager's session. # COOKIE_DOMAIN= # ================================================================== # Personalization facet (Project doc §R10) -- runtime # ================================================================== # # Generic "customers filter the shop by a facet" feature. For THIS # deployment the facet is "Pet Type" (dog/cat/reptile/…); other shops # configure a different facet, or none. The facet VALUES are managed by # an admin inside template-manager; only the DEFINITION is set here. # # FACET_KEY is the master switch. Leave it UNSET to run a plain, # unfiltered shop: no sign-in prompt, no template filtering, the editor # shows all stickers, and the account/admin facet UIs stay hidden. # The facet's stable key. Setting it turns the feature ON. Becomes the # facet_key on customer_facet_values rows + the audienceTags scope. # FACET_KEY=pet_type # Display label shown in the UI ("Choose your {LABEL}", account heading, # admin section title). # FACET_LABEL=Pet Type # Selection mode: `multi` (default) lets a customer pick several values # (e.g. dog + cat); `single` restricts to one. # FACET_SELECTION_MODE=multi # Require an admin to categorize every template before saving it # (>=1 facet value, or marked General/universal). Default false. Turn # this ON whenever the shop filters strictly, so an untagged template # is never hidden silently. THIS shop: true. # REQUIRE_TEMPLATE_CATEGORY=true # Require a signed-in customer to choose at least one value before # browsing. Default false. THIS shop: false (the prompt nudges but is # skippable; choosing nothing shows everything). # REQUIRE_FACET_SELECTION=false # ================================================================== # Image upscale service (goods-editor, 2026-06-10) -- runtime # ================================================================== # # Optional REST service that upscales low-resolution customer photos # before they hit the print render (goods-editor's /api/export # pipeline). goods-editor reads these variables itself # (imageUpscalerFromEnv inside mountEditorApi) -- no server.js wiring # needed; setting UPSCALE_SERVICE_URL is the master switch. Unset = # feature fully dormant, renders byte-identical to before. # # The editor independently FLAGS low-res images to customers/admins # in the UI regardless of this setting -- the service only improves # what actually prints. Upscaling is best-effort: if the service is # down or slow, the render proceeds with the original pixels. # # Expected contract (adapt goods-editor's src/server/lib/ # imageUpscaler.js if your service differs -- it's the single point # of adaptation): # POST $UPSCALE_SERVICE_URL with JSON # { "image": "", "mime": "image/png", "scale": 2 } # -> 200 with an image/* binary body, OR JSON { "image": "" } # Master switch. e.g. a Real-ESRGAN container in the same Dokploy # project: http://upscaler:5000/upscale # UPSCALE_SERVICE_URL= # Optional credential. Sent as 'Authorization: Bearer ' unless # UPSCALE_SERVICE_AUTH_HEADER names a custom header (e.g. X-Api-Key), # in which case the bare key is sent in that header. # UPSCALE_SERVICE_API_KEY= # UPSCALE_SERVICE_AUTH_HEADER=Authorization # Per-request abort + the service's integer scale ceiling. # UPSCALE_SERVICE_TIMEOUT_MS=30000 # UPSCALE_SERVICE_MAX_SCALE=4 # ────────────────────────────────────────────────────────────────── # Build-time — values the `docker compose build` step uses # ────────────────────────────────────────────────────────────────── # Only needed when BUILDING the image, not when running it. The # image talks to git.kadil.dev to fetch the `goods-editor` git+ssh: # npm dependency during `npm install`. See the Dockerfile docblock # for the full mechanism. # # The Dockerfile expects a BASE64-ENCODED private key (not raw PEM) # because that's the format Dokploy's Build-time Secrets textarea # requires — it's dotenv-parsed and can't handle multi-line values. # We use base64 for local builds too so the Dockerfile has one input # format. Encode like this: # # base64 < ~/.ssh/goods-editor-deploy | tr -d '\n' > /tmp/key.b64 # # Recommended: generate a dedicated read-only deploy key rather than # reusing a personal key (see Dockerfile docblock for the ssh-keygen # + Gitea deploy-key registration steps). # # Path to a base64-encoded key file. Uncomment one for local builds: # SSH_KEY_FILE=/Users/khalid/.ssh/goods-editor-deploy.b64 # SSH_KEY_FILE=/home/deploy/.ssh/goods-editor-deploy.b64 # Alternative: env-var-based (CI). To use this mode, also flip # docker-compose.yml's `secrets.ssh_key` block from `file:` to # `environment:` (see comments there). Set the base64 contents via # the shell or your CI secret store: # # SSH_KEY_B64="$(base64 < ~/.ssh/goods-editor-deploy | tr -d '\n')" \ # docker compose build # # Don't put the actual base64 in this .env file. The string is long # enough that dotenv parsers may struggle, and committing real key # material — even base64 — to a repo is a leak waiting to happen. # # ── Dokploy ─────────────────────────────────────────────────────── # The entries above DON'T apply to Dokploy. In Dokploy, paste the # base64 directly into the application's Environment → Build-time # Secrets textarea as a single line: # # ssh_key= # # Dokploy parses that textarea as dotenv and passes the value into # the build as a BuildKit secret named `ssh_key`.