Monorepo for Tangled
tangled.org
1{
2 config,
3 pkgs,
4 lib,
5 ...
6}: let
7 cfg = config.services.tangled.knotmirror;
8in
9 with lib; {
10 options.services.tangled.knotmirror = {
11 enable = mkOption {
12 type = types.bool;
13 default = false;
14 description = "Enable a tangled knot";
15 };
16
17 package = mkOption {
18 type = types.package;
19 description = "Package to use for the knotmirror";
20 };
21
22 tap-package = mkOption {
23 type = types.package;
24 description = "tap package to use for the knotmirror";
25 };
26
27 listenAddr = mkOption {
28 type = types.str;
29 default = "0.0.0.0:7000";
30 description = "Address to listen on";
31 };
32
33 adminListenAddr = mkOption {
34 type = types.str;
35 default = "127.0.0.1:7200";
36 description = "Address to listen on";
37 };
38
39 metricsListenAddr = mkOption {
40 type = types.str;
41 default = "0.0.0.0:7100";
42 description = "Listen address for the Prometheus metrics endpoint";
43 };
44
45 hostname = mkOption {
46 type = types.str;
47 example = "my.knotmirror.com";
48 description = "Hostname for the server (required)";
49 };
50
51 dbUrl = mkOption {
52 type = types.str;
53 example = "postgresql://...";
54 description = "Database URL. postgresql expected (required)";
55 };
56
57 atpPlcUrl = mkOption {
58 type = types.str;
59 default = "https://plc.directory";
60 description = "atproto PLC directory";
61 };
62
63 atpRelayUrl = mkOption {
64 type = types.str;
65 default = "https://relay1.us-east.bsky.network";
66 description = "atproto relay";
67 };
68
69 fullNetwork = mkOption {
70 type = types.bool;
71 default = false;
72 description = "Whether to automatically mirror from entire network";
73 };
74
75 knotUseSSL = mkOption {
76 type = types.bool;
77 default = true;
78 description = "Use SSL for knot connection";
79 };
80
81 knotSSRF = mkOption {
82 type = types.bool;
83 default = true;
84 description = "enable SSRF protection for knots";
85 };
86
87 tap = {
88 port = mkOption {
89 type = types.port;
90 default = 7480;
91 description = "Internal port to run the knotmirror tap";
92 };
93
94 dbUrl = mkOption {
95 type = types.str;
96 default = "sqlite:///var/lib/knotmirror-tap/tap.db";
97 description = "database connection string (sqlite://path or postgres://...)";
98 };
99 };
100 };
101 config = mkIf cfg.enable {
102 environment.systemPackages = [
103 pkgs.git
104 cfg.package
105 ];
106
107 services.redis.servers.knotmirror = {
108 enable = true;
109 port = 6377;
110 };
111
112 systemd.services.tap-knotmirror = {
113 description = "knotmirror tap service";
114 after = ["network.target"];
115 wantedBy = ["multi-user.target"];
116 serviceConfig = {
117 LogsDirectory = "knotmirror-tap";
118 StateDirectory = "knotmirror-tap";
119 Environment = [
120 "TAP_BIND=:${toString cfg.tap.port}"
121 "TAP_PLC_URL=${cfg.atpPlcUrl}"
122 "TAP_RELAY_URL=${cfg.atpRelayUrl}"
123 "TAP_RESYNC_PARALLELISM=10"
124 "TAP_DATABASE_URL=${cfg.tap.dbUrl}"
125 "TAP_RETRY_TIMEOUT=60s"
126 "TAP_COLLECTION_FILTERS=sh.tangled.repo"
127 (
128 if cfg.fullNetwork
129 then "TAP_SIGNAL_COLLECTION=sh.tangled.repo"
130 else "TAP_FULL_NETWORK=false"
131 )
132 ];
133 ExecStart = "${getExe cfg.tap-package} run";
134 };
135 };
136
137 systemd.services.knotmirror = {
138 description = "knotmirror service";
139 after = ["network.target" "tap-knotmirror.service"];
140 wantedBy = ["multi-user.target"];
141 path = [
142 pkgs.git
143 ];
144 serviceConfig = {
145 LogsDirectory = "knotmirror";
146 StateDirectory = "knotmirror";
147 Environment = [
148 # TODO: add environment variables
149 "MIRROR_LISTEN=${cfg.listenAddr}"
150 "MIRROR_HOSTNAME=${cfg.hostname}"
151 "MIRROR_TAP_URL=http://localhost:${toString cfg.tap.port}"
152 "MIRROR_DB_URL=${cfg.dbUrl}"
153 "MIRROR_REDIS_ADDR=localhost:6377"
154 "MIRROR_GIT_BASEPATH=/var/lib/knotmirror/repos"
155 "MIRROR_KNOT_USE_SSL=${boolToString cfg.knotUseSSL}"
156 "MIRROR_KNOT_SSRF=${boolToString cfg.knotSSRF}"
157 "MIRROR_RESYNC_PARALLELISM=12"
158 "MIRROR_METRICS_LISTEN=${cfg.metricsListenAddr}"
159 "MIRROR_ADMIN_LISTEN=${cfg.adminListenAddr}"
160 "MIRROR_SLURPER_CONCURRENCY=4"
161 ];
162 ExecStart = "${getExe cfg.package} serve";
163 Restart = "always";
164 };
165 };
166 };
167 }