forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
fork
Configure Feed
Select the types of activity you want to include in your feed.
1{
2 description = "atproto github";
3
4 inputs = {
5 nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
6 indigo = {
7 url = "github:oppiliappan/indigo";
8 flake = false;
9 };
10 htmx-src = {
11 url = "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js";
12 flake = false;
13 };
14 lucide-src = {
15 url = "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip";
16 flake = false;
17 };
18 inter-fonts-src = {
19 url = "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip";
20 flake = false;
21 };
22 ibm-plex-mono-src = {
23 url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-mono%401.1.0/ibm-plex-mono.zip";
24 flake = false;
25 };
26 gitignore = {
27 url = "github:hercules-ci/gitignore.nix";
28 inputs.nixpkgs.follows = "nixpkgs";
29 };
30 };
31
32 outputs = {
33 self,
34 nixpkgs,
35 indigo,
36 htmx-src,
37 lucide-src,
38 gitignore,
39 inter-fonts-src,
40 ibm-plex-mono-src,
41 }: let
42 supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
43 forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
44 nixpkgsFor = forAllSystems (system:
45 import nixpkgs {
46 inherit system;
47 overlays = [self.overlays.default];
48 });
49 inherit (gitignore.lib) gitignoreSource;
50 in {
51 overlays.default = final: prev: let
52 goModHash = "sha256-2vljseczrvsl2T0P9k69ro72yU59l5fp9r/sszmXYY4=";
53 buildCmdPackage = name:
54 final.buildGoModule {
55 pname = name;
56 version = "0.1.0";
57 src = gitignoreSource ./.;
58 subPackages = ["cmd/${name}"];
59 vendorHash = goModHash;
60 CGO_ENABLED = 0;
61 };
62 in {
63 indigo-lexgen = final.buildGoModule {
64 pname = "indigo-lexgen";
65 version = "0.1.0";
66 src = indigo;
67 subPackages = ["cmd/lexgen"];
68 vendorHash = "sha256-pGc29fgJFq8LP7n/pY1cv6ExZl88PAeFqIbFEhB3xXs=";
69 doCheck = false;
70 };
71
72 appview = with final;
73 final.pkgsStatic.buildGoModule {
74 pname = "appview";
75 version = "0.1.0";
76 src = gitignoreSource ./.;
77 postUnpack = ''
78 pushd source
79 mkdir -p appview/pages/static/{fonts,icons}
80 cp -f ${htmx-src} appview/pages/static/htmx.min.js
81 cp -rf ${lucide-src}/*.svg appview/pages/static/icons/
82 cp -f ${inter-fonts-src}/web/InterVariable*.woff2 appview/pages/static/fonts/
83 cp -f ${inter-fonts-src}/web/InterDisplay*.woff2 appview/pages/static/fonts/
84 cp -f ${ibm-plex-mono-src}/fonts/complete/woff2/IBMPlexMono-Regular.woff2 appview/pages/static/fonts/
85 ${pkgs.tailwindcss}/bin/tailwindcss -i input.css -o appview/pages/static/tw.css
86 popd
87 '';
88 doCheck = false;
89 subPackages = ["cmd/appview"];
90 vendorHash = goModHash;
91 CGO_ENABLED = 1;
92 stdenv = pkgsStatic.stdenv;
93 };
94
95 knotserver = with final;
96 final.pkgsStatic.buildGoModule {
97 pname = "knotserver";
98 version = "0.1.0";
99 src = gitignoreSource ./.;
100 nativeBuildInputs = [final.makeWrapper];
101 subPackages = ["cmd/knotserver"];
102 vendorHash = goModHash;
103 installPhase = ''
104 runHook preInstall
105
106 mkdir -p $out/bin
107 cp $GOPATH/bin/knotserver $out/bin/knotserver
108
109 wrapProgram $out/bin/knotserver \
110 --prefix PATH : ${pkgs.git}/bin
111
112 runHook postInstall
113 '';
114 CGO_ENABLED = 1;
115 };
116 knotserver-unwrapped = final.pkgsStatic.buildGoModule {
117 pname = "knotserver";
118 version = "0.1.0";
119 src = gitignoreSource ./.;
120 subPackages = ["cmd/knotserver"];
121 vendorHash = goModHash;
122 CGO_ENABLED = 1;
123 };
124 repoguard = buildCmdPackage "repoguard";
125 keyfetch = buildCmdPackage "keyfetch";
126 };
127 packages = forAllSystems (system: {
128 inherit
129 (nixpkgsFor."${system}")
130 indigo-lexgen
131 appview
132 knotserver
133 knotserver-unwrapped
134 repoguard
135 keyfetch
136 ;
137 });
138 defaultPackage = forAllSystems (system: nixpkgsFor.${system}.appview);
139 formatter = forAllSystems (system: nixpkgsFor."${system}".alejandra);
140 devShells = forAllSystems (system: let
141 pkgs = nixpkgsFor.${system};
142 staticShell = pkgs.mkShell.override {
143 stdenv = pkgs.pkgsStatic.stdenv;
144 };
145 in {
146 default = staticShell {
147 nativeBuildInputs = [
148 pkgs.go
149 pkgs.air
150 pkgs.gopls
151 pkgs.httpie
152 pkgs.indigo-lexgen
153 pkgs.litecli
154 pkgs.websocat
155 pkgs.tailwindcss
156 pkgs.nixos-shell
157 ];
158 shellHook = ''
159 mkdir -p appview/pages/static/{fonts,icons}
160 cp -f ${htmx-src} appview/pages/static/htmx.min.js
161 cp -rf ${lucide-src}/*.svg appview/pages/static/icons/
162 cp -f ${inter-fonts-src}/web/InterVariable*.woff2 appview/pages/static/fonts/
163 cp -f ${inter-fonts-src}/web/InterDisplay*.woff2 appview/pages/static/fonts/
164 cp -f ${ibm-plex-mono-src}/fonts/complete/woff2/IBMPlexMono-Regular.woff2 appview/pages/static/fonts/
165 '';
166 };
167 });
168 apps = forAllSystems (system: let
169 pkgs = nixpkgsFor."${system}";
170 air-watcher = name:
171 pkgs.writeShellScriptBin "run"
172 ''
173 ${pkgs.air}/bin/air -c /dev/null \
174 -build.cmd "${pkgs.tailwindcss}/bin/tailwindcss -i input.css -o ./appview/pages/static/tw.css && ${pkgs.go}/bin/go build -o ./out/${name}.out ./cmd/${name}/main.go" \
175 -build.bin "./out/${name}.out" \
176 -build.include_ext "go,html,css"
177 '';
178 in {
179 watch-appview = {
180 type = "app";
181 program = ''${air-watcher "appview"}/bin/run'';
182 };
183 watch-knotserver = {
184 type = "app";
185 program = ''${air-watcher "knotserver"}/bin/run'';
186 };
187 });
188
189 nixosModules.appview = {
190 config,
191 pkgs,
192 lib,
193 ...
194 }:
195 with lib; {
196 options = {
197 services.tangled-appview = {
198 enable = mkOption {
199 type = types.bool;
200 default = false;
201 description = "Enable tangled appview";
202 };
203 port = mkOption {
204 type = types.int;
205 default = 3000;
206 description = "Port to run the appview on";
207 };
208 cookie_secret = mkOption {
209 type = types.str;
210 default = "00000000000000000000000000000000";
211 description = "Cookie secret";
212 };
213 };
214 };
215
216 config = mkIf config.services.tangled-appview.enable {
217 systemd.services.tangled-appview = {
218 description = "tangled appview service";
219 wantedBy = ["multi-user.target"];
220
221 serviceConfig = {
222 ListenStream = "0.0.0.0:${toString config.services.tangled-appview.port}";
223 ExecStart = "${self.packages.${pkgs.system}.appview}/bin/appview";
224 Restart = "always";
225 };
226
227 environment = {
228 TANGLED_DB_PATH = "appview.db";
229 TANGLED_COOKIE_SECRET = config.services.tangled-appview.cookie_secret;
230 };
231 };
232 };
233 };
234
235 nixosModules.knotserver = {
236 config,
237 pkgs,
238 lib,
239 ...
240 }: let
241 cfg = config.services.tangled-knotserver;
242 in
243 with lib; {
244 options = {
245 services.tangled-knotserver = {
246 enable = mkOption {
247 type = types.bool;
248 default = false;
249 description = "Enable a tangled knotserver";
250 };
251
252 appviewEndpoint = mkOption {
253 type = types.str;
254 default = "https://tangled.sh";
255 description = "Appview endpoint";
256 };
257
258 gitUser = mkOption {
259 type = types.str;
260 default = "git";
261 description = "User that hosts git repos and performs git operations";
262 };
263
264 openFirewall = mkOption {
265 type = types.bool;
266 default = true;
267 description = "Open port 22 in the firewall for ssh";
268 };
269
270 stateDir = mkOption {
271 type = types.path;
272 default = "/home/${cfg.gitUser}";
273 description = "Tangled knot data directory";
274 };
275
276 repo = {
277 scanPath = mkOption {
278 type = types.path;
279 default = cfg.stateDir;
280 description = "Path where repositories are scanned from";
281 };
282
283 mainBranch = mkOption {
284 type = types.str;
285 default = "main";
286 description = "Default branch name for repositories";
287 };
288 };
289
290 server = {
291 listenAddr = mkOption {
292 type = types.str;
293 default = "0.0.0.0:5555";
294 description = "Address to listen on";
295 };
296
297 internalListenAddr = mkOption {
298 type = types.str;
299 default = "127.0.0.1:5444";
300 description = "Internal address for inter-service communication";
301 };
302
303 secretFile = mkOption {
304 type = lib.types.path;
305 example = "KNOT_SERVER_SECRET=<hash>";
306 description = "File containing secret key provided by appview (required)";
307 };
308
309 dbPath = mkOption {
310 type = types.path;
311 default = "${cfg.stateDir}/knotserver.db";
312 description = "Path to the database file";
313 };
314
315 hostname = mkOption {
316 type = types.str;
317 example = "knot.tangled.sh";
318 description = "Hostname for the server (required)";
319 };
320
321 dev = mkOption {
322 type = types.bool;
323 default = false;
324 description = "Enable development mode (disables signature verification)";
325 };
326 };
327 };
328 };
329
330 config = mkIf cfg.enable {
331 environment.systemPackages = with pkgs; [git];
332
333 system.activationScripts.gitConfig = ''
334 mkdir -p "${cfg.repo.scanPath}"
335 chown -R ${cfg.gitUser}:${cfg.gitUser} \
336 "${cfg.repo.scanPath}"
337
338 mkdir -p "${cfg.stateDir}/.config/git"
339 cat > "${cfg.stateDir}/.config/git/config" << EOF
340 [user]
341 name = Git User
342 email = git@example.com
343 EOF
344 chown -R ${cfg.gitUser}:${cfg.gitUser} \
345 "${cfg.stateDir}"
346 '';
347
348 users.users.${cfg.gitUser} = {
349 isSystemUser = true;
350 useDefaultShell = true;
351 home = cfg.stateDir;
352 createHome = true;
353 group = cfg.gitUser;
354 };
355
356 users.groups.${cfg.gitUser} = {};
357
358 services.openssh = {
359 enable = true;
360 extraConfig = ''
361 Match User ${cfg.gitUser}
362 AuthorizedKeysCommand /etc/ssh/keyfetch_wrapper
363 AuthorizedKeysCommandUser nobody
364 '';
365 };
366
367 environment.etc."ssh/keyfetch_wrapper" = {
368 mode = "0555";
369 text = ''
370 #!${pkgs.stdenv.shell}
371 ${self.packages.${pkgs.system}.keyfetch}/bin/keyfetch \
372 -repoguard-path ${self.packages.${pkgs.system}.repoguard}/bin/repoguard \
373 -internal-api "http://${cfg.server.internalListenAddr}" \
374 -git-dir "${cfg.repo.scanPath}" \
375 -log-path /tmp/repoguard.log
376 '';
377 };
378
379 systemd.services.knotserver = {
380 description = "knotserver service";
381 after = ["network.target" "sshd.service"];
382 wantedBy = ["multi-user.target"];
383 serviceConfig = {
384 User = cfg.gitUser;
385 WorkingDirectory = cfg.stateDir;
386 Environment = [
387 "KNOT_REPO_SCAN_PATH=${cfg.repo.scanPath}"
388 "KNOT_REPO_MAIN_BRANCH=${cfg.repo.mainBranch}"
389 "APPVIEW_ENDPOINT=${cfg.appviewEndpoint}"
390 "KNOT_SERVER_INTERNAL_LISTEN_ADDR=${cfg.server.internalListenAddr}"
391 "KNOT_SERVER_LISTEN_ADDR=${cfg.server.listenAddr}"
392 "KNOT_SERVER_DB_PATH=${cfg.server.dbPath}"
393 "KNOT_SERVER_HOSTNAME=${cfg.server.hostname}"
394 ];
395 EnvironmentFile = cfg.server.secretFile;
396 ExecStart = "${self.packages.${pkgs.system}.knotserver}/bin/knotserver";
397 Restart = "always";
398 };
399 };
400
401 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [22];
402 };
403 };
404
405 nixosConfigurations.knotVM = nixpkgs.lib.nixosSystem {
406 system = "x86_64-linux";
407 modules = [
408 self.nixosModules.knotserver
409 ({
410 config,
411 pkgs,
412 ...
413 }: {
414 virtualisation.memorySize = 2048;
415 virtualisation.cores = 2;
416 services.getty.autologinUser = "root";
417 environment.systemPackages = with pkgs; [curl vim git];
418 systemd.tmpfiles.rules = let
419 u = config.services.tangled-knotserver.gitUser;
420 g = config.services.tangled-knotserver.gitUser;
421 in [
422 "d /var/lib/knotserver 0770 ${u} ${g} - -" # Create the directory first
423 "f+ /var/lib/knotserver/secret 0660 ${u} ${g} - KNOT_SERVER_SECRET=5b42390da4c6659f34c9a545adebd8af82c4a19960d735f651e3d582623ba9f2"
424 ];
425 services.tangled-knotserver = {
426 enable = true;
427 server = {
428 secretFile = "/var/lib/knotserver/secret";
429 hostname = "localhost:6000";
430 listenAddr = "0.0.0.0:6000";
431 };
432 };
433 })
434 ];
435 };
436 };
437}