Saltar al contenido principal

Almacenamiento y base de datos

Almacenamiento de blobs​

Los mensajes en bruto, los adjuntos y los archivos viven en almacenamiento de objetos. Postgres guarda sus metadatos y quién los usa. Cada archivo se guarda una vez por espacio de trabajo, bajo su SHA-256, sin importar cuántos mensajes lo lleven. Un archivo que ya nada usa se borra mediante un barrido cada hora una vez pasadas CAMPBOOKS_BLOBS__SWEEP_GRACE_HOURS horas (24 por defecto). Los archivos mayores que CAMPBOOKS_BLOBS__MAX_OBJECT_BYTES (50 MiB por defecto) se rechazan.

Disco local (por defecto)​

CAMPBOOKS_BLOBS__BACKEND=fs guarda los archivos en CAMPBOOKS_BLOBS__PATH (/data/blobs, el volumen blobs-data). Las escrituras son atómicas: un archivo temporal, volcado a disco y luego renombrado a su sitio.

Almacén compatible con S3​

Sirve cualquier almacén que hable la API de S3 (AWS S3, Garage, MinIO, Ceph, Cloudflare R2). Crea un bucket privado y una clave de acceso limitada a él, y define esto en .env:

CAMPBOOKS_BLOBS__BACKEND=s3
CAMPBOOKS_BLOBS__S3__BUCKET=campbooks-blobs
CAMPBOOKS_BLOBS__S3__ENDPOINT=https://s3.example.com # unset for AWS
CAMPBOOKS_BLOBS__S3__REGION=us-east-1 # what your store expects
CAMPBOOKS_BLOBS__S3__ACCESS_KEY_ID=...
CAMPBOOKS_BLOBS__S3__SECRET_ACCESS_KEY=...
CAMPBOOKS_BLOBS__S3__PATH_STYLE=true # most self-hosted stores

El servidor comprueba al arrancar que llega al bucket y se niega a arrancar si no. Las subidas grandes se hacen por partes. Añade al bucket una regla de ciclo de vida que aborte las subidas multiparte incompletas tras un día, para que un fallo a mitad de subida no deje nada atrás. El volumen blobs-data no se usa con este backend.

Cifrado en reposo​

Campbooks aún no cifra los archivos por sí mismo. Usa el cifrado de tu almacén: cifrado del lado del servidor en el bucket, o un disco cifrado bajo blobs-data para el backend local.

Cambiar de backend​

Copia todo el árbol (claves <workspace>/<aa>/<sha256>, salvo el prefijo _staging/) al nuevo almacén con el servidor detenido, y luego cambia los ajustes. Las claves son idénticas en disco y en un bucket.

Credenciales de la base de datos​

Postgres omite la seguridad a nivel de fila para los superusuarios y los roles BYPASSRLS. Una consulta que pueda ejecutar RESET ROLE también puede volver a los poderes propios del usuario. Por eso el servidor usa dos credenciales:

UsuarioAjusteSe usa paraDebe ser
ejecuciónCAMPBOOKS_DATABASE_URL (y, opcional, CAMPBOOKS_DATABASE_PASSWORD_FILE)cada petición y tareaLOGIN, no superusuario, sin BYPASSRLS, no propietario de nada, miembro de campbooks_app
migraciónCAMPBOOKS_DATABASE_MIGRATION_URLaplicar migraciones al arrancar, nada másel propietario del esquema (o un superusuario)

Postgres incluido​

No hay nada que hacer. El servicio puntual db-init crea el usuario campbooks_runtime en cada up (es idempotente y también actualiza una instalación existente). Genera la contraseña una sola vez en el volumen db-secrets, y el servidor la lee de /run/db/runtime-password. El usuario propietario sigue siendo campbooks con POSTGRES_PASSWORD, usado solo para migraciones.

Para rotar la contraseña de ejecución, borra el archivo del volumen y vuelve a iniciar:

docker compose run --rm --entrypoint rm db-init /run/db/runtime-password
docker compose up -d

Tu propio Postgres o uno gestionado​

Crea el usuario una vez, como administrador. Las migraciones concederán después a campbooks_app lo que necesite cada tabla.

CREATE ROLE campbooks_app NOLOGIN; -- skip if it exists
CREATE ROLE campbooks_runtime LOGIN PASSWORD '...' NOSUPERUSER NOBYPASSRLS
NOCREATEDB NOCREATEROLE;
GRANT campbooks_app TO campbooks_runtime;
-- Allow the migration user to run the first migration's grants:
GRANT campbooks_app TO <migration user>; -- skip for a superuser

Después define CAMPBOOKS_DATABASE_URL=postgres://campbooks_runtime:<password>@host/db, o prescinde de la contraseña y apunta CAMPBOOKS_DATABASE_PASSWORD_FILE a un archivo que la contenga. Define CAMPBOOKS_DATABASE_MIGRATION_URL con la URL del propietario. En los servicios gestionados, el propietario es el usuario que creó la base de datos. No lo reutilices como usuario de ejecución.

También necesitas pgvector, que la imagen incluida (pgvector/pgvector:pg17) ya trae.

Aislamiento de espacios de trabajo​

Las filas de cada espacio de trabajo están protegidas por la seguridad a nivel de fila de Postgres además de por las consultas. El servidor consulta con un usuario que no puede eludir las políticas, y se niega a arrancar si pudiera.

Al arrancar, el servidor se niega a iniciar, y explica por qué, cuando el rol con el que consulta:

  • es superusuario,
  • tiene BYPASSRLS,
  • es miembro de un rol así, o
  • es propietario de tablas con seguridad a nivel de fila.

Cuando hay un usuario de migración configurado, también se niega si el usuario de ejecución aún pudiera cambiar a un rol privilegiado. Solo la antigua configuración de una única URL (un usuario privilegiado que ejecuta SET ROLE campbooks_app) sigue funcionando, con una advertencia para separar los usuarios. Si aún no puedes separarlos, consulta Solución de problemas.