Configuration
All runtime configuration comes from environment variables. Docker Compose reads them from a local .env file.
.env is git-ignored and must stay that way — it holds JWT_SECRET, CONFIG_ENCRYPTION_KEY, SMTP and payment credentials. API responses never echo secrets back (a stored secret is reported as a has_secret boolean).
Required
| Var | Notes |
|---|---|
DATABASE_URL | Postgres DSN. Boot fails without it. |
JWT_SECRET | HMAC signing key for all session tokens and capability URLs. 32+ characters in production. |
CONFIG_ENCRYPTION_KEY | Seals tenant credentials at rest. Required in production and must differ from JWT_SECRET. |
SUPERADMIN_EMAIL / SUPERADMIN_PASSWORD | The platform superadmin created by the seed. Production requires a strong password (12+ characters, no defaults). |
Core
| Var | Default | Notes |
|---|---|---|
HTTP_PORT | 8080 | API listen port. |
ENV | development | production switches logs to JSON and turns on the production guards. |
LOG_LEVEL | info | debug / info / warn / error. |
JWT_TTL | 24h | Staff and vendor token lifetime. |
STOREFRONT_SESSION_TTL | 720h | Buyer session lifetime. Active buyers slide it forward via POST /storefront/auth/refresh, so only 30 days of inactivity signs them out. |
PLATFORM_BASE_DOMAIN | sokisoko.local | The platform's own domain; tenant storefronts live on their own domains or under this one. |
ADMIN_APP_URL | http://localhost:5173 | Where invite and reset emails send staff. |
VENDOR_PORTAL_URL | http://localhost:5174 | Where vendor invites send applicants. |
SIGNUP_VERIFY_URL | http://localhost:5173/verify-signup | Self-serve organization signup verification link. |
SEED_DEMO | off | Creates demo logins and data. Must be off in production. |
Database pool
| Var | Default |
|---|---|
DB_MAX_CONNS | 20 |
DB_MAX_CONN_IDLE_TIME | 5m |
PDF & email
| Var | Default | Notes |
|---|---|---|
GOTENBERG_URL | (empty) | Empty → stub PDF renderer. |
MAILGUN_API_KEY / MAILGUN_DOMAIN | — | Both set → Mailgun HTTPS API transport (preferred). |
MAILGUN_BASE_URL | https://api.mailgun.net | Use https://api.eu.mailgun.net for EU domains. |
SMTP_HOST / SMTP_PORT / SMTP_USERNAME / SMTP_PASSWORD | — / 587 / — / — | SMTP fallback, used only when Mailgun is unset. Neither set → log transport (emails printed, not sent). |
EMAIL_FROM | Sokisoko <no-reply@sokisoko.local> | Platform sender identity. |
PLATFORM_LEADS_EMAIL | (empty) | Where enquiries from the platform site are mailed. |
Transport order: Mailgun API → SMTP → log. These are the platform defaults; each tenant can set its own sender identity and bring its own mailbox or Mailgun account from Admin → Settings → Configuration, in which case the worker sends through the tenant's account.
Storage, payments, procurement
| Var | Default | Notes |
|---|---|---|
MEDIA_ROOT | /data/media | Blob directory for uploads; a shared volume in multi-node deploys. |
PAYMENTS_GATEWAY | mock | mock, or a real provider; STRIPE_SECRET_KEY for Stripe. |
PAYOUT_PROVIDER | manual | How marketplace payouts are disbursed. |
PUNCHOUT_STOREFRONT_URL | / | Landing URL after a punchout session starts. |
PUNCHOUT_TTL | 1h | Punchout session lifetime. |
EDI_SENDER_ID | SOKISOKO | Our identity on outbound X12 documents. |
ERP providers (platform app credentials)
Only the OAuth-based providers need anything here; a provider without credentials is shown in the admin catalog but cannot be connected.
| Var | Provider |
|---|---|
QBO_CLIENT_ID / QBO_CLIENT_SECRET / QBO_REDIRECT_URI / QBO_ENVIRONMENT (sandbox) / QBO_WEBHOOK_VERIFIER_TOKEN | QuickBooks Online |
XERO_CLIENT_ID / XERO_CLIENT_SECRET / XERO_REDIRECT_URI / XERO_WEBHOOK_KEY | Xero |
BC_CLIENT_ID / BC_CLIENT_SECRET | Microsoft Dynamics 365 Business Central |
AI
| Var | Default | Notes |
|---|---|---|
AI_PROVIDER | deterministic | Narrator/designer provider; deterministic needs no key and is always the fallback. |
ANTHROPIC_API_KEY / AI_MODEL | — / claude-opus-4-8 | Anthropic provider. |
AI_CHAT_BASE_URL / AI_CHAT_API_KEY / AI_CHAT_MODEL | Groq OpenAI-compatible endpoint / — / openai/gpt-oss-120b | The assistant's chat model. |
AI_FALLBACK_BASE_URL / AI_FALLBACK_API_KEY / AI_FALLBACK_MODEL | OpenRouter / — / — | Used when the chat provider fails. |
AI_WRITER_MODEL / AI_DESIGNER_MODEL | (empty) | Override models for copywriting and page design. |
ASSISTANT_INTERNAL_URL | http://api:8080 | Loopback the assistant uses to execute actions through the real routes. |
Agent API, realtime, push
| Var | Notes |
|---|---|
API_PUBLIC_URL | The public URL clients dial for the API (e.g. https://api.example.com). Unset → the OAuth 2.1 server and MCP endpoint are not mounted. Must equal the issuer clients see. |
SITE_PUBLIC_URL | The platform site URL advertised in agent metadata. |
MCP_RESOURCE_ORIGINS | Extra origins allowed to present as the MCP resource. |
PUSHER_APP_ID / PUSHER_KEY / PUSHER_SECRET / PUSHER_CLUSTER | Live updates in the apps (optional; polling otherwise). |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT | Web push for buyer message alerts. |
Observability
Metrics are opt-in: set OTEL_EXPORTER_OTLP_ENDPOINT and the API exports via OTLP (database-pool gauges and HTTP metrics). Unset → no exporter, zero overhead.
Related topics
Search engines: Google Search Console
Google verifies a storefront domain by fetching /google<token>.html. Put the 16-character token from Search Console's "HTML file" method into the tenant's seo.google_site_verification setting (org scope); the storefront then serves https://<store>/google<token>.html with the body Google expects. Tenants without the setting keep answering 404 there. Bing uses the same pattern through seo.bing_verification and /BingSiteAuth.xml.
Search engines: IndexNow
Every tenant storefront tells Bing, Yandex and the engines built on them which URLs changed, through IndexNow. Nothing to configure: a key is generated into the seo.indexnow_key setting the first time a store submits, and the storefront serves it at /{key}.txt as the ownership proof.
What submits, and when:
- Product changes from every writer — an ERP sync (push or poll), AI description drafting, the spec backfill, and product edits in the console. Writers send product ids; the
indexnow_submitjob resolves them to/p/{slug}and posts once per batch. - Category create, rename (old and new slug) and delete →
/c/{slug}. - Page publish and withdraw →
/{slug}. - Nightly sweep (
indexnow_sweep, 03:00 UTC) — every product, category, page and facet landing that changed since the last sweep, straight from the same listings the sitemap serves. This is the net under the per-write pings.
Submissions are skipped for localhost and unverified hosts, and never fail the write that triggered them. The landing site (a static build) is submitted by the landing deploy role after each publish, using landing_indexnow_key and landing_indexnow_host from the deploy variables; the key file lives in web/landing/public/.