-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
158 lines (140 loc) · 8.1 KB
/
Copy path.env.example
File metadata and controls
158 lines (140 loc) · 8.1 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
# Copia este archivo a .env para desarrollo local.
# Genera secrets reales con: bash scripts/generate-local-secrets.sh
# NUNCA subas tu .env real a git.
#
# Esta plantilla está alineada con `app/core/config.py::Settings`
# (M30 — drift purgado en el audit pass: REDIS_URL, CIRCUIT_BREAKER_*,
# OTEL_EXPORTER_OTLP_ENDPOINT y similares no tenían call site real
# en el branch `core` y se eliminaron).
# ── App ────────────────────────────────────────────────────────────────────────
APP_ENV=local
APP_NAME=CopilotoIA Core
API_HOST=0.0.0.0
API_PORT=8000
# Nivel de log: DEBUG para desarrollo, INFO para producción
LOG_LEVEL=INFO
# Indica que hay un reverse proxy (nginx/ALB/Cloudflare) delante que
# SOBREESCRIBE X-Forwarded-For. Cuando false (default), el rate limiter
# usa el peer IP TCP (no spoofable). Setear true solo si el proxy strip
# + reinyecta el header correcto. Sin proxy: dejar en false.
TRUST_PROXY_FORWARDED_FOR=false
# ── Base de datos (PostgreSQL) ─────────────────────────────────────────────────
DATABASE_URL=postgresql://copiloto_app:change-me-app-password@postgres:5432/copilotoia
# DATABASE_ADMIN_URL solo lo usan los scripts de bootstrap/migración
# (`infra/postgres/*.sql` corre como `copiloto_admin`). El runtime de la
# app usa exclusivamente DATABASE_URL con el rol limitado `copiloto_app`.
DATABASE_ADMIN_URL=postgresql://copiloto_admin:change-me-admin-password@postgres:5432/copilotoia
POSTGRES_DB=copilotoia
POSTGRES_USER=copiloto_admin
POSTGRES_PASSWORD=change-me-admin-password
APP_DB_USER=copiloto_app
APP_DB_PASSWORD=change-me-app-password
# Pool de conexiones asyncpg. Aumentar max_size si hay carga sostenida
# en endpoints lentos; un solo WS estancado puede saturar la app si
# max_size es bajo.
DB_POOL_MIN_SIZE=1
DB_POOL_MAX_SIZE=10
DB_POOL_COMMAND_TIMEOUT_SECONDS=30.0
# ── Autenticación JWT ──────────────────────────────────────────────────────────
JWT_ISSUER=copilotoia-local
JWT_AUDIENCE=copilotoia-panel
# Mínimo 16 caracteres. Generar con: openssl rand -base64 48
JWT_SECRET=change-me-super-long-jwt-secret-min-32-chars
# TTL del JWKS cache de Auth0 (en segundos). El default de 5min cumple
# con el SLO de rotación de keys de Auth0 sin pegarle al endpoint en
# cada request.
AUTH0_JWKS_CACHE_TTL_SECONDS=300
# Auth0/OIDC se carga desde .env.auth0.local, generado por scripts/configure-auth0.sh.
# No dupliques aquí AUTH0_DOMAIN, AUTH0_AUDIENCE ni AUTH0_CLAIMS_NAMESPACE.
#
# El script genera DOS bloques de credenciales, NO los mezcles:
#
# AUTH0_ADMIN_CLIENT_ID / AUTH0_ADMIN_CLIENT_SECRET_FILE
# → regular web app del panel (authorization_code + refresh_token).
# Usado SOLO para el login del Admin Panel (app/admin/routes.py).
#
# AUTH0_SERVICE_CLIENT_ID / AUTH0_SERVICE_CLIENT_SECRET_FILE (M59)
# → M2M app `copilotoia-service-m2m` para Auth0 Management API.
# Cuando está seteado, `app.services.auth0_admin` puede:
# - invitar usuarios reales (POST /tickets/password-change con email)
# - bloquear / desbloquear logins (PATCH /users/{id} blocked=...)
# - resetear MFA enrollments (DELETE /users/{id}/multifactor/*)
# - borrar usuarios (DELETE /users/{id})
# Sin esto, los handlers de members funcionan en modo LOCAL ONLY
# (sólo `app.user_tenant_roles`, sin propagar a Auth0).
# Cache TTL del Management API token: AUTH0_MGMT_TOKEN_CACHE_TTL_SECONDS
# (default 20h, Auth0 emite tokens con 24h por default).
# Enforcement de MFA para roles privilegiados (admin/owner/platform_owner).
# Default true: producción queda protegida. Ponlo en false SOLO en entornos
# cuyo plan de Auth0 no incluye el add-on de MFA (el factor OTP y el Action
# challengeWith no están disponibles), para que el desarrollo local no quede
# bloqueado por un flujo de MFA que Auth0 no puede servir.
MFA_ENFORCEMENT_ENABLED=true
# M60/A-003 — fallback al header spoofable `x-admin-user-email` cuando
# el access_token NO trae el claim `email` namespaced.
#
# Default true por COMPAT con instalaciones existentes. Setear a false
# DESPUÉS de re-correr `scripts/configure-auth0.sh` (que deploya el
# Action que emite el claim).
#
# Con false: el Core sólo confía en el JWT firmado por Auth0 → no es
# spoofable. Si el Action no emite el claim, el email queda None y
# la reconciliación de invitaciones M57 (vía email) no funciona.
# Verificar con: ver log `auth.email_from_header_fallback` en stdout
# del Core después de un login → si NO aparece, el claim ya llega
# en el token y es seguro pasar a false.
AUTH0_TRUST_ADMIN_EMAIL_HEADER=true
# ─── Email transaccional (M61 — Resend) ──────────────────────────────────────
# Resend es el provider de email para invitaciones, notificaciones, etc.
# Sign up free: https://resend.com (3K emails/mes free, $20/mo 50K).
#
# Configurar EN ORDEN:
# 1. Crear API key en https://resend.com/api-keys
# 2. Verificar tu dominio (SPF + DKIM + DMARC) en https://resend.com/domains
# Sin dominio verificado, los emails caen a spam o son rechazados.
# 3. Guardar la key en .secrets/resend-api-key (chmod 600):
# echo -n 're_xxxxxxxxxxxx' > .secrets/resend-api-key
# chmod 600 .secrets/resend-api-key
# 4. Apuntar el setting al file:
RESEND_API_KEY_FILE=.secrets/resend-api-key
# RESEND_API_KEY= # alternativa plaintext (para tests/k8s secrets)
# Sender — el `from` del email. Tiene que ser un dominio verificado en
# Resend (paso 2 arriba). Si dejás el default `[email protected]`
# en un tenant Resend distinto, los emails se rechazan con `from_not_verified`.
EMAIL_FROM_NAME=CopilotoIA
# URL pública del SPA para construir los `accept_url` de las invitaciones.
# Producción: 'https://app.copilotoia.com'. Local: 'http://localhost:3000'.
APP_PUBLIC_URL=http://localhost:3000
# TTL de los tokens de invitación (default 7 días).
INVITATION_TOKEN_TTL_SECONDS=604800
# Anti-bulk-spam: max invitaciones por hora por admin.
INVITATION_SEND_RATE_PER_HOUR=20
# Token interno para comunicación entre servicios (mínimo 16 caracteres)
SERVICE_TOKEN=change-me-internal-service-token-min-32-chars
# AUDIT-48 — rotación dual. Cuando rotás el SERVICE_TOKEN, poné el viejo
# acá temporalmente; el backend acepta cualquiera de los dos hasta que
# completés el rollout y borres este campo. Vacío == no segundo token.
SERVICE_TOKEN_NEXT=
# Master key Fernet usada para cifrar las API keys de proveedores IA
# en `app.platform_secrets.ciphertext`.
# Genera una con:
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# `scripts/generate-local-secrets.sh` la crea automáticamente para dev.
AI_PROVIDER_MASTER_KEY=change-me-fernet-32-bytes-base64urlsafe-44-chars=
# ── Almacenamiento S3 / MinIO ──────────────────────────────────────────────────
S3_ENDPOINT_URL=http://minio:9000
S3_BUCKET=copilotoia-local
S3_ACCESS_KEY_ID=copilotoia-minio
S3_SECRET_ACCESS_KEY=change-me-minio-password
# ── Rate limiting (por IP + tenant si está en el path) ─────────────────────────
RATE_LIMIT_PER_MIN=60
RATE_LIMIT_WEBHOOK_PER_MIN=600
# AUDIT-46 — cap + TTL del dict in-memory de buckets para evitar
# memory leak por rotación NAT/IPv6.
RATE_LIMIT_BUCKET_TTL_SECONDS=900
RATE_LIMIT_BUCKET_MAX_ENTRIES=10000
# ── Observabilidad ─────────────────────────────────────────────────────────────
# `/metrics` (Prometheus exposition) restringido a IPs allowlisted.
# Lista separada por comas; vacío → endpoint inaccesible. No soporta CIDR.
OBSERVABILITY_ALLOWED_IPS=