this repo has no description
1{
2 nixpkgs,
3 system,
4 hostSystem,
5 self,
6}: let
7 envVar = name: let
8 var = builtins.getEnv name;
9 in
10 if var == ""
11 then throw "\$${name} must be defined, see https://docs.tangled.org/hacking-on-tangled.html#hacking-on-tangled for more details"
12 else var;
13 envVarOr = name: default: let
14 var = builtins.getEnv name;
15 in
16 if var != ""
17 then var
18 else default;
19
20 plcUrl = envVarOr "TANGLED_VM_PLC_URL" "https://plc.directory";
21 jetstream = envVarOr "TANGLED_VM_JETSTREAM_ENDPOINT" "wss://jetstream1.us-west.bsky.network/subscribe";
22in
23 nixpkgs.lib.nixosSystem {
24 inherit system;
25 modules = [
26 self.nixosModules.did-method-plc
27 self.nixosModules.bluesky-jetstream
28 self.nixosModules.bluesky-relay
29 self.nixosModules.knot
30 self.nixosModules.spindle
31 ({
32 lib,
33 config,
34 pkgs,
35 ...
36 }: {
37 virtualisation.vmVariant.virtualisation = {
38 host.pkgs = import nixpkgs {system = hostSystem;};
39
40 graphics = false;
41 memorySize = 2048;
42 diskSize = 10 * 1024;
43 cores = 2;
44 forwardPorts = [
45 # caddy
46 {
47 from = "host";
48 host.port = 80;
49 guest.port = 80;
50 }
51 {
52 from = "host";
53 host.port = 443;
54 guest.port = 443;
55 }
56 {
57 from = "host";
58 proto = "udp";
59 host.port = 443;
60 guest.port = 443;
61 }
62 # ssh
63 {
64 from = "host";
65 host.port = 2222;
66 guest.port = 22;
67 }
68 # knot
69 {
70 from = "host";
71 host.port = 6444;
72 guest.port = 6444;
73 }
74 # spindle
75 {
76 from = "host";
77 host.port = 6555;
78 guest.port = 6555;
79 }
80 ];
81 sharedDirectories = {
82 # We can't use the 9p mounts directly for most of these
83 # as SQLite is incompatible with them. So instead we
84 # mount the shared directories to a different location
85 # and copy the contents around on service start/stop.
86 caddyData = {
87 source = "$TANGLED_VM_DATA_DIR/caddy";
88 target = config.services.caddy.dataDir;
89 };
90 knotData = {
91 source = "$TANGLED_VM_DATA_DIR/knot";
92 target = "/mnt/knot-data";
93 };
94 spindleData = {
95 source = "$TANGLED_VM_DATA_DIR/spindle";
96 target = "/mnt/spindle-data";
97 };
98 spindleLogs = {
99 source = "$TANGLED_VM_DATA_DIR/spindle-logs";
100 target = "/var/log/spindle";
101 };
102 };
103 };
104 # This is fine because any and all ports that are forwarded to host are explicitly marked above, we don't need a separate guest firewall
105 networking.firewall.enable = false;
106 # resolve `*.tngl.boltless.dev` to host
107 services.dnsmasq.enable = true;
108 services.dnsmasq.settings.address = "/tngl.boltless.dev/10.0.2.2";
109 security.pki.certificates = [
110 (builtins.readFile ../contrib/certs/root.crt)
111 ];
112 time.timeZone = "Europe/London";
113 services.timesyncd.enable = lib.mkVMOverride true;
114 services.getty.autologinUser = "root";
115 environment.systemPackages = with pkgs; [curl vim git sqlite litecli];
116 virtualisation.docker.extraOptions = ''
117 --dns 172.17.0.1
118 '';
119 services.tangled.knot = {
120 enable = true;
121 motd = "Welcome to the development knot!\n";
122 server = {
123 owner = envVar "TANGLED_VM_KNOT_OWNER";
124 hostname = envVarOr "TANGLED_VM_KNOT_HOST" "localhost:6444";
125 plcUrl = plcUrl;
126 jetstreamEndpoint = jetstream;
127 listenAddr = "0.0.0.0:6444";
128 };
129 };
130 services.tangled.spindle = {
131 enable = true;
132 server = {
133 owner = envVar "TANGLED_VM_SPINDLE_OWNER";
134 hostname = envVarOr "TANGLED_VM_SPINDLE_HOST" "localhost:6555";
135 plcUrl = plcUrl;
136 jetstreamEndpoint = jetstream;
137 listenAddr = "0.0.0.0:6555";
138 dev = true;
139 queueSize = 100;
140 maxJobCount = 2;
141 secrets = {
142 provider = "sqlite";
143 };
144 };
145 };
146 services.did-method-plc.enable = true;
147 services.bluesky-pds = {
148 enable = true;
149 # overriding package version to support emails
150 package = pkgs.bluesky-pds.overrideAttrs (old: rec {
151 version = "0.4.188";
152 src = pkgs.fetchFromGitHub {
153 owner = "bluesky-social";
154 repo = "pds";
155 tag = "v${version}";
156 hash = "sha256-t8KdyEygXdbj/5Rhj8W40e1o8mXprELpjsKddHExmo0=";
157 };
158 pnpmDeps = pkgs.fetchPnpmDeps {
159 inherit version src;
160 pname = old.pname;
161 sourceRoot = old.sourceRoot;
162 fetcherVersion = 2;
163 hash = "sha256-lQie7f8JbWKSpoavnMjHegBzH3GB9teXsn+S2SLJHHU=";
164 };
165 });
166 settings = {
167 LOG_ENABLED = "true";
168
169 PDS_JWT_SECRET = "8cae8bffcc73d9932819650791e4e89a";
170 PDS_ADMIN_PASSWORD = "d6a902588cd93bee1af83f924f60cfd3";
171 PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX = "2e92e336a50a618458e1097d94a1db86ec3fd8829d7735020cbae80625c761d7";
172
173 PDS_EMAIL_SMTP_URL = envVarOr "TANGLED_VM_PDS_EMAIL_SMTP_URL" null;
174 PDS_EMAIL_FROM_ADDRESS = envVarOr "TANGLED_VM_PDS_EMAIL_FROM_ADDRESS" null;
175
176 PDS_DID_PLC_URL = "http://localhost:8080";
177 PDS_CRAWLERS = "https://relay.tngl.boltless.dev";
178 PDS_HOSTNAME = "pds.tngl.boltless.dev";
179 PDS_PORT = 3000;
180 };
181 };
182 services.bluesky-relay = {
183 enable = true;
184 };
185 services.bluesky-jetstream = {
186 enable = true;
187 livenessTtl = 300;
188 websocketUrl = "ws://localhost:3000/xrpc/com.atproto.sync.subscribeRepos";
189 };
190 services.caddy = {
191 enable = true;
192 configFile = pkgs.writeText "Caddyfile" ''
193 {
194 debug
195 cert_lifetime 3601d
196 pki {
197 ca local {
198 intermediate_lifetime 3599d
199 }
200 }
201 }
202
203 plc.tngl.boltless.dev {
204 tls internal
205 reverse_proxy http://localhost:8080
206 }
207
208 *.pds.tngl.boltless.dev, pds.tngl.boltless.dev {
209 tls internal
210 reverse_proxy http://localhost:3000
211 }
212
213 jetstream.tngl.boltless.dev {
214 tls internal
215 reverse_proxy http://localhost:6008
216 }
217
218 relay.tngl.boltless.dev {
219 tls internal
220 reverse_proxy http://localhost:2470
221 }
222
223 knot.tngl.boltless.dev {
224 tls internal
225 reverse_proxy http://localhost:6444
226 }
227
228 spindle.tngl.boltless.dev {
229 tls internal
230 reverse_proxy http://localhost:6555
231 }
232 '';
233 };
234 users = {
235 # So we don't have to deal with permission clashing between
236 # blank disk VMs and existing state
237 users.${config.services.tangled.knot.gitUser}.uid = 666;
238 groups.${config.services.tangled.knot.gitUser}.gid = 666;
239
240 # TODO: separate spindle user
241 };
242 systemd.services = let
243 mkDataSyncScripts = source: target: {
244 enableStrictShellChecks = true;
245
246 preStart = lib.mkBefore ''
247 mkdir -p ${target}
248 ${lib.getExe pkgs.rsync} -a ${source}/ ${target}
249 '';
250
251 postStop = lib.mkAfter ''
252 ${lib.getExe pkgs.rsync} -a ${target}/ ${source}
253 '';
254
255 serviceConfig.PermissionsStartOnly = true;
256 };
257 in {
258 knot = mkDataSyncScripts "/mnt/knot-data" config.services.tangled.knot.stateDir;
259 spindle = mkDataSyncScripts "/mnt/spindle-data" (builtins.dirOf config.services.tangled.spindle.server.dbPath);
260 };
261 })
262 ];
263 }