Monorepo for Tangled tangled.org
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: let 7 cfg = config.services.tangled.spindle; 8in 9 with lib; { 10 options = { 11 services.tangled.spindle = { 12 enable = mkOption { 13 type = types.bool; 14 default = false; 15 description = "Enable a tangled spindle"; 16 }; 17 package = mkOption { 18 type = types.package; 19 description = "Package to use for the spindle"; 20 }; 21 tap-package = mkOption { 22 type = types.package; 23 description = "Package to use for the spindle"; 24 }; 25 26 atpRelayUrl = mkOption { 27 type = types.str; 28 default = "https://relay1.us-east.bsky.network"; 29 description = "atproto relay"; 30 }; 31 32 server = { 33 listenAddr = mkOption { 34 type = types.str; 35 default = "0.0.0.0:6555"; 36 description = "Address to listen on"; 37 }; 38 39 stateDir = mkOption { 40 type = types.path; 41 default = "/var/lib/spindle"; 42 description = "Tangled spindle data directory"; 43 }; 44 45 hostname = mkOption { 46 type = types.str; 47 example = "my.spindle.com"; 48 description = "Hostname for the server (required)"; 49 }; 50 51 plcUrl = mkOption { 52 type = types.str; 53 default = "https://plc.directory"; 54 description = "atproto PLC directory"; 55 }; 56 57 dev = mkOption { 58 type = types.bool; 59 default = false; 60 description = "Enable development mode (disables signature verification)"; 61 }; 62 63 owner = mkOption { 64 type = types.str; 65 example = "did:plc:qfpnj4og54vl56wngdriaxug"; 66 description = "DID of owner (required)"; 67 }; 68 69 maxJobCount = mkOption { 70 type = types.int; 71 default = 2; 72 example = 5; 73 description = "Maximum number of concurrent jobs to run"; 74 }; 75 76 queueSize = mkOption { 77 type = types.int; 78 default = 100; 79 example = 100; 80 description = "Maximum number of jobs queue up"; 81 }; 82 83 secrets = { 84 provider = mkOption { 85 type = types.str; 86 default = "sqlite"; 87 description = "Backend to use for secret management, valid options are 'sqlite', and 'openbao'."; 88 }; 89 90 openbao = { 91 proxyAddr = mkOption { 92 type = types.str; 93 default = "http://127.0.0.1:8200"; 94 }; 95 mount = mkOption { 96 type = types.str; 97 default = "spindle"; 98 }; 99 }; 100 }; 101 }; 102 103 pipelines = { 104 nixery = mkOption { 105 type = types.str; 106 default = "nixery.tangled.sh"; # note: this is *not* on tangled.org yet 107 description = "Nixery instance to use"; 108 }; 109 110 workflowTimeout = mkOption { 111 type = types.str; 112 default = "5m"; 113 description = "Timeout for each step of a pipeline"; 114 }; 115 }; 116 }; 117 }; 118 119 config = mkIf cfg.enable { 120 virtualisation.docker.enable = true; 121 122 systemd.services.spindle-tap = { 123 description = "spindle tap service"; 124 after = ["network.target" "docker.service"]; 125 wantedBy = ["multi-user.target"]; 126 serviceConfig = { 127 LogsDirectory = "spindle-tap"; 128 StateDirectory = "spindle-tap"; 129 Environment = [ 130 "TAP_BIND=:2480" 131 "TAP_PLC_URL=${cfg.server.plcUrl}" 132 "TAP_RELAY_URL=${cfg.atpRelayUrl}" 133 "TAP_DATABASE_URL=sqlite:///var/lib/spindle-tap/tap.db" 134 "TAP_RETRY_TIMEOUT=3s" 135 "TAP_COLLECTION_FILTERS=${concatStringsSep "," [ 136 "sh.tangled.repo" 137 "sh.tangled.repo.collaborator" 138 "sh.tangled.spindle.member" 139 "sh.tangled.repo.pull" 140 ]}" 141 # temporary hack to listen for repo.pull from non-tangled users 142 "TAP_SIGNAL_COLLECTION=sh.tangled.repo.pull" 143 ]; 144 ExecStart = "${getExe cfg.tap-package} run"; 145 }; 146 }; 147 148 systemd.services.spindle = { 149 description = "spindle service"; 150 after = ["network.target" "docker.service" "spindle-tap.service"]; 151 wantedBy = ["multi-user.target"]; 152 path = [ 153 pkgs.git 154 ]; 155 serviceConfig = { 156 LogsDirectory = "spindle"; 157 StateDirectory = "spindle"; 158 Environment = [ 159 "SPINDLE_SERVER_LISTEN_ADDR=${cfg.server.listenAddr}" 160 "SPINDLE_SERVER_DATA_DIR=${cfg.server.stateDir}" 161 "SPINDLE_SERVER_HOSTNAME=${cfg.server.hostname}" 162 "SPINDLE_SERVER_PLC_URL=${cfg.server.plcUrl}" 163 "SPINDLE_SERVER_DEV=${lib.boolToString cfg.server.dev}" 164 "SPINDLE_SERVER_OWNER=${cfg.server.owner}" 165 "SPINDLE_SERVER_MAX_JOB_COUNT=${toString cfg.server.maxJobCount}" 166 "SPINDLE_SERVER_QUEUE_SIZE=${toString cfg.server.queueSize}" 167 "SPINDLE_SERVER_SECRETS_PROVIDER=${cfg.server.secrets.provider}" 168 "SPINDLE_SERVER_SECRETS_OPENBAO_PROXY_ADDR=${cfg.server.secrets.openbao.proxyAddr}" 169 "SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT=${cfg.server.secrets.openbao.mount}" 170 "SPINDLE_SERVER_TAP_URL=http://localhost:2480" 171 "SPINDLE_NIXERY_PIPELINES_NIXERY=${cfg.pipelines.nixery}" 172 "SPINDLE_NIXERY_PIPELINES_WORKFLOW_TIMEOUT=${cfg.pipelines.workflowTimeout}" 173 ]; 174 ExecStart = "${cfg.package}/bin/spindle"; 175 Restart = "always"; 176 }; 177 }; 178 }; 179 }