From e04b7e11da9b8da1458f8c9667eb914364de646e Mon Sep 17 00:00:00 2001 From: yukkop Date: Sat, 6 Jun 2026 13:26:36 +0000 Subject: [PATCH] feat: `ente`: added --- nixos/module/hectic/service/ente.nix | 224 +++++++++++++++++++++++++ nixos/system/hectic-lab/attic.nix | 3 +- nixos/system/hectic-lab/ente.nix | 42 +++++ nixos/system/hectic-lab/hectic-lab.nix | 16 +- sus/hectic-lab.yaml | 10 +- 5 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 nixos/module/hectic/service/ente.nix create mode 100644 nixos/system/hectic-lab/ente.nix diff --git a/nixos/module/hectic/service/ente.nix b/nixos/module/hectic/service/ente.nix new file mode 100644 index 00000000..3fa25362 --- /dev/null +++ b/nixos/module/hectic/service/ente.nix @@ -0,0 +1,224 @@ +{ ... }: { + lib, + config, + ... +}: let + cfg = config.hectic.services.ente; + + webHostNames = [ + cfg.domains.accounts + cfg.domains.cast + cfg.domains.photos + ]; +in { + options.hectic.services.ente = { + enable = lib.mkEnableOption "Ente Photos self-hosted service"; + + apiDomain = lib.mkOption { + type = lib.types.str; + description = "Public hostname for the Ente Museum API."; + }; + + domains = { + accounts = lib.mkOption { + type = lib.types.str; + description = "Public hostname for the Ente accounts web app."; + }; + + cast = lib.mkOption { + type = lib.types.str; + description = "Public hostname for the Ente cast web app."; + }; + + albums = lib.mkOption { + type = lib.types.str; + description = "Public hostname for public Ente album links."; + }; + + photos = lib.mkOption { + type = lib.types.str; + description = "Public hostname for the Ente Photos web app."; + }; + }; + + maxUploadSize = lib.mkOption { + type = lib.types.str; + default = "10G"; + description = "Maximum request body accepted by nginx in front of Museum."; + }; + + disableRegistration = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether Museum should reject new account registration."; + }; + + smtp = { + enable = lib.mkEnableOption "SMTP delivery for Ente verification emails"; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "SMTP host Museum uses to send verification emails."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 25; + description = "SMTP port Museum uses to send verification emails."; + }; + + email = lib.mkOption { + type = lib.types.str; + description = "From email address used by Museum."; + }; + + senderName = lib.mkOption { + type = lib.types.str; + default = "Ente Photos"; + description = "Display name used for Ente verification emails."; + }; + + encryption = lib.mkOption { + type = lib.types.nullOr (lib.types.enum [ "tls" "ssl" ]); + default = null; + description = "Optional SMTP encryption mode. Leave null for local plaintext SMTP."; + }; + }; + + storage = { + bucket = lib.mkOption { + type = lib.types.str; + description = "S3-compatible bucket used by Ente for photo object storage."; + }; + + endpoint = lib.mkOption { + type = lib.types.str; + description = "S3-compatible endpoint URL."; + }; + + region = lib.mkOption { + type = lib.types.str; + description = "S3-compatible region name."; + }; + + hotStorage = lib.mkOption { + type = lib.types.enum [ + "b2-eu-cen" + "wasabi-eu-central-2-v3" + "scw-eu-fr-v3" + ]; + default = "b2-eu-cen"; + description = '' + Museum's primary hot-storage key. Upstream requires one of its + historical S3 storage identifiers even when the backing provider is a + generic S3-compatible service. + ''; + }; + + usePathStyleUrls = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether Museum should use path-style S3 URLs."; + }; + }; + + secrets = { + encryptionKeyFile = lib.mkOption { + type = lib.types.path; + description = "File containing Museum key.encryption."; + }; + + hashKeyFile = lib.mkOption { + type = lib.types.path; + description = "File containing Museum key.hash."; + }; + + jwtSecretFile = lib.mkOption { + type = lib.types.path; + description = "File containing Museum jwt.secret."; + }; + + s3AccessKeyFile = lib.mkOption { + type = lib.types.path; + description = "File containing the S3 access key."; + }; + + s3SecretKeyFile = lib.mkOption { + type = lib.types.path; + description = "File containing the S3 secret key."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + services.ente = { + api = { + enable = true; + enableLocalDB = true; + domain = cfg.apiDomain; + + nginx.enable = true; + + settings = { + key = { + encryption._secret = cfg.secrets.encryptionKeyFile; + hash._secret = cfg.secrets.hashKeyFile; + }; + + jwt.secret._secret = cfg.secrets.jwtSecretFile; + + s3 = { + hot_storage.primary = cfg.storage.hotStorage; + derived-storage = cfg.storage.hotStorage; + are_local_buckets = false; + use_path_style_urls = cfg.storage.usePathStyleUrls; + + ${cfg.storage.hotStorage} = { + key._secret = cfg.secrets.s3AccessKeyFile; + secret._secret = cfg.secrets.s3SecretKeyFile; + endpoint = cfg.storage.endpoint; + region = cfg.storage.region; + bucket = cfg.storage.bucket; + }; + }; + + internal.disable-registration = cfg.disableRegistration; + + smtp = lib.mkIf cfg.smtp.enable ({ + inherit (cfg.smtp) host port email; + sender-name = cfg.smtp.senderName; + } // lib.optionalAttrs (cfg.smtp.encryption != null) { + encryption = cfg.smtp.encryption; + }); + }; + }; + + web = { + enable = true; + domains = { + api = cfg.apiDomain; + inherit (cfg.domains) accounts cast albums photos; + }; + }; + }; + + services.nginx.virtualHosts = + (lib.genAttrs webHostNames (_: { + enableACME = true; + forceSSL = true; + })) // { + ${cfg.apiDomain} = { + enableACME = true; + forceSSL = true; + extraConfig = lib.mkForce '' + client_max_body_size ${cfg.maxUploadSize}; + ''; + locations."/".extraConfig = '' + proxy_read_timeout 600s; + proxy_send_timeout 600s; + ''; + }; + }; + }; +} diff --git a/nixos/system/hectic-lab/attic.nix b/nixos/system/hectic-lab/attic.nix index d1275db3..3d5e733d 100644 --- a/nixos/system/hectic-lab/attic.nix +++ b/nixos/system/hectic-lab/attic.nix @@ -8,6 +8,7 @@ hectic.services.attic = { enable = true; hostName = "cache.${domain}"; + port = 8081; environmentFile = config.sops.secrets."atticd/environment".path; storage = { bucket = "cache-hectic-lab"; @@ -23,7 +24,7 @@ client_max_body_size 0; ''; locations."/" = { - proxyPass = "http://127.0.0.1:8080"; + proxyPass = "http://127.0.0.1:8081"; }; }; } diff --git a/nixos/system/hectic-lab/ente.nix b/nixos/system/hectic-lab/ente.nix new file mode 100644 index 00000000..554d19a9 --- /dev/null +++ b/nixos/system/hectic-lab/ente.nix @@ -0,0 +1,42 @@ +{ + domain, + ... +}: { + config, + ... +}: let + enteDomain = "ente.${domain}"; +in { + hectic.services.ente = { + enable = true; + apiDomain = "api.${enteDomain}"; + disableRegistration = false; + + domains = { + accounts = "accounts.${enteDomain}"; + cast = "cast.${enteDomain}"; + albums = "albums.${enteDomain}"; + photos = "photos.${enteDomain}"; + }; + + smtp = { + enable = true; + host = "mail.${domain}"; + email = "security@${domain}"; + }; + + storage = { + bucket = "ente-hectic-lab"; + endpoint = "https://hel1.your-objectstorage.com"; + region = "hel1"; + }; + + secrets = { + encryptionKeyFile = config.sops.secrets."ente/key-encryption".path; + hashKeyFile = config.sops.secrets."ente/key-hash".path; + jwtSecretFile = config.sops.secrets."ente/jwt-secret".path; + s3AccessKeyFile = config.sops.secrets."ente/s3-access-key".path; + s3SecretKeyFile = config.sops.secrets."ente/s3-secret-key".path; + }; + }; +} diff --git a/nixos/system/hectic-lab/hectic-lab.nix b/nixos/system/hectic-lab/hectic-lab.nix index 744b16b3..3eb763de 100644 --- a/nixos/system/hectic-lab/hectic-lab.nix +++ b/nixos/system/hectic-lab/hectic-lab.nix @@ -37,6 +37,13 @@ let hashedPasswordFile = config.sops.secrets."mailserver/${name}/hashedPassword".path; }; }; + mkEnteSecret = name: { + name = "ente/${name}"; + value = { + owner = "ente"; + group = "ente"; + }; + }; in { imports = [ self.nixosModules.hectic @@ -50,6 +57,7 @@ in { (import ./attic.nix { inherit flake self inputs domain; }) (import ./containers.nix { inherit flake self inputs; }) + (import ./ente.nix { inherit domain; }) (import ./mechabellum.nix { inherit flake self inputs domain; }) (import (./. + "/sentinèlla.nix") { inherit flake self inputs domain; }) ]; @@ -115,7 +123,13 @@ in { }; "atticd/environment" = {}; "wg-bfs/private-key" = {}; - }; + } // builtins.listToAttrs (map mkEnteSecret [ + "key-encryption" + "key-hash" + "jwt-secret" + "s3-access-key" + "s3-secret-key" + ]); }; users.users.root.openssh.authorizedKeys.keys = [ diff --git a/sus/hectic-lab.yaml b/sus/hectic-lab.yaml index 9bb45bf0..8a554d46 100644 --- a/sus/hectic-lab.yaml +++ b/sus/hectic-lab.yaml @@ -26,6 +26,12 @@ acme: porkbun: api_key: ENC[AES256_GCM,data:HPfPUNe2N+1xrJ6+s+MaQo20r/Mlb7+U7hRyd28s+PzVG+DBp7H/WQ54WkIRrwGOCMrb7gM0B3wBq2TbIQjHuQ0amwA=,iv:ITg2xcff+3jL5wAN4Ku0CYditv0PzytWyE/yWEzX5FM=,tag:wRo/8ThcETau+jw7sFXoIw==,type:str] secret_api_key: ENC[AES256_GCM,data:Rl+SNkFgYdWC+Mhrcgaw5+eOvhFt9OU8QKlDyh3KDFM477a3dXQlhYZjQGFcR1T/1FIIT/6/XOqDO/1s/dd1yxuftlI=,iv:JT2rSayOGmSnN0pBhIkPCRY+zQhhM0JsF+SNmLn4OeM=,tag:WxC1EiJns3VXIDRT2R3QCQ==,type:str] +ente: + key-encryption: ENC[AES256_GCM,data:kja0HZTO13bcJY72kk4aXvM4LDXOyzS5jG26auxGQX/jA4rT1nA9jet3ASM=,iv:ppEU5xVOE3QcHJUkY4PxukaSM9CUoyHz8pTlpb8oquk=,tag:0q49qseE2N4fWwHaB6fjgQ==,type:str] + key-hash: ENC[AES256_GCM,data:wYDrOwy2/WnrQtqvzZLTI4rD83RkSkvBuD5vREreLk9wW18eoEEQumHACRJVdQR1EZxMje4/CTTCRvLHKcOc3o/llRFJ8vLBqdHhTEQeUZ3IAKh1BWDRKg==,iv:syi6krvE13qLeCk7jF2cCfp+niEnhA00+aAHkGU5p9Y=,tag:O8dqC3z0e2lDRsXgt1Jb5A==,type:str] + jwt-secret: ENC[AES256_GCM,data:eeeEKFEYQORkohScWaa8CMi01ja2xm/ugg8qTROAvqDLPxsRDLTSmGtViEQ=,iv:muWnFhviaUwcZbYTbdh2guhl6NEX8BcGEX7xU41ZKSM=,tag:Z930RePBGmN5QqM1VYaEKQ==,type:str] + s3-access-key: ENC[AES256_GCM,data:pBzVhJ7Yotr2VSMiTPYibA3LHZA=,iv:fb8DPWqNdLI4E/JAdnlljUXpImzs90sIGTp9nSYqNuE=,tag:eJfv11MQJE41yNs1vka+rw==,type:str] + s3-secret-key: ENC[AES256_GCM,data:75RblYJK5Ou4PDbATeYgcm+F+yiFoeqbYKqx2oNh1sJ1GV3hh8z3UQ==,iv:Tr05iUoToD/0B3DFkLYzmEJbQGAa02YsrpVOPD9J7pw=,tag:MXrp8byymCEM1Diderximg==,type:str] atticd: environment: ENC[AES256_GCM,data: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,iv:fw6IAduBaMMocxZrka2ZdOdNxAEQarGZXSi8DFCVNT8=,tag:CO9QQCZw8EGeS4Y4Xx7p1Q==,type:str] sops: @@ -75,7 +81,7 @@ sops: Yk43ZmlTc09aNFV1VjdjN2RWQlFWTDQKcYSvA2lHP8GS0lkYY19Tm8RXmFHQX5Ck qV2Fn22Fic4M5FVKDEMfaO6WmeXgki9a8dGeO9LlC+Phf16SOq7eLw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-06-05T12:36:09Z" - mac: ENC[AES256_GCM,data:OCAG7TQlSU+l8n+m7scbHt8WFoIbR0q/UsRFYWJX0KKzapks1ocYoOluovkrWzbL+GlknVkjfL5iEproILC6UdoSTEFu8t50bU5QCOizsi0xURZWyXCi5dPZ0z8zQCCWjqyj0MPGK6aZM+JLpRnpHgDu7Ivy3sfi659lDqgWsbI=,iv:g9sD2Y2uZ0PiqpjIqvG9cVRlB+Y3rM9z7AP72cq8IGo=,tag:c97+Dy8g1XOASRmuNVyL/A==,type:str] + lastmodified: "2026-06-06T12:32:43Z" + mac: ENC[AES256_GCM,data:cmATu3qAaY7B74NvbejyMQMflh3akc0eLV+yal0NrTSE3rMd8rXYiAv1d2j/RxNd2ZLvnj5g6PFBP3O7v0JuYjKcNOvImz7SR2RRIaEWfT9Nl7XGtpTn5EpIi5KiL3unsqZTjvGrRywyLVfg0bxdKxX7RLnrBInX/zPVvMRNCL0=,iv:YpQdmsloPs3LrXA2FodziBCfxNfKrImPSe7TLNO1KH0=,tag:0By4hddfHl/0a5souDsJBw==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2