Repository navigation
Expand file tree
/
Copy path.env.example
More file actions
174 lines (152 loc) · 9.32 KB
/
Copy path.env.example
File metadata and controls
174 lines (152 loc) · 9.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# Copy this file to .env.local and fill in the values you need.
# Never commit .env.local — it is gitignored.
# Optional. When set, the AI assistant (the `ask` command in the terminal)
# answers using OpenAI. Without it, `ask` streams a few canned demo replies.
# Get a key at https://platform.openai.com/api-keys.
OPENAI_API_KEY=sk-proj-replace-me
# Optional. Model the AI chat will call. Defaults to gpt-4o-mini.
# Examples: gpt-4o-mini, gpt-4o, gpt-4.1-mini.
OPENAI_MODEL=gpt-4o-mini
# Optional. Embedding model used to index the CV/dossiers for the RAG agent.
# Defaults to text-embedding-3-small (1536 dimensions).
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
# Optional. Model for the CV / cover-letter generator in /admin/generator.
# Falls back to OPENAI_MODEL.
# OPENAI_GENERATOR_MODEL=gpt-4o
# Optional. Limits for the assistant (`ask`): messages per visitor inside a
# sliding window, the window length in ms, and a site-wide daily cap. Counted
# in Postgres when DATABASE_URL works, in memory otherwise.
# CHAT_RATE_MAX=10
# CHAT_RATE_WINDOW_MS=3600000
# CHAT_DAILY_MAX=300
# RECOMENDADA em producao. Sal secreto do HMAC que pseudonimiza o visitante do
# chat (src/lib/visitor-hash.ts): o IP cru nunca vira linha no banco, o que se
# grava e HMAC-SHA256(sal, ip) truncado. Sem sal, um sha256(ip) seria um IP em
# claro com passos a mais (o IPv4 inteiro se pre-computa num laptop). Minimo de
# 16 chars. Gere com: openssl rand -base64 32
#
# AUSENTE (ou com menos de 16 chars) o app NAO para — e esta a variavel que
# MUDA COMPORTAMENTO DE PRODUCAO sem falhar. O processo sorteia um sal proprio
# no primeiro uso e escreve UM aviso no log; dai em diante:
# - o hash deixa de ser estavel entre reinicios e entre workers, entao o
# limite POR VISITANTE (CHAT_RATE_MAX) zera a cada deploy/restart e cada
# worker conta o seu — quem quiser mais cota so espera o proximo deploy;
# - CHAT_DAILY_MAX (teto do site, que nao depende do hash) continua segurando
# o gasto: foi por isso que degradar pareceu preferivel a gravar um
# identificador reversivel;
# - o painel para de agrupar as sessoes do mesmo visitante.
# Avaliacao (nao aplicada aqui, e mudanca de codigo): o padrao seguro seria
# falhar RUIDOSAMENTE — lancar no boot quando NODE_ENV=production e a variavel
# faltar, e manter o sal efemero so em dev. Uma defesa que se desliga sozinha
# num deploy e pior do que uma que nao sobe, porque ninguem fica sabendo.
# CHAT_IP_SALT=
# Optional. Terminal fallback: when "true", input that matches no command and
# has three or more words (and does not start with "/") is forwarded to the AI
# assistant as if typed after `ask`. Off by default so typos never spend the
# assistant's quota. Public (inlined at build time), so rebuild after changing
# it; in the Docker stack it reaches the image as a build arg (see DEPLOY.md).
NEXT_PUBLIC_TERMINAL_ASK_FALLBACK=false
# Optional. When set, the contact form sends real emails through Resend.
# Without it the form logs the payload server-side and shows the success state.
RESEND_API_KEY=
# Optional. Base URL of the Ghost CMS instance the blog reads from.
# Example: https://cms.maarkn.dev
GHOST_URL=
# Optional. Content API key from the Ghost integration.
# Without it the blog falls back to a small set of mock posts.
GHOST_CONTENT_API_KEY=
# PostgreSQL (with the pgvector extension) used by Prisma for the admin CMS at
# /admin and the RAG knowledge base. Two options for local dev:
# (a) Existing local Postgres (recommended if you already run one). Create the
# database once (CREATE DATABASE maarkn_website;) and enable pgvector, then:
# postgresql://USER:PASSWORD@localhost:5432/maarkn_website?schema=public
# (b) The bundled docker compose (npm run db:up), which ships pgvector on host
# port 5433:
# postgresql://maarkn:maarkn@localhost:5433/maarkn_website?schema=public
# In the production self-host stack (docker-compose.prod.yml) the app reaches
# Postgres via the service hostname `postgres` instead of `localhost`.
DATABASE_URL=postgresql://USER:PASSWORD@localhost:5432/maarkn_website?schema=public
# Random secret used to sign Auth.js (NextAuth) tokens. Generate one with:
# openssl rand -base64 32
AUTH_SECRET=
# Seed-only credentials for the admin user. Read by prisma/seed.ts when running
# `npm run db:seed`. After seeding you can rotate the password through the admin UI.
ADMIN_EMAIL=admin@maarkn.dev
ADMIN_PASSWORD=
# Optional. Directory where admin uploads (project covers) are written and
# served from /uploads/<path>. Defaults to ./uploads; the Docker image sets
# /data/uploads (a named volume in production).
# UPLOAD_DIR=./uploads
# Throttle DURAVEL de brute force no login do admin (src/lib/login-throttle.ts).
# O e-mail do admin e publico e /api/auth/callback/credentials e atingivel direto,
# entao a checagem roda no topo de `authorize`, antes do bcrypt. Contador por IP,
# janela fixa, na tabela McpRateLimit. Defaults abaixo (opcionais).
# LOGIN_RATE_MAX=10 # tentativas por IP dentro da janela
# LOGIN_RATE_WINDOW_MS=900000 # janela em ms (15 min)
# ---------------------------------------------------------------------------
# De onde sai o IP do cliente (a chave de TODO rate limit por IP)
# ---------------------------------------------------------------------------
# Opcional. Quantos proxies CONFIAVEIS existem na frente da app
# (src/lib/trusted-client-ip.ts, usado pelo throttle do login e pelo rate limit
# por IP do /api/mcp). O IP sai do ULTIMO salto confiavel do X-Forwarded-For,
# nunca do elemento mais a esquerda — esse e o pedaco que o CLIENTE escreve, e
# um header novo a cada tentativa daria um balde novo a cada tentativa.
# Padrao 1; minimo 1 (nao existe "0": nem o NextAuth nem o route handler
# enxergam o IP do socket, entao o header do proxy e a unica fonte).
#
# AUSENTE = 1, que e o valor CERTO para a stack de hoje: em docker-compose.prod
# o container nao publica porta e so o Traefik o alcanca, um salto. O numero e
# propriedade do DEPLOY, nao do codigo: no dia em que um CDN/WAF entrar na
# frente do Traefik a cadeia ganha um salto, o valor 1 passa a apontar para um
# elemento escrito pelo cliente, e tanto o throttle do login quanto o teto de
# 60/min por IP do MCP voltam a ser contornaveis. Mudou a topologia, mude esta
# variavel no MESMO commit.
# TRUSTED_PROXY_HOPS=1
# Opcional. Sobrescreve TRUSTED_PROXY_HOPS so para o throttle do login, para o
# caso de /api/auth entrar por um caminho com numero de saltos diferente.
# AUSENTE = o valor de TRUSTED_PROXY_HOPS (e, sem ele tambem, 1). Valor invalido
# ou <= 0 tambem cai nesse fallback; nao ha como pedir "confie em zero proxies".
# LOGIN_TRUSTED_PROXY_HOPS=1
# ---------------------------------------------------------------------------
# Servidor MCP (/api/mcp) — F3
# ---------------------------------------------------------------------------
# OBRIGATORIO para o MCP funcionar. Pepper usado no HMAC-SHA256 que protege as
# chaves de API: o hash vai para a tabela ApiKey, o pepper NAO. Um vazamento
# somente do banco nao permite verificar palpite offline. Minimo de 32 chars.
# Gere com: openssl rand -base64 48
# Trocar o pepper INVALIDA todas as chaves existentes (e o procedimento de
# emergencia se voce suspeitar que o banco vazou).
MCP_KEY_PEPPER=
# Opcional. Origens de NAVEGADOR autorizadas a chamar /api/mcp, separadas por
# virgula. Vazio (padrao) = nenhum navegador. Clientes MCP nativos (Claude Code,
# ChatGPT) nao mandam Origin e nao sao afetados. Isto fecha DNS rebinding e CSRF.
MCP_ALLOWED_ORIGINS=
# Opcionais. Rate limit DURAVEL do MCP (tabela McpRateLimit, janela fixa).
# Padroes entre parenteses; dimensionados para a sincronizacao do vault caber
# com folga. Qualquer falha ao contar NEGA a requisicao (fail-closed).
# MCP_RATE_IP_PER_MIN=60 # requisicoes por IP, antes de autenticar
# MCP_RATE_KEY_PER_MIN=120 # requisicoes HTTP por chave
# MCP_RATE_KEY_PER_HOUR=3000 # idem, por hora
# MCP_RATE_TOOL_PER_MIN=240 # chamadas de tool por chave
# MCP_RATE_WRITE_PER_MIN=120 # chamadas de tool de ESCRITA por chave
# MCP_MAX_BODY_BYTES=4194304 # teto do corpo da requisicao (4 MiB)
# MCP_MAX_BATCH_MESSAGES=20 # mensagens JSON-RPC por requisicao; ausente = 20
# # (sem teto, UM POST vira N chamadas de tool e o
# # batch multiplica o custo de cada limite acima)
# O IP que esses baldes usam como chave vem de TRUSTED_PROXY_HOPS — ver a secao
# "De onde sai o IP do cliente" acima.
# ---------------------------------------------------------------------------
# Particao publico/privado do corpus de RAG (F7 — exfiltracao)
# ---------------------------------------------------------------------------
# Opcional. Arquivos de knowledge/** que podem alimentar o chat ANONIMO, por
# caminho relativo, separados por virgula. Tudo o que nao estiver aqui (nem
# declarar `visibility: public` nas primeiras 20 linhas do proprio arquivo) e
# ingerido como `private`: continua servindo o gerador autenticado, mas nunca o
# chat publico. Default: cv.md
# KNOWLEDGE_PUBLIC_SOURCES=cv.md
# Opcional. Contatos do PROPRIO Marco — os unicos que podem sair em claro num
# material gerado (CV/carta) ou na prosa devolvida pelas tools de leitura do
# MCP. Qualquer outro e-mail/telefone no texto gerado REPROVA a geracao
# (regras SEC1/SEC2) e e mascarado nas respostas de leitura.
# Default: markimkr@gmail.com,+55 62 98173 6748
# GENERATOR_OWN_CONTACTS=