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