| 1 | self: { config, lib, pkgs, ... }: |
| 2 | let |
| 3 | cfg = config.services.vineyard; |
| 4 | toml = pkgs.formats.toml { }; |
| 5 | peerToml = peer: { |
| 6 | url = peer.url; |
| 7 | query_limit = peer.queryLimit; |
| 8 | } // lib.optionalAttrs (peer.rescanFrom != null) { |
| 9 | rescan_from = peer.rescanFrom; |
| 10 | }; |
| 11 | configFile = toml.generate "vineyard.toml" { |
| 12 | listen = cfg.listen; |
| 13 | public_url = cfg.publicUrl; |
| 14 | database = "/var/lib/vineyard/events.sqlite"; |
| 15 | forge_pubkeys = cfg.forgePubkeys; |
| 16 | peers = map peerToml cfg.peers; |
| 17 | allow_private_discovery = cfg.allowPrivateDiscovery; |
| 18 | limits = cfg.limits; |
| 19 | }; |
| 20 | port = lib.strings.toIntBase10 (lib.last (lib.splitString ":" cfg.listen)); |
| 21 | in |
| 22 | { |
| 23 | options.services.vineyard = { |
| 24 | enable = lib.mkEnableOption "vineyard"; |
| 25 | package = lib.mkOption { |
| 26 | type = lib.types.package; |
| 27 | default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; |
| 28 | description = "vineyard package to run"; |
| 29 | }; |
| 30 | listen = lib.mkOption { |
| 31 | type = lib.types.str; |
| 32 | default = "127.0.0.1:7777"; |
| 33 | description = "address and port for vineyard to listen on"; |
| 34 | }; |
| 35 | publicUrl = lib.mkOption { |
| 36 | type = lib.types.str; |
| 37 | default = ""; |
| 38 | description = "public websocket url for vineyard"; |
| 39 | }; |
| 40 | forgePubkeys = lib.mkOption { |
| 41 | type = lib.types.listOf lib.types.str; |
| 42 | default = [ ]; |
| 43 | description = "locally hosted forge public keys in hex"; |
| 44 | }; |
| 45 | peers = lib.mkOption { |
| 46 | type = lib.types.listOf (lib.types.submodule { |
| 47 | options = { |
| 48 | url = lib.mkOption { |
| 49 | type = lib.types.str; |
| 50 | description = "peer relay url"; |
| 51 | }; |
| 52 | queryLimit = lib.mkOption { |
| 53 | type = lib.types.ints.positive; |
| 54 | default = 500; |
| 55 | description = "peer query limit"; |
| 56 | }; |
| 57 | rescanFrom = lib.mkOption { |
| 58 | type = lib.types.nullOr lib.types.ints.unsigned; |
| 59 | default = null; |
| 60 | description = "peer rescan timestamp"; |
| 61 | }; |
| 62 | }; |
| 63 | }); |
| 64 | default = [ ]; |
| 65 | description = "peer relays for event exchange"; |
| 66 | }; |
| 67 | allowPrivateDiscovery = lib.mkOption { |
| 68 | type = lib.types.bool; |
| 69 | default = false; |
| 70 | description = "allow discovered relays on private networks"; |
| 71 | }; |
| 72 | limits = lib.mkOption { |
| 73 | type = lib.types.attrsOf lib.types.int; |
| 74 | default = { }; |
| 75 | description = "relay limit overrides"; |
| 76 | }; |
| 77 | openFirewall = lib.mkOption { |
| 78 | type = lib.types.bool; |
| 79 | default = false; |
| 80 | description = "open firewall for listen port"; |
| 81 | }; |
| 82 | }; |
| 83 | config = lib.mkIf cfg.enable { |
| 84 | assertions = [ |
| 85 | { |
| 86 | assertion = cfg.publicUrl != ""; |
| 87 | message = "publicUrl must be set"; |
| 88 | } |
| 89 | { |
| 90 | assertion = cfg.forgePubkeys != [ ]; |
| 91 | message = "forgePubkeys must not be empty"; |
| 92 | } |
| 93 | ]; |
| 94 | networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ port ]; |
| 95 | systemd.services.vineyard = { |
| 96 | description = "vineyard nostr relay"; |
| 97 | wantedBy = [ "multi-user.target" ]; |
| 98 | after = [ "network-online.target" ]; |
| 99 | wants = [ "network-online.target" ]; |
| 100 | serviceConfig = { |
| 101 | ExecStartPre = "${cfg.package}/bin/vineyard check-config --config ${configFile}"; |
| 102 | ExecStart = "${cfg.package}/bin/vineyard serve --config ${configFile}"; |
| 103 | DynamicUser = true; |
| 104 | StateDirectory = "vineyard"; |
| 105 | StateDirectoryMode = "0700"; |
| 106 | WorkingDirectory = "/var/lib/vineyard"; |
| 107 | UMask = "0077"; |
| 108 | Restart = "on-failure"; |
| 109 | RestartSec = "5"; |
| 110 | NoNewPrivileges = true; |
| 111 | ProtectSystem = "strict"; |
| 112 | ProtectHome = true; |
| 113 | PrivateTmp = true; |
| 114 | PrivateDevices = true; |
| 115 | ProtectKernelTunables = true; |
| 116 | ProtectKernelModules = true; |
| 117 | ProtectControlGroups = true; |
| 118 | RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ]; |
| 119 | RestrictNamespaces = true; |
| 120 | LockPersonality = true; |
| 121 | RestrictRealtime = true; |
| 122 | RestrictSUIDSGID = true; |
| 123 | RemoveIPC = true; |
| 124 | CapabilityBoundingSet = ""; |
| 125 | }; |
| 126 | }; |
| 127 | }; |
| 128 | } |