This repository has no description
4.6 kB
164 lines
1{
2 self,
3 config,
4 pkgs,
5 ...
6}:
7let
8 kanvibe = self.packages.${pkgs.system}.kanvibe;
9 port = 4242;
10in
11{
12 users.users.kanban = {
13 isNormalUser = true;
14 home = "/var/lib/kanvibe";
15 createHome = true;
16 group = "users";
17 openssh.authorizedKeys.keys = [
18 self.keys.ssh.barnacle.public
19 self.keys.ssh.yubi_pgp.public
20 self.keys.ssh.yubi1.public
21 self.keys.ssh.yubi2.public
22 self.keys.ssh.solo2.public
23 self.keys.ssh.xerxes.public
24 self.keys.ssh.massulus.public
25 ];
26 };
27
28 clan.core.vars.generators.vibe-kanban = {
29 files."htpasswd" = {
30 owner = "root";
31 group = "nginx";
32 mode = "0640";
33 };
34 files."password" = {
35 owner = "kanban";
36 group = "kanban";
37 mode = "0400";
38 };
39 runtimeInputs = with pkgs; [
40 apacheHttpd
41 coreutils
42 ];
43 script = ''
44 password=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24)
45 echo "$password" > "$out/password"
46 htpasswd -nbB kanban "$password" > "$out/htpasswd"
47 '';
48 };
49
50 services.postgresql = {
51 enable = true;
52 ensureDatabases = [ "kanban" ];
53 ensureUsers = [
54 {
55 name = "kanban";
56 ensureDBOwnership = true;
57 }
58 ];
59 };
60
61 systemd.services.kanvibe = {
62 description = "KanVibe AI Agent Kanban Board";
63 wantedBy = [ "multi-user.target" ];
64 after = [
65 "network-online.target"
66 "postgresql.service"
67 ];
68 wants = [ "network-online.target" ];
69 requires = [ "postgresql.service" ];
70
71 path = with pkgs; [
72 tmux
73 git
74 openssh
75 nodejs
76 bash
77 coreutils
78 nix
79 ];
80
81 environment = {
82 PORT = toString port;
83 NODE_ENV = "production";
84 KANVIBE_USER = "kanban";
85 };
86
87 serviceConfig = {
88 Type = "simple";
89 LoadCredential = "password:${config.clan.core.vars.generators.vibe-kanban.files."password".path}";
90 ExecStart =
91 let
92 startScript = pkgs.writeShellScript "kanvibe-start" ''
93 export KANVIBE_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/password")"
94 export DATABASE_URL="postgresql:///kanban?host=/run/postgresql"
95
96 # Run TypeORM migrations
97 cd ${kanvibe}/lib/kanvibe
98 ${pkgs.nodejs}/bin/node -e "
99 require('tsx/cjs');
100 const ds = require('./src/lib/typeorm-cli.config.ts').default;
101 ds.initialize().then(d => d.runMigrations()).then(() => { console.log('Migrations done'); process.exit(0); }).catch(e => { console.error(e); process.exit(1); });
102 "
103
104 exec ${kanvibe}/bin/kanvibe
105 '';
106 in
107 toString startScript;
108 Restart = "on-failure";
109 RestartSec = 10;
110 User = "kanban";
111 Group = "users";
112 WorkingDirectory = "/var/lib/kanvibe";
113
114 # Hardening
115 NoNewPrivileges = true;
116 ProtectSystem = "strict";
117 ProtectHome = "read-only";
118 ReadWritePaths = [ "/var/lib/kanvibe" ];
119 PrivateTmp = true;
120 };
121 };
122
123 services.nginx.virtualHosts."kanban.lassul.us" = {
124 enableACME = true;
125 forceSSL = true;
126 locations."/" = {
127 proxyPass = "http://127.0.0.1:${toString port}";
128 proxyWebsockets = true;
129 extraConfig = ''
130 auth_basic "Kanban";
131 auth_basic_user_file ${config.clan.core.vars.generators.vibe-kanban.files."htpasswd".path};
132 proxy_set_header Host $host;
133 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
134 proxy_set_header X-Forwarded-Proto $scheme;
135 proxy_read_timeout 300s;
136 proxy_send_timeout 300s;
137 '';
138 };
139 locations."/api/terminal/" = {
140 proxyPass = "http://127.0.0.1:${toString (port - 1)}";
141 proxyWebsockets = true;
142 extraConfig = ''
143 auth_basic "Kanban";
144 auth_basic_user_file ${config.clan.core.vars.generators.vibe-kanban.files."htpasswd".path};
145 proxy_set_header Host $host;
146 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
147 proxy_set_header X-Forwarded-Proto $scheme;
148 proxy_read_timeout 3600s;
149 proxy_send_timeout 3600s;
150 '';
151 };
152 locations."/api/board/" = {
153 proxyPass = "http://127.0.0.1:${toString (port - 1)}";
154 proxyWebsockets = true;
155 extraConfig = ''
156 auth_basic "Kanban";
157 auth_basic_user_file ${config.clan.core.vars.generators.vibe-kanban.files."htpasswd".path};
158 proxy_set_header Host $host;
159 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
160 proxy_set_header X-Forwarded-Proto $scheme;
161 '';
162 };
163 };
164}