diff --git a/flake.nix b/flake.nix --- a/flake.nix +++ b/flake.nix @@ -353,6 +353,16 @@ services.tangled.appview.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.appview; }; + nixosModules.knotmirror = { + lib, + pkgs, + ... + }: { + imports = [./nix/modules/knotmirror.nix]; + + services.tangled.knotmirror.tap-package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.tap; + services.tangled.knotmirror.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.knotmirror; + }; nixosModules.knot = { lib, pkgs, diff --git a/nix/modules/knotmirror.nix b/nix/modules/knotmirror.nix new file mode 100644 --- /dev/null +++ b/nix/modules/knotmirror.nix @@ -0,0 +1,143 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.services.tangled.knotmirror; +in + with lib; { + options.services.tangled.knotmirror = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable a tangled knot"; + }; + + package = mkOption { + type = types.package; + description = "Package to use for the knotmirror"; + }; + + tap-package = mkOption { + type = types.package; + description = "tap package to use for the knotmirror"; + }; + + listenAddr = mkOption { + type = types.str; + default = "0.0.0.0:7000"; + description = "Address to listen on"; + }; + + adminListenAddr = mkOption { + type = types.str; + default = "127.0.0.1:7200"; + description = "Address to listen on"; + }; + + hostname = mkOption { + type = types.str; + example = "my.knotmirror.com"; + description = "Hostname for the server (required)"; + }; + + dbUrl = mkOption { + type = types.str; + example = "postgresql://..."; + description = "Database URL. postgresql expected (required)"; + }; + + atpPlcUrl = mkOption { + type = types.str; + default = "https://plc.directory"; + description = "atproto PLC directory"; + }; + + atpRelayUrl = mkOption { + type = types.str; + default = "https://relay1.us-east.bsky.network"; + description = "atproto relay"; + }; + + fullNetwork = mkOption { + type = types.bool; + default = false; + description = "Whether to automatically mirror from entire network"; + }; + + tap = { + port = mkOption { + type = types.port; + default = 7480; + description = "Internal port to run the knotmirror tap"; + }; + + dbUrl = mkOption { + type = types.str; + default = "sqlite:///var/lib/knotmirror-tap/tap.db"; + description = "database connection string (sqlite://path or postgres://...)"; + }; + }; + }; + config = mkIf cfg.enable { + environment.systemPackages = [ + pkgs.git + cfg.package + ]; + + systemd.services.tap-knotmirror = { + description = "knotmirror tap service"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + LogsDirectory = "knotmirror-tap"; + StateDirectory = "knotmirror-tap"; + Environment = [ + "TAP_BIND=:${toString cfg.tap.port}" + "TAP_PLC_URL=${cfg.atpPlcUrl}" + "TAP_RELAY_URL=${cfg.atpRelayUrl}" + "TAP_RESYNC_PARALLELISM=10" + "TAP_DATABASE_URL=${cfg.tap.dbUrl}" + "TAP_RETRY_TIMEOUT=60s" + "TAP_COLLECTION_FILTERS=sh.tangled.repo" + ( + if cfg.fullNetwork + then "TAP_SIGNAL_COLLECTION=sh.tangled.repo" + else "TAP_FULL_NETWORK=false" + ) + ]; + ExecStart = "${getExe cfg.tap-package} run"; + }; + }; + + systemd.services.knotmirror = { + description = "knotmirror service"; + after = ["network.target" "tap-knotmirror.service"]; + wantedBy = ["multi-user.target"]; + path = [ + pkgs.git + ]; + serviceConfig = { + LogsDirectory = "knotmirror"; + StateDirectory = "knotmirror"; + Environment = [ + # TODO: add environment variables + "MIRROR_LISTEN=${cfg.listenAddr}" + "MIRROR_HOSTNAME=${cfg.hostname}" + "MIRROR_TAP_URL=http://localhost:${toString cfg.tap.port}" + "MIRROR_DB_URL=${cfg.dbUrl}" + "MIRROR_GIT_BASEPATH=/var/lib/knotmirror/repos" + "MIRROR_KNOT_USE_SSL=true" + "MIRROR_KNOT_SSRF=true" + "MIRROR_RESYNC_PARALLELISM=12" + "MIRROR_METRICS_LISTEN=127.0.0.1:7100" + "MIRROR_ADMIN_LISTEN=${cfg.adminListenAddr}" + "MIRROR_SLURPER_CONCURRENCY=4" + ]; + ExecStart = "${getExe cfg.package} serve"; + Restart = "always"; + }; + }; + }; + }