Three RED-blessed non-blocking follow-ups on the KV-optional work. 1. Drop the non-atomic getdel() shim in lib/redis.ts. It shadowed BOTH backends' native atomic GETDEL with a get->del pipeline (other commands interleave between GET and DEL), reintroducing the one-time login-code double-use race that fetchAndDeleteLoginCodeData relies on GETDEL to prevent. ioredis maps getdel to the single-round-trip Redis GETDEL command; MemoryKV.getdel now reads+deletes in one un-yielded step (was `await this.get()` then delete — the await yielded the event loop, letting two racers both observe the value). 2. Bound MemoryKV growth. Lazy on-read eviction never fires for write-once- never-read keys (rl:<ip> rate-limit counters, one-shot tokens), so the map grew unbounded until the pod OOMKilled. Add sweepExpired() (one O(n) active- expire pass) run on an unref'd 60s timer, started by lib/redis.ts for the server singleton only (edge-gated; no test imports that module, so unit runs stay timer-free). MemoryKV stays pure (zero imports). 3. Document in .env.example that re-enabling multi-replica REQUIRES KV_URL (single-replica-by-construction otherwise: SQLite-on-RWO + in-process KV). The one-time non-silent in-process warning already exists in lib/kv/select.ts; no metrics surface exists, so log + doc is sufficient. Tests: +3 cases (getdel atomicity under concurrent race; 100k-key sweep reclaims to 0; sweep retains live/no-TTL keys). 28 pass, 3 skip (select.ts skips without ioredis in a bare checkout), 0 fail. Co-authored-by: Hanzo AI <ai@hanzo.ai>
105 lines
4.4 KiB
Bash
105 lines
4.4 KiB
Bash
NEXTAUTH_SECRET=my-superstrong-secret
|
||
NEXTAUTH_URL=http://localhost:3000
|
||
|
||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||
NEXT_PUBLIC_MARKETING_URL=http://localhost:3000
|
||
|
||
NEXT_PUBLIC_APP_BASE_HOST=localhost
|
||
|
||
# These variables are from Vercel Storage Postgres
|
||
POSTGRES_PRISMA_URL=
|
||
POSTGRES_PRISMA_URL_NON_POOLING=
|
||
# This variable is from Vercel Storage Blob
|
||
BLOB_READ_WRITE_TOKEN=
|
||
|
||
# Hanzo IAM OAuth (required for production)
|
||
IAM_URL="https://hanzo.id"
|
||
IAM_CLIENT_ID=""
|
||
IAM_CLIENT_SECRET=""
|
||
# IAM_PROVIDER_NAME="Hanzo"
|
||
|
||
# Google client id and secret (deprecated — use Hanzo IAM above)
|
||
# GOOGLE_CLIENT_ID=
|
||
# GOOGLE_CLIENT_SECRET=
|
||
|
||
# This variable is from Resend to send emails
|
||
RESEND_API_KEY=
|
||
|
||
# This variable is from Tinybird to publish and read event data
|
||
TINYBIRD_TOKEN=
|
||
|
||
# These variables are from Vercel and used for setting up custom domains
|
||
PROJECT_ID_VERCEL=
|
||
TEAM_ID_VERCEL=
|
||
AUTH_BEARER_TOKEN=
|
||
|
||
# Upstash QStash – required for queues and background jobs
|
||
# Get your QStash Token here: https://upstash.com/docs/qstash/overall/getstarted
|
||
QSTASH_TOKEN=
|
||
QSTASH_CURRENT_SIGNING_KEY=
|
||
QSTASH_NEXT_SIGNING_KEY=
|
||
|
||
# HANKO - required for signup with passkey
|
||
# Get your HANKO keys here: https://docs.hanko.io/passkey-api/reference/credentials/list-credentials
|
||
HANKO_API_KEY=add-your-hanko-api-key
|
||
NEXT_PUBLIC_HANKO_TENANT_ID=add-your-hanko-tenent-id
|
||
|
||
# Trigger v3
|
||
TRIGGER_SECRET_KEY=
|
||
TRIGGER_API_URL=https://api.trigger.dev
|
||
|
||
# [[STORAGE]]
|
||
# Defines the storage transport to use. Available options: vercel (default) | s3
|
||
NEXT_PUBLIC_UPLOAD_TRANSPORT="vercel"
|
||
# REQUIRED: Defines the distribution host (without protocol) to use for the storage transport. Either cloudfront, s3, or vercel blob host. Used in next.config.mjs configuration.
|
||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_HOST="<BLOB_STORE_ID>.public.blob.vercel-storage.com" | "<DISTRIBUTION_ID>.cloudfront.net" | "<BUCKET_NAME>.s3.<REGION>.amazonaws.com"
|
||
# OPTIONAL: Defines the endpoint to use for the S3 storage transport. Relevant when using third-party S3-compatible providers.
|
||
NEXT_PRIVATE_UPLOAD_ENDPOINT=""
|
||
# Defines the region to use for the S3 storage transport. Defaults to us-east-1.
|
||
NEXT_PRIVATE_UPLOAD_REGION="us-east-1"
|
||
# REQUIRED: Defines the bucket to use for the S3 storage transport.
|
||
NEXT_PRIVATE_UPLOAD_BUCKET="YOUR_BUCKET_NAME"
|
||
# REQUIRED: Defines the access key ID to use for the S3 storage transport.
|
||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=""
|
||
# REQUIRED: Defines the secret access key to use for the S3 storage transport.
|
||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=""
|
||
# Defines the Cloudfront distribution domain to use for the S3 storage transport. Relevant when using Cloudfront.
|
||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_DOMAIN=""
|
||
# Defines the public key ID to sign Cloudfront URLs. Relevant when using Cloudfront.
|
||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_ID=""
|
||
# Defines the private key contents to sign Cloudfront URLs. Relevant when using Cloudfront. Should be a PEM-encoded RSA 2048 private key.
|
||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_CONTENTS=
|
||
|
||
# Encryption key for document passwords.
|
||
NEXT_PRIVATE_DOCUMENT_PASSWORD_KEY=my-superstrong-document-secret
|
||
|
||
# [[HANZO KV]] — OPTIONAL. Leave UNSET to run on the in-process backend
|
||
# (single-replica correct: cache, rate-limit, tus upload locks, export/download
|
||
# job stores and digest queues all work with no external datastore). SET it to
|
||
# an external Hanzo KV instance for multi-replica HA (shared state across pods).
|
||
# Accepts the Hanzo KV brand scheme kv:// (kvs:// for TLS) or a redis:// DSN.
|
||
# A malformed value fails CLOSED (the app throws rather than silently degrade).
|
||
#
|
||
# Re-enabling multi-replica (replicas > 1) REQUIRES KV_URL. dataroom is
|
||
# single-replica-by-construction otherwise: SQLite on a ReadWriteOnce volume plus
|
||
# the in-process KV. Without KV_URL each pod owns its OWN map, so sessions,
|
||
# rate-limit windows and tus upload locks split-brain across replicas — scaling
|
||
# out needs a shared DB AND KV_URL set.
|
||
# KV_URL=kv://:password@hanzo-kv:6379
|
||
KV_URL=
|
||
|
||
# [[TUS UPLOAD LOCKER]] — for bulk upload via tus.io, an exclusive locker
|
||
# prevents data corruption. It uses the Hanzo KV client above (KV_URL); with
|
||
# KV_URL unset the lock is in-process (correct for a single replica). The legacy
|
||
# Upstash REST locker below is unused and kept only for reference.
|
||
UPSTASH_REDIS_REST_LOCKER_URL=
|
||
UPSTASH_REDIS_REST_LOCKER_TOKEN=
|
||
|
||
# [[VERIFICATION]]
|
||
# Secret used to generate the checksum for the verification URL
|
||
NEXT_PRIVATE_VERIFICATION_SECRET=
|
||
|
||
# [[INCOMING WEBHOOKS]]
|
||
NEXT_PUBLIC_WEBHOOK_BASE_URL=
|
||
NEXT_PUBLIC_WEBHOOK_BASE_HOST=
|