Skip to content

Administration

Audience: Administrators, Developers · Tags: admin, configuration, environment, operator

This page is the operator's guide to configuring a SnapLink backend instance. SnapLink is configured entirely through environment variables (typically a .env file, or your platform's secret/config store) — there is no admin-panel equivalent for most of these yet. Every variable below is real: its name and default match backend/.env.example exactly. Nothing here is aspirational.

For the customer-facing side of administration (workspaces, members, roles), see Missing permissions and Security.

Server

Variable Default Purpose
NODE_ENV — development or production. Affects logging verbosity and error detail.
PORT 4000 The port the API listens on.
CORS_ORIGINS — Comma-separated list of frontend origins allowed to call the API from a browser.
LOG_LEVEL info Log verbosity (e.g. info, debug).

Persistence

Variable Default Purpose
DB_DRIVER memory memory (non-durable, in-process) or postgres (durable).
DATABASE_URL — Required when DB_DRIVER=postgres. Standard Postgres connection string.
DATABASE_SSL true true for managed/Supabase Postgres; false for a local Docker Postgres instance without certificates.

Memory vs Postgres — read this before you deploy anything real. With DB_DRIVER=memory (the default), every account, workspace, flow, connection, and run lives in a process-local in-memory store. It is fast to boot and perfect for a demo, an evaluation instance, or local development — and it is wiped on every restart (deploy, crash, scale event, or plain Ctrl+C). Never point real users, real connectors, or anything you need to keep at a memory-mode instance. Set DB_DRIVER=postgres with a real DATABASE_URL to make the instance durable.

Auth

Variable Default Purpose
SUPABASE_URL — Recorded alongside the JWT secret; part of the auth boundary configuration.
SUPABASE_JWT_SECRET — The HS256 secret SnapLink's backend uses to sign and verify its own bearer tokens. Required — without it, the API fails closed and every request is rejected.

SnapLink's login/signup is self-issued, not delegated to Supabase: when a user signs up or logs in (POST /auth/signup, POST /auth/login), the backend itself issues an HS256 JWT (default lifetime 7 days) signed with SUPABASE_JWT_SECRET, and verifies that same signature on every subsequent request. The variable name is a holdover from an earlier Supabase-Auth integration; the secret today is purely SnapLink's own signing key. Generate a long, random value and treat it exactly like any other signing secret — rotating it invalidates every existing session token.

If this variable is missing or empty, requests fail with a 500 AUTH_NOT_CONFIGURED error rather than an open/anonymous fallback — SnapLink fails closed on purpose.

AI / Snap Studio

Variable Default Purpose
AI_PROVIDER_API_KEY — Enables Snap Studio's real-LLM drafting engine (engine: "llm").
AI_PROVIDER_MODEL claude-opus-4-8 Model id (first-party Anthropic) or, when using Azure AI Foundry, the Foundry deployment name.
AI_FOUNDRY_BASE_URL — Optional. Routes Studio's drafting agent through an Azure AI Foundry Anthropic-compatible endpoint instead of the first-party Anthropic API.

Snap Studio's drafting assistant (Describe → Review → Save & test) turns a plain-English prompt into a flow plan. Without AI_PROVIDER_API_KEY set, Studio does not error or degrade visibly — it automatically and silently falls back to a deterministic, keyword-based rules planner (engine: "rules"). This is a normal operating mode, not a broken state; every plan response reports which engine produced it via its engine field.

Azure AI Foundry option. Set AI_FOUNDRY_BASE_URL to your Foundry resource's Anthropic-compatible endpoint, e.g.:

AI_FOUNDRY_BASE_URL=https://<your-foundry-resource>.services.ai.azure.com/anthropic

When set, AI_PROVIDER_API_KEY holds your Foundry key and AI_PROVIDER_MODEL holds the Foundry deployment name (e.g. claude-sonnet-4-6) rather than a first-party Anthropic model id. Leave AI_FOUNDRY_BASE_URL unset to use the first-party Anthropic API directly.

What's sent to the model. Only the user's own prompt text plus a static description of SnapLink's connector catalog. AI provider keys are never logged, and no workspace data, run data, or connector credentials are ever included in a drafting request.

Security, SSRF & rate limits

Variable Default Purpose
REST_ALLOW_PRIVATE_NETWORK false SSRF guard for the REST connector. Must stay false in production. Only for local dev/testing against loopback targets.
RATE_LIMIT_WINDOW_SECONDS 60 Fixed-window size for webhook ingress rate limiting.
RATE_LIMIT_PER_ENDPOINT 120 Max webhook requests per flow's endpoint, per window.
RATE_LIMIT_PER_IP 240 Max webhook requests per source IP, per window.
RATE_LIMIT_PER_WORKSPACE 600 Max webhook requests per workspace, per window.

Rate limiting applies to webhook ingress, not general authenticated API traffic. It is a fixed-window limiter (no external dependency required) — see Rate limiting for the customer-facing behavior and how to raise these limits.

Secrets & infra

Variable Default Purpose
CREDENTIAL_ENCRYPTION_KEY — 32-byte, base64-encoded key used to encrypt connector credentials at rest.
SECRET_PROVIDER memory memory (dev, not encrypted) | encrypted (AES-256-GCM at rest, requires CREDENTIAL_ENCRYPTION_KEY) | azure (documented seam).
INFRA_BACKEND memory Cache / rate-limit backing store: memory or redis.
REDIS_URL — Required when INFRA_BACKEND=redis.

Generate CREDENTIAL_ENCRYPTION_KEY once per environment and store it outside source control (secret manager, platform env config). Losing it means existing encrypted credentials can no longer be decrypted.

Async / distributed execution seam

Variable Default Purpose
ASYNC_MODE custom off | events | commands | full | custom — the unified knob for the async/queue seam.
ASYNC_ADAPTER inprocess inprocess or rabbitmq.
RABBITMQ_URL — Required when ASYNC_ADAPTER=rabbitmq; otherwise unused.

This seam is disabled by default and optional — core flow execution works fully without it. It exists for distributed-execution deployments that want to move triggering/dispatch onto an event bus or command queue instead of handling everything in-process. (Two lower-level flags, ASYNC_EVENT_BUS_ENABLED and ASYNC_COMMAND_QUEUE_ENABLED, also exist and are what ASYNC_MODE derives under the hood — operators should set ASYNC_MODE and leave those alone unless you have a specific reason not to.)

SMTP (Email connector)

The Email connector sends through SMTP configured by the operator, not by individual flow authors: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM. See Email connector for provider setup (Gmail, Brevo, Mailtrap) and troubleshooting. If these aren't set, Email actions fail at runtime with error code auth.

CORS configuration

CORS_ORIGINS is a comma-separated allowlist of the exact frontend origins (scheme + host + port) permitted to call the API from a browser, e.g.:

CORS_ORIGINS=https://app.example.com,http://localhost:5173

If a frontend origin isn't listed here, browser requests from it are blocked by CORS before they reach your handlers — this is the single most common cause of "my frontend can't reach the API" during setup. Update this list whenever you add a new frontend deployment (staging, a new custom domain, a local dev port).

Secrets hygiene checklist

  • Never commit a real .env file. Commit only .env.example with empty/placeholder values.
  • Bearer tokens and connector credentials are never logged and never echoed back in API responses.
  • SMTP passwords and AI provider keys belong in operator environment config only — never paste them into a flow's fields.
  • Treat SUPABASE_JWT_SECRET and CREDENTIAL_ENCRYPTION_KEY as high-value secrets: generate them randomly, store them in your platform's secret manager, and restrict who can read them.
  • Rotating SUPABASE_JWT_SECRET invalidates every existing session (all users are signed out). Plan for that.

What's NOT configurable yet

Being direct about the current edges, so you can plan around them:

  • No per-workspace SMTP. SMTP is a single, instance-wide operator setting — every workspace on this instance shares it. Per-workspace mail configuration is Planned.
  • No SSO/SAML. Login is SnapLink's own email/password auth only.
  • No roles beyond owner / admin / member. There is no custom-role editor.
  • No billing tiers or usage quotas enforced at the environment-variable level.
  • No uptime SLA or compliance certification (e.g. SOC 2) — see Security for the honest current posture.
  • No published OpenAPI spec file yet — see API Reference for the hand-written endpoint list.

Example .env

A realistic starting point for a Postgres-backed, encrypted-secrets instance with first-party Anthropic AI drafting enabled. Replace every value marked <...> — do not reuse these as real secrets.

NODE_ENV=production
PORT=4000
CORS_ORIGINS=https://app.example.com

LOG_LEVEL=info

DB_DRIVER=postgres
DATABASE_URL=postgres://snaplink:<db-password>@db.internal:5432/snaplink
DATABASE_SSL=true

SUPABASE_URL=https://<project>.supabase.co
SUPABASE_JWT_SECRET=<long-random-signing-secret>

CREDENTIAL_ENCRYPTION_KEY=<32-byte-base64-key>
SECRET_PROVIDER=encrypted

AI_PROVIDER_API_KEY=<anthropic-api-key>
AI_PROVIDER_MODEL=claude-opus-4-8
# AI_FOUNDRY_BASE_URL=https://<your-foundry-resource>.services.ai.azure.com/anthropic

REST_ALLOW_PRIVATE_NETWORK=false
RATE_LIMIT_WINDOW_SECONDS=60
RATE_LIMIT_PER_ENDPOINT=120
RATE_LIMIT_PER_IP=240
RATE_LIMIT_PER_WORKSPACE=600

INFRA_BACKEND=memory
REDIS_URL=

ASYNC_MODE=custom
ASYNC_ADAPTER=inprocess
RABBITMQ_URL=

SMTP_HOST=smtp-relay.brevo.com
SMTP_PORT=587
SMTP_USER=<smtp-user>
SMTP_PASS=<smtp-password>
SMTP_FROM=notifications@example.com

Related: Security · Email connector · API Reference · Missing permissions

Was this helpful? 👍 👎