forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
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 docs/hacking.md 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.knot
27 self.nixosModules.spindle
28 ({
29 lib,
30 config,
31 pkgs,
32 ...
33 }: {
34 virtualisation.vmVariant.virtualisation = {
35 host.pkgs = import nixpkgs {system = hostSystem;};
36
37 graphics = false;
38 memorySize = 2048;
39 diskSize = 10 * 1024;
40 cores = 2;
41 forwardPorts = [
42 # ssh
43 {
44 from = "host";
45 host.port = 2222;
46 guest.port = 22;
47 }
48 # knot
49 {
50 from = "host";
51 host.port = 6000;
52 guest.port = 6000;
53 }
54 # spindle
55 {
56 from = "host";
57 host.port = 6555;
58 guest.port = 6555;
59 }
60 ];
61 sharedDirectories = {
62 # We can't use the 9p mounts directly for most of these
63 # as SQLite is incompatible with them. So instead we
64 # mount the shared directories to a different location
65 # and copy the contents around on service start/stop.
66 knotData = {
67 source = "$TANGLED_VM_DATA_DIR/knot";
68 target = "/mnt/knot-data";
69 };
70 spindleData = {
71 source = "$TANGLED_VM_DATA_DIR/spindle";
72 target = "/mnt/spindle-data";
73 };
74 spindleLogs = {
75 source = "$TANGLED_VM_DATA_DIR/spindle-logs";
76 target = "/var/log/spindle";
77 };
78 };
79 };
80 # 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
81 networking.firewall.enable = false;
82 services.dnsmasq.enable = true;
83 services.dnsmasq.settings.address = "/tngl.boltless.dev/10.0.2.2";
84 security.pki.certificates = [
85 (builtins.readFile ../local-infra/cert/localtangled/root.crt)
86 ];
87 time.timeZone = "Europe/London";
88 services.getty.autologinUser = "root";
89 environment.systemPackages = with pkgs; [curl vim git sqlite litecli];
90 services.tangled.knot = {
91 enable = true;
92 motd = "Welcome to the development knot!\n";
93 server = {
94 owner = envVar "TANGLED_VM_KNOT_OWNER";
95 hostname = envVarOr "TANGLED_VM_KNOT_HOST" "localhost:6000";
96 plcUrl = plcUrl;
97 jetstreamEndpoint = jetstream;
98 listenAddr = "0.0.0.0:6000";
99 };
100 };
101 services.tangled.spindle = {
102 enable = true;
103 server = {
104 owner = envVar "TANGLED_VM_SPINDLE_OWNER";
105 hostname = envVarOr "TANGLED_VM_SPINDLE_OWNER" "localhost:6555";
106 plcUrl = plcUrl;
107 jetstreamEndpoint = jetstream;
108 listenAddr = "0.0.0.0:6555";
109 dev = true;
110 queueSize = 100;
111 maxJobCount = 2;
112 secrets = {
113 provider = "sqlite";
114 };
115 };
116 };
117 users = {
118 # So we don't have to deal with permission clashing between
119 # blank disk VMs and existing state
120 users.${config.services.tangled.knot.gitUser}.uid = 666;
121 groups.${config.services.tangled.knot.gitUser}.gid = 666;
122
123 # TODO: separate spindle user
124 };
125 systemd.services = let
126 mkDataSyncScripts = source: target: {
127 enableStrictShellChecks = true;
128
129 preStart = lib.mkBefore ''
130 mkdir -p ${target}
131 ${lib.getExe pkgs.rsync} -a ${source}/ ${target}
132 '';
133
134 postStop = lib.mkAfter ''
135 ${lib.getExe pkgs.rsync} -a ${target}/ ${source}
136 '';
137
138 serviceConfig.PermissionsStartOnly = true;
139 };
140 in {
141 knot = mkDataSyncScripts "/mnt/knot-data" config.services.tangled.knot.stateDir;
142 spindle = mkDataSyncScripts "/mnt/spindle-data" (builtins.dirOf config.services.tangled.spindle.server.dbPath);
143 };
144 })
145 ];
146 }