# Dotacje AI - Backend Environment Variables Example # LLM Providers GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY" # xAI / Grok (HF Space Secret: XAI_API_KEY) — fallback gdy Gemini 403/billing/dunning XAI_API_KEY="YOUR_XAI_API_KEY" GROK_API_KEY="YOUR_GROK_API_KEY" # XAI_MODEL="grok-4.3" # XAI_BASE_URL="https://api.x.ai/v1" # Kolejność providerów (primary,fallback). # Na Hugging Face Spaces: ustaw PREFER_XAI=true LUB LLM_PRIMARY=xai żeby od razu # iść na Grok i pominąć nieudane próby Gemini (szybszy generate-full przy 403/billing). # LLM_FALLBACK_ORDER=gemini,xai # LLM_PRIMARY=xai # PREFER_XAI=false # GEMINI_HEALTH_PROBE=true GEMINI_EMBEDDING_MODEL="gemini-embedding-001" GEMINI_EMBEDDING_DIMENSIONS="768" # EMBEDDINGS_PROVIDER=auto # auto | hash (hash gdy Gemini embeddings 403) # Vector Store (Pinecone) — wymagane do ingestu i semantycznego wyszukiwania PINECONE_API_KEY="YOUR_PINECONE_API_KEY" PINECONE_INDEX_NAME="dotacje" # PINECONE_ENVIRONMENT="us-east-1" # opcjonalnie, zależnie od regionu indeksu # ENABLE_PINECONE_SEMANTIC="true" # Database (PostgreSQL production / Hugging Face Spaces) # Local docker-compose: postgresql://user:password@localhost:5432/dotacje # Managed / HF Spaces (Aiven, Neon, Supabase, Render): ALWAYS use SSL — # DATABASE_URL="postgresql://user:pass@host:5432/dbname?sslmode=require" # Or omit ?sslmode= and set: # DB_SSLMODE=require # Remote hosts default to sslmode=require; localhost defaults to prefer. # HF: set secrets in Space Settings → Variables (repo .env is NOT deployed). # Neon → Aiven: docs/MIGRATION_NEON_TO_AIVEN.md # DB_POOL_RECYCLE=1800 # Tracing and Observability LANGCHAIN_API_KEY="YOUR_LANGSMITH_API_KEY" LANGCHAIN_TRACING_V2="true" LANGCHAIN_PROJECT="GrantForgeAI" # Frontend (Vercel): optional Sentry browser DSN — omit to skip init (ErrorBoundary still works) # VITE_SENTRY_DSN="https://...@o....ingest.sentry.io/..." # VITE_POSTHOG_KEY="phc_..." # VITE_CLERK_PUBLISHABLE_KEY="pk_..." # VITE_API_URL="https://your-api.example.com/api" # Graph Database (GraphRAG) NEO4J_URI="neo4j+s://your-neo4j-instance.databases.neo4j.io" NEO4J_USERNAME="neo4j" NEO4J_PASSWORD="your-secure-password" # Web Scraping (Required for Grant extraction) # Crawl4AI — darmowy scraper (bez klucza API). Ustaw CRAWL4AI_USE_BROWSER=1 dla stron wymagających JS. # CRAWL4AI_USE_BROWSER=0 # Regional RPO BIP parsers (F4.4) # ENABLE_RPO_BIP_FETCH=true # 6 woj.: lubelskie, podkarpackie, łódzkie, mazowieckie, śląskie, wielkopolskie — cache 6h # Development Flags # Set to 'true' to serve fallback mock data if scraping yields < 3 results MOCK_GRANTS="false" # ── Auth / Clerk (P0 security) ────────────────────────────────────────────── # Required in production (HF Spaces Secrets) — fail-closed without these: # CLERK_ISSUER="https://YOUR_INSTANCE.clerk.accounts.dev" # Or pin JWKS directly (preferred if custom domain): # CLERK_JWKS_URL="https://YOUR_INSTANCE.clerk.accounts.dev/.well-known/jwks.json" # Optional JWT audience check (enable when Clerk JWT template sets aud): # CLERK_AUDIENCE="grantforge-api" # Clerk webhook signing secret (Dashboard → Webhooks → Signing Secret): # CLERK_WEBHOOK_SECRET="whsec_..." # Without it, POST /api/webhooks/clerk returns 500 (fail-closed). Startup logs a warning. # Dev-only backdoor (NEVER set on HF production): # ENV=dev # ALLOW_DEV_TOKEN=true # Internal seed endpoint (admin JWT required; also blocked in prod unless): # ALLOW_INTERNAL_SEED=true # Explicit frontend origins for CORS (comma-separated); FRONTEND_URL also added: # CORS_ORIGINS="https://grantforge-ai.vercel.app,https://grantforge.pl" # FRONTEND_URL="https://grantforge-ai.vercel.app" # ── Multi-worker SSE tickets (generator stream) ────────────────────────────── # Optional Redis; otherwise SQLite file under TMPDIR is used (multi-worker safe). # REDIS_URL=redis://localhost:6379/0 # TICKET_REDIS_URL=redis://localhost:6379/1 # STREAM_TICKET_TTL_SECONDS=90 # TICKET_STORE_PATH=/tmp # ── Company registries (enrichment) ────────────────────────────────────────── # GUS_API_KEY= # BIR1.1 production key (20 chars) # CEIDG_API_KEY= # dane.biznes.gov.pl CEIDG v2 # CEIDG_API_BASE=https://dane.biznes.gov.pl/api/ceidg/v2 # CEIDG_DISABLED=false # SUDOP_API_BASE=https://api-sudop.uokik.gov.pl/sudop-api/1.0.0 # SUDOP_DISABLED=false # BizRaport financial control — official API /api/dane (never commit real credentials) # BIZRAPORT_EMAIL=your@email.pl # BIZRAPORT_PASSWORD= # BIZRAPORT_BASE_URL=https://api.bizraport.pl # BIZRAPORT_ROZSZERZ_POLACZENIA=false # BIZRAPORT_API_URL=https://your-proxy.example/bizraport/{nip} # BIZRAPORT_API_KEY= # BIZRAPORT_DISABLED=false # BIZRAPORT_TIMEOUT=12 # ── Grant Pulse (phases A–D) ───────────────────────────────────────────────── # ENABLE_PULSE_WORKER=true # process ResearchJob + eurlex batch in scheduler # DISABLE_PULSE_WORKER=false # PULSE_WORKER_MAX_JOBS=25 # PULSE_NEW_DAYS=14 # "new" window # PULSE_CLOSING_DAYS=30 # "closing" window # ENABLE_DEADLINE_BACKFILL=true # enqueue/persist missing deadlines from text # DOC_INTEL_MIN_CHARS=120 # HTML thinner than this → Crawl4AI fallback # ENABLE_STEALTH_FETCH=true # curl_cffi Chrome fingerprint (PARP/BGK/WAF) # STEALTH_IMPERSONATE=chrome # chrome|chrome120|chrome124|chrome131 # STEALTH_TIMEOUT=30 # STEALTH_DOMAINS=parp.gov.pl,bgk.pl,ncbr.gov.pl # SSL_RELAXED_DOMAINS=wfosigw.pl,zus.pl # PDF/HTML TLS verify=False after primary fail # FLAKY_FETCH_DOMAINS=funduszeeuropejskie.gov.pl # FLAKY_FETCH_TIMEOUT=45 # FLAKY_PREFER_CACHE=true # FLAKY_CACHE_ONLY=false # true = skip live scrape for flaky domains # ENABLE_LIVE_RESEARCH=true # detect_grant_changes (false on HF free by default) # ENABLE_EURLEX_BATCH=true # batch CELEX/DU grounding (legal-ID only, never free-text) # ENABLE_EURLEX_NETWORK=false # true = live EUR-Lex SPARQL (off for CI/HF) # ENABLE_LAW_WATCH=true # ENABLE_STRATEGY_CASCADE=true # strategy recommend cascade 2026 (default on); false → paths still returned, cascade_enforced=false # ENABLE_ELIGIBILITY_SPINE=true # eligibility spine gates for readiness (default on) # ENABLE_TAX_PATHS=true # tax checklist attach on tax_psi_ulgi (default on) # ENABLE_BK2021=false # BK2021 B2B stub (default OFF); ON = draft alert only, never live scrape # LAW_WATCHLIST_JSON=[{"program":"FENG","url":"https://..."}] # LAW_WATCHLIST_PATH=/path/to/watchlist.json # CLERK_WEBHOOK_SECRET=whsec_... # required for Clerk webhooks (fail-closed without) # GUS_API_KEY= # BIR 1.1 company lookup (prod) # REDIS_URL= # multi-worker SSE tickets (falls back to SQLite) # ADMIN_EMAIL=bogmaz1@gmail.com # Nexus Control allowlist (or ADMIN_EMAILS=a@x,b@y) # ADMIN_USER_IDS=user_xxx # Clerk user ids (comma-separated) # ADMIN_CLERK_IDS= # alias of ADMIN_USER_IDS # CLERK_SECRET_KEY= # optional: enrich admin check from Clerk user API # GENERATOR_STREAM_TICKET_TTL=1800 # SSE ticket lifetime (seconds) for autopilot stream # STREAM_TICKET_TTL_SECONDS=90 # default for other short-lived tickets