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