diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md index 4d7306ddf87ec..0eb029b337dd0 100644 --- a/nixos/doc/manual/release-notes/rl-2505.section.md +++ b/nixos/doc/manual/release-notes/rl-2505.section.md @@ -125,6 +125,8 @@ - [victorialogs][https://docs.victoriametrics.com/victorialogs/], log database from VictoriaMetrics. Available as [services.victorialogs](#opt-services.victorialogs.enable) +- [haven](https://github.com/bitvora/haven), is a high availability vault for events on nostr. Available as [services.haven](options.html#opt-services.haven.enable). + - [nostr-rs-relay](https://git.sr.ht/~gheartsfield/nostr-rs-relay/), This is a nostr relay, written in Rust. Available as [services.nostr-rs-relay](options.html#opt-services.nostr-rs-relay.enable). - [Prometheus Node Cert Exporter](https://github.com/amimof/node-cert-exporter), a prometheus exporter to check for SSL cert expiry. Available under [services.prometheus.exporters.node-cert](#opt-services.prometheus.exporters.node-cert.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index a09388b9058a1..1a3213bef6490 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1504,6 +1504,7 @@ ./services/web-apps/guacamole-client.nix ./services/web-apps/guacamole-server.nix ./services/web-apps/hatsu.nix + ./services/web-apps/haven.nix ./services/web-apps/healthchecks.nix ./services/web-apps/hedgedoc.nix ./services/web-apps/hledger-web.nix diff --git a/nixos/modules/services/web-apps/haven.nix b/nixos/modules/services/web-apps/haven.nix new file mode 100644 index 0000000000000..9fa801802648b --- /dev/null +++ b/nixos/modules/services/web-apps/haven.nix @@ -0,0 +1,243 @@ +{ + config, + pkgs, + lib, + ... +}: +let + cfg = config.services.haven; +in +{ + options.services.haven = { + enable = lib.mkEnableOption "haven"; + + package = lib.mkPackageOption pkgs "haven" { }; + + port = lib.mkOption { + default = 3355; + type = lib.types.port; + description = "Listen on this port."; + }; + + relayUrl = lib.mkOption { + type = lib.types.str; + description = "The URL of the relay."; + }; + + ownerNpub = lib.mkOption { + type = lib.types.str; + description = "The NPUB of the owner."; + }; + + ownerName = lib.mkOption { + type = lib.types.str; + description = "The name of the owner. Used for relay names and descriptions."; + default = "a nostrich"; + }; + + blastrRelays = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "List of relay configurations for blastr"; + example = lib.literalExpression '' + [ + "relay.example.com" + ] + ''; + }; + + importRelays = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "List of relay configurations for importing historical events"; + example = lib.literalExpression '' + [ + "relay.example.com" + ] + ''; + }; + + settings = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + description = "Additional environment variables to set for the Haven service. See https://github.com/bitvora/haven for documentation."; + example = lib.literalExpression '' + { + PRIVATE_RELAY_NAME = "My Custom Relay Name"; + BACKUP_PROVIDER = "s3"; + } + ''; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + Path to a file containing sensitive environment variables. See https://github.com/bitvora/haven for documentation. + The file should contain environment-variable assignments like: + S3_SECRET_KEY=mysecretkey + S3_ACCESS_KEY_ID=myaccesskey + ''; + example = "/var/lib/haven/secrets.env"; + }; + }; + + config = lib.mkIf cfg.enable { + users.users.haven = { + description = "Haven daemon user"; + group = "haven"; + isSystemUser = true; + }; + + users.groups.haven = { }; + + systemd.services.haven = { + description = "haven"; + wants = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + environment = { + OWNER_NPUB = cfg.ownerNpub; + RELAY_URL = cfg.relayUrl; + RELAY_PORT = toString cfg.port; + RELAY_BIND_ADDRESS = "0.0.0.0"; # Can be set to a specific IP4 or IP6 address ("" for all interfaces) + DB_ENGINE = "badger"; # badger, lmdb (lmdb works best with an nvme, otherwise you might have stability issues) + LMDB_MAPSIZE = toString 0; # 0 for default (currently ~273GB), or set to a different size in bytes, e.g. 10737418240 for 10GB + BLOSSOM_PATH = "blossom/"; + + ## Private Relay Settings + PRIVATE_RELAY_NAME = " ${cfg.ownerName}'s private relay"; + PRIVATE_RELAY_NPUB = cfg.ownerNpub; + PRIVATE_RELAY_DESCRIPTION = "A safe place to store my drafts and ecash"; + PRIVATE_RELAY_ICON = "https://i.nostr.build/6G6wW.gif"; + + ## Private Relay Rate Limiters + PRIVATE_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = toString 50; + PRIVATE_RELAY_EVENT_IP_LIMITER_INTERVAL = toString 1; + PRIVATE_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = toString 100; + PRIVATE_RELAY_ALLOW_EMPTY_FILTERS = "true"; + PRIVATE_RELAY_ALLOW_COMPLEX_FILTERS = "true"; + PRIVATE_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = toString 3; + PRIVATE_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = toString 5; + PRIVATE_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = toString 9; + + ## Chat Relay Settings + CHAT_RELAY_NAME = " ${cfg.ownerName}'s chat relay"; + CHAT_RELAY_NPUB = cfg.ownerNpub; + CHAT_RELAY_DESCRIPTION = "a relay for private chats"; + CHAT_RELAY_ICON = "https://i.nostr.build/6G6wW.gif"; + CHAT_RELAY_WOT_DEPTH = toString 3; + CHAT_RELAY_WOT_REFRESH_INTERVAL_HOURS = toString 24; + CHAT_RELAY_MINIMUM_FOLLOWERS = toString 3; + + ## Chat Relay Rate Limiters + CHAT_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = toString 50; + CHAT_RELAY_EVENT_IP_LIMITER_INTERVAL = toString 1; + CHAT_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = toString 100; + CHAT_RELAY_ALLOW_EMPTY_FILTERS = "false"; + CHAT_RELAY_ALLOW_COMPLEX_FILTERS = "false"; + CHAT_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = toString 3; + CHAT_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = toString 3; + CHAT_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = toString 9; + + ## Outbox Relay Settings + OUTBOX_RELAY_NAME = " ${cfg.ownerName}'s outbox relay"; + OUTBOX_RELAY_NPUB = cfg.ownerNpub; + OUTBOX_RELAY_DESCRIPTION = "a relay and Blossom server for public messages and media"; + OUTBOX_RELAY_ICON = "https://i.nostr.build/6G6wW.gif"; + + ## Outbox Relay Rate Limiters + OUTBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = toString 10; + OUTBOX_RELAY_EVENT_IP_LIMITER_INTERVAL = toString 60; + OUTBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = toString 100; + OUTBOX_RELAY_ALLOW_EMPTY_FILTERS = "false"; + OUTBOX_RELAY_ALLOW_COMPLEX_FILTERS = "false"; + OUTBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = toString 3; + OUTBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = toString 1; + OUTBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = toString 9; + + ## Inbox Relay Settings + INBOX_RELAY_NAME = " ${cfg.ownerName}'s inbox relay"; + INBOX_RELAY_NPUB = cfg.ownerNpub; + INBOX_RELAY_DESCRIPTION = "send your interactions with my notes here"; + INBOX_RELAY_ICON = "https://i.nostr.build/6G6wW.gif"; + INBOX_PULL_INTERVAL_SECONDS = toString 600; + + ## Inbox Relay Rate Limiters + INBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = toString 10; + INBOX_RELAY_EVENT_IP_LIMITER_INTERVAL = toString 1; + INBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = toString 20; + INBOX_RELAY_ALLOW_EMPTY_FILTERS = "false"; + INBOX_RELAY_ALLOW_COMPLEX_FILTERS = "false"; + INBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = toString 3; + INBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = toString 1; + INBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = toString 9; + + ## Import Settings + IMPORT_START_DATE = "2025-01-01"; + IMPORT_QUERY_INTERVAL_SECONDS = toString 600; + IMPORT_SEED_RELAYS_FILE = "${pkgs.writeText "relays_import.json" ( + builtins.toJSON cfg.importRelays + )}"; + + ## Backup Settings + BACKUP_PROVIDER = "none"; # s3, none (or leave blank to disable) + BACKUP_INTERVAL_HOURS = toString 1; + + ## Generic S3 Bucket Backup Settings - REQUIRED IF BACKUP_PROVIDER="s3" + S3_ACCESS_KEY_ID = "access"; + S3_SECRET_KEY = "secret"; + S3_ENDPOINT = "nyc3.digitaloceanspaces.com"; + S3_REGION = "nyc3"; + S3_BUCKET_NAME = "backups"; + + ## Blastr Settings + BLASTR_RELAYS_FILE = "${pkgs.writeText "relays_blastr.json" (builtins.toJSON cfg.blastrRelays)}"; + } // cfg.settings; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/haven"; + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; + User = "haven"; + Group = "haven"; + Restart = "on-failure"; + Type = "simple"; + + RuntimeDirectory = "haven"; + StateDirectory = "haven"; + WorkingDirectory = "/var/lib/haven"; + + # Create symlink to templates in the working directory + ExecStartPre = "+${pkgs.coreutils}/bin/ln -sfT ${cfg.package}/share/haven/templates /var/lib/haven/templates"; + + PrivateTmp = true; + PrivateUsers = true; + PrivateDevices = true; + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + MemoryDenyWriteExecute = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectClock = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectControlGroups = true; + LockPersonality = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + RestrictRealtime = true; + ProtectHostname = true; + CapabilityBoundingSet = ""; + SystemCallFilter = [ + "@system-service" + ]; + SystemCallArchitectures = "native"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ + felixzieger + ]; +}