| Region & hostnames |
Values assume D1 = Option A. For Option B: BETTER_AUTH_URL and APP_DOMAIN = https://app.eu.agentset.ai; leave NEXT_PUBLIC_APP_HOSTNAME / NEXT_PUBLIC_API_HOSTNAME unset; use app.eu.agentset.ai in the GitHub/Google callbacks, the Stripe endpoint URL, the R2 CORS origin and the PostHog authorized URLs. |
NEXT_PUBLIC_DEPLOYMENT_REGION new | eu | ● | ● | – | us on the US stack. Drives model and provider allowlists, Stripe region metadata and startup guards. Must differ. |
NEXT_PUBLIC_APP_SHORT_DOMAIN | eu.agentset.ai | ● | – | – | Build-time. Also yields cname.eu.agentset.ai. The default silently falls back to agentset.ai, so set it. Must differ. |
NEXT_PUBLIC_APP_HOSTNAME new | eu.agentset.ai | ● | – | – | Option A only. Unset on US (falls back to app.<SHORT_DOMAIN>). |
NEXT_PUBLIC_API_HOSTNAME new | api.eu.agentset.ai | ● | – | – | Option A only. |
APP_DOMAIN required | https://eu.agentset.ai | ● | ● | – | Exists as optional in packages/emails; becomes required on EU (R1). Email links and List-Unsubscribe. Must be added to turbo.json globalEnv. |
NEXT_PUBLIC_VERCEL_ENV | Vercel system variable | ● | – | – | Turn on "Automatically expose System Environment Variables", or links fall back to localhost. |
NEXT_PUBLIC_APP_NAME | unset (defaults to Agentset) | ○ | – | – | Shared with US is fine. |
| Auth |
BETTER_AUTH_URL | https://eu.agentset.ai | ● | – | – | Exact app origin, no trailing slash (checked against the request origin). |
BETTER_AUTH_SECRET | openssl rand -base64 32 | ● | – | – | Must be unique per region. Never copy the US value. |
GITHUB_CLIENT_ID
GITHUB_CLIENT_SECRET | <from "Agentset EU" OAuth app> | ● | – | – | Must differ. |
GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET | <from new Web client> | ● | – | – | Must differ. Same Google Cloud project. |
| Database & Redis |
DATABASE_URL | <Supabase transaction pooler :6543>?pgbouncer=true&sslmode=require&uselibpqcompat=true | ● | ● | – | Must differ. TLS per L1; test before turning on Enforce SSL. Also needed at build time (Prisma generate). |
DIRECT_URL | <Supabase session pooler :5432>?sslmode=require&uselibpqcompat=true | – | – | – | Migrations only: .env.eu.local or a GitHub secret for the migrate workflow. Same TLS parameters; check with db:status:eu. |
DATABASE_CA_CERT new | <PEM from Database → Settings → SSL Configuration> | ○ | ○ | – | Only if L1 pins the Supabase CA (verify-full) instead of the URL parameters above. |
REDIS_URL | <Upstash REST URL> | ● | ● | – | Same database as the Modal TCP credentials. Must differ. |
REDIS_TOKEN | <Upstash REST token, not read-only> | ● | ● | – | |
REDIS_HOST
REDIS_PORT
REDIS_PASSWORD | <Upstash TCP endpoint> / 6379 / <password> | – | – | ● | Parsing service writes chunk batches here. |
| AI providers |
DEFAULT_AZURE_RESOURCE_NAME | <EU resource subdomain> | ● | ● | – | Must differ. The startup guard rejects the US resource. |
DEFAULT_AZURE_API_KEY | <KEY 1 of the EU resource> | ● | ● | – | Jobs embed chunks; web embeds queries and runs LLMs. |
DEFAULT_COHERE_BASE_URL new | https://<eu-resource>.services.ai.azure.com/providers/cohere | ● | ◐ | – | Unset on US (SDK default api.cohere.com). Required on EU: the R4 guard refuses to start without it, in web and in every job (jobs never rerank). |
DEFAULT_COHERE_API_KEY | <Azure resource KEY 1> | ● | ◐ | – | On EU this holds the Foundry key; a Cohere dashboard key is not needed. Jobs: required by the engine env import until J6. |
DEFAULT_ZEROENTROPY_API_KEY | <key from eu-dashboard.zeroentropy.dev> | ● | ◐ | – | US keys don't work on the EU API. Must differ. Jobs: required by the engine env import until J6. |
DEFAULT_ZEROENTROPY_BASE_URL new | https://eu-api.zeroentropy.dev/v1 | ● | ◐ | – | Passed explicitly so the SDK never falls back to the US API. Checked by the R4 guard in every job. |
DEFAULT_TURBOPUFFER_API_KEY | <EU organization key> | ● | ● | – | Must differ (separate org). |
DEFAULT_TURBOPUFFER_REGION new | aws-eu-central-1 | ● | ● | – | Defaults to aws-us-east-1 so US is unchanged. Never change after launch. |
DEFAULT_PINECONE_API_KEY
DEFAULT_PINECONE_HOST | unset | ○ | ○ | – | optional on EU after E2 (required by env validation until then); managed Pinecone is rejected on EU. |
SECONDARY_PINECONE_API_KEY
SECONDARY_PINECONE_HOST | unset | ○ | ○ | – | optional on EU same as above. |
| Parsing service link |
PARTITION_API_URL | <EU Modal web endpoint, base URL, no /ingest> | ◐ | ● | – | Jobs call it. Web: build-time env validation, and R4 checks it is the EU URL. Must differ. |
PARTITION_API_KEY | openssl rand -hex 32 | ◐ | ● | ● | Same value as AGENTSET_API_KEY in the Modal secret. Must differ from US. Web: build-time env validation. |
| Object storage (R2) |
S3_ENDPOINT | https://<ACCOUNT_ID>.eu.r2.cloudflarestorage.com | ● | ● | – | The guard asserts .eu.. The non-EU endpoint can't see EU buckets. |
S3_ACCESS_KEY
S3_SECRET_KEY | <R2 token scoped to the 3 EU buckets> | ● | ● | – | Must differ. |
S3_BUCKET | <EU uploads bucket> | ● | ● | – | Must equal R2_CHUNKS_BUCKET_NAME. |
IMAGES_S3_BUCKET | <EU images bucket> | ● | ● | – | Must equal R2_BUCKET_NAME. |
ASSETS_S3_BUCKET | <EU assets bucket> | ● | ● | – | |
ASSETS_S3_URL | https://eu-assets.agentset.ai | ● | ● | – | Also feeds the next/image allowlist once R1 derives it from this variable. |
| Jobs (Trigger.dev) |
TRIGGER_SECRET_KEY | <tr_prod_ key of the EU project> | ● | – | – | Must differ: a US key would run EU jobs in the US project. |
TRIGGER_PROJECT_ID | <proj_ ref of the EU project> | – | – | – | Deploy time only: packages/jobs/.env.eu.local (git-ignored) or a GitHub secret. |
TRIGGER_ACCESS_TOKEN | <tr_pat_ personal access token> | – | – | – | GitHub secret, only for CI deploys. |
TRIGGER_API_URL | unset (Tier 1) | – | – | ○ | Only for a self-hosted EU Trigger.dev (Tier 2). Defaults to https://api.trigger.dev. |
| Billing |
STRIPE_API_KEY | <EU restricted rk_live_ key> | ● | ● | – | Separate key so it can be revoked on its own. |
NEXT_PUBLIC_STRIPE_PUBLIC_KEY | <account pk_live_ key> | ● | ◐ | – | Shared with US is fine. Jobs: required by the Stripe env import until J6. |
STRIPE_WEBHOOK_SECRET | <whsec_ of the EU endpoint> | ● | – | – | Must differ. |
| Email |
RESEND_API_KEY | <sending-only key restricted to mail.eu.agentset.ai> | ● | ● | – | Jobs send the webhook-failure emails. |
EMAIL_FROM_DOMAIN new | mail.eu.agentset.ai | ● | ● | – | Defaults to agentset.ai on US. |
| Analytics, logs & alerts |
NEXT_PUBLIC_POSTHOG_KEY | <EU project phc_ key> or unset | ○ | – | – | Unset disables product analytics. |
NEXT_PUBLIC_POSTHOG_HOST new | https://eu.i.posthog.com | ● | – | – | Default is the US host. The UI host is derived from it. |
NEXT_PUBLIC_POSTHOG_ASSETS_HOST new | https://eu-assets.i.posthog.com | ● | – | – | Used by the /_proxy rewrites. |
TINYBIRD_API_URL | https://api.eu-central-1.aws.tinybird.co | ● | ● | – | Must differ. |
TINYBIRD_API_KEY | <resource-scoped token> | ● | ● | – | Append + read on the one datasource and pipe. |
DISCORD_HOOK_ALERTS
DISCORD_HOOK_CRON
DISCORD_HOOK_SUBSCRIBERS
DISCORD_HOOK_ERRORS | <EU channel webhook URLs> | ○ | – | – | Optional; must differ from US channels. |
| Vercel API (customer domains) |
VERCEL_PROJECT_ID | <EU project prj_ ID> | ● | – | – | Must differ: customer domains attach to the EU project. |
VERCEL_TEAM_ID | <same team_ ID> | ● | – | – | Shared with US is fine. |
VERCEL_API_TOKEN | <new team-scoped token> | ● | – | – | Separate token, stored as Sensitive. |
| Parsing service (Modal EU secret & deploy) |
AGENTSET_REGION new | eu | – | – | ● | Deploy time (shell/CI): selects the app name, secret name and region="eu" / routing_region="eu-west"; the environment comes from MODAL_ENVIRONMENT. Runtime too (kill-switch), so it must also be in the EU secret. Unset keeps US behaviour. |
MODAL_ENVIRONMENT | eu | – | – | – | Deploy time (shell/CI) only. Selects the Modal environment for deploy and secret lookup; never deploy EU into the default environment. |
AGENTSET_API_KEY | = PARTITION_API_KEY | – | – | ● | Checked on every request. |
DATALAB_API_KEY | <EU Datalab team key> | – | – | ● | Must differ (separate team). |
DATALAB_PROCESSING_LOCATION new | eu | – | – | ● | Sent on every Datalab request. Unset on US. |
R2_ENDPOINT_URL | https://<ACCOUNT_ID>.eu.r2.cloudflarestorage.com | – | – | ● | Same as S3_ENDPOINT. |
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY | <EU-scoped R2 token> | – | – | ● | |
R2_CHUNKS_BUCKET_NAME | = S3_BUCKET | – | – | ● | Mismatch shows up as "Document JSON not found". |
R2_BUCKET_NAME | = IMAGES_S3_BUCKET | – | – | ● | |
R2_PUBLIC_URL | per D7 (default https://eu-files.agentset.ai) | – | – | ● | Final before the first EU ingest: written into chunk text. |
FIRECRAWL_API_KEY | unset (crawl off) | – | – | ○ | optional on EU after the kill-switch change. |
YOUTUBE_API_KEY
PROXY_USERNAME
PROXY_PASSWORD | unset (YouTube off) | – | – | ○ | optional on EU after the kill-switch change. |
MODAL_TOKEN_ID
MODAL_TOKEN_SECRET | <workspace API token> | – | – | – | GitHub secrets, only for CI deploys. |
| Tooling (optional) |
NEXT_TELEMETRY_DISABLED | 1 | ○ | – | – | Keeps Next.js build telemetry off the processor list. |
CHECKPOINT_DISABLE | 1 | – | – | – | Prisma CLI telemetry, wherever EU migrations run. |
SKIP_ENV_VALIDATION | never set on EU | – | – | – | Validation is the first line of defence against a missing EU value. |