diff --git a/DISCOURSE_RECOVERY.md b/DISCOURSE_RECOVERY.md deleted file mode 100644 index e3ffb70..0000000 --- a/DISCOURSE_RECOVERY.md +++ /dev/null @@ -1,139 +0,0 @@ -# Discourse State and Database Recovery - -This restores Discourse from the BorgBase backup configured in `discourse.nix`. -It replaces the current Discourse state directory and PostgreSQL database with -the selected Borg archive. - -The backup contains: - -- `/var/lib/discourse` -- `/var/backup/discourse/discourse.dump` - -## Requirements - -- BorgBase repo URL: - `ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo` -- Decrypted Borg SSH private key from the flash drive -- Borg repository passphrase from the flash drive -- A target server that has already been switched to this NixOS config -- A shell with `borg`, `openssh`, `postgresql`, and `rsync` - -On NixOS or another machine with Nix: - -```sh -nix-shell -p borgbackup openssh postgresql rsync -``` - -## Prepare Borg Access - -Copy the Borg SSH key into a local recovery directory: - -```sh -mkdir -p ~/borg-recovery/discourse -cp /path/to/flash/borg-private-key ~/borg-recovery/discourse/borg-private-key -chmod 600 ~/borg-recovery/discourse/borg-private-key -``` - -Set the Borg connection environment: - -```sh -export BORG_REPO='ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo' -export BORG_RSH='ssh -i ~/borg-recovery/discourse/borg-private-key -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new' -``` - -Read the Borg passphrase without showing it on screen: - -```sh -read -rsp 'Borg passphrase: ' BORG_PASSPHRASE -export BORG_PASSPHRASE -echo -``` - -## Extract a Backup - -List available archives: - -```sh -borg list -``` - -Choose an archive name from the list, then extract only the Discourse state and -database dump into a temporary directory. Do not extract directly into `/`. - -```sh -export ARCHIVE='ARCHIVE_NAME_FROM_BORG_LIST' - -mkdir -p ~/borg-recovery/discourse/extract -cd ~/borg-recovery/discourse/extract - -borg extract ::$ARCHIVE var/lib/discourse var/backup/discourse/discourse.dump -``` - -## Restore on the Server - -Run these commands from `~/borg-recovery/discourse/extract` on the target -server. They assume the NixOS config has already created the `discourse` user, -`postgresql.service`, and `discourse-postgresql.service`. - -Capture absolute tool paths so they still work through `sudo`: - -```sh -RSYNC="$(command -v rsync)" -PSQL="$(command -v psql)" -DROPDB="$(command -v dropdb)" -PG_RESTORE="$(command -v pg_restore)" -``` - -Stop Discourse and make sure PostgreSQL is running: - -```sh -sudo systemctl stop discourse.service -sudo systemctl start postgresql.service -``` - -Restore `/var/lib/discourse`: - -```sh -sudo "$RSYNC" -a --delete ./var/lib/discourse/ /var/lib/discourse/ -sudo chown -R discourse:discourse /var/lib/discourse -``` - -Replace the `discourse` PostgreSQL database: - -```sh -sudo -u postgres "$PSQL" -d postgres -v ON_ERROR_STOP=1 \ - -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'discourse';" - -sudo -u postgres "$DROPDB" --if-exists discourse -sudo systemctl restart discourse-postgresql.service - -sudo -u postgres "$PG_RESTORE" \ - --exit-on-error \ - --no-owner \ - --role=discourse \ - --dbname=discourse \ - ./var/backup/discourse/discourse.dump - -sudo systemctl restart discourse-postgresql.service -``` - -Start Discourse: - -```sh -sudo systemctl start discourse.service -``` - -## Verify - -Check the service and recent logs: - -```sh -sudo systemctl status discourse.service --no-pager -sudo journalctl -u discourse.service -b --no-pager -n 100 -``` - -If networking and DNS are already restored, open: - -```text -https://discourse.torahimderecheretz.com -``` diff --git a/discourse.nix b/discourse.nix deleted file mode 100644 index d51b58f..0000000 --- a/discourse.nix +++ /dev/null @@ -1,131 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -{ - services.discourse = { - enable = true; - admin = { - email = "yisroel.d.baum@gmail.com"; - fullName = "Yisroel Baum"; - passwordFile = config.sops.secrets."discourse-admin-password".path; - username = "yisroeldbaum"; - }; - database.ignorePostgresqlVersion = true; - hostname = "discourse.torahimderecheretz.com"; - mail = { - notificationEmailAddress = "system@torahimderecheretz.com"; - contactEmailAddress = "system@torahimderecheretz.com"; - - outgoing = { - serverAddress = "in-v3.mailjet.com"; - port = 587; - username = "1ebdd73d944240c1a9c3201e52a1e8c6"; - passwordFile = config.sops.secrets."discourse-mail-key".path; - authentication = "login"; - }; - }; - secretKeyBaseFile = config.sops.secrets."discourse-secret-key".path; - siteSettings = { - required = { - title = "Torah Im Derech Eretz"; - site_description = "A forum to discuss the ideas of Rav Shamshon Refael Hirsch"; - }; - }; - }; - - services.borgbackup.jobs.discourse = { - paths = [ - "/var/lib/discourse" - "/var/backup/discourse/discourse.dump" - ]; - repo = "ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo"; - user = "root"; - group = "root"; - encryption = { - mode = "repokey-blake2"; - passCommand = "${pkgs.coreutils}/bin/cat ${config.sops.secrets."borg-passphrase".path}"; - }; - doInit = true; - compression = "auto,zstd"; - startAt = "*-*-* 07:15:00"; - persistentTimer = true; - prune.keep = { - daily = 7; - weekly = 4; - monthly = 6; - }; - readWritePaths = [ "/var/backup/discourse" ]; - environment.BORG_RSH = "ssh -i ${config.sops.secrets."borg-private-key".path} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.config/borg/known_hosts"; - preHook = '' - discourseWasActive=/root/.cache/borg/discourse-was-active - dumpFile=/var/backup/discourse/discourse.dump - - if ${pkgs.systemd}/bin/systemctl is-active --quiet discourse.service; then - touch "$discourseWasActive" - ${pkgs.systemd}/bin/systemctl stop discourse.service - else - rm -f "$discourseWasActive" - fi - - rm -f "$dumpFile" - umask 077 - ${lib.getExe' pkgs.su "su"} -s ${pkgs.runtimeShell} ${config.services.postgresql.superUser} -c '${config.services.postgresql.package}/bin/pg_dump --format=custom --dbname=${config.services.discourse.database.name}' > "$dumpFile" - ''; - postHook = '' - discourseWasActive=/root/.cache/borg/discourse-was-active - dumpFile=/var/backup/discourse/discourse.dump - - rm -f "$dumpFile" - - if [ -e "$discourseWasActive" ]; then - ${pkgs.systemd}/bin/systemctl start discourse.service - rm -f "$discourseWasActive" - fi - ''; - }; - - systemd.tmpfiles.rules = [ - "d /var/backup/discourse 0700 root root - -" - ]; - - systemd.services.borgbackup-job-discourse = { - after = [ - "sops-install-secrets.service" - "network-online.target" - "postgresql.target" - "discourse-postgresql.service" - ]; - wants = [ - "network-online.target" - "postgresql.target" - "discourse-postgresql.service" - ]; - }; - - sops.secrets = { - "discourse-admin-password" = { - sopsFile = ./secrets/discourseAdminPassword.yaml; - mode = "0400"; - key = "password"; - owner = "discourse"; - group = "discourse"; - }; - "discourse-secret-key" = { - sopsFile = ./secrets/discourseSecretKey.yaml; - mode = "0400"; - key = "key"; - owner = "discourse"; - group = "discourse"; - }; - "discourse-mail-key" = { - sopsFile = ./secrets/discourseMailKey.yaml; - mode = "0400"; - key = "key"; - owner = "discourse"; - group = "discourse"; - }; - }; -} diff --git a/flake.lock b/flake.lock index cb1d765..bdd385d 100644 --- a/flake.lock +++ b/flake.lock @@ -122,34 +122,18 @@ "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "tide": { "inputs": { "nixpkgs": [ "nixpkgs" - ], - "utils": "utils_2" + ] }, "locked": { - "lastModified": 1778485449, - "narHash": "sha256-Y8p1JKfU7I1T33qI7F6zXobtQqLqry4NVBb2eAlnBqc=", + "lastModified": 1784446675, + "narHash": "sha256-r7X6qZeRLdOskfw3Ktz9VbatkHu/4Tb0wjpYx8AVZdc=", "ref": "refs/heads/master", - "rev": "ae9dd787a6ac64c300251cf047274dd76af6d2d6", - "revCount": 137, + "rev": "21cca99e9e06f7aec0d14ba782cecad5e56ab098", + "revCount": 140, "type": "git", "url": "https://git.yisroelbaum.com/yisroelbaum/TIDE" }, @@ -175,24 +159,6 @@ "repo": "flake-utils", "type": "github" } - }, - "utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 3921e65..08739f7 100644 --- a/flake.nix +++ b/flake.nix @@ -45,7 +45,6 @@ nixosConfigurations.nixos = nixpkgs.lib.nixosSystem { modules = [ ./configuration.nix - ./discourse.nix ./tide.nix ./rabbi-gerzi.nix home-manager.nixosModules.home-manager diff --git a/secrets/README.md b/secrets/README.md index 5350e1a..b4eb134 100644 --- a/secrets/README.md +++ b/secrets/README.md @@ -15,12 +15,14 @@ host's age key. ``` sudo grep "public key" /var/lib/sops-nix/key.txt ``` -3. On a workstation, put that public key into `.sops.yaml` at - the repo root and encrypt `tide.yaml.example` into - `tide.yaml`. +3. On a workstation, put that public key into `.sops.yaml` at the repository + root and encrypt the required secret files. -`tide.yaml` is encrypted and committed. `tide.yaml.example` is -the plaintext template. +## TIDE + +Create `tide.yaml` from `tide.yaml.example` and encrypt it with sops. It must +contain `admin-password`, `mail-key`, and `secret-key`. The encrypted file is +committed; the example contains placeholders only. ## Rabbi Gerzi diff --git a/secrets/discourseAdminPassword.yaml b/secrets/discourseAdminPassword.yaml deleted file mode 100644 index 398e0ef..0000000 --- a/secrets/discourseAdminPassword.yaml +++ /dev/null @@ -1,16 +0,0 @@ -password: ENC[AES256_GCM,data:6/a9U7I2joMSkfiysYgMrBN3AE3DRUjQsZw=,iv:EDeN/IClY/yNkVi/FLiTeeOjkj5Df/1l4XXzf5STot0=,tag:8uoXPXJuL4JlMGzgtK6D+w==,type:str] -sops: - age: - - recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvYWtUZ2JYU3VNaHQ1bXVL - V0ZmRVIxVDg2SE5GcU5UMTZjcnVOUlBZOHlzCjc5elV3bk54ak05WStvNGUvNCtP - aVNJT0tFTVFMb2NKTlpValJ6YWFVcEEKLS0tIDlEUjRncWFsUGptZTBmSm1idm0z - ZUgxUHhvejMreHUrbDVwTkxwOE1DSkUK62qiTlgQvKruDj3Bh/8nJH+5CBl+fzbJ - q5ryzSfTpEcSfBwRENqIOYMOTjzr+y+JGymjokc9rCYb0NPQUFpHzg== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-06-02T18:00:59Z" - mac: ENC[AES256_GCM,data:H/+voyLLpsGxgTNjN96PL+I1tpW8Ylo+mWVTbsaUMnnOZIkfA81MzEjtdWOs0Pg4AysAeMbmJUob+gTtSfcqnVvhv/FBVHuPji+Vz0cj0TM8k7oLlaIgeL/MAZ1mhrxDIUK5pQYSVnaaWrQ43Z4DeqaboDY7feUOp8n6emICo5I=,iv:dCPOGBC80ZtM0ObeRRMAGH2f4K5zFtckaxKAJZll8yI=,tag:xwBJzExJXAw0T39hPdmgRw==,type:str] - unencrypted_suffix: _unencrypted - version: 3.12.2 diff --git a/secrets/discourseMailKey.yaml b/secrets/discourseMailKey.yaml deleted file mode 100644 index ff58b3b..0000000 --- a/secrets/discourseMailKey.yaml +++ /dev/null @@ -1,16 +0,0 @@ -key: ENC[AES256_GCM,data:Flvbs7sRCQ3BtSJXWurmVA+QQmvSo/fxrOVJNalBjj8=,iv:ddmk1XCjvNRmmqGhZwOWDlYSkdKz3ryyQwrav/T6Oz8=,tag:DvSjwdK01UAvBL1mYCe7kA==,type:str] -sops: - age: - - recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVUGFmM010b3NhdCtPME5O - RWRkUjF0akVEZVFtNkhZeUVSeWpWL3pKNG1nCllObGlPOTcrSyt1a2NCc0U4TVZM - cWVQbjJocEUySHFYaDFmNzllOGhuQWcKLS0tIHFJeFdUWENxK1RRSlhvSEI4NXlO - TG1wM1FTWjBBWHdHNnBUUE1kNWE4akUK1Ehv5kYNZrbf3m3vH5Oyy/g2mz8kIX8T - l2k1LbhUo3cNzIjkNM05RSrLZEBPItoo2nc9oTAPZiF1U/K76SMP6g== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-06-03T07:59:50Z" - mac: ENC[AES256_GCM,data:E4WwhOqUCmOBXwYUR5nFzth59F50KE7k88NkOBc6ERnX4b5joy0lmExenprH019UVJ/HSXOCWphUMwTat70uzwhaKBtlfa7AWmWyMa6zhIzxTsFagI4h1nWQP00uGb0MM3ULR3mcLtxXyTtZFWuY/d8ttdYBuv2oe29ikJTX610=,iv:gAZPLiYcNGq8LSEpIxDzDqdan+CJKyQ+SnQXHmponns=,tag:3tI0it3h6foT9W7zK3rmxg==,type:str] - unencrypted_suffix: _unencrypted - version: 3.12.2 diff --git a/secrets/discourseSecretKey.yaml b/secrets/discourseSecretKey.yaml deleted file mode 100644 index 0265ff6..0000000 --- a/secrets/discourseSecretKey.yaml +++ /dev/null @@ -1,16 +0,0 @@ -key: ENC[AES256_GCM,data:lZXbXrorH/GGGg93WgnG6GlzNs0NEy0BlForhgKlwllsf2uJWnBukQDDxxI6gUWMp+Uzc3e9plrmGbM7HQfsccDoQux7kJjGdt5CJgou0eh4J8WGw51M+beixVdF6UuCFFiuLERstzpIP1jhmyDfLZjMVTwfoRe0/YP52Ljgl2Y=,iv:pShAMIXtbEuV7Y2o+3NgTIbPPaoZvx9qF7lj60LMabw=,tag:P6vnrqWzJXXhUl+sJrY2gw==,type:str] -sops: - age: - - recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlTHF2cGFIQXE2UDJhaVZX - KzhXcWNPbW0zYjBPbTVReUJKOW1ReEt5ZFFNCjFnU25UVVY5eXFyaFFMbkI1NHZ5 - WHRIZ1pYT1ZVWUFJc2hrcGtYTndPdVEKLS0tIHZUSzkxUjEvU0kzRG93Vnc1ZGQz - WGZ3U2dvYmFIVlYvZ3c1Q2JsQkNGek0KGYlQEPskcDN2VxkHskctCTIjPjse1wLL - 6VcEuidu69gkMqvDXR4bmnt8hxSqdH6OS3qEuDfLpqndjfCh//4CDw== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-06-02T18:08:12Z" - mac: ENC[AES256_GCM,data:jYec4f5lr8jYCVvzpM2a3lpIILOTKauz1DNuDIclL7kGA2QXJhW1njXKhmp/VCVbZLfoHCJk6SHyMcxUcx6HXtmc7FAVpu5+Vc/9N+D3gKlyXewDeC41CFdZ7GU0vtoVQK3KQe82rWl+6SRCWXeqLfmRlh7/pLwZy+bj20kG0XY=,iv:sIYEumkyDVrWZRVZpFPtrM96p82rtxW870LRkpHFsPY=,tag:Ey9L21LN+2HjCReuoqJtKQ==,type:str] - unencrypted_suffix: _unencrypted - version: 3.12.2 diff --git a/secrets/tide.yaml b/secrets/tide.yaml index 6e57edf..99818b7 100644 --- a/secrets/tide.yaml +++ b/secrets/tide.yaml @@ -1,16 +1,18 @@ -tide-env: ENC[AES256_GCM,data:O4B6jatKVbbVoO53QNgh1ra0Ij9cxo64j+wmmeTZWgX7Us3z0FT3hsOsmYWBsCrKFIGpA2meO4QXlowbJOw43sO7CiCFKuRN7rv58XbGk/hevNhUUTrqU8Seha18ZeoOfmUhD1ScJtyC5RkJb1xFnsFWmFtofebyabhcSLXmJz4UC7Vl2BsVZWULjp9LIkAIOcr2Y6Dm6SoXQa3n6p5yt5rKlY/Xfg/zQoczfi0TG+x+P4SzPPv2U8PJQGG1OqQ0bA82wFNcna+mIb6QIvWz27PeBK4pDfDYnA0SqzKEZsol3WgF5LWPXL5zQpsh11oy7YBWdx8omo3KNBx3+tl2/ACYLuTKRgrxP3UIE38hAm4j95IrL88jFWeNtfZ/fMdTckuQKR2vgbN6Q5HYqnYW72uxCYGZGWD+Jq7MAUduAbvzSlurLoHnKCrA+4Pf8Tu/yJBVDN+urhxnWJy8VpGDXFAGA+reXLtNjZXg1y2KNJOijmzEYfwZ8t53MvCdsJnF562mvCag+DXGex6DZ2qqKeXGa3Lw/YTPBXCHjHJfr+0l91NoOjelA33wS+h+RisHw/iCPlt3NHQgJtCXN7JRWM1Hi3p1ADgovvqJ7yBsT3OXD91QIy5NPSergGsj2vvj+9VBEzSTUuHA4KzYgxrjLUgLeohypr4veFs3qnC3/iTx9gkRfcFX0SMPgj1DWy9+pS42Wi+YDgILAQVSSOgrjhnMv2E0nYNEgZcTPS6SKj+Li4V3/OUVjbMj4N6BLsXxbhyFrKH6MF7ZOXU=,iv:yj0aUCReke2gI3uk/Qer6QBSCQgTwZmCaK7aPBtUwDo=,tag:e6NcaEL3f4EFsaGQKIH+BA==,type:str] +admin-password: ENC[AES256_GCM,data:yPOLYZ9oMvZrXcfgJQkGL83qPpHrJe7i4Gw=,iv:WmPO6hDNkB7nckFuJjnZX5/8xXpZkUxP8FkjmYmQxzs=,tag:g1dLh009OOBYYL9UkPz3uw==,type:str] +mail-key: ENC[AES256_GCM,data:/9bT8jECSyMqz45LLbWSV15OAJ4wA/ZiCvi9uYrSZK8=,iv:ZN9taTVcIHxqz63pxir7RWTp4CpjVxUBTH+HO92CvPU=,tag:ufVCA36rVBE2ghbI63QXfA==,type:str] +secret-key: ENC[AES256_GCM,data:pFsKF3A9RZSxeV9FwNKIzp+Avu6YWCCHj9zIeex7nHqhuFNuL8D4blQWPujms3wRTUj/OdCbgh56ZXdnEJ6l0vYBKBnP7tS7v7jBhZWtF03SqukCpu+c2HCO6wNvP0tw9zVVSVp0RRmvLq7KhmL6Y9748/TA08MPCVNVP46Xa2E=,iv:yH4vU6kjM+49ssWVAULJcZ9ny264zWsixGs/et6G1Lk=,tag:UWH3Epqn4yGqeTYeBcrDPg==,type:str] sops: age: - - recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v - enc: | + - enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnQ2RzU0NBUTBhQTBERnBm - RFRPdWlKRWtWMW8zaktGVyt5L3VPTENMakhVCjFsdStSbHFzdmFYcWxuU294VW5Q - MWVQMzlJdmdqTW93Q1MwaVRFaVIvY2sKLS0tIHJxUk96VjRLeWpIanBlNktESDhI - dS9VREordndFSVhnbXFCdUVFN0Z3aG8KN4h2ptJxttY/02FcmRqZa+ujom0LIS11 - cS+qkrk7FnrTbSCNETtjc/FApLTxphptd93zCPJkQtulmo2d9soJlA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZEtYbEhoNW5FVnRlQXIr + TWJaVk5RUmdaUnNKcHRwbElWWCtLQ3l6cm00ClVzNWJZYzRpS21Wckd0dFlEano2 + c1k3TlNZNjU2WGVqUkExblRUZmJpbHcKLS0tIGsvWWhrREoxV2FEREFWQUNpMEZR + MlRTRk13YTljdi9LOW1talBpeDBQdmMKeDvwaLwwYjw1lLFf+/ZRmVGP+chYxa1Y + d/NjatKGm8M/hEKw/MpEKSAMMNAJPUHVwDAeaQTro19TzYerV8e9BQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-05-11T07:36:34Z" - mac: ENC[AES256_GCM,data:VDSB1iKcP9AY4HGHPlQxINlc2wB03EkCd8TcHLfFqE0zNBPYlX0Buf6kqy89/0M9UmRYGS7IlYMEDlFGJ61cidD0Kxk2n9R7P32S75nS//lorX/28Slnwlfe9K4w7eS5p+zwO0QiPY3X87or6KTyi95QBYz6ALp9cpetrRn3nJk=,iv:PPqdmFbXCFmZgfXsEUvuQy1PDde2dD/EnHAFE2TGZIk=,tag:Hv+QMEM8wkjXPUt4TGhvCQ==,type:str] + recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v + lastmodified: "2026-07-19T07:04:49Z" + mac: ENC[AES256_GCM,data:UvfEnQuGSrXNe9Zyel2UEyfcAgh03UgBu0VzSg0lzOgYuK0qPwvezZNXRyfFkHo0AdpIJAmN8pwtavExUWqL8OOUgIcooK7hZJ9S5aXFfLeZ0YhpAZ1TpUmHTNRL7VMOJtLU/phvObggYTHJfLLE92RkwnKxqJ8hIli8VVIa59w=,iv:NYcAEyoNC8cygEczLGUXzuhY/OXTXCpP3BDQJJKQwQU=,tag:PgkGZwmkiCAkO/WPZJyDYQ==,type:str] unencrypted_suffix: _unencrypted - version: 3.12.2 + version: 3.13.2 diff --git a/secrets/tide.yaml.example b/secrets/tide.yaml.example index 40dfc8a..8986906 100644 --- a/secrets/tide.yaml.example +++ b/secrets/tide.yaml.example @@ -1,20 +1,5 @@ -# Encrypt this with sops to produce ./tide.yaml: -# -# sops --encrypt --age $(cat ~/.config/sops/age/keys.txt | grep public | cut -d: -f2 | tr -d ' ') \ -# secrets/tide.yaml.example > secrets/tide.yaml -# -# Or set up .sops.yaml with the host's age public key and run -# `sops secrets/tide.yaml`. -# -# Generate APP_KEY with: -# php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;" +# Encrypt this file with sops to produce ./tide.yaml. -tide-env: | - APP_KEY=base64:REPLACE_ME - DB_PASSWORD=REPLACE_ME - MAIL_HOST=127.0.0.1 - MAIL_PORT=1025 - MAIL_USERNAME= - MAIL_PASSWORD= - MAIL_FROM_ADDRESS=noreply@tide.yisroelbaum.com - MAIL_FROM_NAME=TIDE +admin-password: REPLACE_ME +mail-key: REPLACE_ME +secret-key: REPLACE_ME diff --git a/tide.nix b/tide.nix index 80f5fdd..a34de74 100644 --- a/tide.nix +++ b/tide.nix @@ -1,37 +1,41 @@ -{ - domainName, - ... -}: +{ config, ... }: { services.tide = { enable = true; - domain = "tide.${domainName}"; - apiDomain = "apitide.${domainName}"; - secretsFile = "/run/secrets/tide-env"; - # Reuse the wildcard cert already issued for *.${domainName} - # in configuration.nix instead of requesting a new one per - # subdomain. - nginx.useACMEHost = null; + secretFiles = { + adminPassword = config.sops.secrets."tide-admin-password".path; + mailPassword = config.sops.secrets."tide-mail-password".path; + secretKeyBase = config.sops.secrets."tide-secret-key-base".path; + borgPassphrase = config.sops.secrets."borg-passphrase".path; + borgPrivateKey = config.sops.secrets."borg-private-key".path; + }; }; - # Don't fail evaluation when secrets/tide.yaml is missing (e.g. - # before the operator has encrypted it on a fresh checkout). - # sops-install-secrets will still error at activation time if - # the file is absent, which is the right place for that failure. - sops.validateSopsFiles = false; - - # Use the host's age key generated with age-keygen, not the - # SSH-host-key-derived identity sops-nix falls back to by default. - # The encrypted file's recipient is the public key paired with - # this private key. sops.age.keyFile = "/var/lib/sops-nix/key.txt"; sops.age.sshKeyPaths = [ ]; sops.gnupg.sshKeyPaths = [ ]; - sops.secrets."tide-env" = { - sopsFile = ./secrets/tide.yaml; - # phpfpm reads this via EnvironmentFile, which runs as root - # before dropping to the tide user, so root readable is enough. - mode = "0400"; + sops.secrets = { + "tide-admin-password" = { + sopsFile = ./secrets/tide.yaml; + key = "admin-password"; + mode = "0400"; + owner = "discourse"; + group = "discourse"; + }; + "tide-mail-password" = { + sopsFile = ./secrets/tide.yaml; + key = "mail-key"; + mode = "0400"; + owner = "discourse"; + group = "discourse"; + }; + "tide-secret-key-base" = { + sopsFile = ./secrets/tide.yaml; + key = "secret-key"; + mode = "0400"; + owner = "discourse"; + group = "discourse"; + }; }; }