tangled
alpha
login
or
join now
bwc9876.dev
/
nixos-config
1
fork
atom
Flake for my NixOS devices
1
fork
atom
overview
issues
pulls
pipelines
Tangled Knot Self Host
bwc9876.dev
1 month ago
ac712f1c
6faf3b20
verified
This commit was signed with the committer's
known signature
.
bwc9876.dev
SSH Key Fingerprint:
SHA256:DanMEP/RNlSC7pAVbnXO6wzQV00rqyKj053tz4uH5gQ=
+372
-20
8 changed files
expand all
collapse all
unified
split
flake.lock
flake.nix
homeModules
jj.nix
nushell.nix
nixosConfigurations
black-mesa.nix
nixosModules
tangled.nix
user-bean.nix
res
bleh.txt
+208
flake.lock
···
1
1
{
2
2
"nodes": {
3
3
+
"actor-typeahead-src": {
4
4
+
"flake": false,
5
5
+
"locked": {
6
6
+
"lastModified": 1762835797,
7
7
+
"narHash": "sha256-heizoWUKDdar6ymfZTnj3ytcEv/L4d4fzSmtr0HlXsQ=",
8
8
+
"ref": "refs/heads/main",
9
9
+
"rev": "677fe7f743050a4e7f09d4a6f87bbf1325a06f6b",
10
10
+
"revCount": 6,
11
11
+
"type": "git",
12
12
+
"url": "https://tangled.org/@jakelazaroff.com/actor-typeahead"
13
13
+
},
14
14
+
"original": {
15
15
+
"type": "git",
16
16
+
"url": "https://tangled.org/@jakelazaroff.com/actor-typeahead"
17
17
+
}
18
18
+
},
3
19
"bingus": {
4
20
"inputs": {
5
21
"flakelight": "flakelight",
···
164
180
"type": "github"
165
181
}
166
182
},
183
183
+
"flake-compat_2": {
184
184
+
"flake": false,
185
185
+
"locked": {
186
186
+
"lastModified": 1751685974,
187
187
+
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
188
188
+
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
189
189
+
"type": "tarball",
190
190
+
"url": "https://git.lix.systems/api/v1/repos/lix-project/flake-compat/archive/549f2762aebeff29a2e5ece7a7dc0f955281a1d1.tar.gz?rev=549f2762aebeff29a2e5ece7a7dc0f955281a1d1"
191
191
+
},
192
192
+
"original": {
193
193
+
"type": "tarball",
194
194
+
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
195
195
+
}
196
196
+
},
167
197
"flake-parts": {
168
198
"inputs": {
169
199
"nixpkgs-lib": [
···
182
212
"original": {
183
213
"owner": "hercules-ci",
184
214
"repo": "flake-parts",
215
215
+
"type": "github"
216
216
+
}
217
217
+
},
218
218
+
"flake-utils": {
219
219
+
"inputs": {
220
220
+
"systems": "systems_2"
221
221
+
},
222
222
+
"locked": {
223
223
+
"lastModified": 1731533236,
224
224
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
225
225
+
"owner": "numtide",
226
226
+
"repo": "flake-utils",
227
227
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
228
228
+
"type": "github"
229
229
+
},
230
230
+
"original": {
231
231
+
"owner": "numtide",
232
232
+
"repo": "flake-utils",
185
233
"type": "github"
186
234
}
187
235
},
···
376
424
"type": "github"
377
425
}
378
426
},
427
427
+
"gomod2nix": {
428
428
+
"inputs": {
429
429
+
"flake-utils": "flake-utils",
430
430
+
"nixpkgs": [
431
431
+
"tangled",
432
432
+
"nixpkgs"
433
433
+
]
434
434
+
},
435
435
+
"locked": {
436
436
+
"lastModified": 1763982521,
437
437
+
"narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=",
438
438
+
"owner": "nix-community",
439
439
+
"repo": "gomod2nix",
440
440
+
"rev": "02e63a239d6eabd595db56852535992c898eba72",
441
441
+
"type": "github"
442
442
+
},
443
443
+
"original": {
444
444
+
"owner": "nix-community",
445
445
+
"repo": "gomod2nix",
446
446
+
"type": "github"
447
447
+
}
448
448
+
},
379
449
"hm": {
380
450
"inputs": {
381
451
"nixpkgs": [
···
396
466
"type": "github"
397
467
}
398
468
},
469
469
+
"htmx-src": {
470
470
+
"flake": false,
471
471
+
"locked": {
472
472
+
"narHash": "sha256-nm6avZuEBg67SSyyZUhjpXVNstHHgUxrtBHqJgowU08=",
473
473
+
"type": "file",
474
474
+
"url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"
475
475
+
},
476
476
+
"original": {
477
477
+
"type": "file",
478
478
+
"url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"
479
479
+
}
480
480
+
},
481
481
+
"htmx-ws-src": {
482
482
+
"flake": false,
483
483
+
"locked": {
484
484
+
"narHash": "sha256-2fg6KyEJoO24q0fQqbz9RMaYNPQrMwpZh29tkSqdqGY=",
485
485
+
"type": "file",
486
486
+
"url": "https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.2"
487
487
+
},
488
488
+
"original": {
489
489
+
"type": "file",
490
490
+
"url": "https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.2"
491
491
+
}
492
492
+
},
493
493
+
"ibm-plex-mono-src": {
494
494
+
"flake": false,
495
495
+
"locked": {
496
496
+
"lastModified": 1731402384,
497
497
+
"narHash": "sha256-OwUmrPfEehLDz0fl2ChYLK8FQM2p0G1+EMrGsYEq+6g=",
498
498
+
"type": "tarball",
499
499
+
"url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip"
500
500
+
},
501
501
+
"original": {
502
502
+
"type": "tarball",
503
503
+
"url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip"
504
504
+
}
505
505
+
},
399
506
"imperm": {
400
507
"inputs": {
401
508
"home-manager": [
···
419
526
"type": "github"
420
527
}
421
528
},
529
529
+
"indigo": {
530
530
+
"flake": false,
531
531
+
"locked": {
532
532
+
"lastModified": 1753693716,
533
533
+
"narHash": "sha256-DMIKnCJRODQXEHUxA+7mLzRALmnZhkkbHlFT2rCQYrE=",
534
534
+
"owner": "oppiliappan",
535
535
+
"repo": "indigo",
536
536
+
"rev": "5f170569da9360f57add450a278d73538092d8ca",
537
537
+
"type": "github"
538
538
+
},
539
539
+
"original": {
540
540
+
"owner": "oppiliappan",
541
541
+
"repo": "indigo",
542
542
+
"type": "github"
543
543
+
}
544
544
+
},
545
545
+
"inter-fonts-src": {
546
546
+
"flake": false,
547
547
+
"locked": {
548
548
+
"lastModified": 1731687360,
549
549
+
"narHash": "sha256-5vdKKvHAeZi6igrfpbOdhZlDX2/5+UvzlnCQV6DdqoQ=",
550
550
+
"type": "tarball",
551
551
+
"url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip"
552
552
+
},
553
553
+
"original": {
554
554
+
"type": "tarball",
555
555
+
"url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip"
556
556
+
}
557
557
+
},
422
558
"lanzaboote": {
423
559
"inputs": {
424
560
"crane": "crane_2",
···
440
576
"owner": "nix-community",
441
577
"repo": "lanzaboote",
442
578
"type": "github"
579
579
+
}
580
580
+
},
581
581
+
"lucide-src": {
582
582
+
"flake": false,
583
583
+
"locked": {
584
584
+
"lastModified": 1754044466,
585
585
+
"narHash": "sha256-+exBR2OToB1iv7ZQI2S4B0lXA/QRvC9n6U99UxGpJGs=",
586
586
+
"type": "tarball",
587
587
+
"url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip"
588
588
+
},
589
589
+
"original": {
590
590
+
"type": "tarball",
591
591
+
"url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip"
443
592
}
444
593
},
445
594
"mrpack-install": {
···
771
920
"nixvim": "nixvim",
772
921
"nu_plugin_dbus": "nu_plugin_dbus",
773
922
"spoon": "spoon",
923
923
+
"tangled": "tangled",
774
924
"wayland-mpris-idle-inhibit": "wayland-mpris-idle-inhibit"
775
925
}
776
926
},
···
862
1012
"url": "https://codeberg.org/spoonbaker/mono"
863
1013
}
864
1014
},
1015
1015
+
"sqlite-lib-src": {
1016
1016
+
"flake": false,
1017
1017
+
"locked": {
1018
1018
+
"lastModified": 1706631843,
1019
1019
+
"narHash": "sha256-bJoMjirsBjm2Qk9KPiy3yV3+8b/POlYe76/FQbciHro=",
1020
1020
+
"type": "tarball",
1021
1021
+
"url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip"
1022
1022
+
},
1023
1023
+
"original": {
1024
1024
+
"type": "tarball",
1025
1025
+
"url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip"
1026
1026
+
}
1027
1027
+
},
865
1028
"systems": {
866
1029
"locked": {
867
1030
"lastModified": 1681028828,
···
875
1038
"owner": "nix-systems",
876
1039
"repo": "default",
877
1040
"type": "github"
1041
1041
+
}
1042
1042
+
},
1043
1043
+
"systems_2": {
1044
1044
+
"locked": {
1045
1045
+
"lastModified": 1681028828,
1046
1046
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
1047
1047
+
"owner": "nix-systems",
1048
1048
+
"repo": "default",
1049
1049
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
1050
1050
+
"type": "github"
1051
1051
+
},
1052
1052
+
"original": {
1053
1053
+
"owner": "nix-systems",
1054
1054
+
"repo": "default",
1055
1055
+
"type": "github"
1056
1056
+
}
1057
1057
+
},
1058
1058
+
"tangled": {
1059
1059
+
"inputs": {
1060
1060
+
"actor-typeahead-src": "actor-typeahead-src",
1061
1061
+
"flake-compat": "flake-compat_2",
1062
1062
+
"gomod2nix": "gomod2nix",
1063
1063
+
"htmx-src": "htmx-src",
1064
1064
+
"htmx-ws-src": "htmx-ws-src",
1065
1065
+
"ibm-plex-mono-src": "ibm-plex-mono-src",
1066
1066
+
"indigo": "indigo",
1067
1067
+
"inter-fonts-src": "inter-fonts-src",
1068
1068
+
"lucide-src": "lucide-src",
1069
1069
+
"nixpkgs": [
1070
1070
+
"nixpkgs"
1071
1071
+
],
1072
1072
+
"sqlite-lib-src": "sqlite-lib-src"
1073
1073
+
},
1074
1074
+
"locked": {
1075
1075
+
"lastModified": 1769508314,
1076
1076
+
"narHash": "sha256-NO6zA56l1qqBs44oXdMuIjyGIcL0EGwZ95bGnd5ZT0s=",
1077
1077
+
"ref": "refs/heads/master",
1078
1078
+
"rev": "dc2cc2b318297aaca96f29a7db9c6b2eaeba092b",
1079
1079
+
"revCount": 1896,
1080
1080
+
"type": "git",
1081
1081
+
"url": "https://tangled.org/tangled.org/core"
1082
1082
+
},
1083
1083
+
"original": {
1084
1084
+
"type": "git",
1085
1085
+
"url": "https://tangled.org/tangled.org/core"
878
1086
}
879
1087
},
880
1088
"treefmt-nix": {
+3
flake.nix
···
35
35
niri.inputs.nixpkgs.follows = "nixpkgs";
36
36
musnix.url = "github:musnix/musnix";
37
37
musnix.inputs.nixpkgs.follows = "nixpkgs";
38
38
+
tangled.url = "git+https://tangled.org/tangled.org/core";
39
39
+
tangled.inputs.nixpkgs.follows = "nixpkgs";
38
40
39
41
spoon.url = "git+https://codeberg.org/spoonbaker/mono";
40
42
spoon.inputs = {
···
65
67
gh-grader-preview,
66
68
niri,
67
69
musnix,
70
70
+
tangled,
68
71
}:
69
72
flakelight ./. {
70
73
inherit inputs;
+14
-18
homeModules/jj.nix
···
1
1
-
{ ... }:
2
2
-
{
1
1
+
{...}: {
3
2
pkgs,
4
3
config,
5
4
lib,
6
5
...
7
7
-
}:
8
8
-
{
6
6
+
}: {
9
7
options.cow.jj = {
10
8
enable = lib.mkEnableOption "jj + customizations";
11
9
};
12
10
13
13
-
config =
14
14
-
let
15
15
-
conf = config.cow.jj;
16
16
-
in
17
17
-
(lib.mkIf conf.enable {
18
18
-
programs.jujutsu = {
19
19
-
enable = true;
20
20
-
settings = {
21
21
-
ui.pager = "less -FR";
22
22
-
};
11
11
+
config = let
12
12
+
conf = config.cow.jj;
13
13
+
in (lib.mkIf conf.enable {
14
14
+
programs.jujutsu = {
15
15
+
enable = true;
16
16
+
settings = {
17
17
+
ui.pager = "less -FR";
23
18
};
19
19
+
};
24
20
25
25
-
home.packages = with pkgs; [
26
26
-
mergiraf
27
27
-
];
28
28
-
});
21
21
+
home.packages = with pkgs; [
22
22
+
mergiraf
23
23
+
];
24
24
+
});
29
25
}
-1
homeModules/nushell.nix
···
74
74
"cd" = "z";
75
75
"py" = "python";
76
76
"🥺" = "sudo";
77
77
-
"j" = "just";
78
77
};
79
78
};
80
79
};
+46
-1
nixosConfigurations/black-mesa.nix
···
92
92
];
93
93
};
94
94
}
95
95
+
(
96
96
+
{
97
97
+
config,
98
98
+
pkgs,
99
99
+
...
100
100
+
}: {
101
101
+
# Self hosted stuff
102
102
+
103
103
+
cow = {
104
104
+
tangled = {
105
105
+
hostname = "knot.bwc9876.dev";
106
106
+
knot.enable = true;
107
107
+
};
108
108
+
imperm.keep = ["/var/lib/acme"];
109
109
+
};
110
110
+
111
111
+
services.nginx = {
112
112
+
enable = true;
113
113
+
virtualHosts."knot.bwc9876.dev" = {
114
114
+
forceSSL = true;
115
115
+
acmeRoot = null; # Doing DNS challenges
116
116
+
useACMEHost = "bwc9876.dev";
117
117
+
};
118
118
+
};
119
119
+
120
120
+
security.acme = {
121
121
+
acceptTerms = true;
122
122
+
defaults = {
123
123
+
email = "ben@bwc9876.dev";
124
124
+
};
125
125
+
certs."bwc9876.dev" = {
126
126
+
domain = "*.bwc9876.dev";
127
127
+
extraDomainNames = [
128
128
+
"knot.bwc9876.dev"
129
129
+
];
130
130
+
reloadServices = [
131
131
+
"nginx"
132
132
+
];
133
133
+
group = config.services.nginx.group;
134
134
+
dnsProvider = "porkbun";
135
135
+
environmentFile = "/nix/persist/secure/porkbun.env";
136
136
+
};
137
137
+
};
138
138
+
}
139
139
+
)
95
140
{
96
96
-
# for WOL
141
141
+
# WOL
97
142
systemd.network.links."79-eth-wol" = {
98
143
matchConfig = {
99
144
Type = "ether";
+63
nixosModules/tangled.nix
···
1
1
+
{inputs, ...}: {
2
2
+
config,
3
3
+
pkgs,
4
4
+
lib,
5
5
+
...
6
6
+
}: {
7
7
+
imports = [inputs.tangled.nixosModules.knot];
8
8
+
9
9
+
options.cow.tangled = {
10
10
+
hostname = lib.mkOption {
11
11
+
type = lib.types.str;
12
12
+
description = "virtual host for knot and spindle";
13
13
+
default = "knot.bwc9876.dev";
14
14
+
};
15
15
+
knot = {
16
16
+
enable = lib.mkEnableOption "tangled knot service";
17
17
+
};
18
18
+
};
19
19
+
20
20
+
config = let
21
21
+
conf = config.cow.tangled;
22
22
+
knotStateDir = "/var/lib/tangled-knot";
23
23
+
gitUser = "gurt";
24
24
+
in {
25
25
+
cow.imperm.keep = lib.optional conf.knot.enable knotStateDir;
26
26
+
27
27
+
services.tangled = {
28
28
+
knot = lib.mkIf conf.knot.enable {
29
29
+
enable = true;
30
30
+
openFirewall = false;
31
31
+
inherit gitUser;
32
32
+
stateDir = knotStateDir;
33
33
+
repo.scanPath = "${config.services.tangled.knot.stateDir}/repos";
34
34
+
motdFile = ../res/bleh.txt;
35
35
+
server = {
36
36
+
# Pub Port: 5555, Internal Port: 5444
37
37
+
hostname = lib.mkDefault conf.hostname;
38
38
+
owner = lib.mkIf config.cow.bean.enable (lib.mkDefault config.cow.bean.atproto.did);
39
39
+
};
40
40
+
};
41
41
+
};
42
42
+
43
43
+
services.nginx.virtualHosts.${conf.hostname} = lib.mkIf conf.knot.enable {
44
44
+
locations = {
45
45
+
"/" = {
46
46
+
proxyPass = "http://localhost:5555";
47
47
+
recommendedProxySettings = true;
48
48
+
};
49
49
+
"/events" = {
50
50
+
proxyPass = "http://localhost:5555";
51
51
+
proxyWebsockets = true;
52
52
+
recommendedProxySettings = true;
53
53
+
};
54
54
+
};
55
55
+
};
56
56
+
57
57
+
services.openssh = lib.mkIf conf.knot.enable {
58
58
+
enable = true;
59
59
+
settings.AllowUsers = [gitUser];
60
60
+
settings.AllowGroups = [gitUser];
61
61
+
};
62
62
+
};
63
63
+
}
+6
nixosModules/user-bean.nix
···
5
5
...
6
6
}: let
7
7
pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKsVzdJra+x5aEuwTjL1FBOiMh9bftvs8QwsM1xyEbdd";
8
8
+
did = "did:plc:x7tlupbnqot7nu6udnffnv4h";
8
9
in {
9
10
options.cow.bean = {
10
11
enable = lib.mkEnableOption "Bean user";
···
13
14
type = lib.types.nullOr lib.types.str;
14
15
description = "Public Key to Add for Bean";
15
16
default = pubkey;
17
17
+
};
18
18
+
atproto.did = lib.mkOption {
19
19
+
type = lib.types.str;
20
20
+
description = "DID for the bean user";
21
21
+
default = did;
16
22
};
17
23
};
18
24
+32
res/bleh.txt
···
1
1
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢠⡀⢠⡄⠀⡤⠀⣤⡀⠄⠀⠀⢠⡀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣤⠤⠤⠤⢤⣤⠠⠠⡄⠠⠄⣤⡤⢤⣤⡤⠤⠤⣤⠀
2
2
+
⠀⠲⠠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣤⣿⡆⢹⡄⢹⣓⡒⠀⠀⠘⢏⡳⢞⢲⡖⡶⠆⣾⣸⠀⠀⠀⠀⠀⠀⠀⠀⠰⣿⣿⣿⡂⢸⣿⣿⣶⡌⢋⡑⠀⣠⣼⣿⣄⡤⡤⠀⠀
3
3
+
⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠋⣉⣛⠀⠁⠐⣘⢢⠀⠀⠀⠛⡍⠉⠡⠐⠀⠛⠛⠘⠀⠀⠀⢠⣬⠀⠆⠀⣿⣿⣿⣿⣿⣿⡟⢿⣿⣿⠆⢈⣛⣀⡘⠀⣰⡶⠀⠀⠀
4
4
+
⠀⠱⣆⡀⠀⢁⡄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣿⣤⠘⣁⠨⣗⠐⠶⠤⠀⡤⠤⠀⠀⠀⣘⣂⡀⠀⠀⠀⣌⠁⠆⠀⢠⣿⡿⣿⣯⣾⣿⠿⢛⣩⢴⣾⣯⡥⠴⣾⡿⡄⠀⠀⠀⠀
5
5
+
⠀⠀⠉⠉⠀⠈⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⠗⠀⠉⠀⠊⠶⠀⠀⠀⠁⢀⠀⠀⠀⢹⡿⠃⠀⠀⠀⠙⠆⠀⠀⠐⣿⣿⢿⡟⠉⢀⡶⠟⢁⣈⣶⢆⡉⢰⣿⠐⠀⠀⠀⢀⠀
6
6
+
⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣄⡀⠀⢀⠀⠀⠁⠀⢁⠀⠐⣶⣠⣄⢀⣩⣦⣦⣤⣈⣁⣀⣤⣦⣼⣆⣼⣄⣤⣶⣿⠉⣈⢡⡶⣯⣵⣾⣿⣿⣿⣮⣟⣻⡏⠁⠀⠀⠀⠋⠐
7
7
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣶⣶⣶⣽⣤⣮⣼⣦⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⡾⣫⣿⣾⣿⣿⣿⣿⣿⣻⣿⣶⠟⠁⠀⠀⡐⠁⠀⠀
8
8
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣿⣿⣿⣿⣿⣿⡿⣡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠌⠀⠀⠀⠊⢄⢛⣓⠓
9
9
+
⠀⠀⠠⠁⡀⠂⠀⠀⠀⠀⠀⠀⠀⢴⣶⣿⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠿⠃⠀⠀⢀⠐⠌⡂⠦⣌⢏
10
10
+
⠀⠠⠁⠂⠄⡐⠀⠄⠀⠀⠀⡒⠶⢿⣿⣿⠟⠛⠛⠛⠛⠿⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⡄⠀⢀⠠⢀⠊⠤⡙⢦⡙⣮
11
11
+
⠀⣂⠑⠈⠐⠀⠀⠀⠀⢰⠤⣣⣾⣿⣿⣿⣶⣦⣤⣤⣀⣀⣀⠀⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠛⠛⠉⠉⠉⠉⠉⡛⠻⠿⠿⠀⠀⠀⢁⠂⡘⠤⣓⡬⣷⣿
12
12
+
⠀⢀⠀⠀⠉⠀⠀⠙⠁⢀⣀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡄⠠⣈⠥⣳⡜⣷⣿⣿
13
13
+
⠀⡈⠔⠂⠀⠀⠀⡔⠚⠟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⢰⢣⢞⣱⣾⣿⣿⣿
14
14
+
⠀⠈⠀⠀⠀⠀⠀⢀⣀⣠⡽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠻⢿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡝⣎⢯⣻⣿⣿⣿⣿
15
15
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⠛⠻⠛⡟⣿⢻⠤⡤⠄⠈⠈⠉⠉⠁⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣏⢣⡈⢼⡡⣿⣿⣿⣿
16
16
+
⠀⠀⠡⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠈⣁⢛⡞⠲⠃⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣭⣝⣋⣛⣺⢷⠿⣿⣿⣿
17
17
+
⠀⡂⠀⠠⠈⠀⣀⣠⣤⣤⡤⠴⠶⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⡀⠘⠄⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⡿⠿⠟⠟⠂⠤⠉⠯⢍⣙⣻⢿⠿⣻⣿
18
18
+
⠀⣤⡴⠖⠋⠉⠉⢉⣁⣠⣤⡴⠶⠾⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⢋⡛⠛⠳⠶⢶⣬⣄⣊⣘⠉⠟⡿⢿
19
19
+
⠀⣛⣠⣶⡶⠞⠛⠛⠉⠉⣀⡤⠴⢒⡒⣹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⣿⣿⣶⡿⠦⠁⠀⢐⢚⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣣⣴⣶⣤⣌⣛⠓⠶⢤⣄⣀⡈⠉⠛⠛⠿⢶⣾
20
20
+
⠀⠛⠉⠀⠀⢀⣤⣴⣞⠫⠝⠒⠉⣡⢤⢸⣿⣿⣿⣿⣿⣿⣿⣻⣿⡿⠿⠟⠉⠀⠀⠀⠀⠠⣚⣿⠻⣿⣿⣿⣿⣿⣿⠛⢻⡽⢯⣿⠛⠁⢿⡙⠛⠾⢷⣶⡌⡙⠛⠴⡆⠤⠀⠀⠀
21
21
+
⠀⠀⠀⣠⠶⠋⠉⠀⢀⢠⠠⠖⠋⠁⠠⣾⣿⣿⣿⣿⣿⣿⡇⢉⣀⣠⣤⣠⣾⣷⣶⣦⣷⣾⣿⣿⡧⠈⣉⡉⠛⠋⠁⢶⠮⣹⠏⠀⠀⠀⠀⠈⠉⠒⢤⣈⠉⠛⠳⣤⣤⣁⠲⢅⡀
22
22
+
⠀⡴⠛⠉⢀⠠⡐⠰⠉⠀⡀⠄⡀⠤⢰⣿⣿⣿⣿⣿⣿⡿⢡⣾⡿⢟⣿⣿⣿⣿⣿⣿⣿⣿⣿⠘⣡⣾⣿⣿⣦⠈⠏⡏⣸⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠁⠂⠄⠀⠈⠛⢇⡀⠩
23
23
+
⠀⠀⣠⠰⠌⠂⠁⡀⠄⠡⠐⢠⠀⠁⢸⣿⣿⣿⣿⡿⠏⣠⠞⣧⣻⣿⣽⣻⣿⣿⣿⣿⣿⡿⣟⣼⣟⣛⣿⣯⣿⣿⠂⣼⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠂
24
24
+
⠀⠡⠄⡁⠂⠔⡀⠐⣈⠡⠘⢄⠂⠀⢘⣿⣿⣿⣿⠋⣀⢧⡛⣬⢳⡽⣏⡿⣶⣿⣻⣿⢿⣟⣭⣿⢿⣯⢻⢵⢯⢿⡆⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠂⠄⠀
25
25
+
⠀⠁⠒⠀⠉⠐⠀⠁⠐⡈⠱⠈⠆⠀⢈⣿⣿⣿⡇⢰⠉⠖⠹⢈⢳⡘⣁⠻⣉⠻⢿⡿⢿⡟⡿⣹⡏⡾⣉⡏⠾⣏⡙⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
26
26
+
⠀⠡⠒⡀⠎⣁⠂⠆⢢⠁⢆⠒⡈⠅⠘⣻⣿⣿⠁⣏⠷⣘⢧⢺⣌⢳⡙⣦⢋⡷⢺⡗⣾⣟⡷⣯⣽⣳⣭⡛⣶⠯⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡀⠄⠀⠄⠐⠀⠀⠀
27
27
+
⠀⢡⠘⠤⡑⠠⠌⡘⠤⡉⠦⡑⣌⠸⡀⠘⣿⣿⡆⣭⢻⡜⢎⡵⣌⢳⡙⣆⠫⡜⣽⣻⣵⢯⡿⣽⡷⢯⣞⠽⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠄⡐⠈⠀⢀⠀⠀⠀
28
28
+
⠀⢢⠘⡰⢈⠱⡈⡔⠡⢎⡱⢡⠆⡣⢽⡄⢙⣿⡇⠌⡳⣜⢣⡜⡜⢦⡹⣌⢳⡙⡶⣿⣞⣯⢿⡱⣭⢛⠈⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠐⠈⢀⠀⠀⠀⠀
29
29
+
⠀⢠⠃⡔⡈⢆⠱⣈⢑⡊⡔⢣⢜⡰⢣⣞⡄⢹⡧⠀⣳⢬⡓⡼⡙⢎⡱⢎⡣⣝⡳⣿⡽⢞⡫⡕⠎⢈⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠂⠌⡀⢀⠈⠀⠀
30
30
+
⠀⠀⠈⠰⠡⠊⠔⠂⠧⠜⠜⠲⠎⠭⠣⠎⠡⠴⠿⠥⠡⠬⠳⠱⠙⠎⠱⠃⠹⠘⠱⠁⠌⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠂⠐⠀⠠⠀⠀
31
31
+
32
32
+