Aliquot

Ambiente

Variáveis de ambiente obrigatórias e opcionais. O schema em apps/server/src/bootstrap/env.ts é a fonte de verdade.

Última atualização 27 de out. de 2026

Todas as env vars são validadas por um schema Zod em apps/server/src/bootstrap/env.ts. O servidor falha na inicialização com um erro claro se uma var obrigatória estiver faltando ou malformada. Adapters opcionais sobem como no-op quando sua configuração está vazia — você nunca precisa preencher todas as chaves.

Mínimo para subir

Estas quatro são o piso. Sem elas o servidor sai cedo:

BETTER_AUTH_SECRET=<openssl rand -base64 32>
BETTER_AUTH_URL=http://localhost:3005
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/boilerplate?schema=public
CORS_ORIGINS=http://localhost:3004

Gere o auth secret com openssl rand -base64 32. CORS_ORIGINS é uma lista separada por vírgula de origins permitidos para o browser.

Auth providers (opcional)

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

Deixe ambos vazios para desabilitar o Google OAuth. O magic link é configurado via seu provider de email — veja o módulo notifications.

Storage (opcional)

STORAGE_PROVIDER=null            # null | local | s3 | uploadthing
# S3 / R2 / MinIO
STORAGE_S3_BUCKET=
STORAGE_S3_REGION=
STORAGE_S3_ACCESS_KEY_ID=
STORAGE_S3_SECRET_ACCESS_KEY=
STORAGE_S3_ENDPOINT=             # defina para R2/MinIO; deixe vazio para AWS
STORAGE_S3_PUBLIC_BASE_URL=      # base do CDN
# Disco local (dev)
STORAGE_LOCAL_ROOT=
STORAGE_LOCAL_PUBLIC_BASE_URL=
# UploadThing
UPLOADTHING_TOKEN=
UPLOADTHING_APP_ID=

STORAGE_PROVIDER=null (o default) pula o registro de um adapter, então endpoints que precisam de uploads retornam 503. O provider local constrói URLs públicas a partir de BETTER_AUTH_URL (a URL do server), não APP_URL — uploads são servidos pela API, não pelo Next.js.

Background jobs (opcional)

REDIS_URL=redis://localhost:6379
JOBS_REDIS_URL=                  # padrão: REDIS_URL
JOBS_PREFIX=bull
BULL_BOARD_ENABLED=true          # monta a UI em /admin/queues

Sem Redis, o BullMQ é registrado mas nenhum worker conecta. BULL_BOARD_ENABLED=true monta uma UI Bull Board protegida em /admin/queues.

Rate limiting

RATE_LIMIT_REDIS_ENABLED=true    # cai para memória quando REDIS_URL está vazio
RATE_LIMIT_READ_PER_MIN=300
RATE_LIMIT_WRITE_PER_MIN=60
RATE_LIMIT_AUTH_IP_PER_MIN=30
RATE_LIMIT_AUTH_EMAIL_PER_15MIN=5

Os quatro tiers (read, write, auth-ip, auth-email) têm cada um seu próprio prefixo Redis (rl:read:, rl:write:, etc.) para que os counters nunca colidam. Os handlers do limiter são memoizados sobre a referência RateLimitDeps, fazendo com que o modo memória se comporte de forma idêntica ao modo Redis (um store compartilhado por tier).

AI chat (opcional)

ANTHROPIC_API_KEY=               # vazio = stream stub, sem chamadas upstream
AI_MODEL=claude-haiku-4-5        # modelo padrão, sempre permitido
AI_ALLOWED_MODELS=               # modelos extras que o cliente pode pedir (separados por vírgula)
AI_MAX_OUTPUT_TOKENS=1024        # limite rígido de tokens gerados por completion
AI_STREAM_TIMEOUT_MS=120000     # teto de tempo (ms) por stream de chat; aborta o upstream

POST /api/v1/ai/chat rejeita com 422 qualquer model fora de AI_MODEL + AI_ALLOWED_MODELS, então um caller não consegue escolher um modelo mais caro.

Observabilidade (opcional)

SENTRY_DSN=                      # deixe vazio para desabilitar
SENTRY_TRACES_SAMPLE_RATE=0.1
APP_VERSION=

OTEL_EXPORTER_OTLP_ENDPOINT=     # deixe vazio para desabilitar trace export
OTEL_SERVICE_NAME=mern-saas-server

METRICS_ENABLED=true             # endpoint /metrics do Prometheus

O OTel deve subir antes de Express/Prisma carregarem. O primeiro import em apps/server/src/index.ts é ./otel-init.js por causa disso. Não reordene.

O Sentry server SDK é @sentry/bun (não @sentry/node). logger.error(...) não auto-captura; use o helper captureError(err, ctx) para erros que devem paginar alguém.

Billing (depende do provider)

Defina PAYMENT_PROVIDER como um de stripe, mercado-pago, polar, depois preencha as chaves desse provider:

PAYMENT_PROVIDER=stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
# ou
PAYMENT_PROVIDER=mercado-pago
MERCADO_PAGO_ACCESS_TOKEN=
MERCADO_PAGO_WEBHOOK_SECRET=
# ou
PAYMENT_PROVIDER=polar
POLAR_ACCESS_TOKEN=
POLAR_WEBHOOK_SECRET=
POLAR_SERVER=sandbox   # ou production

[!WARNING] POLAR_SERVER é sandbox por default fora de produção. Com NODE_ENV=production e PAYMENT_PROVIDER=polar ele deve ser definido explicitamente (sandbox ou production): o server se recusa a subir se faltar, assim uma variável esquecida não manda tráfego real ao sandbox da Polar.

Veja Billing para a estratégia e setup de webhooks.

Usage metering (opcional)

Desabilitado por default. Ative a flag depois que o downstream registrar seus meters e um mapa plano → cap.

USAGE_METERING_ENABLED=false               # defina "true" para enforçar cotas
USAGE_FREE_PRICE_ID=price_free_synthetic   # chave sintética do tier Free; não é um price real do Polar

Veja Usage metering para a referência completa.

Pagamentos cripto (opcional)

O módulo crypto-payments (Aliquot) sobe sem nenhuma destas. Enquanto CRYPTO_MASTER_MNEMONIC não estiver definida, todo endpoint /api/v1/crypto/* e /api/v1/public/* responde 503 CRYPTO_NOT_CONFIGURED; o resto do server não é afetado.

CRYPTO_MASTER_MNEMONIC="<BIP-39 mnemonic>"   # custody; unset → crypto endpoints answer 503
CRYPTO_RPC_POLYGON_AMOY=https://polygon-amoy-bor-rpc.publicnode.com,https://polygon-amoy.drpc.org
CRYPTO_RPC_BSC_TESTNET=https://bsc-testnet-rpc.publicnode.com
CRYPTO_RPC_BASE_SEPOLIA=https://sepolia.base.org,https://base-sepolia-rpc.publicnode.com
# CRYPTO_RPC_POLYGON_MAINNET= / CRYPTO_RPC_BSC_MAINNET= / CRYPTO_RPC_BASE_MAINNET=
# CRYPTO_CONFIRMATIONS_POLYGON_AMOY=5  # dev-only override (defaults: Polygon 64, BSC 15, Base 30)
CRYPTO_PLATFORM_FEE_BPS=50             # default processing fee (0.50 %) for orgs without their own config
CRYPTO_PAYOUT_FEE_POLYGON=0.10         # flat token fee per payout, per network family
CRYPTO_PAYOUT_FEE_BSC=0.15
CRYPTO_PAYOUT_FEE_BASE=0.10
# CRYPTO_LOGS_MAX_RANGE_BSC_TESTNET=200      # max blocks per eth_getLogs call (default 200, all 6 networks)
# CRYPTO_NATIVE_PRICE_USD_POLYGON=0.50       # fixed native price for refund fee quotes (BSC 600, BASE 3000)
# CRYPTO_CHECKOUT_BASE_URL=https://pay.example.com  # hosted checkout origin; defaults to APP_URL
# TELEGRAM_BOT_TOKEN=                  # Telegram Mini-App initData verification
  • CRYPTO_MASTER_MNEMONIC é a wallet de custódia (HD path m/44'/60'/0'/0/i: o índice 0 é a hot wallet, os endereços de depósito das invoices começam em 1000). O processo da API deriva endereços só a partir da chave pública estendida; a assinatura acontece no worker. Precisa ser um mnemonic BIP-39 válido em inglês.
  • CRYPTO_RPC_<NETWORK> aceita URLs separadas por vírgula (fallback do viem, a primeira é a principal). Uma rede sem URLs aparece desabilitada em GET /api/v1/crypto/networks e não pode receber invoices. Para chains locais rode bun run chain:dev e aponte estas variáveis para as URLs impressas.
  • NEXT_PUBLIC_CRYPTO_RPC_<NETWORK> (cliente, apps/client/.env.local, opcional, uma URL) é o RPC que o botão "Pagar com wallet" do checkout entrega à wallet do comprador quando a wallet ainda não conhece a rede (wallet_addEthereumChain). Sem definir, o checkout usa o RPC público da rede. Defina apenas para chains locais, por exemplo NEXT_PUBLIC_CRYPTO_RPC_POLYGON_AMOY=http://127.0.0.1:18002, para que uma wallet do navegador envie para a mesma chain anvil que o servidor escaneia. É embutida no build: reconstrua (ou reinicie o next dev) depois de mudá-la.
  • NEXT_PUBLIC_SITE_URL (cliente, opcional) é a origem canônica do site público: metadata, Open Graph, sitemap, robots, JSON-LD e llms.txt. Sem definir, é https://aliquot.cc, o domínio de produção. Defina para um deploy de staging. É embutida no build.
  • CRYPTO_CHECKOUT_BASE_URL monta pay_url (<base>/pay/<invoiceId>), os links de resgate de refunds (<base>/pay/<invoiceId>/refund/<token>) e as URLs de payment links (<base>/l/<slug>).
  • CRYPTO_LOGS_MAX_RANGE_<NETWORK> limita o intervalo de blocos de cada chamada eth_getLogs (default 200: sepolia.base.org não aceita mais). O scanner também divide pela metade qualquer intervalo que o provider recuse por tamanho e alcança um backlog em passos limitados, então raramente é preciso mexer.
  • CRYPTO_NATIVE_PRICE_USD_<FAMILY> (POLYGON 0.50, BSC 600, BASE 3000) converte o gas de um refund em unidades de stablecoin: o comprador recebe gross − max(gas × max fee × preço, 0.01), fixado no resgate.
  • TELEGRAM_BOT_TOKEN é o token do bot que abre o checkout como Telegram Mini-App (/pay/<invoiceId>?tg=1). POST /api/v1/public/telegram/verify verifica com ele a assinatura do initData da Mini-App (HMAC-SHA256, secret = HMAC("WebAppData", token)) e rejeita um auth_date com mais de 24 h. Sem o token esse endpoint responde 503 TELEGRAM_NOT_CONFIGURED; o checkout continua funcionando.
  • O worker (bun run worker) roda o caminho do dinheiro: scanner de chain (2 s), expiração (10 s), sweeps (15 s), payouts (3 s), refunds (5 s). Precisa de Redis; sem Redis nada é escaneado nem enviado. O gas nativo que chega à hot wallet (índice 0) é registrado automaticamente como funding do operador (EQUITY:OPERATOR_FUNDING).

[!WARNING] Com NODE_ENV=production o server se recusa a subir com o mnemonic público de desenvolvimento (o commitado em apps/server/.env e .env.example), e rejeita overrides CRYPTO_CONFIRMATIONS_* para mainnets.

Adicionando uma nova variável

  1. Edite apps/server/src/bootstrap/env.ts e estenda o schema Zod. Vars obrigatórias precisam de .min(1); opcionais usam .optional() ou .default(...).
  2. Adicione uma entrada documentada em apps/server/.env.example.
  3. Leia de env.YOUR_VAR — nunca de process.env diretamente.

Nesta página