diff --git a/flake.lock b/flake.lock index 53fd2d5..9400c88 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1771587924, - "narHash": "sha256-eVYOGmF8nQBhudJyU6lHdgJI87kvGz8JyCq5/Vi9Mjk=", + "lastModified": 1772153824, + "narHash": "sha256-T65qXmlcD9qFpPTi+mOXsn4dIkO2N8Ls67nqmuzepv0=", "owner": "catppuccin", "repo": "nix", - "rev": "b0c65edbf31c2ad3d84438d82c2310f2c28373f3", + "rev": "4b0f5b7bf7b3eeb484d49524f3c9791864ab9362", "type": "github" }, "original": { @@ -22,11 +22,11 @@ }, "crane": { "locked": { - "lastModified": 1771121070, - "narHash": "sha256-aIlv7FRXF9q70DNJPI237dEDAznSKaXmL5lfK/Id/bI=", + "lastModified": 1771796463, + "narHash": "sha256-9bCDuUzpwJXcHMQYMS1yNuzYMmKO/CCwCexpjWOl62I=", "owner": "ipetkov", "repo": "crane", - "rev": "a2812c19f1ed2e5ed5ce2ef7109798b575c180e1", + "rev": "3d3de3313e263e04894f284ac18177bd26169bad", "type": "github" }, "original": { @@ -111,11 +111,11 @@ ] }, "locked": { - "lastModified": 1769996383, - "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", "type": "github" }, "original": { @@ -173,11 +173,11 @@ "zon2nix": "zon2nix" }, "locked": { - "lastModified": 1771991459, - "narHash": "sha256-VRbgXfW0DM3Kx3JEWFKj9LZQGng6FwfCDCfgoIweRVk=", + "lastModified": 1772465579, + "narHash": "sha256-dXfemWSyeQ6DHQXYSjDwYlvDQMyBoIyh7so849orUsw=", "owner": "ghostty-org", "repo": "ghostty", - "rev": "4c8f2bc77b218349839b8e929a981a2bdf4734a8", + "rev": "8cddd384c6ef614a373cf52b2a0e835c7e85134a", "type": "github" }, "original": { @@ -236,11 +236,11 @@ ] }, "locked": { - "lastModified": 1771851181, - "narHash": "sha256-gFgE6mGUftwseV3DUENMb0k0EiHd739lZexPo5O/sdQ=", + "lastModified": 1772380461, + "narHash": "sha256-O3ukj3Bb3V0Tiy/4LUfLlBpWypJ9P0JeUgsKl2nmZZY=", "owner": "nix-community", "repo": "home-manager", - "rev": "9a4b494b1aa1b93d8edf167f46dc8e0c0011280c", + "rev": "f140aa04d7d14f8a50ab27f3691b5766b17ae961", "type": "github" }, "original": { @@ -279,11 +279,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1771834715, - "narHash": "sha256-5VI2KiMifx3Dca7nDJzctO3HpnS6zrvesdkLoZBrQRY=", + "lastModified": 1772216104, + "narHash": "sha256-1TnGN26vnCEQk5m4AavJZxGZTb/6aZyphemRPRwFUfs=", "owner": "nix-community", "repo": "lanzaboote", - "rev": "b798c53da0f7e521317a5413335096a21070cf0b", + "rev": "dbe5112de965bbbbff9f0729a9789c20a65ab047", "type": "github" }, "original": { @@ -302,11 +302,11 @@ "xwayland-satellite-unstable": "xwayland-satellite-unstable" }, "locked": { - "lastModified": 1771940378, - "narHash": "sha256-qe5t8E8uK5eSgPTxtfcde3VO8fnIr/Tu+hn72FDry/E=", + "lastModified": 1772433239, + "narHash": "sha256-5pPusMALo7ZYEoW/iHUxK7rLk3Kg8sJ8Sdf7IcfK5HA=", "owner": "sodiboo", "repo": "niri-flake", - "rev": "f8899e60a1425d21a03a05ac2c069a85398039b5", + "rev": "c56af55f5563f7c7043ed45ed2566a69a638448d", "type": "github" }, "original": { @@ -336,11 +336,11 @@ "niri-unstable": { "flake": false, "locked": { - "lastModified": 1771849386, - "narHash": "sha256-CFvjBjS2LxbBMR3Lu6wZhME6ck3CXyKUufRoJA5tlmw=", + "lastModified": 1772207631, + "narHash": "sha256-Jkkg+KqshFO3CbTszVVpkKN2AOObYz+wMsM3ONo1z5g=", "owner": "YaLTeR", "repo": "niri", - "rev": "2dc6f4482c4eeed75ea8b133d89cad8658d38429", + "rev": "e708f546153f74acf33eb183b3b2992587a701e5", "type": "github" }, "original": { @@ -357,11 +357,11 @@ ] }, "locked": { - "lastModified": 1771563879, - "narHash": "sha256-vA5hocvdGhr+jfBN7A7ogeZqIz2qx01EixXwdVsQcnE=", + "lastModified": 1772386632, + "narHash": "sha256-sm6OpWZuoDwR53KNlsY482YOoHFWlWYwt0wHmqLkRGE=", "owner": "nix-community", "repo": "NixOS-WSL", - "rev": "379d20c55f552e91fb9f3f0382e4a97d3f452943", + "rev": "be894604b2aa2184c0b3d3b44995acd0da14dc0c", "type": "github" }, "original": { @@ -385,11 +385,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1771903837, - "narHash": "sha256-sdaqdnsQCv3iifzxwB22tUwN/fSHoN7j2myFW5EIkGk=", + "lastModified": 1772047000, + "narHash": "sha256-7DaQVv4R97cii/Qdfy4tmDZMB2xxtyIvNGSwXBBhSmo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951", + "rev": "1267bb4920d0fc06ea916734c11b0bf004bbe17e", "type": "github" }, "original": { @@ -401,11 +401,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1771848320, - "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", + "lastModified": 1772198003, + "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2fc6539b481e1d2569f25f8799236694180c0993", + "rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61", "type": "github" }, "original": { @@ -417,11 +417,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1771923393, - "narHash": "sha256-lJiQtL47xYV37Vod9KgPS7dmi1x51A5+chJE48qKimw=", - "rev": "ea7f1f06811ce7fcc81d6c6fd4213150c23edcf2", + "lastModified": 1772419343, + "narHash": "sha256-xAHiI07j8nMZuA53r40AoWgYH9bKCgaVowNq5MpkE3g=", + "rev": "93178f6a00c22fcdee1c6f5f9ab92f2072072ea9", "type": "tarball", - "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre953762.ea7f1f06811c/nixexprs.tar.xz?lastModified=1771923393&rev=ea7f1f06811ce7fcc81d6c6fd4213150c23edcf2" + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre956909.93178f6a00c2/nixexprs.tar.xz" }, "original": { "type": "tarball", @@ -448,14 +448,15 @@ "inputs": { "nixpkgs": [ "nixpkgs" - ] + ], + "noctalia-qs": "noctalia-qs" }, "locked": { - "lastModified": 1771979542, - "narHash": "sha256-0hqvh2D8tdwFG9pT9Ds1EysZYPTKpVJCunnXQ31MUZ8=", + "lastModified": 1772453415, + "narHash": "sha256-8TCMSFCBZdutKryFKX72GOb/NWL9/vB5rswgWXV/EuM=", "owner": "noctalia-dev", "repo": "noctalia-shell", - "rev": "fe8e23f1d0e6ed4bdefd061e6834c4d61f2266be", + "rev": "8ebf2bf33220c62f3c5e937a318eceb25dd17228", "type": "github" }, "original": { @@ -464,6 +465,27 @@ "type": "github" } }, + "noctalia-qs": { + "inputs": { + "nixpkgs": [ + "noctalia", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772227064, + "narHash": "sha256-f821ZSoGpa/aXrWq0gPpea9qBnX8KDyavGKkptz2Mog=", + "owner": "noctalia-dev", + "repo": "noctalia-qs", + "rev": "0741d27d2f7db567270f139c5d1684614ecf9863", + "type": "github" + }, + "original": { + "owner": "noctalia-dev", + "repo": "noctalia-qs", + "type": "github" + } + }, "pre-commit": { "inputs": { "flake-compat": "flake-compat_2", @@ -474,11 +496,11 @@ ] }, "locked": { - "lastModified": 1770726378, - "narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=", + "lastModified": 1771858127, + "narHash": "sha256-Gtre9YoYl3n25tJH2AoSdjuwcqij5CPxL3U3xysYD08=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae", + "rev": "49bbbfc218bf3856dfa631cead3b052d78248b83", "type": "github" }, "original": { @@ -530,11 +552,11 @@ ] }, "locked": { - "lastModified": 1771125043, - "narHash": "sha256-ldf/s49n6rOAxl7pYLJGGS1N/assoHkCOWdEdLyNZkc=", + "lastModified": 1771988922, + "narHash": "sha256-Fc6FHXtfEkLtuVJzd0B6tFYMhmcPLuxr90rWfb/2jtQ=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "4912f951a26dc8142b176be2c2ad834319dc06e8", + "rev": "f4443dc3f0b6c5e6b77d923156943ce816d1fcb9", "type": "github" }, "original": { @@ -550,11 +572,11 @@ ] }, "locked": { - "lastModified": 1771889317, - "narHash": "sha256-YV17Q5lEU0S9ppw08Y+cs4eEQJBuc79AzblFoHORLMU=", + "lastModified": 1772401007, + "narHash": "sha256-YHykQg0h9hrlZGpMcywnaFzQ1Kn/5YNCCOSaaAl6z7Q=", "owner": "Mic92", "repo": "sops-nix", - "rev": "b027513c32e5b39b59f64626b87fbe168ae02094", + "rev": "d8be5ea4cd3bc363492ab5bc6e874ccdc5465fe4", "type": "github" }, "original": { @@ -646,11 +668,11 @@ "xwayland-satellite-unstable": { "flake": false, "locked": { - "lastModified": 1771787042, - "narHash": "sha256-7bM6Y4KldhKnfopSALF8XALxcX7ehkomXH9sPl4MXp0=", + "lastModified": 1772429643, + "narHash": "sha256-M+bAeCCcjBnVk6w/4dIVvXvpJwOKnXjwi/lDbaN6Yws=", "owner": "Supreeeme", "repo": "xwayland-satellite", - "rev": "33c344fee50504089a447a8fef5878cf4f6215fc", + "rev": "10f985b84cdbcc3bbf35b3e7e43d1b2a84fa9ce2", "type": "github" }, "original": { diff --git a/modules/pds/nixos.nix b/modules/pds/nixos.nix index dd5db9f..551b40c 100644 --- a/modules/pds/nixos.nix +++ b/modules/pds/nixos.nix @@ -1,7 +1,6 @@ { config, lib, - pkgs, options, ... }: @@ -16,177 +15,7 @@ let ; cfg = config.services.pds-with-backups; - - pdsUser = "pds"; - pdsGroup = "pds"; - inherit (cfg) secretsFiles; - - litestreamConfig = pkgs.writeText "litestream-pds-config.yml" '' - dbs: - - dir: ${cfg.dataDir} - pattern: "*.sqlite" - recursive: true - watch: true - replica: - type: s3 - path: ${cfg.backupS3Prefix} - bucket: ''${S3_BUCKET} - ''; - - restoreScript = pkgs.writeShellApplication { - name = "pds-litestream-restore"; - runtimeInputs = with pkgs; [ - awscli2 - litestream - gnugrep - gnused - coreutils - findutils - gawk - ]; - excludeShellChecks = [ - "SC1091" - "SC2046" - "SC2168" - "SC1090" - "SC2043" - ]; - text = '' - main() { - set -euo pipefail - - echo "[PDS Restore] Starting automatic restore from S3..." - - for f in ${toString secretsFiles}; do - if [ -f "$f" ]; then - set -a - source "$f" - set +a - else - echo "[PDS Restore] Error: Secrets file not found: $f" - exit 1 - fi - done - - if [ -z "''${S3_BUCKET:-}" ]; then - echo "[PDS Restore] Error: S3_BUCKET not set in secrets file" - exit 1 - fi - - s3Bucket="''${S3_BUCKET}" - s3Prefix="${cfg.backupS3Prefix}" - - run_aws() { - local envArgs=() - if [ -n "''${AWS_ENDPOINT_URL:-}" ]; then - envArgs+=("AWS_ENDPOINT_URL=''${AWS_ENDPOINT_URL}") - fi - env "''${envArgs[@]}" aws "$@" - } - - run_litestream() { - local envArgs=() - if [ -n "''${AWS_ENDPOINT_URL:-}" ]; then - envArgs+=("AWS_ENDPOINT_URL=''${AWS_ENDPOINT_URL}") - fi - env "''${envArgs[@]}" litestream "$@" - } - - echo "[PDS Restore] Verifying S3 connectivity..." - local retries=5 - local connected=false - for i in $(seq 1 $retries); do - if run_aws s3 ls "s3://$s3Bucket/" &>/dev/null; then - connected=true - break - fi - echo "[PDS Restore] Waiting for S3 bucket (attempt $i/$retries)..." - sleep 2 - done - - if [ "$connected" = false ]; then - echo "[PDS Restore] Error: Cannot connect to S3 bucket: $s3Bucket" - exit 1 - fi - - echo "[PDS Restore] S3 connection verified" - - local objects - objects=$(run_aws s3 ls "s3://$s3Bucket/$s3Prefix" --recursive 2>/dev/null | awk '{print $4}' || true) - - local databases - databases=$(echo "$objects" | grep '\.sqlite/' | sed 's|.*/\([^/]*\.sqlite\).*|\1|' | sort -u || true) - - if [ -z "$databases" ]; then - echo "[PDS Restore] No databases found in S3 at s3://$s3Bucket/$s3Prefix" - echo "[PDS Restore] New deployment - skipping restore." - exit 0 - fi - - echo "[PDS Restore] Found databases to restore:" - echo "$databases" - echo "" - - mkdir -p "${cfg.dataDir}" - - local restoredCount=0 - for db in $databases; do - local localPath="${cfg.dataDir}/$db" - local s3DbPath="$s3Prefix/$db" - local s3DbUrl="s3://$s3Bucket/$s3DbPath" - - if [ -f "$localPath" ]; then - echo "[PDS Restore] Database already exists locally: $db (skipping)" - continue - fi - - echo "[PDS Restore] Restoring database: $db" - mkdir -p "$(dirname "$localPath")" - - if run_litestream restore -if-db-not-exists -if-replica-exists -o "$localPath" "$s3DbUrl"; then - echo "[PDS Restore] Successfully restored: $db" - restoredCount=$((restoredCount + 1)) - - if [ -f "$localPath" ]; then - chown ${pdsUser}:${pdsGroup} "$localPath" - chmod 644 "$localPath" - fi - else - echo "[PDS Restore] Warning: Failed to restore $db" - fi - done - - echo "" - echo "[PDS Restore] Restore completed. Restored $restoredCount database(s)." - - if [ $restoredCount -eq 0 ]; then - echo "[PDS Restore] No new databases restored. PDS will start with fresh state." - fi - } - - main - ''; - }; - - healthCheckScript = pkgs.writeShellScript "pds-healthcheck" '' - set -euo pipefail - - if ! systemctl is-active --quiet bluesky-pds; then - echo "[PDS HealthCheck] PDS service is not running" - exit 1 - fi - - if [ -f "${cfg.dataDir}/primary.sqlite" ]; then - if ! systemctl is-active --quiet litestream-pds; then - echo "[PDS HealthCheck] Litestream service is not running" - exit 1 - fi - fi - - echo "[PDS HealthCheck] All services healthy" - exit 0 - ''; in { options.services.pds-with-backups = { @@ -210,20 +39,6 @@ in example = [ "/run/secrets/pds.env" ]; }; - backupS3Prefix = mkOption { - type = types.strMatching "[^/].*[^/]"; - default = "backups"; - description = "S3 directory prefix for Litestream replicas."; - example = "pds-backups"; - }; - - backupLogDir = mkOption { - type = types.path; - default = "/var/log/pds-backup"; - description = "Directory for backup and restore logs."; - internal = true; - }; - settings = mkOption { inherit (options.services.bluesky-pds.settings) type; default = { }; @@ -247,114 +62,5 @@ in ]; environmentFiles = secretsFiles; }; - - users.users.${pdsUser} = { - isSystemUser = true; - group = pdsGroup; - description = "Bluesky PDS service user"; - }; - users.groups.${pdsGroup} = { }; - - systemd.tmpfiles.rules = [ - "d ${cfg.dataDir} 0755 ${pdsUser} ${pdsGroup} -" - "d ${cfg.backupLogDir} 0755 ${pdsUser} ${pdsGroup} -" - ]; - - systemd.services.bluesky-pds = { - after = [ - "network.target" - "pds-restore.service" - ]; - wants = [ "pds-restore.service" ]; - serviceConfig.Restart = lib.mkDefault "on-failure"; - serviceConfig.RestartSec = "10s"; - }; - - systemd.services.pds-restore = { - description = "PDS Automatic Restore from S3"; - wantedBy = [ "multi-user.target" ]; - before = [ "bluesky-pds.service" ]; - - serviceConfig = { - Type = "oneshot"; - ExecStart = "${restoreScript}/bin/pds-litestream-restore"; - EnvironmentFile = secretsFiles; - User = pdsUser; - Group = pdsGroup; - RemainAfterExit = true; - - NoNewPrivileges = true; - ProtectSystem = "strict"; - ProtectHome = true; - PrivateTmp = true; - RestrictRealtime = true; - }; - }; - - systemd.services.litestream-pds = { - description = "Litestream real-time replication for PDS databases"; - after = [ - "network.target" - "pds-restore.service" - "bluesky-pds.service" - ]; - requires = [ "bluesky-pds.service" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - ExecStart = "${pkgs.litestream}/bin/litestream replicate -config ${litestreamConfig}"; - EnvironmentFile = secretsFiles; - User = pdsUser; - Group = pdsGroup; - Restart = "on-failure"; - RestartSec = "5s"; - - NoNewPrivileges = true; - ProtectSystem = "strict"; - ProtectHome = true; - ReadWritePaths = [ - cfg.dataDir - cfg.backupLogDir - ]; - RestrictRealtime = true; - MemoryDenyWriteExecute = true; - }; - }; - - systemd.services.pds-healthcheck = { - description = "PDS Health Check"; - after = [ - "bluesky-pds.service" - "litestream-pds.service" - ]; - requires = [ "bluesky-pds.service" ]; - - serviceConfig = { - Type = "oneshot"; - ExecStart = healthCheckScript; - User = pdsUser; - Group = pdsGroup; - - NoNewPrivileges = true; - ProtectSystem = "strict"; - ProtectHome = true; - }; - - startAt = "hourly"; - }; - - systemd.timers.pds-healthcheck = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnCalendar = "hourly"; - Persistent = true; - }; - }; - - environment.systemPackages = with pkgs; [ - litestream - restoreScript - awscli2 - ]; }; }