+1
.gitignore
+1
.gitignore
···
···
1
+
.env
+1
common/ssh.nix
+1
common/ssh.nix
···
1
{
2
sshKeys = [
3
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICf5rx2r0w98r5lOpxr+/hScABDnk1UfgTH8T2WzeNp4 icy@kvothe"
4
+
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICJPYX06+qKr9IHWfkgCtHbExoBOOwS/+iAWbog9bAdk icy@wyndle"
5
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMj1Dn9YuFo2BNr993ymBa6nzyyIKAURIqMbUtfI8+4X op@mantis"
6
];
7
}
+100
-34
flake.lock
+100
-34
flake.lock
···
1
{
2
"nodes": {
3
"colmena": {
4
"inputs": {
5
"flake-compat": "flake-compat",
···
29
]
30
},
31
"locked": {
32
-
"lastModified": 1751854533,
33
-
"narHash": "sha256-U/OQFplExOR1jazZY4KkaQkJqOl59xlh21HP9mI79Vc=",
34
"owner": "nix-community",
35
"repo": "disko",
36
-
"rev": "16b74a1e304197248a1bc663280f2548dbfcae3c",
37
"type": "github"
38
},
39
"original": {
···
58
"type": "github"
59
}
60
},
61
"flake-utils": {
62
"locked": {
63
"lastModified": 1659877975,
···
73
"type": "github"
74
}
75
},
76
-
"gitignore": {
77
"inputs": {
78
"nixpkgs": [
79
"tangled",
80
"nixpkgs"
81
]
82
},
83
"locked": {
84
-
"lastModified": 1709087332,
85
-
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
86
-
"owner": "hercules-ci",
87
-
"repo": "gitignore.nix",
88
-
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
89
"type": "github"
90
},
91
"original": {
92
-
"owner": "hercules-ci",
93
-
"repo": "gitignore.nix",
94
"type": "github"
95
}
96
},
···
134
"indigo": {
135
"flake": false,
136
"locked": {
137
-
"lastModified": 1745333930,
138
-
"narHash": "sha256-83fIHqDE+dfnZ88HaNuwfKFO+R0RKAM1WxMfNh/Matk=",
139
"owner": "oppiliappan",
140
"repo": "indigo",
141
-
"rev": "e4e59280737b8676611fc077a228d47b3e8e9491",
142
"type": "github"
143
},
144
"original": {
···
163
"lucide-src": {
164
"flake": false,
165
"locked": {
166
-
"lastModified": 1742302029,
167
-
"narHash": "sha256-OyPVtpnC4/AAmPq84Wt1r1Gcs48d9KG+UBCtZK87e9k=",
168
"type": "tarball",
169
-
"url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip"
170
},
171
"original": {
172
"type": "tarball",
173
-
"url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip"
174
}
175
},
176
"nixery-flake": {
177
"flake": false,
178
"locked": {
179
-
"lastModified": 1745149613,
180
-
"narHash": "sha256-rcSnsnSWA0IUjmbG2iSpvVB0702tcR3zIyU3iFJBo0g=",
181
"owner": "tazjin",
182
"repo": "nixery",
183
-
"rev": "c6d4692b1b6eb105c9abce0411d2ef4b8708a6e1",
184
"type": "github"
185
},
186
"original": {
···
207
},
208
"nixpkgs_2": {
209
"locked": {
210
-
"lastModified": 1751792365,
211
-
"narHash": "sha256-J1kI6oAj25IG4EdVlg2hQz8NZTBNYvIS0l4wpr9KcUo=",
212
"owner": "nixos",
213
"repo": "nixpkgs",
214
-
"rev": "1fd8bada0b6117e6c7eb54aad5813023eed37ccb",
215
"type": "github"
216
},
217
"original": {
···
223
},
224
"nixpkgs_3": {
225
"locked": {
226
-
"lastModified": 1746904237,
227
-
"narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=",
228
"owner": "nixos",
229
"repo": "nixpkgs",
230
-
"rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956",
231
"type": "github"
232
},
233
"original": {
···
275
"type": "github"
276
}
277
},
278
"tangled": {
279
"inputs": {
280
-
"gitignore": "gitignore",
281
"htmx-src": "htmx-src",
282
"htmx-ws-src": "htmx-ws-src",
283
"ibm-plex-mono-src": "ibm-plex-mono-src",
···
288
"sqlite-lib-src": "sqlite-lib-src"
289
},
290
"locked": {
291
-
"lastModified": 1751967742,
292
-
"narHash": "sha256-nkZ1Ny6BaW/KD4BWtWm2kJ5+92kNSWcj4iyqyIBq7yw=",
293
"ref": "refs/heads/master",
294
-
"rev": "643a3ac18326821472691488635cc66f4ffdbf2b",
295
-
"revCount": 884,
296
"type": "git",
297
-
"url": "https://tangled.sh/@tangled.sh/core"
298
},
299
"original": {
300
"type": "git",
301
-
"url": "https://tangled.sh/@tangled.sh/core"
302
}
303
}
304
},
···
1
{
2
"nodes": {
3
+
"actor-typeahead-src": {
4
+
"flake": false,
5
+
"locked": {
6
+
"lastModified": 1762835797,
7
+
"narHash": "sha256-heizoWUKDdar6ymfZTnj3ytcEv/L4d4fzSmtr0HlXsQ=",
8
+
"ref": "refs/heads/main",
9
+
"rev": "677fe7f743050a4e7f09d4a6f87bbf1325a06f6b",
10
+
"revCount": 6,
11
+
"type": "git",
12
+
"url": "https://tangled.org/@jakelazaroff.com/actor-typeahead"
13
+
},
14
+
"original": {
15
+
"type": "git",
16
+
"url": "https://tangled.org/@jakelazaroff.com/actor-typeahead"
17
+
}
18
+
},
19
"colmena": {
20
"inputs": {
21
"flake-compat": "flake-compat",
···
45
]
46
},
47
"locked": {
48
+
"lastModified": 1766150702,
49
+
"narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=",
50
"owner": "nix-community",
51
"repo": "disko",
52
+
"rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378",
53
"type": "github"
54
},
55
"original": {
···
74
"type": "github"
75
}
76
},
77
+
"flake-compat_2": {
78
+
"flake": false,
79
+
"locked": {
80
+
"lastModified": 1751685974,
81
+
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
82
+
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
83
+
"type": "tarball",
84
+
"url": "https://git.lix.systems/api/v1/repos/lix-project/flake-compat/archive/549f2762aebeff29a2e5ece7a7dc0f955281a1d1.tar.gz?rev=549f2762aebeff29a2e5ece7a7dc0f955281a1d1"
85
+
},
86
+
"original": {
87
+
"type": "tarball",
88
+
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
89
+
}
90
+
},
91
"flake-utils": {
92
"locked": {
93
"lastModified": 1659877975,
···
103
"type": "github"
104
}
105
},
106
+
"flake-utils_2": {
107
+
"inputs": {
108
+
"systems": "systems"
109
+
},
110
+
"locked": {
111
+
"lastModified": 1731533236,
112
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
113
+
"owner": "numtide",
114
+
"repo": "flake-utils",
115
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
116
+
"type": "github"
117
+
},
118
+
"original": {
119
+
"owner": "numtide",
120
+
"repo": "flake-utils",
121
+
"type": "github"
122
+
}
123
+
},
124
+
"gomod2nix": {
125
"inputs": {
126
+
"flake-utils": "flake-utils_2",
127
"nixpkgs": [
128
"tangled",
129
"nixpkgs"
130
]
131
},
132
"locked": {
133
+
"lastModified": 1763982521,
134
+
"narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=",
135
+
"owner": "nix-community",
136
+
"repo": "gomod2nix",
137
+
"rev": "02e63a239d6eabd595db56852535992c898eba72",
138
"type": "github"
139
},
140
"original": {
141
+
"owner": "nix-community",
142
+
"repo": "gomod2nix",
143
"type": "github"
144
}
145
},
···
183
"indigo": {
184
"flake": false,
185
"locked": {
186
+
"lastModified": 1753693716,
187
+
"narHash": "sha256-DMIKnCJRODQXEHUxA+7mLzRALmnZhkkbHlFT2rCQYrE=",
188
"owner": "oppiliappan",
189
"repo": "indigo",
190
+
"rev": "5f170569da9360f57add450a278d73538092d8ca",
191
"type": "github"
192
},
193
"original": {
···
212
"lucide-src": {
213
"flake": false,
214
"locked": {
215
+
"lastModified": 1754044466,
216
+
"narHash": "sha256-+exBR2OToB1iv7ZQI2S4B0lXA/QRvC9n6U99UxGpJGs=",
217
"type": "tarball",
218
+
"url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip"
219
},
220
"original": {
221
"type": "tarball",
222
+
"url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip"
223
}
224
},
225
"nixery-flake": {
226
"flake": false,
227
"locked": {
228
+
"lastModified": 1762501370,
229
+
"narHash": "sha256-WO2NvvFB3KkFfChE5F6ghog7mvBAVKpMsQMqwadZT4k=",
230
"owner": "tazjin",
231
"repo": "nixery",
232
+
"rev": "be8a4005de3f27f95e677e7b61abef387d4a840d",
233
"type": "github"
234
},
235
"original": {
···
256
},
257
"nixpkgs_2": {
258
"locked": {
259
+
"lastModified": 1767379071,
260
+
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
261
"owner": "nixos",
262
"repo": "nixpkgs",
263
+
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
264
"type": "github"
265
},
266
"original": {
···
272
},
273
"nixpkgs_3": {
274
"locked": {
275
+
"lastModified": 1766070988,
276
+
"narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=",
277
"owner": "nixos",
278
"repo": "nixpkgs",
279
+
"rev": "c6245e83d836d0433170a16eb185cefe0572f8b8",
280
"type": "github"
281
},
282
"original": {
···
324
"type": "github"
325
}
326
},
327
+
"systems": {
328
+
"locked": {
329
+
"lastModified": 1681028828,
330
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
331
+
"owner": "nix-systems",
332
+
"repo": "default",
333
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
334
+
"type": "github"
335
+
},
336
+
"original": {
337
+
"owner": "nix-systems",
338
+
"repo": "default",
339
+
"type": "github"
340
+
}
341
+
},
342
"tangled": {
343
"inputs": {
344
+
"actor-typeahead-src": "actor-typeahead-src",
345
+
"flake-compat": "flake-compat_2",
346
+
"gomod2nix": "gomod2nix",
347
"htmx-src": "htmx-src",
348
"htmx-ws-src": "htmx-ws-src",
349
"ibm-plex-mono-src": "ibm-plex-mono-src",
···
354
"sqlite-lib-src": "sqlite-lib-src"
355
},
356
"locked": {
357
+
"lastModified": 1767683698,
358
+
"narHash": "sha256-MFrfNmTKTdOOsyXUvvqPwH6zqvDZZpURnd7QdJkVOgU=",
359
"ref": "refs/heads/master",
360
+
"rev": "b31a2a3590fefc4c70817f94a20076df2428b4d3",
361
+
"revCount": 1791,
362
"type": "git",
363
+
"url": "https://tangled.org/@tangled.org/core"
364
},
365
"original": {
366
"type": "git",
367
+
"url": "https://tangled.org/@tangled.org/core"
368
}
369
}
370
},
+104
-33
flake.nix
+104
-33
flake.nix
···
1
{
2
description = "nix infra for tangled";
3
inputs = {
4
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
5
-
tangled.url = "git+https://tangled.sh/@tangled.sh/core";
6
colmena.url = "github:zhaofengli/colmena/release-0.4.x";
7
disko = {
8
url = "github:nix-community/disko";
···
16
};
17
};
18
19
-
outputs =
20
-
{ nixpkgs, disko, colmena, nixery-flake, tangled, ... }:
21
{
22
-
nixosConfigurations.nixery = nixpkgs.lib.nixosSystem {
23
-
system = "x86_64-linux";
24
-
modules = [
25
-
disko.nixosModules.disko
26
-
tangled.nixosModules.spindle
27
-
./hosts/nixery/configuration.nix
28
-
];
29
};
30
colmenaHive = colmena.lib.makeHive {
31
meta = {
32
-
nixpkgs = nixpkgs.legacyPackages.x86_64-linux;
33
specialArgs = {
34
nixery-pkgs = import nixery-flake.outPath {
35
-
pkgs = import nixpkgs { system = "x86_64-linux"; };
36
};
37
-
commonArgs = import ./common/ssh.nix;
38
};
39
};
40
41
defaults = { pkgs, ... }: {
42
-
environment.systemPackages = [
43
-
pkgs.curl
44
-
];
45
};
46
-
nixery = { pkgs, ... }: {
47
-
deployment = {
48
-
targetHost = "nixery.tangled.sh";
49
-
targetPort = 22;
50
-
targetUser = "tangler";
51
-
buildOnTarget = true;
52
-
};
53
-
nixpkgs.system = "x86_64-linux";
54
55
-
imports = [
56
-
disko.nixosModules.disko
57
-
tangled.nixosModules.spindle
58
-
./hosts/nixery/configuration.nix
59
-
./hosts/nixery/services/nginx.nix
60
-
./hosts/nixery/services/nixery.nix
61
-
];
62
-
time.timeZone = "Europe/Helsinki";
63
-
};
64
};
65
};
66
}
···
1
{
2
description = "nix infra for tangled";
3
+
4
inputs = {
5
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
6
+
tangled.url = "git+https://tangled.org/@tangled.org/core";
7
colmena.url = "github:zhaofengli/colmena/release-0.4.x";
8
disko = {
9
url = "github:nix-community/disko";
···
17
};
18
};
19
20
+
outputs = { nixpkgs, disko, colmena, nixery-flake, tangled, ... }:
21
+
let
22
+
system = "x86_64-linux";
23
+
commonArgs = import ./common/ssh.nix;
24
+
25
+
# Helper function to create nixosConfiguration
26
+
mkHost = hostname: extraModules:
27
+
nixpkgs.lib.nixosSystem {
28
+
inherit system;
29
+
specialArgs = { inherit commonArgs; };
30
+
modules = [
31
+
disko.nixosModules.disko
32
+
./hosts/${hostname}/configuration.nix
33
+
] ++ extraModules;
34
+
};
35
+
36
+
# Helper function to create colmena host
37
+
mkColmenaHost = hostname: targetHost: targetPort: extraModules:
38
+
{
39
+
deployment = {
40
+
inherit targetHost;
41
+
inherit targetPort;
42
+
targetUser = "tangler";
43
+
buildOnTarget = true;
44
+
};
45
+
nixpkgs.system = system;
46
+
time.timeZone = "Europe/Helsinki";
47
+
imports = [
48
+
disko.nixosModules.disko
49
+
./hosts/${hostname}/configuration.nix
50
+
] ++ extraModules;
51
+
};
52
+
53
+
# Host configurations
54
+
hosts = {
55
+
appview = {
56
+
modules = [
57
+
tangled.nixosModules.appview
58
+
./hosts/appview/services/appview.nix
59
+
./hosts/appview/services/nginx.nix
60
+
];
61
+
target = "95.111.205.38";
62
+
};
63
+
64
+
pds = {
65
+
modules = [
66
+
./hosts/pds/services/nginx.nix
67
+
./hosts/pds/services/pds.nix
68
+
];
69
+
target = "tngl.sh";
70
+
};
71
+
72
+
nixery = {
73
+
modules = [
74
+
tangled.nixosModules.spindle
75
+
./hosts/nixery/services/nginx.nix
76
+
./hosts/nixery/services/openbao/openbao.nix
77
+
./hosts/nixery/services/openbao/proxy.nix
78
+
./hosts/nixery/services/nixery.nix
79
+
];
80
+
target = "nixery.tangled.sh";
81
+
};
82
+
83
+
spindle = {
84
+
modules = [
85
+
tangled.nixosModules.spindle
86
+
./hosts/spindle/services/openbao/openbao.nix
87
+
./hosts/spindle/services/openbao/proxy.nix
88
+
./hosts/spindle/services/spindle.nix
89
+
./hosts/spindle/services/nginx.nix
90
+
];
91
+
target = "spindle.alpha.tangled.sh";
92
+
};
93
+
94
+
knot1 = {
95
+
modules = [
96
+
tangled.nixosModules.knot
97
+
./hosts/knot1/services/knot.nix
98
+
./hosts/knot1/services/nginx.nix
99
+
];
100
+
target = "knot1.alpha.tangled.sh";
101
+
};
102
+
};
103
+
in
104
{
105
+
# nixos-anywhere and nixos-rebuild use these
106
+
nixosConfigurations = {
107
+
appview = mkHost "appview" hosts.appview.modules;
108
+
pds = mkHost "pds" hosts.pds.modules;
109
+
nixery = mkHost "nixery" hosts.nixery.modules;
110
+
spindle = mkHost "spindle" hosts.spindle.modules;
111
+
knot1 = mkHost "knot1" hosts.knot1.modules;
112
};
113
+
114
+
# colmena uses this
115
colmenaHive = colmena.lib.makeHive {
116
meta = {
117
+
nixpkgs = nixpkgs.legacyPackages.${system};
118
specialArgs = {
119
+
inherit commonArgs;
120
nixery-pkgs = import nixery-flake.outPath {
121
+
pkgs = import nixpkgs { inherit system; };
122
};
123
};
124
};
125
126
defaults = { pkgs, ... }: {
127
+
environment.systemPackages = [ pkgs.curl ];
128
};
129
130
+
appview = mkColmenaHost "appview" hosts.appview.target 2222 hosts.appview.modules;
131
+
pds = mkColmenaHost "pds" hosts.pds.target 22 hosts.pds.modules;
132
+
nixery = mkColmenaHost "nixery" hosts.nixery.target 22 hosts.nixery.modules;
133
+
spindle = mkColmenaHost "spindle" hosts.spindle.target 22 hosts.spindle.modules;
134
+
knot1 = mkColmenaHost "knot1" hosts.knot1.target 22 hosts.knot1.modules;
135
};
136
};
137
}
+62
hosts/appview/configuration.nix
+62
hosts/appview/configuration.nix
···
···
1
+
{ modulesPath
2
+
, lib
3
+
, pkgs
4
+
, ...
5
+
} @ args:
6
+
{
7
+
imports = [
8
+
(modulesPath + "/installer/scan/not-detected.nix")
9
+
(modulesPath + "/profiles/qemu-guest.nix")
10
+
./disk-config.nix
11
+
];
12
+
boot.loader.grub = {
13
+
# no need to set devices, disko will add all devices that have a EF02 partition to the list already
14
+
# devices = [ ];
15
+
efiSupport = true;
16
+
efiInstallAsRemovable = true;
17
+
};
18
+
19
+
networking.hostName = "appview-arn";
20
+
services = {
21
+
openssh.enable = true;
22
+
openssh.ports = [2222];
23
+
};
24
+
25
+
# networking.extraHosts = ''
26
+
# 85.9.211.103 knot1.tangled.sh
27
+
# '';
28
+
29
+
30
+
nix = {
31
+
extraOptions = ''
32
+
experimental-features = nix-command flakes ca-derivations
33
+
warn-dirty = false
34
+
keep-outputs = false
35
+
'';
36
+
};
37
+
38
+
environment.systemPackages = map lib.lowPrio [
39
+
pkgs.curl
40
+
pkgs.gitMinimal
41
+
];
42
+
43
+
users.users.tangler = {
44
+
extraGroups = [ "networkmanager" "wheel" ];
45
+
openssh.authorizedKeys.keys = args.commonArgs.sshKeys;
46
+
isNormalUser = true;
47
+
};
48
+
49
+
security.sudo.extraRules = [
50
+
{
51
+
users = [ "tangler" ];
52
+
commands = [
53
+
{
54
+
command = "ALL";
55
+
options = [ "NOPASSWD" ];
56
+
}
57
+
];
58
+
}
59
+
];
60
+
61
+
system.stateVersion = "25.05";
62
+
}
+56
hosts/appview/disk-config.nix
+56
hosts/appview/disk-config.nix
···
···
1
+
# Example to create a bios compatible gpt partition
2
+
{ lib, ... }:
3
+
{
4
+
disko.devices = {
5
+
disk.disk1 = {
6
+
device = lib.mkDefault "/dev/vda";
7
+
type = "disk";
8
+
content = {
9
+
type = "gpt";
10
+
partitions = {
11
+
boot = {
12
+
name = "boot";
13
+
size = "1M";
14
+
type = "EF02";
15
+
};
16
+
esp = {
17
+
name = "ESP";
18
+
size = "500M";
19
+
type = "EF00";
20
+
content = {
21
+
type = "filesystem";
22
+
format = "vfat";
23
+
mountpoint = "/boot";
24
+
};
25
+
};
26
+
root = {
27
+
name = "root";
28
+
size = "100%";
29
+
content = {
30
+
type = "lvm_pv";
31
+
vg = "pool";
32
+
};
33
+
};
34
+
};
35
+
};
36
+
};
37
+
lvm_vg = {
38
+
pool = {
39
+
type = "lvm_vg";
40
+
lvs = {
41
+
root = {
42
+
size = "100%FREE";
43
+
content = {
44
+
type = "filesystem";
45
+
format = "ext4";
46
+
mountpoint = "/";
47
+
mountOptions = [
48
+
"defaults"
49
+
];
50
+
};
51
+
};
52
+
};
53
+
};
54
+
};
55
+
};
56
+
}
+11
hosts/appview/services/appview.nix
+11
hosts/appview/services/appview.nix
+6
hosts/appview/services/litestream.nix
+6
hosts/appview/services/litestream.nix
+53
hosts/appview/services/nginx-alpha.nix
+53
hosts/appview/services/nginx-alpha.nix
···
···
1
+
{ config, pkgs, ... }:
2
+
{
3
+
services.nginx = {
4
+
enable = true;
5
+
recommendedTlsSettings = true;
6
+
recommendedOptimisation = true;
7
+
recommendedGzipSettings = true;
8
+
9
+
# Fix proxy headers hash warnings
10
+
appendHttpConfig = ''
11
+
proxy_headers_hash_max_size 1024;
12
+
proxy_headers_hash_bucket_size 128;
13
+
'';
14
+
15
+
virtualHosts = {
16
+
# AppView service on alpha.tangled.sh
17
+
"alpha.tangled.sh" = {
18
+
forceSSL = true;
19
+
enableACME = true;
20
+
21
+
locations."/" = {
22
+
proxyPass = "http://127.0.0.1:3000";
23
+
extraConfig = ''
24
+
proxy_http_version 1.1;
25
+
proxy_set_header Host $host;
26
+
proxy_set_header X-Real-IP $remote_addr;
27
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
28
+
proxy_set_header X-Forwarded-Proto $scheme;
29
+
client_max_body_size 100M;
30
+
'';
31
+
};
32
+
33
+
# WebSocket support for /logs endpoint
34
+
locations."~ /logs$" = {
35
+
proxyPass = "http://127.0.0.1:3000";
36
+
proxyWebsockets = true;
37
+
extraConfig = ''
38
+
proxy_read_timeout 86400;
39
+
'';
40
+
};
41
+
};
42
+
};
43
+
};
44
+
45
+
# Open firewall ports
46
+
networking.firewall.allowedTCPPorts = [ 80 443 ];
47
+
48
+
# ACME configuration for Let's Encrypt
49
+
security.acme = {
50
+
acceptTerms = true;
51
+
defaults.email = "team@tangled.org";
52
+
};
53
+
}
+106
hosts/appview/services/nginx.nix
+106
hosts/appview/services/nginx.nix
···
···
1
+
{ config, pkgs, ... }:
2
+
{
3
+
services.nginx = {
4
+
enable = true;
5
+
recommendedProxySettings = true;
6
+
recommendedTlsSettings = true;
7
+
recommendedOptimisation = true;
8
+
recommendedGzipSettings = true;
9
+
10
+
streamConfig = ''
11
+
upstream knot-sailor {
12
+
server 94.237.110.185:22;
13
+
}
14
+
15
+
server {
16
+
listen 22;
17
+
listen [::]:22;
18
+
proxy_pass knot-sailor;
19
+
}
20
+
'';
21
+
22
+
virtualHosts = {
23
+
# Redirect tangled.sh โ tangled.org
24
+
"tangled.sh" = {
25
+
serverAliases = [ "www.tangled.sh" ];
26
+
locations."/" = {
27
+
return = "301 https://tangled.org$request_uri";
28
+
};
29
+
forceSSL = true;
30
+
enableACME = true;
31
+
};
32
+
33
+
# Redirect strings.tangled.sh โ tangled.org/strings/*
34
+
"strings.tangled.sh" = {
35
+
locations."/" = {
36
+
return = "301 https://tangled.org/strings$request_uri";
37
+
};
38
+
forceSSL = true;
39
+
enableACME = true;
40
+
};
41
+
42
+
# Redirect strings.tangled.org โ tangled.org/strings/*
43
+
"strings.tangled.org" = {
44
+
locations."/" = {
45
+
return = "301 https://tangled.org/strings$request_uri";
46
+
};
47
+
forceSSL = true;
48
+
enableACME = true;
49
+
};
50
+
51
+
# Main app on tangled.org
52
+
"tangled.org" = {
53
+
serverAliases = [ "www.tangled.org" ];
54
+
55
+
forceSSL = true;
56
+
enableACME = true;
57
+
58
+
extraConfig = ''
59
+
# Redirect www โ bare domain
60
+
if ($host = www.tangled.org) {
61
+
return 301 https://tangled.org$request_uri;
62
+
}
63
+
64
+
client_max_body_size 100M;
65
+
'';
66
+
67
+
locations."~ ^/@tangled\\.sh(/.*)?$" = {
68
+
extraConfig = ''
69
+
rewrite ^/@tangled\.sh(.*)$ https://tangled.org/@tangled.org$1 permanent;
70
+
'';
71
+
};
72
+
73
+
locations."~ ^/tangled\\.sh(/.*)?$" = {
74
+
extraConfig = ''
75
+
rewrite ^/tangled\.sh(.*)$ https://tangled.org/tangled.org$1 permanent;
76
+
'';
77
+
};
78
+
79
+
80
+
locations."~ /logs$" = {
81
+
proxyPass = "http://127.0.0.1:3000";
82
+
proxyWebsockets = true;
83
+
extraConfig = ''
84
+
proxy_read_timeout 86400;
85
+
'';
86
+
};
87
+
88
+
locations."/" = {
89
+
proxyPass = "http://127.0.0.1:3000";
90
+
extraConfig = ''
91
+
client_max_body_size 100M;
92
+
'';
93
+
};
94
+
};
95
+
};
96
+
};
97
+
98
+
# Open firewall ports
99
+
networking.firewall.allowedTCPPorts = [ 80 443 2222 22 ];
100
+
101
+
# ACME configuration for Let's Encrypt
102
+
security.acme = {
103
+
acceptTerms = true;
104
+
defaults.email = "team@tangled.org";
105
+
};
106
+
}
+57
hosts/knot1/configuration.nix
+57
hosts/knot1/configuration.nix
···
···
1
+
{ modulesPath
2
+
, lib
3
+
, pkgs
4
+
, ...
5
+
} @ args:
6
+
{
7
+
imports = [
8
+
(modulesPath + "/installer/scan/not-detected.nix")
9
+
(modulesPath + "/profiles/qemu-guest.nix")
10
+
./disk-config.nix
11
+
];
12
+
boot.loader.grub = {
13
+
# no need to set devices, disko will add all devices that have a EF02 partition to the list already
14
+
# devices = [ ];
15
+
efiSupport = true;
16
+
efiInstallAsRemovable = true;
17
+
};
18
+
19
+
networking.hostName = "knot1-ams";
20
+
services = {
21
+
openssh.enable = true;
22
+
};
23
+
24
+
25
+
nix = {
26
+
extraOptions = ''
27
+
experimental-features = nix-command flakes ca-derivations
28
+
warn-dirty = false
29
+
keep-outputs = false
30
+
'';
31
+
};
32
+
33
+
environment.systemPackages = map lib.lowPrio [
34
+
pkgs.curl
35
+
pkgs.gitMinimal
36
+
];
37
+
38
+
users.users.tangler = {
39
+
extraGroups = [ "networkmanager" "wheel" "docker" ];
40
+
openssh.authorizedKeys.keys = args.commonArgs.sshKeys;
41
+
isNormalUser = true;
42
+
};
43
+
44
+
security.sudo.extraRules = [
45
+
{
46
+
users = [ "tangler" ];
47
+
commands = [
48
+
{
49
+
command = "ALL";
50
+
options = [ "NOPASSWD" ];
51
+
}
52
+
];
53
+
}
54
+
];
55
+
56
+
system.stateVersion = "25.05";
57
+
}
+56
hosts/knot1/disk-config.nix
+56
hosts/knot1/disk-config.nix
···
···
1
+
# Example to create a bios compatible gpt partition
2
+
{ lib, ... }:
3
+
{
4
+
disko.devices = {
5
+
disk.disk1 = {
6
+
device = lib.mkDefault "/dev/vda";
7
+
type = "disk";
8
+
content = {
9
+
type = "gpt";
10
+
partitions = {
11
+
boot = {
12
+
name = "boot";
13
+
size = "1M";
14
+
type = "EF02";
15
+
};
16
+
esp = {
17
+
name = "ESP";
18
+
size = "500M";
19
+
type = "EF00";
20
+
content = {
21
+
type = "filesystem";
22
+
format = "vfat";
23
+
mountpoint = "/boot";
24
+
};
25
+
};
26
+
root = {
27
+
name = "root";
28
+
size = "100%";
29
+
content = {
30
+
type = "lvm_pv";
31
+
vg = "pool";
32
+
};
33
+
};
34
+
};
35
+
};
36
+
};
37
+
lvm_vg = {
38
+
pool = {
39
+
type = "lvm_vg";
40
+
lvs = {
41
+
root = {
42
+
size = "100%FREE";
43
+
content = {
44
+
type = "filesystem";
45
+
format = "ext4";
46
+
mountpoint = "/";
47
+
mountOptions = [
48
+
"defaults"
49
+
];
50
+
};
51
+
};
52
+
};
53
+
};
54
+
};
55
+
};
56
+
}
+11
hosts/knot1/services/knot.nix
+11
hosts/knot1/services/knot.nix
+35
hosts/knot1/services/nginx.nix
+35
hosts/knot1/services/nginx.nix
···
···
1
+
{
2
+
services.nginx = {
3
+
enable = true;
4
+
virtualHosts = {
5
+
"knot1.alpha.tangled.sh" = {
6
+
forceSSL = true;
7
+
enableACME = true;
8
+
locations."/" = {
9
+
proxyPass = "http://127.0.0.1:5555";
10
+
11
+
extraConfig = ''
12
+
proxy_set_header X-Forwarded-For $remote_addr;
13
+
proxy_set_header Host $host;
14
+
proxy_set_header X-Real-IP $remote_addr;
15
+
proxy_set_header X-Forwarded-Proto $scheme;
16
+
'';
17
+
};
18
+
locations."/events" = {
19
+
proxyPass = "http://127.0.0.1:5555";
20
+
extraConfig = ''
21
+
proxy_set_header X-Forwarded-For $remote_addr;
22
+
proxy_set_header Host $host;
23
+
proxy_set_header Upgrade $http_upgrade;
24
+
proxy_set_header Connection "upgrade";
25
+
'';
26
+
};
27
+
};
28
+
};
29
+
};
30
+
security.acme = {
31
+
acceptTerms = true;
32
+
defaults.email = "team@tangled.org";
33
+
};
34
+
networking.firewall.allowedTCPPorts = [ 80 443 ];
35
+
}
+9
-1
hosts/nixery/configuration.nix
+9
-1
hosts/nixery/configuration.nix
···
19
networking.hostName = "nixery";
20
services = {
21
openssh.enable = true;
22
+
tangled.spindle = {
23
enable = true;
24
server = {
25
owner = "did:plc:wshs7t2adsemcrrd4snkeqli"; # @tangled.sh
26
hostname = "spindle.tangled.sh";
27
listenAddr = "127.0.0.1:6555";
28
+
queueSize = 100;
29
+
maxJobCount = 2;
30
+
secrets = {
31
+
provider = "openbao";
32
+
};
33
+
};
34
+
pipelines = {
35
+
workflowTimeout = "15m";
36
};
37
};
38
};
+94
hosts/nixery/services/docker-cleanup.nix
+94
hosts/nixery/services/docker-cleanup.nix
···
···
1
+
{ config, pkgs, ... }:
2
+
3
+
{
4
+
systemd.services.docker-cleanup = {
5
+
description = "Docker cleanup service - removes unused containers, networks, images, and volumes";
6
+
7
+
serviceConfig = {
8
+
Type = "oneshot";
9
+
User = "root";
10
+
ExecStart = pkgs.writeShellScript "docker-cleanup" ''
11
+
set -e
12
+
13
+
echo "Starting Docker cleanup at $(date)"
14
+
15
+
# remove containers running for 15+ minutes
16
+
echo "Removing containers running for 15+ minutes..."
17
+
CONTAINERS_TO_REMOVE=$(${pkgs.docker}/bin/docker ps --format "table {{.ID}}\t{{.RunningFor}}" --no-trunc | awk '
18
+
/minute/ {
19
+
if ($2 >= 15) print $1
20
+
}
21
+
/hour/ {
22
+
print $1
23
+
}
24
+
/day/ {
25
+
print $1
26
+
}
27
+
/week/ {
28
+
print $1
29
+
}
30
+
/month/ {
31
+
print $1
32
+
}
33
+
/year/ {
34
+
print $1
35
+
}
36
+
')
37
+
38
+
if [ -n "$CONTAINERS_TO_REMOVE" ]; then
39
+
echo "Found containers to remove: $CONTAINERS_TO_REMOVE"
40
+
echo "$CONTAINERS_TO_REMOVE" | xargs -r ${pkgs.docker}/bin/docker rm -f
41
+
else
42
+
echo "No containers running for 15+ minutes found"
43
+
fi
44
+
45
+
# remove stopped containers
46
+
echo "Removing stopped containers..."
47
+
${pkgs.docker}/bin/docker container prune -f || true
48
+
49
+
# remove unused networks (excluding default networks)
50
+
echo "Removing unused networks..."
51
+
${pkgs.docker}/bin/docker network prune -f || true
52
+
53
+
# remove unused images (dangling images only by default)
54
+
echo "Removing dangling images..."
55
+
${pkgs.docker}/bin/docker image prune -f || true
56
+
57
+
# remove unused volumes
58
+
echo "Removing unused volumes..."
59
+
${pkgs.docker}/bin/docker volume prune -f || true
60
+
61
+
echo "Docker cleanup completed at $(date)"
62
+
'';
63
+
64
+
PrivateNetwork = false; # Needs network access for Docker
65
+
ProtectSystem = "strict";
66
+
ProtectHome = true;
67
+
NoNewPrivileges = true;
68
+
69
+
# Logging
70
+
StandardOutput = "journal";
71
+
StandardError = "journal";
72
+
};
73
+
74
+
# ensure docker is running before cleanup
75
+
after = [ "docker.service" ];
76
+
requires = [ "docker.service" ];
77
+
78
+
# don't restart on failure
79
+
restartIfChanged = false;
80
+
};
81
+
82
+
# timer to run the cleanup service every n minutes
83
+
systemd.timers.docker-cleanup = {
84
+
description = "Timer for Docker cleanup service";
85
+
timerConfig = {
86
+
OnCalendar = "*:0/15"; # run every 15 minutes
87
+
Persistent = true; # persist timer across reboots
88
+
WakeSystem = false; # run immediately if the system was powered off when timer should have run
89
+
};
90
+
wantedBy = [ "timers.target" ];
91
+
};
92
+
93
+
virtualisation.docker.enable = true;
94
+
}
+10
-1
hosts/nixery/services/nginx.nix
+10
-1
hosts/nixery/services/nginx.nix
···
27
proxy_set_header X-Forwarded-For $remote_addr;
28
proxy_set_header Host $host;
29
proxy_set_header Upgrade $http_upgrade;
30
+
proxy_set_header Connection "upgrade";
31
+
'';
32
+
};
33
+
locations."/logs/" = {
34
+
proxyPass = "http://localhost:6555";
35
+
extraConfig = ''
36
+
proxy_set_header X-Forwarded-For $remote_addr;
37
+
proxy_set_header Host $host;
38
+
proxy_set_header Upgrade $http_upgrade;
39
+
proxy_set_header Connection "upgrade";
40
'';
41
};
42
};
+39
hosts/nixery/services/openbao/openbao.nix
+39
hosts/nixery/services/openbao/openbao.nix
···
···
1
+
{ config, pkgs, lib, ... }:
2
+
{
3
+
# Create openbao user and group
4
+
users.groups.openbao = {};
5
+
6
+
users.users.openbao = {
7
+
isSystemUser = true;
8
+
group = "openbao";
9
+
home = "/var/lib/openbao";
10
+
createHome = true;
11
+
description = "OpenBao service user";
12
+
};
13
+
14
+
systemd.services.openbao = {
15
+
serviceConfig = {
16
+
DynamicUser = lib.mkForce false;
17
+
User = "openbao";
18
+
Group = "openbao";
19
+
};
20
+
};
21
+
22
+
services.openbao = {
23
+
enable = true;
24
+
settings = {
25
+
ui = true;
26
+
27
+
listener.default = {
28
+
type = "tcp";
29
+
address = "127.0.0.1:8201";
30
+
tls_disable = true;
31
+
};
32
+
33
+
cluster_addr = "http://127.0.0.1:8202";
34
+
api_addr = "http://127.0.0.1:8201";
35
+
36
+
storage.raft.path = "/var/lib/openbao";
37
+
};
38
+
};
39
+
}
+100
hosts/nixery/services/openbao/proxy.nix
+100
hosts/nixery/services/openbao/proxy.nix
···
···
1
+
{ pkgs, ... }:
2
+
3
+
{
4
+
systemd.services.openbao-proxy = {
5
+
description = "OpenBao Proxy with Auto-Auth";
6
+
after = [ "network.target" ];
7
+
wantedBy = [ "multi-user.target" ];
8
+
serviceConfig = {
9
+
User = "root";
10
+
ExecStart = "${pkgs.openbao}/bin/bao proxy -config=/etc/openbao/proxy.hcl";
11
+
Restart = "always";
12
+
RestartSec = "5";
13
+
LimitNOFILE = "65536";
14
+
};
15
+
};
16
+
17
+
18
+
19
+
environment.etc."openbao/proxy.hcl".text = ''
20
+
vault {
21
+
address = "http://localhost:8201"
22
+
23
+
# Retry configuration
24
+
retry {
25
+
num_retries = 5
26
+
}
27
+
}
28
+
29
+
# Auto-Auth using AppRole
30
+
auto_auth {
31
+
method "approle" {
32
+
mount_path = "auth/approle"
33
+
config = {
34
+
role_id_file_path = "/etc/openbao/role-id"
35
+
secret_id_file_path = "/etc/openbao/secret-id"
36
+
remove_secret_id_file_after_reading = false
37
+
}
38
+
}
39
+
40
+
# Write authenticated token to file
41
+
sink "file" {
42
+
config = {
43
+
path = "/var/lib/openbao/token"
44
+
mode = 0640
45
+
}
46
+
}
47
+
}
48
+
49
+
# API Proxy listener for Spindle
50
+
listener "tcp" {
51
+
address = "127.0.0.1:8200"
52
+
tls_disable = true
53
+
54
+
# Security headers
55
+
require_request_header = false
56
+
57
+
# Enable proxy API for management
58
+
proxy_api {
59
+
enable_quit = true
60
+
}
61
+
}
62
+
63
+
# Enable API proxy with auto-auth token
64
+
api_proxy {
65
+
use_auto_auth_token = true
66
+
}
67
+
68
+
cache {
69
+
}
70
+
71
+
# Logging configuration
72
+
log_level = "info"
73
+
log_format = "standard"
74
+
log_file = "/var/log/openbao/proxy.log"
75
+
log_rotate_duration = "24h"
76
+
log_rotate_max_files = 30
77
+
78
+
# Process management
79
+
pid_file = "/var/lib/openbao/proxy.pid"
80
+
81
+
# Disable idle connections for reliability
82
+
disable_idle_connections = ["auto-auth", "proxying"]
83
+
'';
84
+
85
+
# Create necessary directories and files
86
+
systemd.tmpfiles.rules = [
87
+
# Directories
88
+
"d /var/lib/openbao 0755 root root -"
89
+
"d /var/lib/openbao/cache 0755 root root -"
90
+
"d /var/log/openbao 0755 root root -"
91
+
"d /etc/openbao 0755 root root -"
92
+
93
+
# Credential files (content must be populated externally)
94
+
"f /etc/openbao/role-id 0600 root root -"
95
+
"f /etc/openbao/secret-id 0600 root root -"
96
+
97
+
# Configuration file
98
+
"f /etc/openbao/proxy.hcl 0644 root root -"
99
+
];
100
+
}
+57
hosts/pds/configuration.nix
+57
hosts/pds/configuration.nix
···
···
1
+
{ modulesPath
2
+
, lib
3
+
, pkgs
4
+
, ...
5
+
} @ args:
6
+
{
7
+
imports = [
8
+
(modulesPath + "/installer/scan/not-detected.nix")
9
+
(modulesPath + "/profiles/qemu-guest.nix")
10
+
./disk-config.nix
11
+
];
12
+
boot.loader.grub = {
13
+
# no need to set devices, disko will add all devices that have a EF02 partition to the list already
14
+
# devices = [ ];
15
+
efiSupport = true;
16
+
efiInstallAsRemovable = true;
17
+
};
18
+
19
+
networking.hostName = "pds";
20
+
services = {
21
+
openssh.enable = true;
22
+
};
23
+
24
+
25
+
nix = {
26
+
extraOptions = ''
27
+
experimental-features = nix-command flakes ca-derivations
28
+
warn-dirty = false
29
+
keep-outputs = false
30
+
'';
31
+
};
32
+
33
+
environment.systemPackages = map lib.lowPrio [
34
+
pkgs.curl
35
+
pkgs.gitMinimal
36
+
];
37
+
38
+
users.users.tangler = {
39
+
extraGroups = [ "networkmanager" "wheel" ];
40
+
openssh.authorizedKeys.keys = args.commonArgs.sshKeys;
41
+
isNormalUser = true;
42
+
};
43
+
44
+
security.sudo.extraRules = [
45
+
{
46
+
users = [ "tangler" ];
47
+
commands = [
48
+
{
49
+
command = "ALL";
50
+
options = [ "NOPASSWD" ];
51
+
}
52
+
];
53
+
}
54
+
];
55
+
56
+
system.stateVersion = "25.05";
57
+
}
+56
hosts/pds/disk-config.nix
+56
hosts/pds/disk-config.nix
···
···
1
+
# Example to create a bios compatible gpt partition
2
+
{ lib, ... }:
3
+
{
4
+
disko.devices = {
5
+
disk.disk1 = {
6
+
device = lib.mkDefault "/dev/vda";
7
+
type = "disk";
8
+
content = {
9
+
type = "gpt";
10
+
partitions = {
11
+
boot = {
12
+
name = "boot";
13
+
size = "1M";
14
+
type = "EF02";
15
+
};
16
+
esp = {
17
+
name = "ESP";
18
+
size = "500M";
19
+
type = "EF00";
20
+
content = {
21
+
type = "filesystem";
22
+
format = "vfat";
23
+
mountpoint = "/boot";
24
+
};
25
+
};
26
+
root = {
27
+
name = "root";
28
+
size = "100%";
29
+
content = {
30
+
type = "lvm_pv";
31
+
vg = "pool";
32
+
};
33
+
};
34
+
};
35
+
};
36
+
};
37
+
lvm_vg = {
38
+
pool = {
39
+
type = "lvm_vg";
40
+
lvs = {
41
+
root = {
42
+
size = "100%FREE";
43
+
content = {
44
+
type = "filesystem";
45
+
format = "ext4";
46
+
mountpoint = "/";
47
+
mountOptions = [
48
+
"defaults"
49
+
];
50
+
};
51
+
};
52
+
};
53
+
};
54
+
};
55
+
};
56
+
}
+35
hosts/pds/services/nginx.nix
+35
hosts/pds/services/nginx.nix
···
···
1
+
{
2
+
services.nginx = {
3
+
enable = true;
4
+
virtualHosts."tngl.sh" = {
5
+
forceSSL = true;
6
+
enableACME = true;
7
+
8
+
# match exact root
9
+
locations."= /" = {
10
+
extraConfig = ''
11
+
return 301 https://tangled.sh;
12
+
'';
13
+
};
14
+
15
+
# match all other paths
16
+
locations."/" = {
17
+
proxyPass = "http://localhost:3000";
18
+
extraConfig = ''
19
+
proxy_set_header Host $host;
20
+
proxy_set_header X-Real-IP $remote_addr;
21
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
22
+
proxy_set_header X-Forwarded-Proto $scheme;
23
+
proxy_set_header Upgrade $http_upgrade;
24
+
proxy_set_header Connection "upgrade";
25
+
'';
26
+
};
27
+
};
28
+
};
29
+
30
+
security.acme = {
31
+
acceptTerms = true;
32
+
defaults.email = "anirudh@tangled.sh";
33
+
};
34
+
networking.firewall.allowedTCPPorts = [ 80 443 ];
35
+
}
+13
hosts/pds/services/pds.nix
+13
hosts/pds/services/pds.nix
+57
hosts/spindle/configuration.nix
+57
hosts/spindle/configuration.nix
···
···
1
+
{ modulesPath
2
+
, lib
3
+
, pkgs
4
+
, ...
5
+
} @ args:
6
+
{
7
+
imports = [
8
+
(modulesPath + "/installer/scan/not-detected.nix")
9
+
(modulesPath + "/profiles/qemu-guest.nix")
10
+
./disk-config.nix
11
+
];
12
+
boot.loader.grub = {
13
+
# no need to set devices, disko will add all devices that have a EF02 partition to the list already
14
+
# devices = [ ];
15
+
efiSupport = true;
16
+
efiInstallAsRemovable = true;
17
+
};
18
+
19
+
networking.hostName = "spindle-waw";
20
+
services = {
21
+
openssh.enable = true;
22
+
};
23
+
24
+
25
+
nix = {
26
+
extraOptions = ''
27
+
experimental-features = nix-command flakes ca-derivations
28
+
warn-dirty = false
29
+
keep-outputs = false
30
+
'';
31
+
};
32
+
33
+
environment.systemPackages = map lib.lowPrio [
34
+
pkgs.curl
35
+
pkgs.gitMinimal
36
+
];
37
+
38
+
users.users.tangler = {
39
+
extraGroups = [ "networkmanager" "wheel" "docker" ];
40
+
openssh.authorizedKeys.keys = args.commonArgs.sshKeys;
41
+
isNormalUser = true;
42
+
};
43
+
44
+
security.sudo.extraRules = [
45
+
{
46
+
users = [ "tangler" ];
47
+
commands = [
48
+
{
49
+
command = "ALL";
50
+
options = [ "NOPASSWD" ];
51
+
}
52
+
];
53
+
}
54
+
];
55
+
56
+
system.stateVersion = "25.05";
57
+
}
+56
hosts/spindle/disk-config.nix
+56
hosts/spindle/disk-config.nix
···
···
1
+
# Example to create a bios compatible gpt partition
2
+
{ lib, ... }:
3
+
{
4
+
disko.devices = {
5
+
disk.disk1 = {
6
+
device = lib.mkDefault "/dev/vda";
7
+
type = "disk";
8
+
content = {
9
+
type = "gpt";
10
+
partitions = {
11
+
boot = {
12
+
name = "boot";
13
+
size = "1M";
14
+
type = "EF02";
15
+
};
16
+
esp = {
17
+
name = "ESP";
18
+
size = "500M";
19
+
type = "EF00";
20
+
content = {
21
+
type = "filesystem";
22
+
format = "vfat";
23
+
mountpoint = "/boot";
24
+
};
25
+
};
26
+
root = {
27
+
name = "root";
28
+
size = "100%";
29
+
content = {
30
+
type = "lvm_pv";
31
+
vg = "pool";
32
+
};
33
+
};
34
+
};
35
+
};
36
+
};
37
+
lvm_vg = {
38
+
pool = {
39
+
type = "lvm_vg";
40
+
lvs = {
41
+
root = {
42
+
size = "100%FREE";
43
+
content = {
44
+
type = "filesystem";
45
+
format = "ext4";
46
+
mountpoint = "/";
47
+
mountOptions = [
48
+
"defaults"
49
+
];
50
+
};
51
+
};
52
+
};
53
+
};
54
+
};
55
+
};
56
+
}
+37
hosts/spindle/services/nginx.nix
+37
hosts/spindle/services/nginx.nix
···
···
1
+
{
2
+
services.nginx = {
3
+
enable = true;
4
+
virtualHosts = {
5
+
"spindle.alpha.tangled.sh" = {
6
+
forceSSL = true;
7
+
enableACME = true;
8
+
locations."/" = {
9
+
proxyPass = "http://127.0.0.1:6555";
10
+
};
11
+
locations."/events" = {
12
+
proxyPass = "http://127.0.0.1:6555";
13
+
extraConfig = ''
14
+
proxy_set_header X-Forwarded-For $remote_addr;
15
+
proxy_set_header Host $host;
16
+
proxy_set_header Upgrade $http_upgrade;
17
+
proxy_set_header Connection "upgrade";
18
+
'';
19
+
};
20
+
locations."/logs/" = {
21
+
proxyPass = "http://127.0.0.1:6555";
22
+
extraConfig = ''
23
+
proxy_set_header X-Forwarded-For $remote_addr;
24
+
proxy_set_header Host $host;
25
+
proxy_set_header Upgrade $http_upgrade;
26
+
proxy_set_header Connection "upgrade";
27
+
'';
28
+
};
29
+
};
30
+
};
31
+
};
32
+
security.acme = {
33
+
acceptTerms = true;
34
+
defaults.email = "team@tangled.org";
35
+
};
36
+
networking.firewall.allowedTCPPorts = [ 80 443 ];
37
+
}
+39
hosts/spindle/services/openbao/openbao.nix
+39
hosts/spindle/services/openbao/openbao.nix
···
···
1
+
{ config, pkgs, lib, ... }:
2
+
{
3
+
# Create openbao user and group
4
+
users.groups.openbao = {};
5
+
6
+
users.users.openbao = {
7
+
isSystemUser = true;
8
+
group = "openbao";
9
+
home = "/var/lib/openbao";
10
+
createHome = true;
11
+
description = "OpenBao service user";
12
+
};
13
+
14
+
systemd.services.openbao = {
15
+
serviceConfig = {
16
+
DynamicUser = lib.mkForce false;
17
+
User = "openbao";
18
+
Group = "openbao";
19
+
};
20
+
};
21
+
22
+
services.openbao = {
23
+
enable = true;
24
+
settings = {
25
+
ui = true;
26
+
27
+
listener.default = {
28
+
type = "tcp";
29
+
address = "127.0.0.1:8201";
30
+
tls_disable = true;
31
+
};
32
+
33
+
cluster_addr = "http://127.0.0.1:8202";
34
+
api_addr = "http://127.0.0.1:8201";
35
+
36
+
storage.raft.path = "/var/lib/openbao";
37
+
};
38
+
};
39
+
}
+100
hosts/spindle/services/openbao/proxy.nix
+100
hosts/spindle/services/openbao/proxy.nix
···
···
1
+
{ pkgs, ... }:
2
+
3
+
{
4
+
systemd.services.openbao-proxy = {
5
+
description = "OpenBao Proxy with Auto-Auth";
6
+
after = [ "network.target" ];
7
+
wantedBy = [ "multi-user.target" ];
8
+
serviceConfig = {
9
+
User = "root";
10
+
ExecStart = "${pkgs.openbao}/bin/bao proxy -config=/etc/openbao/proxy.hcl";
11
+
Restart = "always";
12
+
RestartSec = "5";
13
+
LimitNOFILE = "65536";
14
+
};
15
+
};
16
+
17
+
18
+
19
+
environment.etc."openbao/proxy.hcl".text = ''
20
+
vault {
21
+
address = "http://localhost:8201"
22
+
23
+
# Retry configuration
24
+
retry {
25
+
num_retries = 5
26
+
}
27
+
}
28
+
29
+
# Auto-Auth using AppRole
30
+
auto_auth {
31
+
method "approle" {
32
+
mount_path = "auth/approle"
33
+
config = {
34
+
role_id_file_path = "/etc/openbao/role-id"
35
+
secret_id_file_path = "/etc/openbao/secret-id"
36
+
remove_secret_id_file_after_reading = false
37
+
}
38
+
}
39
+
40
+
# Write authenticated token to file
41
+
sink "file" {
42
+
config = {
43
+
path = "/var/lib/openbao/token"
44
+
mode = 0640
45
+
}
46
+
}
47
+
}
48
+
49
+
# API Proxy listener for Spindle
50
+
listener "tcp" {
51
+
address = "127.0.0.1:8200"
52
+
tls_disable = true
53
+
54
+
# Security headers
55
+
require_request_header = false
56
+
57
+
# Enable proxy API for management
58
+
proxy_api {
59
+
enable_quit = true
60
+
}
61
+
}
62
+
63
+
# Enable API proxy with auto-auth token
64
+
api_proxy {
65
+
use_auto_auth_token = true
66
+
}
67
+
68
+
cache {
69
+
}
70
+
71
+
# Logging configuration
72
+
log_level = "info"
73
+
log_format = "standard"
74
+
log_file = "/var/log/openbao/proxy.log"
75
+
log_rotate_duration = "24h"
76
+
log_rotate_max_files = 30
77
+
78
+
# Process management
79
+
pid_file = "/var/lib/openbao/proxy.pid"
80
+
81
+
# Disable idle connections for reliability
82
+
disable_idle_connections = ["auto-auth", "proxying"]
83
+
'';
84
+
85
+
# Create necessary directories and files
86
+
systemd.tmpfiles.rules = [
87
+
# Directories
88
+
"d /var/lib/openbao 0755 root root -"
89
+
"d /var/lib/openbao/cache 0755 root root -"
90
+
"d /var/log/openbao 0755 root root -"
91
+
"d /etc/openbao 0755 root root -"
92
+
93
+
# Credential files (content must be populated externally)
94
+
"f /etc/openbao/role-id 0600 root root -"
95
+
"f /etc/openbao/secret-id 0600 root root -"
96
+
97
+
# Configuration file
98
+
"f /etc/openbao/proxy.hcl 0644 root root -"
99
+
];
100
+
}
+19
hosts/spindle/services/spindle.nix
+19
hosts/spindle/services/spindle.nix
···
···
1
+
{ config, pkgs, ... }:
2
+
{
3
+
services.tangled.spindle = {
4
+
enable = true;
5
+
server = {
6
+
owner = "did:plc:wshs7t2adsemcrrd4snkeqli"; # @tangled.sh
7
+
hostname = "spindle.alpha.tangled.sh";
8
+
listenAddr = "127.0.0.1:6555";
9
+
queueSize = 100;
10
+
maxJobCount = 2;
11
+
secrets = {
12
+
provider = "openbao";
13
+
};
14
+
};
15
+
pipelines = {
16
+
workflowTimeout = "15m";
17
+
};
18
+
};
19
+
}