{ config, lib, pkgs, homeyConfig, ... }: # Paperless-ngx — document management with OCR. # # Auth model: HTTP Remote User SSO. Authelia authenticates via Caddy # forward_auth and sets the Remote-User header; Paperless trusts it and # auto-creates/logs in the user. No separate Paperless login needed. # # The admin user (set via homey.paperless.adminUser) is created as a # superuser on first start. Its password is randomly generated and never # used — all logins go through Authelia. # # Requires a Redis sidecar for Celery task workers. # # Volume layout: # /paperless/data/ → /usr/src/paperless/data (DB, index) # /paperless/media/ → /usr/src/paperless/media (document files) # /paperless/consume/ → /usr/src/paperless/consume (drop folder) # /paperless/export/ → /usr/src/paperless/export (export output) # # Secrets consumed from sops: # paperless/secret_key let cfg = config.homey.paperless; dataDir = config.homey.storage.mountPoint; domain = homeyConfig.domain; in { options.homey.paperless = { enable = lib.mkEnableOption "Paperless-ngx document management"; image = lib.mkOption { type = lib.types.str; default = "ghcr.io/paperless-ngx/paperless-ngx:latest"; }; redisImage = lib.mkOption { type = lib.types.str; default = "docker.io/redis:7-alpine"; }; port = lib.mkOption { type = lib.types.port; default = 8083; description = "Host port Paperless listens on (bound to 127.0.0.1)."; }; }; config = lib.mkIf cfg.enable { # ----------------------------------------------------------------------- # Secrets # ----------------------------------------------------------------------- sops.secrets."paperless/secret_key" = { owner = "root"; }; # ----------------------------------------------------------------------- # Redis — Celery task queue, stateless (no persistent storage) # ----------------------------------------------------------------------- virtualisation.oci-containers.containers.paperless-redis = { image = cfg.redisImage; extraOptions = [ "--network=homey" ]; }; systemd.services."podman-paperless-redis" = { after = lib.mkAfter [ "podman-homey-network.service" ]; requires = lib.mkAfter [ "podman-homey-network.service" ]; }; # ----------------------------------------------------------------------- # Paperless container # ----------------------------------------------------------------------- virtualisation.oci-containers.containers.paperless = { image = cfg.image; ports = [ "127.0.0.1:${toString cfg.port}:8000" ]; environment = { PAPERLESS_REDIS = "redis://paperless-redis:6379"; PAPERLESS_URL = "https://paperless.${domain}"; PAPERLESS_ALLOWED_HOSTS = "paperless.${domain}"; PAPERLESS_CORS_ALLOWED_HOSTS = "https://paperless.${domain}"; PAPERLESS_TIME_ZONE = homeyConfig.timezone; PAPERLESS_OCR_LANGUAGE = "eng"; USERMAP_UID = "1000"; USERMAP_GID = "1000"; # SSO via Authelia: Caddy's forward_auth copies Remote-User from # Authelia's response; Gunicorn/WSGI exposes it as HTTP_REMOTE_USER. PAPERLESS_ENABLE_HTTP_REMOTE_USER = "true"; PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME = "HTTP_REMOTE_USER"; # Redirect to Authelia on logout so the SSO session is also cleared. PAPERLESS_LOGOUT_REDIRECT_URL = "https://auth.${domain}"; }; environmentFiles = [ "/run/paperless-secrets.env" ]; volumes = [ "${dataDir}/paperless/data:/usr/src/paperless/data" "${dataDir}/paperless/media:/usr/src/paperless/media" "${dataDir}/paperless/consume:/usr/src/paperless/consume" "${dataDir}/paperless/export:/usr/src/paperless/export" ]; extraOptions = [ "--network=homey" ]; }; # ----------------------------------------------------------------------- # ExecStartPre: write ephemeral secrets env file # ----------------------------------------------------------------------- systemd.services."podman-paperless" = { serviceConfig = { ExecStartPre = [ (pkgs.writeShellScript "paperless-write-secrets" '' set -euo pipefail install -m 600 /dev/null /run/paperless-secrets.env printf '%s\n' \ "PAPERLESS_SECRET_KEY=$(cat ${config.sops.secrets."paperless/secret_key".path})" \ >> /run/paperless-secrets.env '') ]; }; postStop = "rm -f /run/paperless-secrets.env"; after = lib.mkAfter [ "mnt-data.mount" "podman-paperless-redis.service" "podman-homey-network.service" ]; requires = lib.mkAfter [ "mnt-data.mount" "podman-paperless-redis.service" "podman-homey-network.service" ]; }; # ----------------------------------------------------------------------- # Uptime Kuma monitor # ----------------------------------------------------------------------- homey.monitoring.monitors = [{ name = "Paperless"; url = "https://paperless.${domain}"; interval = 60; }]; }; }