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 time.timeZone = "Europe/London";
83 services.getty.autologinUser = "root";
84 environment.systemPackages = with pkgs; [curl vim git sqlite litecli];
85 services.tangled.knot = {
86 enable = true;
87 motd = "Welcome to the development knot!\n";
88 server = {
89 owner = envVar "TANGLED_VM_KNOT_OWNER";
90 hostname = envVarOr "TANGLED_VM_KNOT_HOST" "localhost:6000";
91 plcUrl = plcUrl;
92 jetstreamEndpoint = jetstream;
93 listenAddr = "0.0.0.0:6000";
94 };
95 };
96 services.tangled.spindle = {
97 enable = true;
98 server = {
99 owner = envVar "TANGLED_VM_SPINDLE_OWNER";
100 hostname = envVarOr "TANGLED_VM_SPINDLE_HOST" "localhost:6555";
101 plcUrl = plcUrl;
102 jetstreamEndpoint = jetstream;
103 listenAddr = "0.0.0.0:6555";
104 dev = true;
105 queueSize = 100;
106 maxJobCount = 2;
107 secrets = {
108 provider = "sqlite";
109 };
110 };
111 };
112 users = {
113 # So we don't have to deal with permission clashing between
114 # blank disk VMs and existing state
115 users.${config.services.tangled.knot.gitUser}.uid = 666;
116 groups.${config.services.tangled.knot.gitUser}.gid = 666;
117
118 # TODO: separate spindle user
119 };
120 systemd.services = let
121 mkDataSyncScripts = source: target: {
122 enableStrictShellChecks = true;
123
124 preStart = lib.mkBefore ''
125 mkdir -p ${target}
126 ${lib.getExe pkgs.rsync} -a ${source}/ ${target}
127 '';
128
129 postStop = lib.mkAfter ''
130 ${lib.getExe pkgs.rsync} -a ${target}/ ${source}
131 '';
132
133 serviceConfig.PermissionsStartOnly = true;
134 };
135 in {
136 knot = mkDataSyncScripts "/mnt/knot-data" config.services.tangled.knot.stateDir;
137 spindle = mkDataSyncScripts "/mnt/spindle-data" (builtins.dirOf config.services.tangled.spindle.server.dbPath);
138 };
139 })
140 ];
141 }