Files
greeneclipseandClaude Fable 5 d03c2a5c57 Auto-derive session cookie domain; AI prompt page = formula builder hub
Session (no env needed):
- SESSION_COOKIE_DOMAIN env removed. Config.SessionCookieParentDomain()
  derives the cookie Domain from WEB_ORIGIN + PUBLIC_API_URL, which the
  API already requires: sibling hosts of one parent (descrybe.io +
  api.descrybe.io) share the parent domain so SvelteKit SSR (/admin
  gate, user switching) receives the session cookie; localhost, IPs,
  same-host, and unrelated hosts stay host-only. Deploying the new build
  is the whole fix — nothing to configure.

AI generation prompt page:
- Each section now embeds its formula editor next to the per-language
  prompt instructions: Title = full title formula builder (preview,
  elements, separator, variable selector, custom variables), Description
  = description formula sections editor (type + instructions + export
  id, drag reorder), Meta = meta title / meta description formula
  fields. One Save writes categories.prompt + title_template +
  description_template together; Assign copies all three to the
  selected categories.
- New $lib/categories/formula-variables.ts loads every usable field for
  the builder: custom variables (/api/variables), company attributes
  (/api/attributes — attribute_key, name, unit, example), and standard
  fields (/api/standard-fields). Used by both the prompt page and the
  title-formula page (which previously ignored attributes).

Verified locally: svelte-check clean for changed files, unit tests pass,
and the full save contract exercised over HTTP as the page does it
(login → load variables/attributes/standard-fields → PATCH prompt +
title-formula + description-formula → round-trip read), then the test
category restored via repair-category-prompts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 01:12:23 +02:00

164 lines
8.3 KiB
Bash

# Descrybe v2 - REQUIRED bootstrap env only.
# Copy to repo-root `.env` (single source of truth). Do not create apps/api/.env.
# Product secrets (OpenAI, Stripe, mail, Pinecone, store connectors)
# belong in the admin dashboard -- do NOT put them here:
# platform: /admin/settings (stripe.* , feeds.private_url_allowlist)
# tenant: /integrations/ai , /integrations/email , /stores
# EPREL enrichment is on by default for all plans (public EU API). No activation
# step. Optional kill-switch only: EPREL_ENABLED=false here, or eprel.enabled=false
# under /admin/settings.
#
# How apps load env:
# - Go API/worker/mailhooks: config.Load() loads this monorepo-root `.env`
# (never overrides already-set process env). Override path: DOTENV_PATH=...
# - npm run dev:api / scripts/run-in-dir.mjs / run-api.ps1 / migrate.*: same root `.env`
# - SvelteKit: Vite envDir + kit.env.dir = monorepo root; PUBLIC_API_URL (+ optional PUBLIC_CSRF_COOKIE_NAME)
#
# Generate secrets locally (never commit real values):
# openssl rand -hex 32
# Postgres (required). Default matches docker-compose local DB on :5433.
DATABASE_URL=postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable
# development | staging | production
# Production fails closed: SESSION_SECURE=true, https WEB_ORIGIN, APP_ENCRYPTION_KEY,
# TOKEN_SIGNING_SECRET (see config.Load validate).
APP_ENV=development
# API listen address (npm run dev:api forces :28471 so a stale local .env cannot bind :8080)
HTTP_ADDR=:28471
# Optional Prometheus scrape (no secrets). Docs: docs/production-readiness.md#ops-prometheus-scrape
# Examples: deploy/prometheus/scrape.example.yml + alerts.example.yml
# METRICS_PUBLIC=1 # prod only: expose /metrics beyond loopback (private VIP/mesh only)
# METRICS_ADDR=127.0.0.1:9091 # worker-only listener for sync_* series
# Background worker — included in `npm run dev` (api+web+worker).
# Also: npm run dev:worker | make worker | .\scripts\run-api.ps1 worker
# Required for processing jobs, Woo/Shopify claim, support AI auto, billing, and GET /readyz.
# Browser origin for CORS / cookies (must be https + non-loopback in production).
# Dev web is Vite :28472 (strictPort). npm run dev forces matching WEB_ORIGIN / PUBLIC_API_URL.
# Local: API also accepts the localhost/127.0.0.1 twin for credentialed CORS (same port).
WEB_ORIGIN=http://localhost:28472
# Public API origin (web PUBLIC_API_URL should match; empty web = same-origin + Vite proxy)
PUBLIC_API_URL=http://localhost:28471
# Session cookie Secure flag (required true when APP_ENV=production|prod).
# Keep false on http://localhost — Secure cookies are dropped by the browser on HTTP.
SESSION_SECURE=false
# Browser smoke (codehelper site=local-descrybe). Never commit a real password.
# Set to the local demo password from docs/demo-user.md (a1-primary / demo),
# OR store it out-of-repo: printf '%s' "$DESCRYBE_SMOKE_PASS" | codehelper connections set-secret --name local-descrybe
# (then password_ref=secret). Unset/empty -> headless Sign-in submits no password -> /api/auth/me 401.
# DESCRYBE_SMOKE_PASS=
# Session / invite token HMAC. Required in production. openssl rand -hex 32
TOKEN_SIGNING_SECRET=
# AES key for credentials at rest (Woo/email/AI BYOK ciphertext).
# Required in production. Prefer this over legacy CREDENTIALS_ENCRYPTION_KEY.
# openssl rand -hex 32
APP_ENCRYPTION_KEY=
# --- Production host (uncomment / set on the server; setup --production helps) ---
# APP_ENV=production
# SESSION_SECURE=true
# WEB_ORIGIN=https://descrybe.io
# PUBLIC_API_URL=https://api.descrybe.io
# HTTP_ADDR=127.0.0.1:28471
# TRUSTED_PROXIES=127.0.0.1
# DATABASE_URL=postgres://descrybe:CHANGE_ME@127.0.0.1:5432/descrybe?sslmode=disable
# TOKEN_SIGNING_SECRET and APP_ENCRYPTION_KEY: leave empty and run
# npm run setup
# to generate (or: openssl rand -hex 32).
# Platform invite / forgot-password mail (optional env; prefer /admin/settings).
# EMAIL_DRY_RUN defaults true when unset (safe). Live send needs false + SMTP.
# EMAIL_DRY_RUN=true
# SMTP_ENABLED=false
# SMTP_HOST=
# SMTP_PORT=587
# SMTP_USER=
# SMTP_PASSWORD=
# SMTP_FROM=noreply@descrybe.io
# Cutover / ops toggles (optional)
# HYPERCARE_MODE=false
# STRIPE_MOCK=false
# MAINTENANCE_MODE=false
# READ_ONLY_MODE=false
# --- Optional bootstrap (safe defaults in code; uncomment to override) ---
# TRUSTED_PROXIES - hop-1 reverse-proxy / LB peers only (CIDR or IP, comma-separated).
# When set, TrustedRealIP rewrites RemoteAddr from X-Forwarded-For / X-Real-IP only if
# the TCP peer is on this allowlist. Empty (default) ignores client IP headers (safe
# without a proxy; required behind CDN/LB so auth IP RPM + login lockout key correctly).
# TRUSTED_PROXIES=127.0.0.1,10.0.0.0/8
# RATE_LIMIT_REPLICAS=1
# RATE_LIMIT_MULTI_REPLICA=false
# RATE_LIMIT_BACKEND=memory
# ASSUMPTION (Product 10): in-process rate limits + email login lockout are OK on a single
# API instance. Multi-replica cutover = edge/WAF hard global RPM (see deploy/examples/edge-rate-limit.md).
# Optional RATE_LIMIT_REPLICAS divides HTTP middleware caps only (not lockout/StartLimiter/AI/email)
# — not a shared store. RATE_LIMIT_BACKEND=redis|postgres is docs-only and forced to memory.
# SESSION_COOKIE_NAME=descrybe_session
# Session cookie Domain is derived automatically from WEB_ORIGIN + PUBLIC_API_URL:
# sibling hosts of one parent (descrybe.io + api.descrybe.io) share the parent
# domain so SvelteKit SSR receives the session cookie; localhost stays host-only.
# CSRF_COOKIE_NAME=descrybe_csrf
# PUBLIC_CSRF_COOKIE_NAME=descrybe_csrf
# SESSION_IDLE_HOURS=24
# UPLOAD_DIR=data/uploads
# MAINTENANCE_MODE=false
# READ_ONLY_MODE=false
# CREDENTIALS_ENCRYPTION_KEY= # legacy alias for APP_ENCRYPTION_KEY
# --- Locales (not process-env; listed for agents/ops) ---
# UI dashboard locales (browser localStorage key descrybe-ui-locale; default en):
# en, es, fr, de, it, pt, nl, pl, ja
# Source: apps/web/src/lib/i18n/locales.ts (+ message catalogs under lib/i18n/messages)
# Content / AI language (companies.language via /settings Content Language; default en):
# en, fr, de, es, it, nl, pt, pl, cs, sk, hu, ro, bg, hr, sl, sv, da, fi, el, et, lv, lt, mt, ga, ja
# Sources: apps/web/src/lib/content-languages.ts , apps/api/internal/company/language.go
# Optional EPREL fiche PDF language (prefer /admin/settings eprel.fiche_language):
# EPREL_FICHE_LANGUAGE=EN
# --- Prefer dashboard (optional env fallback still accepted by resolvers) ---
# Platform /admin/settings (GET/PUT /api/admin/settings -> values.*):
# stripe.* , feeds.private_url_allowlist
# Optional EPREL overrides (not required - enrichment defaults on):
# eprel.enabled=false (or EPREL_ENABLED=false in this file) to disable
# eprel.base_url / eprel.timeout / eprel.fiche_language if tuning
# OPENAI_*/RESEND_*/SMTP_* may still work as process-env fallbacks; prefer UI.
# PROCESSING_*, DB_* pool knobs: code defaults; set in process env only if tuning.
# MIGRATE_MYSQL_DSN: migrator CLI only - not API boot.
# --- LLM test provider (optional; no production secrets) ---
# Tiny OpenAI-compatible stub for CI/local processing proofs. See docs/mock-llm.md.
# Start (separate terminal):
# cd apps/api && go run ./cmd/mock-llm -addr 127.0.0.1:18767
# Stub-only knobs (read by mock-llm process, not by Descrybe API):
# MOCK_LLM_API_KEY=local-test
# MOCK_LLM_MODEL=mock-llm
# Platform Completer fallback (root .env - restart api + worker after change):
# OPENAI_API_KEY=local-test
# OPENAI_BASE_URL=http://127.0.0.1:18767/v1
# OPENAI_MODEL=mock-llm
# PROCESSING_RPM=60
# PROCESSING_MAX_RETRIES=3
# Prefer tenant /integrations/ai (custom base + key) over process env when possible.
# Do NOT put real OpenAI/Green Chat secrets here - use dashboard or a private root .env.
# Verify translation + processing: docs/mock-llm.md ("Translation + processing verification").
# --- Web analytics (optional; SvelteKit PUBLIC_* from this root .env) ---
# Google Tag Manager container ID. When unset/empty, no GTM/GA tags load.
# Consent Mode v2 defaults to denied until the cookie banner grants categories.
# Setup: create GA4 property → GTM web container → GA4 Configuration tag with
# Consent Settings (require analytics_storage; ad_* for ads) → publish.
# SPA page views: Custom Event trigger `page_view` from the app. Do NOT also
# enable GTM History Change / GA4 enhanced measurement page_views (double count).
# PUBLIC_GTM_ID=GTM-XXXXXXX