Compare commits

...

2 commits

11 changed files with 59 additions and 419 deletions

View file

@ -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
```

View file

@ -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";
};
};
}

44
flake.lock generated
View file

@ -122,34 +122,18 @@
"type": "github" "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": { "tide": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ]
"utils": "utils_2"
}, },
"locked": { "locked": {
"lastModified": 1778485449, "lastModified": 1784446675,
"narHash": "sha256-Y8p1JKfU7I1T33qI7F6zXobtQqLqry4NVBb2eAlnBqc=", "narHash": "sha256-r7X6qZeRLdOskfw3Ktz9VbatkHu/4Tb0wjpYx8AVZdc=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "ae9dd787a6ac64c300251cf047274dd76af6d2d6", "rev": "21cca99e9e06f7aec0d14ba782cecad5e56ab098",
"revCount": 137, "revCount": 140,
"type": "git", "type": "git",
"url": "https://git.yisroelbaum.com/yisroelbaum/TIDE" "url": "https://git.yisroelbaum.com/yisroelbaum/TIDE"
}, },
@ -175,24 +159,6 @@
"repo": "flake-utils", "repo": "flake-utils",
"type": "github" "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", "root": "root",

View file

@ -45,7 +45,6 @@
nixosConfigurations.nixos = nixpkgs.lib.nixosSystem { nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
modules = [ modules = [
./configuration.nix ./configuration.nix
./discourse.nix
./tide.nix ./tide.nix
./rabbi-gerzi.nix ./rabbi-gerzi.nix
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager

View file

@ -15,12 +15,14 @@ host's age key.
``` ```
sudo grep "public key" /var/lib/sops-nix/key.txt sudo grep "public key" /var/lib/sops-nix/key.txt
``` ```
3. On a workstation, put that public key into `.sops.yaml` at 3. On a workstation, put that public key into `.sops.yaml` at the repository
the repo root and encrypt `tide.yaml.example` into root and encrypt the required secret files.
`tide.yaml`.
`tide.yaml` is encrypted and committed. `tide.yaml.example` is ## TIDE
the plaintext template.
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 ## Rabbi Gerzi

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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: sops:
age: age:
- recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v - enc: |
enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnQ2RzU0NBUTBhQTBERnBm YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZEtYbEhoNW5FVnRlQXIr
RFRPdWlKRWtWMW8zaktGVyt5L3VPTENMakhVCjFsdStSbHFzdmFYcWxuU294VW5Q TWJaVk5RUmdaUnNKcHRwbElWWCtLQ3l6cm00ClVzNWJZYzRpS21Wckd0dFlEano2
MWVQMzlJdmdqTW93Q1MwaVRFaVIvY2sKLS0tIHJxUk96VjRLeWpIanBlNktESDhI c1k3TlNZNjU2WGVqUkExblRUZmJpbHcKLS0tIGsvWWhrREoxV2FEREFWQUNpMEZR
dS9VREordndFSVhnbXFCdUVFN0Z3aG8KN4h2ptJxttY/02FcmRqZa+ujom0LIS11 MlRTRk13YTljdi9LOW1talBpeDBQdmMKeDvwaLwwYjw1lLFf+/ZRmVGP+chYxa1Y
cS+qkrk7FnrTbSCNETtjc/FApLTxphptd93zCPJkQtulmo2d9soJlA== d/NjatKGm8M/hEKw/MpEKSAMMNAJPUHVwDAeaQTro19TzYerV8e9BQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-11T07:36:34Z" recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v
mac: ENC[AES256_GCM,data:VDSB1iKcP9AY4HGHPlQxINlc2wB03EkCd8TcHLfFqE0zNBPYlX0Buf6kqy89/0M9UmRYGS7IlYMEDlFGJ61cidD0Kxk2n9R7P32S75nS//lorX/28Slnwlfe9K4w7eS5p+zwO0QiPY3X87or6KTyi95QBYz6ALp9cpetrRn3nJk=,iv:PPqdmFbXCFmZgfXsEUvuQy1PDde2dD/EnHAFE2TGZIk=,tag:Hv+QMEM8wkjXPUt4TGhvCQ==,type:str] lastmodified: "2026-07-19T07:04:49Z"
mac: ENC[AES256_GCM,data:UvfEnQuGSrXNe9Zyel2UEyfcAgh03UgBu0VzSg0lzOgYuK0qPwvezZNXRyfFkHo0AdpIJAmN8pwtavExUWqL8OOUgIcooK7hZJ9S5aXFfLeZ0YhpAZ1TpUmHTNRL7VMOJtLU/phvObggYTHJfLLE92RkwnKxqJ8hIli8VVIa59w=,iv:NYcAEyoNC8cygEczLGUXzuhY/OXTXCpP3BDQJJKQwQU=,tag:PgkGZwmkiCAkO/WPZJyDYQ==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.12.2 version: 3.13.2

View file

@ -1,20 +1,5 @@
# Encrypt this with sops to produce ./tide.yaml: # Encrypt this file 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;"
tide-env: | admin-password: REPLACE_ME
APP_KEY=base64:REPLACE_ME mail-key: REPLACE_ME
DB_PASSWORD=REPLACE_ME secret-key: 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

View file

@ -1,37 +1,41 @@
{ { config, ... }:
domainName,
...
}:
{ {
services.tide = { services.tide = {
enable = true; enable = true;
domain = "tide.${domainName}"; secretFiles = {
apiDomain = "apitide.${domainName}"; adminPassword = config.sops.secrets."tide-admin-password".path;
secretsFile = "/run/secrets/tide-env"; mailPassword = config.sops.secrets."tide-mail-password".path;
# Reuse the wildcard cert already issued for *.${domainName} secretKeyBase = config.sops.secrets."tide-secret-key-base".path;
# in configuration.nix instead of requesting a new one per borgPassphrase = config.sops.secrets."borg-passphrase".path;
# subdomain. borgPrivateKey = config.sops.secrets."borg-private-key".path;
nginx.useACMEHost = null; };
}; };
# 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.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.sshKeyPaths = [ ]; sops.age.sshKeyPaths = [ ];
sops.gnupg.sshKeyPaths = [ ]; sops.gnupg.sshKeyPaths = [ ];
sops.secrets."tide-env" = { sops.secrets = {
sopsFile = ./secrets/tide.yaml; "tide-admin-password" = {
# phpfpm reads this via EnvironmentFile, which runs as root sopsFile = ./secrets/tide.yaml;
# before dropping to the tide user, so root readable is enough. key = "admin-password";
mode = "0400"; 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";
};
}; };
} }