Producción
Esta página trata sobre llevar una instancia a producción: cómo colocarla detrás de un proxy inverso de la forma correcta, qué significa el invariante de instancia única y cómo respaldarla. Notarium se despliega como un único contenedor — la configuración de producción se reduce a las tres cosas que siguen.
Proxy inverso y cabeceras reenviadas
Delante de la aplicación colocas un proxy inverso (nginx, Caddy, Traefik) que termina el TLS y hace de proxy hacia el puerto de Notarium. El requisito clave: el proxy debe reenviar las cabeceras que describen la dirección externa.
El proxy inverso debe enviar X-Forwarded-Host (o dejar Host intacto) y X-Forwarded-Proto: https. De lo contrario, las mutaciones autenticadas por cookie desde la interfaz se rechazan como cross-origin — el síntoma es «lo veo, pero no puedo guardar». Reenviar X-Forwarded-Proto también hace falta para que la cookie de sesión reciba el flag Secure.
La razón es esta: la comprobación de Origin en las mutaciones contrasta el origen de la petición con la dirección que ve el navegador — y esa dirección llega en una cabecera reenviada. Las llamadas de agentes mediante un PAT de tipo Bearer están exentas de la comprobación (no llevan cookie, así que no hay superficie de CSRF). El proxy, por su parte, debe sobrescribir las cabeceras reenviadas con sus propios valores, en vez de dejar pasar tal cual las que manda el cliente.
Si activas la autorización OAuth para agentes, define PUBLIC_BASE_URL cuando la instancia se ejecute detrás del proxy (por ejemplo, https://notes.example.com) — una dirección externa estable para los metadatos de OAuth. Sin ella, la dirección se deriva de las cabeceras reenviadas. Consulta Configuración.
Confianza en la IP del cliente
Un eje aparte del de la dirección es la IP real del cliente. Contra ella se cuentan dos límites: los intentos de inicio de sesión y la admisión de nuevos clientes OAuth. Detrás de un proxy todas las peticiones llegan desde la misma dirección, así que sin configuración explícita esos límites se contarían por proxy — es decir, contra todo el mundo a la vez.
El ajuste que lo gobierna es TRUST_PROXY: una lista separada por comas de las IP/CIDR de los proxies inmediatos.
# .env — pon la dirección de tu propio contenedor de proxy o de tu host
TRUST_PROXY=172.18.0.0/16
El valor por defecto seguro es dejar la variable sin definir: entonces X-Forwarded-For no influye en absoluto en los límites y ninguna cabecera puede falsificar la IP de otro. Defínela solo cuando conozcas con certeza la dirección de tu proxy, y mantén la lista lo más acotada posible.
Los valores booleanos, los recuentos de saltos, los rangos con nombre y los rangos que abarcan todas las direcciones (/0) se rechazan en el arranque. Confiar en todas las direcciones significaría que cualquier cliente podría asignarse una IP por cabecera y saltarse el límite de inicio de sesión.
Este ajuste no afecta al reenvío de X-Forwarded-Host ni de X-Forwarded-Proto — son ejes independientes, y el contrato de la sección anterior sigue igual.
El invariante de instancia única
Notarium está construido para un único proceso. Dos piezas del estado de autenticación viven en la memoria del proceso:
- límite de tasa de inicio de sesión — los contadores de intentos;
- el registro de sockets SSE — el mecanismo mediante el cual revocar el acceso derriba al instante las conexiones activas.
Detrás de un balanceador de carga con varias instancias y sin almacenamiento compartido, estos mecanismos se rompen: un atacante multiplica el límite entre las instancias, y revocar el acceso en una instancia no cerrará una conexión SSE que se mantiene abierta en otra.
Ambas piezas de estado viven en la memoria del proceso, así que detrás de un balanceador de carga con varias instancias y sin almacenamiento compartido estos mecanismos no funcionan. Mover la base de datos de metadatos a Postgres te da estado compartido, pero eso por sí solo no basta para el escalado horizontal. Mantén una única instancia.
Copias de seguridad
La copia de seguridad canónica es un comando integrado de la imagen, no una copia de los archivos hecha desde fuera: notarium backup arma un ZIP verificado y lo transmite (stream) mientras el servicio sigue en marcha.
docker compose exec -T notarium backup > notarium-$(date -u +%Y%m%dT%H%M%SZ).zip
docker compose exec -T notarium backup verify < notarium-20260731.zip
La verificación va dentro del trabajo periódico, no en un gesto puntual: no cambia nada y detecta la corrupción antes del día en que necesites el archivo de copia de seguridad. Para el trabajo en sí, una simple redirección no basta — toma la secuencia de publicación segura (archivo temporal → volcado a disco → enlace duro atómico) del runbook: Copia de seguridad y restauración. Esa página cubre también la restauración en un volumen limpio y dónde el comando deja de ser aplicable.
meta.db en calientecp /data/meta.db, o copiar el volumen con el servicio en marcha, no es una copia de seguridad: la base de datos de metadatos funciona en modo WAL, puede haber filas ya confirmadas esperando en meta.db-wal, y los archivos copiados uno a uno no son una instantánea de un mismo instante.
Qué entra en la copia de seguridad, y por qué:
| Qué | Función |
|---|---|
/data/spaces | Tus archivos Markdown — la fuente de verdad. |
/data/meta.db | La base de datos de metadatos (historial, usuarios, accesos) — irrecuperable a partir de los archivos. |
/data/jobs | Artefactos y subidas de los trabajos de importación/exportación. |
/data/engine | Los índices derivados del motor. No entran en el archivo de copia de seguridad: se reconstruyen a partir de los archivos. |
Si la base de datos de metadatos se ha movido a Postgres, o las notas viven fuera de la raíz de datos, el comando integrado termina con un error en lugar de producir un archivo parcial — respalda la base de datos y los directorios montados con las herramientas estándar de tu proveedor. Para saber exactamente qué guarda la base de datos de metadatos, consulta la página Base de datos.