old
  1{
  2  den,
  3  lib,
  4  ...
  5}: {
  6  den.aspects.services.proxy.mihomo.rules = {
  7    nixos = {
  8      host,
  9      config,
 10      ...
 11    }: let
 12      allHosts = builtins.concatMap builtins.attrValues (builtins.attrValues den.hosts);
 13      otherHosts = builtins.filter (h: h.name != host.name) allHosts;
 14
 15      hostsWithIpv4ClearText = builtins.filter (h: h.address.ipv4.clearText != null) otherHosts;
 16      hostsWithIpv4Secret = builtins.filter (h: h.address.ipv4.secret.name != null && h.address.ipv4.secret.file != null) otherHosts;
 17      hostsWithIpv6ClearText = builtins.filter (h: h.address.ipv6.clearText != null) otherHosts;
 18      hostsWithIpv6Secret = builtins.filter (h: h.address.ipv6.secret.name != null && h.address.ipv6.secret.file != null) otherHosts;
 19
 20      mkMihomoIpRule = ip: "IP-CIDR,${ip}/32";
 21
 22      clearTextIps =
 23        (map (h: mkMihomoIpRule h.address.ipv4.clearText) hostsWithIpv4ClearText)
 24        ++ (map (h: mkMihomoIpRule h.address.ipv6.clearText) hostsWithIpv6ClearText);
 25
 26      secretIps =
 27        (map (h: {_secret = config.vaultix.templates."mihomo-rules-${h.address.ipv4.secret.name}".path;}) hostsWithIpv4Secret)
 28        ++ (map (h: {_secret = config.vaultix.templates."mihomo-rules-${h.address.ipv6.secret.name}".path;}) hostsWithIpv6Secret);
 29
 30      allHostIps = clearTextIps ++ secretIps;
 31    in {
 32      vaultix = {
 33        secrets = lib.mkMerge (
 34          (map (h: {${h.address.ipv4.secret.name}.file = h.address.ipv4.secret.file;}) hostsWithIpv4Secret)
 35          ++ (map (h: {${h.address.ipv6.secret.name}.file = h.address.ipv6.secret.file;}) hostsWithIpv6Secret)
 36        );
 37        templates =
 38          lib.mergeAttrsList
 39          ((map (h: let
 40                name = h.address.ipv4.secret.name;
 41              in {
 42                "mihomo-rules-${name}".content = mkMihomoIpRule config.vaultix.placeholder.${name};
 43              })
 44              hostsWithIpv4Secret)
 45            ++ (map (h: let
 46                name = h.address.ipv6.secret.name;
 47              in {
 48                "mihomo-rules-${name}".content = mkMihomoIpRule config.vaultix.placeholder.${name};
 49              })
 50              hostsWithIpv6Secret));
 51      };
 52
 53      services.mihomo = {
 54        config = {
 55          rules = [
 56            # === Non-IP ===
 57            # Private
 58            "RULE-SET,reject_non_ip,REJECT"
 59            "RULE-SET,reject_domainset,REJECT"
 60            "RULE-SET,reject_non_ip_drop,REJECT-DROP"
 61            "RULE-SET,reject_non_ip_no_drop,REJECT"
 62            "RULE-SET,tailscale_non_ip,DIRECT"
 63            "RULE-SET,lan_non_ip,DIRECT"
 64
 65            # Game
 66            "DOMAIN-SUFFIX,cm.steampowered.com,DIRECT"
 67            "DOMAIN-SUFFIX,steamserver.net,DIRECT"
 68            "GEOSITE,steam@cn,DIRECT"
 69            "GEOSITE,category-game-platforms-download@cn,DIRECT"
 70
 71            # Should DIRECT
 72            "DST-PORT,22,DIRECT" # For SSH
 73            "DOMAIN-SUFFIX,kagi.com,DIRECT" # DIRECT is faster
 74            "DOMAIN-SUFFIX,mxrouting.net,DIRECT"
 75
 76            # Common
 77            "RULE-SET,cdn_domainset,SELECT"
 78            "RULE-SET,cdn_non_ip,SELECT"
 79            "RULE-SET,stream_non_ip,🇺🇸 - AUTO"
 80            "RULE-SET,telegram_non_ip,🇺🇸 - AUTO"
 81            "RULE-SET,apple_cdn,DIRECT"
 82            "RULE-SET,download_domainset,SELECT"
 83            "RULE-SET,download_non_ip,SELECT"
 84            "RULE-SET,microsoft_cdn_non_ip,DIRECT"
 85            "RULE-SET,apple_cn_non_ip,DIRECT"
 86            "RULE-SET,apple_services,DIRECT"
 87            "RULE-SET,microsoft_non_ip,DIRECT"
 88            "RULE-SET,ai_non_ip,🇺🇸 - AUTO"
 89            "RULE-SET,global_non_ip,SELECT"
 90            "RULE-SET,domestic_non_ip,DIRECT"
 91            "RULE-SET,direct_non_ip,DIRECT"
 92
 93            # === IP ===
 94            "RULE-SET,reject_ip,REJECT"
 95            "RULE-SET,my_hosts,DIRECT"
 96            "RULE-SET,telegram_ip,🇺🇸 - AUTO"
 97            "RULE-SET,stream_ip,🇺🇸 - AUTO"
 98            "RULE-SET,lan_ip,DIRECT"
 99            "RULE-SET,domestic_ip,DIRECT"
100            "RULE-SET,china_ip,DIRECT"
101            "MATCH,SELECT"
102          ];
103
104          rule-providers = let
105            ruleSetClassical = {
106              type = "http";
107              behavior = "classical";
108              interval = 43200;
109              format = "text";
110              proxy = "SELECT";
111            };
112            ruleSetDomain = {
113              type = "http";
114              behavior = "domain";
115              interval = 43200;
116              format = "text";
117              proxy = "SELECT";
118            };
119            ruleSetIpcidr = {
120              type = "http";
121              behavior = "ipcidr";
122              interval = 43200;
123              format = "text";
124              proxy = "SELECT";
125            };
126          in {
127            my_hosts = {
128              type = "inline";
129              behavior = "classical";
130              payload = allHostIps;
131            };
132            tailscale_non_ip = {
133              type = "inline";
134              behavior = "classical";
135              payload = [
136                "PROCESS-NAME,tailscale"
137                "PROCESS-NAME,tailscaled"
138                "PROCESS-NAME,.tailscaled-wrapped"
139                "DOMAIN-SUFFIX,ts.net"
140                "DOMAIN-SUFFIX,net.trin.one"
141                "DOMAIN,controlplane.tailscale.com"
142                "DOMAIN,gate.trin.one"
143              ];
144            };
145            reject_non_ip_no_drop =
146              ruleSetClassical
147              // {
148                url = "https://ruleset.skk.moe/Clash/non_ip/reject-no-drop.txt";
149                path = "./rule_set/sukkaw_ruleset/reject_non_ip_no_drop.txt";
150              };
151            reject_non_ip_drop =
152              ruleSetClassical
153              // {
154                url = "https://ruleset.skk.moe/Clash/non_ip/reject-drop.txt";
155                path = "./rule_set/sukkaw_ruleset/reject_non_ip_drop.txt";
156              };
157            reject_non_ip =
158              ruleSetClassical
159              // {
160                url = "https://ruleset.skk.moe/Clash/non_ip/reject.txt";
161                path = "./rule_set/sukkaw_ruleset/reject_non_ip.txt";
162              };
163            reject_domainset =
164              ruleSetDomain
165              // {
166                url = "https://ruleset.skk.moe/Clash/domainset/reject.txt";
167                path = "./rule_set/sukkaw_ruleset/reject_domainset.txt";
168              };
169            reject_ip =
170              ruleSetClassical
171              // {
172                url = "https://ruleset.skk.moe/Clash/ip/reject.txt";
173                path = "./rule_set/sukkaw_ruleset/reject_ip.txt";
174              };
175            cdn_domainset =
176              ruleSetDomain
177              // {
178                url = "https://ruleset.skk.moe/Clash/domainset/cdn.txt";
179                path = "./rule_set/sukkaw_ruleset/cdn_domainset.txt";
180              };
181            cdn_non_ip =
182              ruleSetDomain
183              // {
184                url = "https://ruleset.skk.moe/Clash/non_ip/cdn.txt";
185                path = "./rule_set/sukkaw_ruleset/cdn_non_ip.txt";
186              };
187            stream_non_ip =
188              ruleSetClassical
189              // {
190                url = "https://ruleset.skk.moe/Clash/non_ip/stream.txt";
191                path = "./rule_set/sukkaw_ruleset/stream_non_ip.txt";
192              };
193            stream_ip =
194              ruleSetClassical
195              // {
196                url = "https://ruleset.skk.moe/Clash/ip/stream.txt";
197                path = "./rule_set/sukkaw_ruleset/stream_ip.txt";
198              };
199            ai_non_ip =
200              ruleSetClassical
201              // {
202                url = "https://ruleset.skk.moe/Clash/non_ip/ai.txt";
203                path = "./rule_set/sukkaw_ruleset/ai_non_ip.txt";
204              };
205            telegram_non_ip =
206              ruleSetClassical
207              // {
208                url = "https://ruleset.skk.moe/Clash/non_ip/telegram.txt";
209                path = "./rule_set/sukkaw_ruleset/telegram_non_ip.txt";
210              };
211            telegram_ip =
212              ruleSetClassical
213              // {
214                url = "https://ruleset.skk.moe/Clash/ip/telegram.txt";
215                path = "./rule_set/sukkaw_ruleset/telegram_ip.txt";
216              };
217            apple_cdn =
218              ruleSetDomain
219              // {
220                url = "https://ruleset.skk.moe/Clash/domainset/apple_cdn.txt";
221                path = "./rule_set/sukkaw_ruleset/apple_cdn.txt";
222              };
223            apple_services =
224              ruleSetClassical
225              // {
226                url = "https://ruleset.skk.moe/Clash/non_ip/apple_services.txt";
227                path = "./rule_set/sukkaw_ruleset/apple_services.txt";
228              };
229            apple_cn_non_ip =
230              ruleSetClassical
231              // {
232                url = "https://ruleset.skk.moe/Clash/non_ip/apple_cn.txt";
233                path = "./rule_set/sukkaw_ruleset/apple_cn_non_ip.txt";
234              };
235            microsoft_cdn_non_ip =
236              ruleSetClassical
237              // {
238                url = "https://ruleset.skk.moe/Clash/non_ip/microsoft_cdn.txt";
239                path = "./rule_set/sukkaw_ruleset/microsoft_cdn_non_ip.txt";
240              };
241            microsoft_non_ip =
242              ruleSetClassical
243              // {
244                url = "https://ruleset.skk.moe/Clash/non_ip/microsoft.txt";
245                path = "./rule_set/sukkaw_ruleset/microsoft_non_ip.txt";
246              };
247            download_domainset =
248              ruleSetDomain
249              // {
250                url = "https://ruleset.skk.moe/Clash/domainset/download.txt";
251                path = "./rule_set/sukkaw_ruleset/download_domainset.txt";
252              };
253            download_non_ip =
254              ruleSetDomain
255              // {
256                url = "https://ruleset.skk.moe/Clash/non_ip/download.txt";
257                path = "./rule_set/sukkaw_ruleset/download_non_ip.txt";
258              };
259            lan_non_ip =
260              ruleSetClassical
261              // {
262                url = "https://ruleset.skk.moe/Clash/non_ip/lan.txt";
263                path = "./rule_set/sukkaw_ruleset/lan_non_ip.txt";
264              };
265            lan_ip =
266              ruleSetClassical
267              // {
268                url = "https://ruleset.skk.moe/Clash/ip/lan.txt";
269                path = "./rule_set/sukkaw_ruleset/lan_ip.txt";
270              };
271            domestic_non_ip =
272              ruleSetClassical
273              // {
274                url = "https://ruleset.skk.moe/Clash/non_ip/domestic.txt";
275                path = "./rule_set/sukkaw_ruleset/domestic_non_ip.txt";
276              };
277            direct_non_ip =
278              ruleSetClassical
279              // {
280                url = "https://ruleset.skk.moe/Clash/non_ip/direct.txt";
281                path = "./rule_set/sukkaw_ruleset/direct_non_ip.txt";
282              };
283            global_non_ip =
284              ruleSetClassical
285              // {
286                url = "https://ruleset.skk.moe/Clash/non_ip/global.txt";
287                path = "./rule_set/sukkaw_ruleset/global_non_ip.txt";
288              };
289            domestic_ip =
290              ruleSetClassical
291              // {
292                url = "https://ruleset.skk.moe/Clash/ip/domestic.txt";
293                path = "./rule_set/sukkaw_ruleset/domestic_ip.txt";
294              };
295            china_ip =
296              ruleSetIpcidr
297              // {
298                url = "https://ruleset.skk.moe/Clash/ip/china_ip.txt";
299                path = "./rule_set/sukkaw_ruleset/china_ip.txt";
300              };
301          };
302        };
303      };
304    };
305  };
306}