mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
65bfb7ece8
Two follow-ups to #3519 (per-app generation spend/velocity caps). 1) OBSERVABILITY of the degrade-to-strictest path. `resolveAppCapLimits` falls back to STRICTEST_APP_CAP_LIMITS on a DB error or a missing `app_blocks` row. That behaviour is right — never uncapped, and a hard deny would turn a transient DB blip into a full generation outage — but it was SILENT, and an app pinned to the strictest ceiling looks exactly like an app that is merely busy. The first symptom would be that app's users hitting abuse rejections they did not earn. (This is the same silent- degradation shape as #3520, which is why it is worth closing here.) Adds `civitai_app_block_cap_limits_degraded_total{reason}` — `db_error` (the read threw: infra; every app degrades at once, page-worthy) vs `missing_row` (the read succeeded and there is no such app: one app, points at an id-minting bug) — plus a paired `console.warn` carrying the specific appBlockId. Follows the existing convention in src/server/metrics/app-block-runtime.metrics.ts (get-or-create against the default registry + a fail-soft emit wrapper); no new mechanism. - NO `app_block_id` prom label, deliberately. `missing_row` fires precisely for ids absent from the app catalog, i.e. the unbounded population known-app-blocks.service.ts exists to clamp, and prom-client retains every distinct label set in the heap forever. The usual clamp needs a DB read — the very thing broken on the `db_error` path. So: alert on the metric, attribute from the log. - NOT a failure path. The metric emit and the log are independently guarded and `recordAppCapLimitsDegrade` is total on its own side, so neither a broken registry nor a throwing console can perturb cap resolution. - NOT on the hot path. Only a cache MISS that DEGRADED emits; a warm hit and a miss that resolves a real row never reach it. Volume is bounded by the 5s fallback-TTL cache, not by submit rate — a 10k-submit burst against one degraded app emits once. 2) RENAME the two absolute-ceiling env knobs. `BLOCK_APP_SPEND_CAP_BUZZ_PER_DAY` / `BLOCK_APP_SPEND_VELOCITY_MAX_GENS` used to BE the ceilings. Since #3519 they are absolute bounds that clamp the tier table AND any per-app moderator override, so an operator reaching for `..._VELOCITY_MAX_GENS` mid-incident would reasonably read it as "set the limit" rather than "bound it". BLOCK_APP_SPEND_CAP_BUZZ_PER_DAY -> BLOCK_APP_SPEND_ABSOLUTE_MAX_BUZZ_PER_DAY BLOCK_APP_SPEND_VELOCITY_MAX_GENS -> BLOCK_APP_SPEND_ABSOLUTE_MAX_GENS_PER_WINDOW The legacy names are still honoured (dp-prod sets neither, but other environments are not enumerable from here, and silently ignoring a set spend guardrail is unacceptable), with a deprecation warning that also states the changed meaning. A valid new value always wins; a set-but-ignored or set-but-unusable legacy value warns too. Exported symbols follow the env names, with the old export names kept as deprecated aliases so pre-rename importers keep compiling. Tests: 41 new across two files. Every new guard was mutation-verified — 14 mutations, each killing a specific named test.
213 lines
7.0 KiB
Plaintext
213 lines
7.0 KiB
Plaintext
# Since .env is gitignored, you can use .env-example to build a new `.env` file when you clone the repo.
|
|
# Keep this file up-to-date when you add new variables to `.env`.
|
|
# This file will be committed to version control, so make sure not to have any secrets in it.
|
|
# If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets.
|
|
# When adding additional env variables, the schema in /env/schema.mjs should be updated accordingly
|
|
# The default values for Prisma, Redis, S3, and Email are set to work with the docker-compose setup
|
|
|
|
# Database
|
|
DATABASE_SSL=false
|
|
DATABASE_URL=postgresql://postgres:postgres@localhost:15432/civitai
|
|
DATABASE_REPLICA_URL=postgresql://postgres:postgres@localhost:15432/civitai
|
|
|
|
NOTIFICATION_DB_URL=postgresql://postgres:postgres@localhost:15434/postgres
|
|
NOTIFICATION_DB_REPLICA_URL=postgresql://postgres:postgres@localhost:15434/postgres
|
|
|
|
DATAPACKET_DATABASE_RO_URL=postgresql://postgres:postgres@localhost:15435/postgres
|
|
|
|
# Redis
|
|
REDIS_URL=redis://:redis@localhost:6379
|
|
REDIS_SYS_URL=redis://:redis@localhost:6378
|
|
# Optional: switch the `system` redis client to Sentinel mode. When set, REDIS_SYS_URL
|
|
# is still parsed for credentials but the connection is established via Sentinel.
|
|
# REDIS_SYS_SENTINELS=localhost:26379,localhost:26380,localhost:26381
|
|
# REDIS_SYS_SENTINEL_NAME=sysmaster # production uses "sysmaster"; match the master group your Sentinel CR declares
|
|
|
|
# Logging
|
|
LOGGING=prisma:error,prisma:warn,seed-metrics-search
|
|
|
|
# Next Auth
|
|
NEXTAUTH_SECRET=thisisnotasecret
|
|
NEXTAUTH_URL=http://localhost:3000
|
|
|
|
# Next Auth Discord Provider
|
|
DISCORD_CLIENT_ID=
|
|
DISCORD_CLIENT_SECRET=
|
|
|
|
# Next Auth GitHub Provider
|
|
GITHUB_CLIENT_ID=
|
|
GITHUB_CLIENT_SECRET=
|
|
|
|
# Next Auth Google Provider
|
|
GOOGLE_CLIENT_ID=
|
|
GOOGLE_CLIENT_SECRET=
|
|
|
|
# Next Auth Reddit Provider
|
|
REDDIT_CLIENT_ID=
|
|
REDDIT_CLIENT_SECRET=
|
|
|
|
# Integrations
|
|
DISCORD_BOT_TOKEN=
|
|
DISCORD_GUILD_ID=
|
|
DISCORD_WEBHOOK_MOD_ALERTS=
|
|
|
|
# File uploading
|
|
S3_UPLOAD_KEY=REFER_TO_README
|
|
S3_UPLOAD_SECRET=REFER_TO_README
|
|
S3_UPLOAD_BUCKET=modelshare
|
|
S3_UPLOAD_REGION=us-east-1
|
|
S3_UPLOAD_ENDPOINT=http://127.0.0.1:9000
|
|
|
|
# Image uploading
|
|
S3_IMAGE_UPLOAD_KEY=
|
|
S3_IMAGE_UPLOAD_SECRET=
|
|
S3_IMAGE_UPLOAD_BUCKET=images
|
|
S3_IMAGE_UPLOAD_REGION=us-east-1
|
|
S3_IMAGE_UPLOAD_ENDPOINT=http://127.0.0.1:9000
|
|
S3_IMAGE_CACHE_BUCKET=cache
|
|
S3_IMAGE_UPLOAD_OVERRIDE=
|
|
|
|
# Client env vars
|
|
NEXT_PUBLIC_IMAGE_LOCATION=http://localhost:3000
|
|
NEXT_PUBLIC_CONTENT_DECTECTION_LOCATION=https://publicstore.civitai.com/content_detection/model.json
|
|
NEXT_PUBLIC_CIVITAI_LINK=http://localhost:3000
|
|
NEXT_PUBLIC_UI_CATEGORY_VIEWS=false
|
|
NEXT_PUBLIC_UI_HOMEPAGE_IMAGES=false
|
|
NEXT_PUBLIC_ADS=true
|
|
|
|
# Clickhouse
|
|
CLICKHOUSE_HOST=http://localhost:18123
|
|
CLICKHOUSE_USERNAME=default
|
|
CLICKHOUSE_PASSWORD=
|
|
CLICKHOUSE_TRACKER_URL=http://localhost:3000
|
|
|
|
# Email
|
|
EMAIL_HOST=localhost
|
|
EMAIL_PORT=1025
|
|
EMAIL_USER=
|
|
EMAIL_PASS=
|
|
EMAIL_FROM=
|
|
|
|
# Endpoint Protection
|
|
JOB_TOKEN=thisisnotatoken
|
|
WEBHOOK_TOKEN=thisisnotatoken
|
|
|
|
# Site Configuration
|
|
UNAUTHENTICATED_DOWNLOAD=true
|
|
UNAUTHENTICATED_LIST_NSFW=false
|
|
SHOW_SFW_IN_NSFW=false
|
|
MAINTENANCE_MODE=false
|
|
RATE_LIMITING=true
|
|
TRPC_ORIGINS=
|
|
|
|
# Security
|
|
SCANNING_ENDPOINT=http://scan-me.civitai.com/enqueue
|
|
SCANNING_TOKEN=thisisnotatoken
|
|
|
|
# Delivery worker
|
|
DELIVERY_WORKER_ENDPOINT=https://delivery-worker.civitai.com/download
|
|
DELIVERY_WORKER_TOKEN=thisisnotatoken
|
|
|
|
# Payments
|
|
PADDLE_SECRET_KEY=thisisnotasecret
|
|
PADDLE_WEBHOOK_SECRET=thisisnotasecret
|
|
NEXT_PUBLIC_PADDLE_TOKEN=thisisnotatoken
|
|
NEXT_PUBLIC_DEFAULT_PAYMENT_PROVIDER=Paddle
|
|
|
|
# Features
|
|
FEATURE_FLAG_EARLY_ACCESS_MODEL=public
|
|
|
|
# MeiliSearch
|
|
SEARCH_HOST=http://localhost:7700
|
|
SEARCH_API_KEY=meilisearch
|
|
NEXT_PUBLIC_SEARCH_HOST=http://localhost:7700
|
|
NEXT_PUBLIC_SEARCH_CLIENT_KEY=meilisearch
|
|
|
|
METRICS_SEARCH_HOST=http://localhost:7700
|
|
METRICS_SEARCH_API_KEY=meilisearch
|
|
|
|
# Debounce window (ms) for flushing model-metric-affected ids into the model
|
|
# search-index update queue. Widening it collapses more of a hot model's repeated
|
|
# metric changes into a single reindex (cost: metric/popularity staleness lags by
|
|
# up to the window). Fail-soft: a bad value falls back to the 45m default. Requires
|
|
# a restart/rollout to take effect. Default 45m.
|
|
# SEARCH_INDEX_MODEL_METRIC_FLUSH_INTERVAL_MS=2700000
|
|
|
|
# Per-call Meilisearch timeout in ms. Calls wrapped via withMeili() fail fast
|
|
# with MeiliCallTimeoutError once exceeded, instead of hanging until Traefik's
|
|
# 30s router timeout fires.
|
|
MEILI_CALL_TIMEOUT_MS=2500
|
|
# Per-pod cap on in-flight Meilisearch calls wrapped via withMeili(). Excess
|
|
# calls fail fast with MeiliCallTimeoutError instead of queueing forever.
|
|
MEILI_CALL_CONCURRENCY=50
|
|
|
|
# Per-backend circuit breaker. If MEILI_CIRCUIT_TRIP_THRESHOLD wrapped-call
|
|
# timeouts accumulate within MEILI_CIRCUIT_WINDOW_SECONDS on a backend, the
|
|
# circuit OPENs and all calls fail at 0ms for MEILI_CIRCUIT_COOLDOWN_SECONDS,
|
|
# then HALF_OPEN issues a single trial request. healthProbe is excluded.
|
|
MEILI_CIRCUIT_TRIP_THRESHOLD=10
|
|
MEILI_CIRCUIT_WINDOW_SECONDS=30
|
|
MEILI_CIRCUIT_COOLDOWN_SECONDS=30
|
|
|
|
# BaseURL
|
|
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
|
|
|
# Recaptcha
|
|
RECAPTCHA_PROJECT_ID=aSampleKey
|
|
NEXT_PUBLIC_RECAPTCHA_KEY=aSampleKey
|
|
|
|
# CF Turnstile
|
|
NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITEKEY=1x00000000000000000000BB
|
|
CLOUDFLARE_TURNSTILE_SECRET=1x0000000000000000000000000000000AA
|
|
|
|
NEXT_PUBLIC_CF_INVISIBLE_TURNSTILE_SITEKEY=1x00000000000000000000BB
|
|
CF_INVISIBLE_TURNSTILE_SECRET=1x0000000000000000000000000000000AA
|
|
|
|
NEXT_PUBLIC_CF_MANAGED_TURNSTILE_SITEKEY=1x00000000000000000000AA
|
|
CF_MANAGED_TURNSTILE_SECRET=1x0000000000000000000000000000000AA
|
|
|
|
ORCHESTRATOR_ENDPOINT=http://localhost
|
|
ORCHESTRATOR_ACCESS_TOKEN=asdf
|
|
|
|
BUZZ_ENDPOINT=http://localhost
|
|
SIGNALS_ENDPOINT=http://localhost
|
|
NEXT_PUBLIC_SIGNALS_ENDPOINT=http://localhost
|
|
|
|
NOW_PAYMENTS_API_URL=http://localhost
|
|
NOW_PAYMENTS_API_KEY=key
|
|
NOW_PAYMENTS_IPN_KEY=key
|
|
|
|
COINBASE_API_URL=http://localhost
|
|
COINBASE_API_KEY=key
|
|
COINBASE_WEBHOOK_SECRET=secret
|
|
|
|
EMERCHANTPAY_WPF_URL=
|
|
EMERCHANTPAY_USERNAME=
|
|
EMERCHANTPAY_PASSWORD=
|
|
|
|
# Shopify merch store — Blue Buzz reward loop
|
|
# SHOPIFY_SHOP_DOMAIN = the *.myshopify.com admin domain (e.g. ff1592-5.myshopify.com)
|
|
SHOPIFY_SHOP_DOMAIN=
|
|
SHOPIFY_WEBHOOK_SECRET=
|
|
# Admin auth: client_credentials grant (preferred). Set ADMIN_TOKEN instead only for a static token.
|
|
SHOPIFY_CLIENT_ID=
|
|
SHOPIFY_CLIENT_SECRET=
|
|
SHOPIFY_ADMIN_TOKEN=
|
|
|
|
FLIPT_URL=""
|
|
FLIPT_FETCHER_SECRET=placeholder
|
|
|
|
IMAGE_SCANNER_NEW=false
|
|
|
|
# App Blocks — per-app generation spend/velocity ABSOLUTE CEILINGS (incident knobs).
|
|
# 🔴 These are UPPER BOUNDS, not the limit an app receives. Each app's actual
|
|
# ceilings come from its server-owned `spendTier` (+ any moderator per-app
|
|
# override); these clamp the tier table AND any override from above, so setting
|
|
# one TIGHTENS every app and can never loosen one. Unset = no extra clamp.
|
|
# Formerly BLOCK_APP_SPEND_CAP_BUZZ_PER_DAY / BLOCK_APP_SPEND_VELOCITY_MAX_GENS —
|
|
# those names are DEPRECATED but still honoured (with a startup warning).
|
|
# BLOCK_APP_SPEND_ABSOLUTE_MAX_BUZZ_PER_DAY=
|
|
# BLOCK_APP_SPEND_ABSOLUTE_MAX_GENS_PER_WINDOW=
|
|
# Window (seconds) the gens-per-window ceiling is measured over. Default 60.
|
|
# BLOCK_APP_SPEND_VELOCITY_WINDOW_SECONDS=
|