···
92
92
"treefmt-nix": "treefmt-nix"
93
93
},
94
94
"locked": {
95
95
-
"lastModified": 1774382987,
96
96
-
"narHash": "sha256-eaLyqSdqnMURHagtF0bQrEBhtn8CCh4pnjzrM3xzWH8=",
95
95
+
"lastModified": 1775143262,
96
96
+
"narHash": "sha256-znGwc79sRyn6IwrdO6uI9SstoE4MWwEVd0Er81KEzug=",
97
97
"ref": "refs/heads/main",
98
98
-
"rev": "b78c204c4cf30ca96bdb978cce87e1046dd0bc92",
99
99
-
"revCount": 13729,
98
98
+
"rev": "e6d60e141ddb458e79c3c7a956ee2e10b8f7af96",
99
99
+
"revCount": 13869,
100
100
"type": "git",
101
101
"url": "https://git.clan.lol/clan/clan-core"
102
102
},
···
121
121
]
122
122
},
123
123
"locked": {
124
124
-
"lastModified": 1774346798,
125
125
-
"narHash": "sha256-rmO3RnayZCiJyo079AysuQZmfB9j4K6PR8AHxWYQAgU=",
126
126
-
"rev": "985091674267edd2e2cbb5fa2b7ecd4f0a21400e",
124
124
+
"lastModified": 1774796937,
125
125
+
"narHash": "sha256-uDcgnNHK1D2oTHOQKsqQUPdDGMuG94dp3Nv8LsnqkEM=",
126
126
+
"rev": "04e10e10c7b4bbf2930f24d139326707a43cbb54",
127
127
"type": "tarball",
128
128
-
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/985091674267edd2e2cbb5fa2b7ecd4f0a21400e.tar.gz"
128
128
+
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/04e10e10c7b4bbf2930f24d139326707a43cbb54.tar.gz"
129
129
},
130
130
"original": {
131
131
"type": "tarball",
···
197
197
]
198
198
},
199
199
"locked": {
200
200
-
"lastModified": 1772408722,
201
201
-
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
200
200
+
"lastModified": 1775087534,
201
201
+
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
202
202
"owner": "hercules-ci",
203
203
"repo": "flake-parts",
204
204
-
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
204
204
+
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
205
205
"type": "github"
206
206
},
207
207
"original": {
···
239
239
]
240
240
},
241
241
"locked": {
242
242
-
"lastModified": 1772408722,
243
243
-
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
242
242
+
"lastModified": 1775087534,
243
243
+
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
244
244
"owner": "hercules-ci",
245
245
"repo": "flake-parts",
246
246
-
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
246
246
+
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
247
247
"type": "github"
248
248
},
249
249
"original": {
···
340
340
]
341
341
},
342
342
"locked": {
343
343
-
"lastModified": 1774104215,
344
344
-
"narHash": "sha256-EAtviqz0sEAxdHS4crqu7JGR5oI3BwaqG0mw7CmXkO8=",
343
343
+
"lastModified": 1774959120,
344
344
+
"narHash": "sha256-Pzk6UbueeWy9WFiDY6iA1aHid+2AMzkS6gg2x2cSkz4=",
345
345
"owner": "cachix",
346
346
"repo": "git-hooks.nix",
347
347
-
"rev": "f799ae951fde0627157f40aec28dec27b22076d0",
347
347
+
"rev": "c06f90f1eb6569bdaf6a4a10cb7e66db4454ac2a",
348
348
"type": "github"
349
349
},
350
350
"original": {
···
424
424
"treefmt-nix": "treefmt-nix_3"
425
425
},
426
426
"locked": {
427
427
-
"lastModified": 1774412760,
428
428
-
"narHash": "sha256-X5xu0MTdqMSSQwRojn8DG8B614FepRR9rJ8CqjG5frI=",
427
427
+
"lastModified": 1775141156,
428
428
+
"narHash": "sha256-tNpLkiOtmxCxCch4zPLoEGyaZK16ryOpQuujtix0AqY=",
429
429
"owner": "numtide",
430
430
"repo": "llm-agents.nix",
431
431
-
"rev": "80c08424d7edbc6a9e6492caa7a68bd64759af19",
431
431
+
"rev": "09cd3ef62649bc47fa3eb34318bb6ef5096654dd",
432
432
"type": "github"
433
433
},
434
434
"original": {
···
471
471
]
472
472
},
473
473
"locked": {
474
474
-
"lastModified": 1773000227,
475
475
-
"narHash": "sha256-zm3ftUQw0MPumYi91HovoGhgyZBlM4o3Zy0LhPNwzXE=",
474
474
+
"lastModified": 1775037210,
475
475
+
"narHash": "sha256-KM2WYj6EA7M/FVZVCl3rqWY+TFV5QzSyyGE2gQxeODU=",
476
476
"owner": "nix-darwin",
477
477
"repo": "nix-darwin",
478
478
-
"rev": "da529ac9e46f25ed5616fd634079a5f3c579135f",
478
478
+
"rev": "06648f4902343228ce2de79f291dd5a58ee12146",
479
479
"type": "github"
480
480
},
481
481
"original": {
···
491
491
]
492
492
},
493
493
"locked": {
494
494
-
"lastModified": 1773000227,
495
495
-
"narHash": "sha256-zm3ftUQw0MPumYi91HovoGhgyZBlM4o3Zy0LhPNwzXE=",
494
494
+
"lastModified": 1775037210,
495
495
+
"narHash": "sha256-KM2WYj6EA7M/FVZVCl3rqWY+TFV5QzSyyGE2gQxeODU=",
496
496
"owner": "LnL7",
497
497
"repo": "nix-darwin",
498
498
-
"rev": "da529ac9e46f25ed5616fd634079a5f3c579135f",
498
498
+
"rev": "06648f4902343228ce2de79f291dd5a58ee12146",
499
499
"type": "github"
500
500
},
501
501
"original": {
···
511
511
]
512
512
},
513
513
"locked": {
514
514
-
"lastModified": 1774156144,
515
515
-
"narHash": "sha256-gdYe9wTPl4ignDyXUl1LlICWj41+S0GB5lG1fKP17+A=",
514
514
+
"lastModified": 1774762074,
515
515
+
"narHash": "sha256-89Mh4Eb/5stVJX6kGagVMijcU2FmfeD8Qv7UXc5d92o=",
516
516
"owner": "nix-community",
517
517
"repo": "nix-index-database",
518
518
-
"rev": "55b588747fa3d7fc351a11831c4b874dab992862",
518
518
+
"rev": "bc13aeaed568be76eab84df88ff39261bb52ff70",
519
519
"type": "github"
520
520
},
521
521
"original": {
···
596
596
},
597
597
"nixos-hardware": {
598
598
"locked": {
599
599
-
"lastModified": 1774018263,
600
600
-
"narHash": "sha256-HHYEwK1A22aSaxv2ibhMMkKvrDGKGlA/qObG4smrSqc=",
599
599
+
"lastModified": 1774933469,
600
600
+
"narHash": "sha256-OrnCQeUO2bqaWUl0lkDWyGWjKsOhtCyd7JSfTedQNUE=",
601
601
"owner": "nixos",
602
602
"repo": "nixos-hardware",
603
603
-
"rev": "2d4b4717b2534fad5c715968c1cece04a172b365",
603
603
+
"rev": "f4c4c2c0c923d7811ac2a63ccc154767e4195337",
604
604
"type": "github"
605
605
},
606
606
"original": {
···
619
619
]
620
620
},
621
621
"locked": {
622
622
-
"lastModified": 1774392971,
623
623
-
"narHash": "sha256-/4+En99Qf63O0syn78Z9zrm70e50tdSYQCURTDNtyQ4=",
622
622
+
"lastModified": 1774999302,
623
623
+
"narHash": "sha256-KJfDmGLMVO8Hr4TrTwOAQpOykLLOSgk9dxkaf2dHzFU=",
624
624
"owner": "simple-nixos-mailserver",
625
625
"repo": "nixos-mailserver",
626
626
-
"rev": "07e82e06d87e048b94280a16bd29e827f6583869",
626
626
+
"rev": "493f0ff8a7571cc4ddd190babfa447489f41c752",
627
627
"type": "gitlab"
628
628
},
629
629
"original": {
···
635
635
},
636
636
"nixpkgs": {
637
637
"locked": {
638
638
-
"lastModified": 1774106199,
639
639
-
"narHash": "sha256-US5Tda2sKmjrg2lNHQL3jRQ6p96cgfWh3J1QBliQ8Ws=",
638
638
+
"lastModified": 1775036866,
639
639
+
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
640
640
"owner": "nixos",
641
641
"repo": "nixpkgs",
642
642
-
"rev": "6c9a78c09ff4d6c21d0319114873508a6ec01655",
642
642
+
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
643
643
"type": "github"
644
644
},
645
645
"original": {
···
658
658
"systems": "systems_4"
659
659
},
660
660
"locked": {
661
661
-
"lastModified": 1774309640,
662
662
-
"narHash": "sha256-8oWL7YLwElBY9ebYri1LlSlhf/gd1Qoqj0nbBwG2yso=",
661
661
+
"lastModified": 1774802402,
662
662
+
"narHash": "sha256-L1UJ/zxKTyyaGGmytH6OYlgQ0HGSMhvPkvU+iz4Mkb8=",
663
663
"owner": "nix-community",
664
664
"repo": "nixvim",
665
665
-
"rev": "28c58bf023bf537354f78d6e496a349d7a0ed554",
665
665
+
"rev": "cbd8536a05d1aae2593cb5c9ace1010c8c5845cb",
666
666
"type": "github"
667
667
},
668
668
"original": {
···
679
679
"treefmt-nix": "treefmt-nix_5"
680
680
},
681
681
"locked": {
682
682
-
"lastModified": 1774216876,
683
683
-
"narHash": "sha256-eqzuLOsNzPxt/dAv2Dp3E+NnBBpiOg478chwN4mrqK8=",
682
682
+
"lastModified": 1774962086,
683
683
+
"narHash": "sha256-ppZmENKI2a3I4BVxSjxgyo/yIGF8FL6jMGp9acJ/P+4=",
684
684
"owner": "pinpox",
685
685
"repo": "opencrow",
686
686
-
"rev": "ac074183b490c51c6734f320b1500462f63565ca",
686
686
+
"rev": "7f8c712d41383e93f4d0e875f9cbe87e512dc781",
687
687
"type": "github"
688
688
},
689
689
"original": {
···
745
745
]
746
746
},
747
747
"locked": {
748
748
-
"lastModified": 1774303811,
749
749
-
"narHash": "sha256-fhG4JAcLgjKwt+XHbjs8brpWnyKUfU4LikLm3s0Q/ic=",
748
748
+
"lastModified": 1774910634,
749
749
+
"narHash": "sha256-B+rZDPyktGEjOMt8PcHKYmgmKoF+GaNAFJhguktXAo0=",
750
750
"owner": "Mic92",
751
751
"repo": "sops-nix",
752
752
-
"rev": "614e256310e0a4f8a9ccae3fa80c11844fba7042",
752
752
+
"rev": "19bf3d8678fbbfbc173beaa0b5b37d37938db301",
753
753
"type": "github"
754
754
},
755
755
"original": {
···
870
870
]
871
871
},
872
872
"locked": {
873
873
-
"lastModified": 1773297127,
874
874
-
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
873
873
+
"lastModified": 1775125835,
874
874
+
"narHash": "sha256-2qYcPgzFhnQWchHo0SlqLHrXpux5i6ay6UHA+v2iH4U=",
875
875
"owner": "numtide",
876
876
"repo": "treefmt-nix",
877
877
-
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
877
877
+
"rev": "75925962939880974e3ab417879daffcba36c4a3",
878
878
"type": "github"
879
879
},
880
880
"original": {
···
912
912
]
913
913
},
914
914
"locked": {
915
915
-
"lastModified": 1773297127,
916
916
-
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
915
915
+
"lastModified": 1775125835,
916
916
+
"narHash": "sha256-2qYcPgzFhnQWchHo0SlqLHrXpux5i6ay6UHA+v2iH4U=",
917
917
"owner": "numtide",
918
918
"repo": "treefmt-nix",
919
919
-
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
919
919
+
"rev": "75925962939880974e3ab417879daffcba36c4a3",
920
920
"type": "github"
921
921
},
922
922
"original": {
···
954
954
]
955
955
},
956
956
"locked": {
957
957
-
"lastModified": 1770228511,
958
958
-
"narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=",
957
957
+
"lastModified": 1773297127,
958
958
+
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
959
959
"owner": "numtide",
960
960
"repo": "treefmt-nix",
961
961
-
"rev": "337a4fe074be1042a35086f15481d763b8ddc0e7",
961
961
+
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
962
962
"type": "github"
963
963
},
964
964
"original": {
···
995
995
]
996
996
},
997
997
"locked": {
998
998
-
"lastModified": 1773297127,
999
999
-
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
998
998
+
"lastModified": 1775125835,
999
999
+
"narHash": "sha256-2qYcPgzFhnQWchHo0SlqLHrXpux5i6ay6UHA+v2iH4U=",
1000
1000
"owner": "numtide",
1001
1001
"repo": "treefmt-nix",
1002
1002
-
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
1002
1002
+
"rev": "75925962939880974e3ab417879daffcba36c4a3",
1003
1003
"type": "github"
1004
1004
},
1005
1005
"original": {
···
1
1
+
{ config, pkgs, lib, ... }:
2
2
+
let
3
3
+
cfg = config.services.ipfs-pin-watcher;
4
4
+
5
5
+
pinWatcherScript = pkgs.writers.writeBash "ipfs-pin-watcher" ''
6
6
+
set -efu
7
7
+
8
8
+
IPFS="${pkgs.kubo}/bin/ipfs"
9
9
+
INOTIFYWAIT="${pkgs.inotify-tools}/bin/inotifywait"
10
10
+
CID_MAP="${cfg.cidMapFile}"
11
11
+
12
12
+
# ensure cid map file exists
13
13
+
touch "$CID_MAP"
14
14
+
15
15
+
log() {
16
16
+
echo "[$(date -Iseconds)] $*"
17
17
+
}
18
18
+
19
19
+
add_file() {
20
20
+
local path="$1"
21
21
+
# skip if not a regular file
22
22
+
[ -f "$path" ] || return 0
23
23
+
# skip partial/temp files
24
24
+
case "$(basename "$path")" in
25
25
+
.* | *.part | *.tmp) return 0 ;;
26
26
+
esac
27
27
+
28
28
+
log "Adding $path"
29
29
+
cid=$($IPFS add --nocopy --pin --quieter "$path" 2>&1) || {
30
30
+
log "Failed to add $path: $cid"
31
31
+
return 0
32
32
+
}
33
33
+
log "Pinned $path -> $cid"
34
34
+
# store mapping (remove old entry for this path first)
35
35
+
${pkgs.gnused}/bin/sed -i "\|^[^ ]* $path$|d" "$CID_MAP"
36
36
+
echo "$cid $path" >> "$CID_MAP"
37
37
+
}
38
38
+
39
39
+
remove_file() {
40
40
+
local path="$1"
41
41
+
# look up CID from our map
42
42
+
cid=$(${pkgs.gawk}/bin/awk -v p="$path" '$0 ~ p {print $1; exit}' "$CID_MAP")
43
43
+
if [ -n "$cid" ]; then
44
44
+
log "Unpinning $cid ($path)"
45
45
+
$IPFS pin rm "$cid" 2>/dev/null || true
46
46
+
# remove from map
47
47
+
${pkgs.gnused}/bin/sed -i "\|^[^ ]* $path$|d" "$CID_MAP"
48
48
+
else
49
49
+
log "No CID found for removed file: $path"
50
50
+
fi
51
51
+
}
52
52
+
53
53
+
# initial sync: add all existing files
54
54
+
log "Starting initial sync of watched directories..."
55
55
+
for dir in ${lib.escapeShellArgs cfg.watchDirs}; do
56
56
+
if [ -d "$dir" ]; then
57
57
+
${pkgs.findutils}/bin/find "$dir" -type f | while read -r f; do
58
58
+
# skip if already in map
59
59
+
if ! ${pkgs.gnugrep}/bin/grep -q " $f$" "$CID_MAP" 2>/dev/null; then
60
60
+
add_file "$f"
61
61
+
fi
62
62
+
done
63
63
+
fi
64
64
+
done
65
65
+
log "Initial sync complete"
66
66
+
67
67
+
# clean up stale entries (files in map but no longer on disk)
68
68
+
log "Cleaning stale entries..."
69
69
+
while IFS=' ' read -r cid path; do
70
70
+
if [ ! -f "$path" ]; then
71
71
+
log "Stale entry: $path (CID: $cid)"
72
72
+
$IPFS pin rm "$cid" 2>/dev/null || true
73
73
+
${pkgs.gnused}/bin/sed -i "\|^[^ ]* $path$|d" "$CID_MAP"
74
74
+
fi
75
75
+
done < "$CID_MAP"
76
76
+
log "Cleanup complete"
77
77
+
78
78
+
# watch for changes
79
79
+
log "Watching directories: ${lib.concatStringsSep ", " cfg.watchDirs}"
80
80
+
$INOTIFYWAIT -m -r \
81
81
+
-e close_write \
82
82
+
-e moved_to \
83
83
+
-e moved_from \
84
84
+
-e delete \
85
85
+
${lib.escapeShellArgs cfg.watchDirs} |
86
86
+
while read -r dir event file; do
87
87
+
path="''${dir}''${file}"
88
88
+
case "$event" in
89
89
+
CLOSE_WRITE*|MOVED_TO*)
90
90
+
add_file "$path"
91
91
+
;;
92
92
+
DELETE*|MOVED_FROM*)
93
93
+
remove_file "$path"
94
94
+
;;
95
95
+
esac
96
96
+
done
97
97
+
'';
98
98
+
in
99
99
+
{
100
100
+
options.services.ipfs-pin-watcher = {
101
101
+
enable = lib.mkEnableOption "IPFS auto-pin watcher";
102
102
+
watchDirs = lib.mkOption {
103
103
+
type = lib.types.listOf lib.types.str;
104
104
+
default = [ ];
105
105
+
description = "Directories to watch for new files to auto-pin in IPFS";
106
106
+
example = [ "/var/download/movies" "/var/download/shows" "/var/download/games" ];
107
107
+
};
108
108
+
cidMapFile = lib.mkOption {
109
109
+
type = lib.types.str;
110
110
+
default = "/var/lib/ipfs/cid-map.txt";
111
111
+
description = "File to store CID-to-path mappings for unpinning";
112
112
+
};
113
113
+
};
114
114
+
115
115
+
config = lib.mkIf cfg.enable {
116
116
+
services.kubo = {
117
117
+
enable = true;
118
118
+
settings = {
119
119
+
Experimental.FilestoreEnabled = true;
120
120
+
# listen on all interfaces so it's reachable via retiolum
121
121
+
Addresses = {
122
122
+
API = [
123
123
+
"/ip4/0.0.0.0/tcp/5001"
124
124
+
"/ip6/::/tcp/5001"
125
125
+
];
126
126
+
Gateway = [
127
127
+
"/ip4/0.0.0.0/tcp/8080"
128
128
+
"/ip6/::/tcp/8080"
129
129
+
];
130
130
+
};
131
131
+
# set a reasonable storage max for garbage collection
132
132
+
Datastore.StorageMax = "100GB";
133
133
+
};
134
134
+
};
135
135
+
136
136
+
# bump inotify limits for large directories
137
137
+
boot.kernel.sysctl."fs.inotify.max_user_watches" = 1048576;
138
138
+
139
139
+
systemd.services.ipfs-pin-watcher = {
140
140
+
description = "Auto-pin files to IPFS using inotify";
141
141
+
after = [ "kubo.service" ];
142
142
+
requires = [ "kubo.service" ];
143
143
+
wantedBy = [ "multi-user.target" ];
144
144
+
serviceConfig = {
145
145
+
ExecStart = pinWatcherScript;
146
146
+
Restart = "always";
147
147
+
RestartSec = "10s";
148
148
+
User = config.services.kubo.user;
149
149
+
Group = config.services.kubo.group;
150
150
+
# ipfs needs access to download dirs
151
151
+
SupplementaryGroups = [ "download" ];
152
152
+
};
153
153
+
};
154
154
+
155
155
+
networking.firewall.allowedTCPPorts = [
156
156
+
4001 # IPFS swarm
157
157
+
5001 # IPFS API
158
158
+
8080 # IPFS gateway
159
159
+
];
160
160
+
networking.firewall.allowedUDPPorts = [
161
161
+
4001 # IPFS swarm (QUIC)
162
162
+
];
163
163
+
};
164
164
+
}