diff options
| author | Martin Fischer <martin@push-f.com> | 2026-10-25 09:08:01 +0100 |
|---|---|---|
| committer | Martin Fischer <martin@push-f.com> | 2026-10-25 19:10:47 +0100 |
| commit | 41212a7a15198e53d205252a02069a7dd6ddadeb (patch) | |
| tree | 38a24eebf956b2453675ac1cefec925faf09de02 /service.nix | |
initial commit
Diffstat (limited to 'service.nix')
| -rw-r--r-- | service.nix | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/service.nix b/service.nix new file mode 100644 index 0000000..37d567e --- /dev/null +++ b/service.nix @@ -0,0 +1,122 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (lib) mkEnableOption mkIf mkMerge mkOption types; + + cfg = config.services.bsky-news; + + mkService = botName: bot: operation: { + description = "Bluesky ${botName} ${operation} job"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + + environment.BSKY_CREDENTIALS_FILE = "%d/bsky"; + + serviceConfig = { + Type = "oneshot"; + ExecStart = lib.escapeShellArgs [ + (lib.getExe' cfg.package "bsky-news") + botName + operation + ]; + + LoadCredential = "bsky:${bot.credentialsFile}"; + DynamicUser = true; + StateDirectory = "bsky-news-${botName}"; + WorkingDirectory = "%S/bsky-news-${botName}"; + UMask = "0077"; + + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + ProtectClock = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictSUIDSGID = true; + }; + }; + + mkTimer = unitName: schedule: randomizedDelaySec: { + description = "Run ${unitName} periodically"; + wantedBy = [ "timers.target" ]; + + timerConfig = { + OnCalendar = schedule; + Persistent = true; + RandomizedDelaySec = randomizedDelaySec; + Unit = "${unitName}.service"; + }; + }; + + servicesForBot = botName: bot: + let + breakingUnit = "bsky-news-${botName}-breaking"; + in + { + ${breakingUnit} = mkService botName bot "breaking"; + }; + + timersForBot = botName: bot: + let + breakingUnit = "bsky-news-${botName}-breaking"; + in + { + ${breakingUnit} = mkTimer breakingUnit bot.breakingNews.schedule bot.breakingNews.randomizedDelaySec; + }; +in +{ + options.services.bsky-news = { + enable = mkEnableOption "periodic Bluesky news bots"; + + package = mkOption { + type = types.package; + default = pkgs.callPackage ./default.nix { }; + defaultText = lib.literalExpression "pkgs.callPackage ./default.nix { }"; + description = "The bsky-news package to run."; + }; + + bots = mkOption { + default = { }; + description = "Bluesky bot accounts and their schedules."; + type = types.attrsOf (types.submodule ({ ... }: { + options = { + credentialsFile = mkOption { + type = types.str; + description = '' + Path to a credentials file, normally an agenix secret. It is + exposed to the process through systemd's credential directory; + BSKY_CREDENTIALS_FILE contains the resulting path. + ''; + }; + + breakingNews = { + schedule = mkOption { + type = types.str; + default = "*:0/10"; + description = "systemd OnCalendar expression for breaking news."; + }; + + randomizedDelaySec = mkOption { + type = types.str; + default = "30s"; + description = "Maximum random delay applied to breaking-news runs."; + }; + }; + + }; + })); + }; + }; + + config = mkIf cfg.enable { + systemd.services = mkMerge (lib.mapAttrsToList servicesForBot cfg.bots); + systemd.timers = mkMerge (lib.mapAttrsToList timersForBot cfg.bots); + }; +} |
