SurfSense/backend/server/.env.example
Anish Sarkar 5697ae9793 feat(license): make the free trial 14 days
LICENSE_TRIAL_DAYS now defaults to 14, and the site, READMEs, docs and plans say 14 days. The backend version moves to 0.1.1.
2026-10-27 01:55:20 +05:30

144 lines
7.2 KiB
Text

# Backend settings. Deployed, set as environment variables with secrets from
# the deployment's secret store. Locally, copy to server/.env and run from backend/:
# PYTHONPATH=plugins uv run --env-file server/.env python -m server
# The port the backend serves on, which the proxy in front targets.
PORT=8000
# Plugin ids, comma-separated. Unset serves every plugin in the image. The app lists
# eight: Reddit and Instagram are left out until they read anonymously again.
PLUGINS=google-search,web-crawl,youtube,tiktok,google-maps,amazon,walmart,indeed
# Required when more than one copy runs, so a Stop reaches the copy with the call.
# Google Search also shares its solved IPs there, so a copy adopts one instead of
# paying for a captcha solve.
REDIS_URL=redis://localhost:6379/0
# off (or 0, false, no) holds no plugin's lifespan: nothing is warmed at startup,
# such as a browser, and no shared pool is closed at shutdown. Unset, on.
# PLUGIN_LIFESPANS=off
# The license check's Keygen, under surfsense_backend's names and with its values.
# The accounts endpoint; unset, Keygen Cloud's. Production's self-hosted Keygen CE is
# reached inside the deployment:
# KEYGEN_API_URL=http://keygen-web:3000/v1/accounts
# SurfSense's Keygen account, surfsense_backend's KEYGEN_ACCOUNT_ID. Not a secret, but the
# backend does not start without it.
# KEYGEN_ACCOUNT_ID=
# A self-hosted Keygen's own hostname, sent as Host with X-Forwarded-Proto: https.
# Unset for Keygen Cloud.
# KEYGEN_HOST=keygen.internal
# The license portal at /api/v1: download, resend, trial and Stripe's webhook, under
# surfsense_backend's names and meanings. Served only when KEYGEN_API_TOKEN is set.
# Secret. Keygen's admin token, which creates, checks out and suspends licenses.
# KEYGEN_API_TOKEN=
# KEYGEN_POLICY_TRIAL=
# KEYGEN_POLICY_INDIVIDUAL=
# KEYGEN_POLICY_TEAM=
# TRUE lets anyone with a permanent address claim one trial.
# LICENSE_TRIAL_ENABLED=FALSE
# LICENSE_TRIAL_DAYS=14
# An ISO date: a trial issued before it runs LICENSE_TRIAL_DAYS from that date.
# LICENSE_TRIAL_EXPIRY_FLOOR=
# More domains to refuse a trial to, comma-separated, beside the built-in list.
# LICENSE_DISPOSABLE_EMAIL_DOMAINS=
# Per hour, on Redis when REDIS_URL is set; 0 turns a limit off.
# LICENSE_RATE_LIMIT_IP_PER_HOUR=10
# LICENSE_RESEND_RATE_LIMIT_PER_HOUR=5
# LICENSE_TRIAL_RATE_LIMIT_PER_HOUR=3
# Secret. Stripe's key, and the signing secret of the webhook at /api/v1/stripe/webhook.
# STRIPE_SECRET_KEY=
# STRIPE_WEBHOOK_SECRET=
# The license prices, which name the plan a Payment Link sold.
# STRIPE_PRICE_LICENSE_INDIVIDUAL=
# STRIPE_PRICE_LICENSE_TEAM=
# The site the license pages are on; CORS lets it and its www or bare twin in.
# NEXT_FRONTEND_URL=https://www.surfsense.com
# The cohort report (python -m license.scripts.license_cohorts) reads each licence's first
# plugin call from the usage meter's admin routes, with USAGE_ADMIN_TOKEN. In this
# backend's own container: http://127.0.0.1:8000.
# PLUGIN_HOST_ADMIN_URL=
# Who the license emails come from and who replies reach; unset, SMTP_FROM.
# SMTP_LICENSE_FROM=
# SMTP_LICENSE_REPLY_TO=
# The mailer, which sends the license emails. Off unless TRUE: the routes that only mail
# then answer 503 rather than claim a send.
# SMTP_ENABLED=FALSE
# SMTP_HOST=
# SMTP_PORT=587
# SMTP_USERNAME=
# Secret.
# SMTP_PASSWORD=
# starttls, tls or none; never guessed from the port.
# SMTP_SECURITY=starttls
# SMTP_TIMEOUT_SECONDS=20
# SMTP_FROM=
# The usage meter: each tools/call counted per UTC day, license, plugin, tool and outcome.
# Its SQLite file, written every minute and at shutdown. Relative to the working
# directory; unset, usage/plugin-usage.sqlite3, which is /srv/remote/usage/ in the
# image: mount a persistent volume there, or the counts go with the container.
# USAGE_DB_PATH=usage/plugin-usage.sqlite3
# Secret. Serves GET /admin/usage/first-seen and /admin/usage/daily?day=YYYY-MM-DD to
# Authorization: Bearer <token>. Unset, both are 404.
# USAGE_ADMIN_TOKEN=
# Secret. The PostHog project key the daily rollup goes to, totals with no license id,
# and the license funnel's personless trial and purchase events.
# Unset, nothing is sent.
# POSTHOG_API_KEY=
# PostHog's ingestion host; unset, the US cloud's.
# POSTHOG_HOST=https://us.i.posthog.com
# dataimpulse or nodemaven. Unset scrapes direct
SCRAPING_PROXY_PROVIDER=nodemaven
# Secret. The endpoint as the vendor's dashboard gives it, without a session or country.
# nodemaven: http://<login>:<password>@gate.nodemaven.com:8080 # pragma: allowlist secret
# dataimpulse: http://<token>:<password>@gw.dataimpulse.com:823 # pragma: allowlist secret
# SCRAPING_PROXY_URL=
# capsolver or 2captcha. Google Search solves Google's reCAPTCHA wall with it.
SCRAPING_CAPTCHA_PROVIDER=capsolver
# Secret. Unset, nothing is solved, and Google Search falls back to SearXNG when walled.
# SCRAPING_CAPTCHA_API_KEY=
# A SearXNG instance serving JSON, Google Search's fallback when Google walls every address.
# SEARXNG_URL=http://searxng:8080
# true runs a headed browser on the image's virtual display: TikTok's profile
# feeds are withheld from a headless one.
CRAWL_HEADED_XVFB_ENABLED=true
# Speed settings, all optional: unset is what was measured. Most are kill switches,
# where 0 brings back the path before the change; a number in brackets is the default.
# Shared by the scraper plugins (proprietary/scraping/):
# SCRAPING_HEDGE=0 no hedged second request anywhere
# SCRAPING_MAX_HTTP=48 plain-HTTP requests in flight across plugins
# SCRAPING_MAX_IDLE_SESSIONS=16 warm sessions kept between calls; 0 keeps none
# SCRAPING_MAX_BROWSERS=3 pooled browsers, one per launch profile
# SCRAPING_MAX_BROWSER_PAGES=6 pooled pages in all, 4 per plugin
# SCRAPING_MAX_KEPT_CONTEXTS=4 contexts kept with their clearance; 0 keeps none
# SCRAPING_BROWSER_IDLE_S=600 idle seconds before a pooled browser closes;
# SCRAPING_DEFAULT_BROWSER_IDLE_S=1800 the same for the default launch profile
# Per plugin:
# GOOGLE_SEARCH_FAST_SETTLE=0, GOOGLE_SEARCH_SHRINK_DOM=0, GOOGLE_SEARCH_PARALLEL_PAGES=0,
# GOOGLE_SEARCH_GOTO_PREFETCH=0, GOOGLE_SEARCH_GOTO_WAVE_SESSION=0,
# GOOGLE_SEARCH_GOTO_AI_SOURCES=0, GOOGLE_SEARCH_SITE_RETRIES=0, GOOGLE_SEARCH_WARM_BROWSER=0
# INDEED_KEPT_CONTEXT=0, INDEED_SPA_DETAILS=0, INDEED_TARGET_CONCURRENCY=1 (3);
# INDEED_PIN_COUNTRY=0; INDEED_WARM_FIRST=1 and INDEED_ALWAYS_SOLVE=1 land on the home
# page and run the Cloudflare solver on every page again
# YOUTUBE_NATIVE_SUBTITLES=0, YOUTUBE_COMMENTS_NEXT_SEED=0, YOUTUBE_HEDGE=0;
# YOUTUBE_KEEP_IDLE=1 keeps warm sessions between calls
# GOOGLE_MAPS_SKIP_NID_MINT=0, GOOGLE_MAPS_KEEP_ALIVE=0, GOOGLE_MAPS_HEDGE_AFTER_S=0 (2.5);
# GOOGLE_MAPS_KEEP_IDLE=1 keeps warm sessions between calls
# AMAZON_HEDGE=0, AMAZON_NO_GOOGLE_REFERER=0, AMAZON_AOD_XHR=0, AMAZON_DETAIL_CONCURRENCY=8 (16)
# WALMART_PARALLEL=0; WALMART_CANONICAL_URL=1 asks for the slug URL directly (its block
# check failed, so it stays off)
# TIKTOK_TRENDING_API=0, TIKTOK_COMMENTS_API=0, TIKTOK_BLOCK_MEDIA=0, TIKTOK_WARM_BROWSER=0,
# TIKTOK_PAGE_HEDGE_S=0 (2), TIKTOK_BROWSER_CONCURRENCY=1 (2);
# TIKTOK_BROWSER_WARMUP=1 and TIKTOK_PAGE_WARMUP=1 bring the msToken and ttwid warm-ups back
# WEB_CRAWL_STATUS_ROUTING=0, WEB_CRAWL_HTML_CAP=0, WEB_CRAWL_RAW_STATIC=0,
# WEB_CRAWL_BROWSER_POOL=0, WEB_CRAWL_CONCURRENCY=1 (6)