# Fiesta configuration — the complete list of settings, with local values. # # How the files are picked (config/config.go): # # APP_ENV unset / local → .env.local then .env # APP_ENV=production → .env.production then .env # # A real environment variable always wins over a file, and no file has to # exist: on the deployed host the values come from the platform's environment # settings (Dokploy / Kubernetes), not from a file. Anything added here must be # added there too — a variable in this file and not in the platform is a # variable that is unset in production. # # Startup checks every required setting and prints everything that is missing # in one go, before any connection is attempted. # # The credentials in the committed .env.production have to be treated as # public: rotate them, and keep new values out of git. # ── Environment ───────────────────────────────────────────────────────────── # local | production. Production requires POS_TOKEN_SECRET and never falls # back to localhost defaults. Set in the real environment, not in a file: a # file cannot decide which file gets loaded. #APP_ENV=local # ── Where it listens ──────────────────────────────────────────────────────── # 1122 locally; production serves on 1009 (matching the Dockerfile's EXPOSE). APP_PORT=1122 # ── The main database (nearledb) ──────────────────────────────────────────── # # ⚠️ POINTING THIS AT PRODUCTION MAKES LOCAL TESTING WRITE TO PRODUCTION. # # There is no "local mode" that protects you: `go run .` against the live host # creates real tenants, real logins and real stock movements, and main.go runs # schema migrations on boot. Startup warns when APP_ENV=local and DB_HOST is # not a local address, but it does not stop you. # # These match docker-compose.local.yml, so `docker compose -f # docker-compose.local.yml up -d` and `go run .` work together with no edits. DB_HOST=localhost DB_PORT=5433 DB_NAME=nearledb DB_USER=nearle DB_PASSWORD=localdev # ── The catalogue database (pgvector) ─────────────────────────────────────── # # A separate connection on purpose, so catalogue work never touches nearledb. # Leave CATALOGUE_DB_HOST blank to start without it: catalogue endpoints then # fail at query time rather than at boot. With a host set, the other four are # required. 5434, not 5432: a developer machine usually has something on 5432. CATALOGUE_DB_HOST=localhost CATALOGUE_DB_PORT=5434 CATALOGUE_DB_NAME=cataloguedb CATALOGUE_DB_USER=nearle CATALOGUE_DB_PASSWORD=localdev # ── Redis — POS terminal presence, under a TTL ────────────────────────────── # # Optional: leave REDIS_HOST blank to run without it. Losing the health board # is an inconvenience; losing a sale is not, so the API runs without it. REDIS_HOST=localhost REDIS_PORT=6379 REDIS_USER=default REDIS_PASSWORD= REDIS_DB=0 # ── DigitalOcean Spaces (S3-compatible) — catalogue product images ────────── # # Optional locally. With USE_S3=true every S3_* value below is required. USE_S3=false S3_ACCESS_KEY= S3_SECRET_KEY= S3_ENDPOINT= S3_BUCKET= S3_REGION= # ── POS terminals — the MQTT broker the in-store tills publish to ─────────── # # Optional locally: with MQTT_URL blank the ingest and the console live stream # stay quiet and the HTTP endpoints still work. In production a blank MQTT_URL # means every till queues its bills silently — on startup you should see # three lines reading "pos: subscribed to nearle/pos/+/+/...". MQTT_URL= MQTT_USER= MQTT_PASSWORD= # Unique per replica: a second connection with the same id evicts the first. # Defaults to HOSTNAME (the pod name) when unset. MQTT_CLIENT_ID= # always | never — otherwise a StatefulSet pod ending in "-0" is elected and # anything else consumes. See messaging/posmqtt.go. #POS_MQTT_CONSUMER= # ── Auth ──────────────────────────────────────────────────────────────────── # Signs POS terminal sessions; at least 16 characters. Falls back to # JWT_SECRET_KEY when unset. Required in production. POS_TOKEN_SECRET=local-dev-signing-secret-not-real JWT_SECRET_KEY= USER_CONTEXT_KEY=nearle # true to make the POS routes require a terminal session. #POS_AUTH_REQUIRED=false # ── Scan-to-order — the embedding model behind product recognition ───────── # # MUST be the model that filled the catalogue's `embedding` column: vectors # from two models are not comparable and pgvector will rank garbage without # complaint. The first search reads the column's width and refuses a mismatch # with an error that names both numbers. # Optional: with no provider the search matches on words alone (works, ranks # worse). openai = any OpenAI-compatible /embeddings endpoint (set # EMBEDDING_BASE_URL for Azure, Ollama, vLLM...); gemini = Google AI Studio. EMBEDDING_PROVIDER= EMBEDDING_MODEL= EMBEDDING_API_KEY= EMBEDDING_BASE_URL= # 0 = the model's default width. EMBEDDING_DIMENSIONS=0 # ── Geocoding ─────────────────────────────────────────────────────────────── # Google Geocoding when set; OpenStreetMap's Nominatim otherwise. GEOCODER_API_KEY= # ── Nearle Buddy ──────────────────────────────────────────────────────────── # # ONE variable. The provider, endpoint and model are defaults in config.go # (openai / api.groq.com / openai/gpt-oss-120b) because each has one right # answer for this product — and three variables that must be typed correctly # into a hosting platform are three ways for the assistant to sit silently off, # which is how it spent its first week. # # The key is the only one that differs per deployment and the only one that # cannot live in this repository. Locally it goes in `.env.secrets`, which git # ignores; in production it is set on the platform. ASSISTANT_API_KEY= # Overrides, none of them needed for the shipped setup. # Ollama on a laptop: ASSISTANT_BASE_URL=http://localhost:11434/v1 and # ASSISTANT_MODEL=llama3 — a local endpoint needs no key. ASSISTANT_PROVIDER= ASSISTANT_BASE_URL= ASSISTANT_MODEL= # Per-tier overrides. ASSISTANT_MODEL alone sets all three. ASSISTANT_MODEL_FAST= ASSISTANT_MODEL_BALANCED= ASSISTANT_MODEL_DEEP=