58 Commits

Author SHA1 Message Date
6f9791746e buna machine 2025-12-10 13:14:14 +07:00
d093103d86 clanService/phonebox: asterisk restart core when extensions.conf changed 2025-12-10 13:01:31 +07:00
1f7ce58067 adhil machine 2025-12-10 11:38:03 +07:00
b3d61ef94e nix fmt 2025-12-10 11:08:12 +07:00
3b2b4ff2a4 clanService/phonebox: asterisk auto reload when pjsip.conf changed 2025-12-10 11:06:52 +07:00
vi
e26caa3429 jukebox: reassign mpd access control to the firewall
dunno how to reliably ensure the mpd service is ordered after each
binds_to address is bound (& so bind(3)able)
2025-12-01 00:00:00 +00:00
vi
6604ec303d neptune: try wifi-only for a bit 2025-12-01 00:00:00 +00:00
vi
c628dd76dd init jukebox module for neptune
fax machine now also stereo
2025-12-01 00:00:00 +00:00
vi
f8f8731303 neptune: lan hosts can stream audio to speakers 2025-12-01 00:00:00 +00:00
vi
3eeb11571d neptune: static v4 address for (usb nic) uplink 2025-12-01 00:00:00 +00:00
vi
beffa195bf neptune: use oriental yggdrasil peers 2025-12-09 11:49:47 +07:00
6f85d03d30 update neptune desciption 2025-12-08 13:20:40 +07:00
77f5647d35 clanService/phonebox: fix 'no address available 2025-12-08 12:37:31 +07:00
7caebda927 change clan-core locked.lastModified 2025-12-08 09:51:27 +07:00
6d92bcade3 mirach is a phonebox 2025-12-08 09:47:01 +07:00
03f575edfe phonebox: wrong strip extension is fixed 2025-12-08 09:33:58 +07:00
08dc583686 alpheratz is a phonebox 2025-12-07 11:29:31 +07:00
eda331d61f add almach to phonebox 2025-12-07 11:24:50 +07:00
b89e62b727 think.greaterchiangmai.com is running on ramus 2025-12-06 11:26:18 +07:00
6a4eeeb34b clanService/phonebox: default ata-ethernet-iface is 2025-12-05 14:51:05 +07:00
701f815a01 phonebox: cleanup vars 2025-12-05 14:31:00 +07:00
188c893e97 phone number scheme change to 2 digits prefix and 2 digits local 2025-12-05 14:14:43 +07:00
3a4253cb67 clanService/phonebox: phone and fax network built on yggdrasil with predefine number 2025-12-05 12:12:01 +07:00
c9ec7371f2 inventory yggdrasil -> yggdrasil-phone-network 2025-12-05 11:27:14 +07:00
39a277a075 use clan-core yggdrasil 2025-12-05 11:22:05 +07:00
79cad87f43 bump clan-core 2025-12-05 10:35:41 +07:00
6312ae3587 bump clan-core 2025-12-05 10:27:27 +07:00
b6297c2d8e rm clanService/asterisk 2025-12-04 17:30:57 +07:00
7067fd43c4 bump clan-core 2025-12-04 10:00:55 +07:00
36ff92a984 nix fmt 2025-12-04 09:57:07 +07:00
62e4ea61e0 almach machine 2025-12-03 14:11:42 +07:00
bba1858c6a mirach machine 2025-12-03 14:01:36 +07:00
58bd02575f alpheratz machine 2025-12-03 13:40:18 +07:00
5ecff1bc9e bump clan-core 2025-12-03 09:58:39 +07:00
7a381a310e tor connection 2025-11-28 14:55:21 +07:00
36c31507e2 Update vars via generator tor_tor for machine vega 2025-11-28 14:54:35 +07:00
bfea371501 Update vars via generator tor_tor for machine sirius 2025-11-28 14:54:32 +07:00
ff0bc698d4 Update vars via generator tor_tor for machine rigel 2025-11-28 14:54:29 +07:00
11c109970a Update vars via generator tor_tor for machine ramus 2025-11-28 14:54:26 +07:00
ad95a8710c Update vars via generator tor_tor for machine b4l 2025-11-28 14:54:23 +07:00
e6760d320d ramus machine 2025-11-26 14:01:11 +07:00
bfeea4156b disappearing default route is fixed by a hacky way 2025-11-21 16:53:55 +07:00
3aa93c1333 nameservers whitehouse 2025-11-18 13:58:59 +07:00
b541e9ff4c Update vars via generator state-version for machine rigel 2025-11-10 14:48:37 +07:00
bdf1e3e5bc Update vars via generator state-version for machine b4l 2025-11-10 14:48:37 +07:00
ab88d74226 sirius WhiteHouse NAS 2025-11-10 14:47:09 +07:00
162707546c whitehouse router: add vlan on wan interface 2025-11-07 15:04:26 +07:00
2c9592c542 more packages on common 2025-11-06 15:57:01 +07:00
0bb6314a03 Update vars via generator state-version for machine vega 2025-11-05 16:22:09 +07:00
13df0a8421 common nixos module 2025-11-05 16:21:50 +07:00
4259d8014e use upstream clan testModule 2025-10-31 13:53:16 +07:00
71a3ca375f nixpkgs go back to rev d7f52a7a640bc54c7bb414cca603835bf8dd4b10 since the new one has issue with clan tests 2025-10-31 13:52:52 +07:00
891504f173 rename checks services 2025-10-30 12:03:24 +07:00
5ffbe0ed9b bump clan-core, nixpkgs 2025-10-30 11:59:22 +07:00
7115a93a0b clanService asterisk 2025-10-22 16:40:59 +07:00
b5f3adacd8 clanService yggdrasil add vars yggdrasil/yggdrasil-subnet 2025-10-21 15:55:18 +07:00
2eb52251cc clanService yggdrasil 2025-10-17 16:58:43 +07:00
be25560858 WhiteHouse router configuration 2025-10-16 14:53:41 +07:00
439 changed files with 30779 additions and 3773 deletions

71
flake.lock generated
View File

@@ -20,11 +20,11 @@
]
},
"locked": {
"lastModified": 1754535625,
"narHash": "sha256-RdT3/DskBjwx74cvHJHb/mLSO2XeSHitSYViNmYGU/k=",
"lastModified": 1764799743,
"narHash": "sha256-MbbiNG/bhqe+4z7ml8TefIs4swSonmiV0CimCntXuCg=",
"ref": "refs/heads/main",
"rev": "f69e28a1333527cdbadb233966a7e19d4b35a1a3",
"revCount": 8886,
"rev": "ddc0f9fabf33ad000676a33e97be6b5df12a4560",
"revCount": 11417,
"type": "git",
"url": "https://git.clan.lol/clan/clan-core"
},
@@ -49,11 +49,11 @@
]
},
"locked": {
"lastModified": 1753067306,
"narHash": "sha256-jyoEbaXa8/MwVQ+PajUdT63y3gYhgD9o7snO/SLaikw=",
"rev": "18dfd42bdb2cfff510b8c74206005f733e38d8b9",
"lastModified": 1762942435,
"narHash": "sha256-zIWGs5FIytTtJN+dhDb8Yx+q4TQI/yczuL539yVcyPE=",
"rev": "0ee328404b12c65e8106bde9e9fab8abf4ecada4",
"type": "tarball",
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/18dfd42bdb2cfff510b8c74206005f733e38d8b9.tar.gz"
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/0ee328404b12c65e8106bde9e9fab8abf4ecada4.tar.gz"
},
"original": {
"type": "tarball",
@@ -88,11 +88,11 @@
]
},
"locked": {
"lastModified": 1753140376,
"narHash": "sha256-7lrVrE0jSvZHrxEzvnfHFE/Wkk9DDqb+mYCodI5uuB8=",
"lastModified": 1764627417,
"narHash": "sha256-D6xc3Rl8Ab6wucJWdvjNsGYGSxNjQHzRc2EZ6eeQ6l4=",
"owner": "nix-community",
"repo": "disko",
"rev": "545aba02960caa78a31bd9a8709a0ad4b6320a5c",
"rev": "5a88a6eceb8fd732b983e72b732f6f4b8269bef3",
"type": "github"
},
"original": {
@@ -139,14 +139,19 @@
"liminix": {
"flake": false,
"locked": {
"lastModified": 1760087246,
"narHash": "sha256-HRUkAS5XDuM7yDnz+TIMAre7kFOuqyHL/y26wTbH6Sg=",
"path": "/home/kurogeek/Desktop/gitea/dan/liminix",
"type": "path"
"lastModified": 1760426231,
"narHash": "sha256-r8c5PKtsxAvtQ/k17GH+WNvP47Lr+AbExLMPdLtvAKE=",
"ref": "refs/heads/fix-gl-ar750",
"rev": "3f1f7c08d440130cce9262a93ce78ed7969d93cd",
"revCount": 1574,
"type": "git",
"url": "https://git.b4l.co.th/newedge/liminix"
},
"original": {
"path": "/home/kurogeek/Desktop/gitea/dan/liminix",
"type": "path"
"ref": "refs/heads/fix-gl-ar750",
"rev": "3f1f7c08d440130cce9262a93ce78ed7969d93cd",
"type": "git",
"url": "https://git.b4l.co.th/newedge/liminix"
}
},
"nix-darwin": {
@@ -157,11 +162,11 @@
]
},
"locked": {
"lastModified": 1751313918,
"narHash": "sha256-HsJM3XLa43WpG+665aGEh8iS8AfEwOIQWk3Mke3e7nk=",
"lastModified": 1764161084,
"narHash": "sha256-HN84sByg9FhJnojkGGDSrcjcbeioFWoNXfuyYfJ1kBE=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "e04a388232d9a6ba56967ce5b53a8a6f713cdfcf",
"rev": "e95de00a471d07435e0527ff4db092c84998698e",
"type": "github"
},
"original": {
@@ -172,11 +177,11 @@
},
"nix-select": {
"locked": {
"lastModified": 1745005516,
"narHash": "sha256-IVaoOGDIvAa/8I0sdiiZuKptDldrkDWUNf/+ezIRhyc=",
"rev": "69d8bf596194c5c35a4e90dd02c52aa530caddf8",
"lastModified": 1763303120,
"narHash": "sha256-yxcNOha7Cfv2nhVpz9ZXSNKk0R7wt4AiBklJ8D24rVg=",
"rev": "3d1e3860bef36857a01a2ddecba7cdb0a14c35a9",
"type": "tarball",
"url": "https://git.clan.lol/api/v1/repos/clan/nix-select/archive/69d8bf596194c5c35a4e90dd02c52aa530caddf8.tar.gz"
"url": "https://git.clan.lol/api/v1/repos/clan/nix-select/archive/3d1e3860bef36857a01a2ddecba7cdb0a14c35a9.tar.gz"
},
"original": {
"type": "tarball",
@@ -185,11 +190,11 @@
},
"nixos-facter-modules": {
"locked": {
"lastModified": 1750412875,
"narHash": "sha256-uP9Xxw5XcFwjX9lNoYRpybOnIIe1BHfZu5vJnnPg3Jc=",
"lastModified": 1764252389,
"narHash": "sha256-3bbuneTKZBkYXlm0bE36kUjiDsasoIC1GWBw/UEJ9T4=",
"owner": "nix-community",
"repo": "nixos-facter-modules",
"rev": "14df13c84552a7d1f33c1cd18336128fbc43f920",
"rev": "5ea68886d95218646d11d3551a476d458df00778",
"type": "github"
},
"original": {
@@ -200,11 +205,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1754278406,
"narHash": "sha256-jvIQTMN5EzoOP5RaGztpVese8a3wqy0M/h6tNzycW28=",
"lastModified": 1761656231,
"narHash": "sha256-krgZxGAIIIKFJS+UB0l8do3sYUDWJc75M72tepmVMzE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "6a489c9482ca676ce23c0bcd7f2e1795383325fa",
"rev": "d7f52a7a640bc54c7bb414cca603835bf8dd4b10",
"type": "github"
},
"original": {
@@ -233,11 +238,11 @@
]
},
"locked": {
"lastModified": 1754328224,
"narHash": "sha256-glPK8DF329/dXtosV7YSzRlF4n35WDjaVwdOMEoEXHA=",
"lastModified": 1764483358,
"narHash": "sha256-EyyvCzXoHrbL467YSsQBTWWg4sR96MH1sPpKoSOelB4=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "49021900e69812ba7ddb9e40f9170218a7eca9f4",
"rev": "5aca6ff67264321d47856a2ed183729271107c9c",
"type": "github"
},
"original": {

View File

@@ -22,8 +22,7 @@
inputs.nixpkgs.follows = "nixpkgs";
};
liminix = {
# url = "git+https://gti.telent.net/dan/liminix?ref=refs/heads/main&rev=29fbb5461d034c4c59b88cbe04937b04ecad18e0";
url = "path:/home/kurogeek/Desktop/gitea/dan/liminix";
url = "git+https://git.b4l.co.th/newedge/liminix?ref=refs/heads/fix-gl-ar750&rev=3f1f7c08d440130cce9262a93ce78ed7969d93cd";
flake = false;
};
};
@@ -38,24 +37,6 @@
systems = [
"x86_64-linux"
];
flake.legacyPackages.qemu-router = import "${inputs.liminix}/default.nix" {
liminix-config = import "${inputs.liminix}/examples/hello-from-qemu.nix";
device = (import "${inputs.liminix}/devices/qemu-aarch64/default.nix");
};
flake.legacyPackages.yada-router = import "${inputs.liminix}/default.nix" {
liminix-config = import ./routers/yada-house/configuration.nix { inherit inputs; };
device = (import ./routers/yada-house/device.nix { inherit inputs; });
};
flake.legacyPackages.qemu-flake = import "${inputs.liminix}/default.nix" {
liminix-config = import ./routers/qemu/configuration.nix { inherit inputs; };
device = (import ./routers/qemu/device.nix { inherit inputs; });
};
flake.legacyPackages.vanilla = import "${inputs.liminix}/default.nix" {
liminix-config = import ./routers/vanilla/configuration.nix { inherit inputs; };
device = (import "${inputs.liminix}/devices/gl-mt300a/default.nix");
};
imports = [
./fmt.nix
./shell.nix
@@ -63,8 +44,24 @@
./machines
./routers
./inventories
./overlays
./tests
./modules/clan/flake-module.nix
./modules/nixos/flake-module.nix
];
perSystem =
{ pkgs, system, ... }:
{
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
overlays = [
inputs.self.overlays.packagesOverlay
];
config = { };
};
packages.think = pkgs.think-gtcm;
packages.think-be = pkgs.think-backend-gtcm;
};
}
);
}

View File

@@ -3,8 +3,24 @@
inventory = {
tags = {
glom = [ "vega" ];
b4l = [ "rigel" ];
glom = [
"vega"
"ramus"
];
w = [ "sirius" ];
b4l = [
"rigel"
"neptune"
];
phonebox = [
"neptune"
"rigel"
"almach"
"alpheratz"
"mirach"
"adhil"
"buna"
];
};
instances = {
@@ -30,6 +46,28 @@
};
};
tor = {
module = {
name = "tor";
input = "clan-core";
};
roles.server.tags."nixos" = { };
};
w-network = {
module = {
name = "zerotier";
input = "clan-core";
};
roles.controller.machines."sirius" = {
settings.allowedIps = [
#kurogeek
"fdfe:7bf:a795:4524:4c99:932b:d36d:b8cc"
];
};
roles.peer.tags.w = { };
};
glom-network = {
module = {
name = "zerotier";
@@ -48,6 +86,31 @@
roles.peer.tags.b4l = { };
};
yggdrasil-phone-network = {
module = {
name = "yggdrasil";
input = "clan-core";
};
roles.default.tags."phonebox" = { };
roles.default.settings.extraPeers = [
"tls://ygg.jjolly.dev:3443"
"tls://[2602:fc24:18:7a42::1]:993"
"tcp://leo.node.3dt.net:9002"
"tcp://ygg-kcmo.incognet.io:8883"
];
};
phonebox = {
module = {
name = "phonebox";
input = "self";
};
roles.default.tags."phonebox" = { };
roles.default.machines."adhil".settings = {
ata-ethernet-iface = "end0";
};
};
pocket-id = {
module = {
name = "pocket-id";
@@ -111,6 +174,32 @@
};
roles.default.machines.b4l = { };
};
pulse-stream = {
module = {
name = "pulse-stream";
input = "self";
};
roles.default.machines.neptune = {
settings.client-ip-ranges = [
"10.0.0.0/24"
];
};
};
jukebox = {
module = {
name = "jukebox";
input = "self";
};
roles.default.machines.neptune = {
settings = {
binds = [ "wlp1s0" ];
disks.m3 = {
uuid = "105D-319E";
mountOptions = [ "utf8" ];
};
};
};
};
};
};
};

View File

@@ -1 +1,25 @@
{}
{
"machines": {
"ramus": {
"installedAt": 1764139649
},
"alpheratz": {
"installedAt": 1764741499
},
"mirach": {
"installedAt": 1764744666
},
"almach": {
"installedAt": 1764745787
},
"neptune": {
"installedAt": 1762147067
},
"adhil": {
"installedAt": 1765277591
},
"buna": {
"installedAt": 1765343708
}
}
}

View File

@@ -0,0 +1,13 @@
{ ... }:
{
nixpkgs.hostPlatform = {
system = "aarch64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
# clan.core.networking.targetHost = "root@";
clan.meta.name = "adhil";
clan.meta.description = "Raspberry Pi 4 SBC board for one of w phone network. (With w office)";
}

56
machines/adhil/disko.nix Normal file
View File

@@ -0,0 +1,56 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/mmc-SD64G_0xfb330ff6";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
end = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
name = "root";
end = "-0";
content = {
type = "filesystem";
format = "f2fs";
mountpoint = "/";
extraArgs = [
"-O"
"extra_attr,inode_checksum,sb_checksum,compression"
];
mountOptions = [
"compress_algorithm=zstd:6,compress_chksum,atgc,gc_merge,lazytime,nodiscard"
];
};
};
};
};
};
};
};
}

1147
machines/adhil/facter.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{ config, ... }:
{
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
# clan.core.networking.targetHost = "root@";
clan.meta.name = "almach";
clan.meta.description = "Radxa X4 SBC board for one of w phone network.";
}

90
machines/almach/disko.nix Normal file
View File

@@ -0,0 +1,90 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/mmc-CUTB42_0x95d64f17";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "nofail" ];
};
};
system = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
swap = {
size = "4G";
content = {
type = "swap";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/nixos" = {
type = "zfs_fs";
options.mountpoint = "/";
mountpoint = "/";
};
"root/home" = {
type = "zfs_fs";
options.mountpoint = "/home";
mountpoint = "/home";
};
"root/tmp" = {
type = "zfs_fs";
mountpoint = "/tmp";
options = {
mountpoint = "/tmp";
sync = "disabled";
};
};
};
};
};
};
}

3852
machines/almach/facter.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{ config, ... }:
{
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
# clan.core.networking.targetHost = "root@";
clan.meta.name = "alpheratz";
clan.meta.description = "Radxa X4 SBC board for one of w phone network.";
}

View File

@@ -0,0 +1,90 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/mmc-CUTB42_0xaedcfa8b";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "nofail" ];
};
};
system = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
swap = {
size = "4G";
content = {
type = "swap";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/nixos" = {
type = "zfs_fs";
options.mountpoint = "/";
mountpoint = "/";
};
"root/home" = {
type = "zfs_fs";
options.mountpoint = "/home";
mountpoint = "/home";
};
"root/tmp" = {
type = "zfs_fs";
mountpoint = "/tmp";
options = {
mountpoint = "/tmp";
sync = "disabled";
};
};
};
};
};
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -19,4 +19,18 @@
services.nginx.virtualHosts."${config.networking.fqdn}" = {
enableACME = true;
};
clan.core.vars.generators.acme = {
share = true;
files.email.secret = false;
prompts.email = {
type = "line";
description = "Email for ACME registeration";
};
script = ''
cat $prompts/email > $out/email
'';
};
}

View File

@@ -0,0 +1,13 @@
{ config, ... }:
{
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
# clan.core.networking.targetHost = "root@";
clan.meta.name = "buna";
clan.meta.description = "Radxa X4 SBC board for one of w phone network. (With w whitehouse)";
}

56
machines/buna/disko.nix Normal file
View File

@@ -0,0 +1,56 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/usb-Generic_MassStorageClass_000000001539-0:0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
end = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
name = "root";
end = "-0";
content = {
type = "filesystem";
format = "f2fs";
mountpoint = "/";
extraArgs = [
"-O"
"extra_attr,inode_checksum,sb_checksum,compression"
];
mountOptions = [
"compress_algorithm=zstd:6,compress_chksum,atgc,gc_merge,lazytime,nodiscard"
];
};
};
};
};
};
};
};
}

4184
machines/buna/facter.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{ config, ... }:
{
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
# clan.core.networking.targetHost = "root@";
clan.meta.name = "mirach";
clan.meta.description = "Radxa X4 SBC board for one of w phone network.";
}

90
machines/mirach/disko.nix Normal file
View File

@@ -0,0 +1,90 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/mmc-CUTB42_0x95d64f33";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "nofail" ];
};
};
system = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
swap = {
size = "4G";
content = {
type = "swap";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/nixos" = {
type = "zfs_fs";
options.mountpoint = "/";
mountpoint = "/";
};
"root/home" = {
type = "zfs_fs";
options.mountpoint = "/home";
mountpoint = "/home";
};
"root/tmp" = {
type = "zfs_fs";
mountpoint = "/tmp";
options = {
mountpoint = "/tmp";
sync = "disabled";
};
};
};
};
};
};
}

3852
machines/mirach/facter.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
{
inputs,
config,
lib,
...
}:
{
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
networking.interfaces.enx00e04c106368.useDHCP = true; # recovery
clan.core.vars.generators.wireless-credentials = {
files = {
essid.secret = false;
psk.secret = true;
};
prompts = {
essid.persist = true;
psk.persist = true;
};
script = ''
cat "$prompts"/essid > $out/essid
prompt_psk=$(cat "$prompts"/psk)
echo "psk=$prompt_psk" > $out/psk
'';
};
networking.wireless =
let
credentials = config.clan.core.vars.generators.wireless-credentials.files;
in
{
enable = true;
secretsFile = credentials.psk.path;
networks.${credentials.essid.value}.pskRaw = "ext:psk";
};
networking.interfaces.wlp1s0 = {
useDHCP = false;
ipv4.addresses = [
{
address = "10.0.0.9";
prefixLength = 24;
}
];
};
services.yggdrasil.settings.Peers = lib.mkForce [
"tcp://newt.barry.town:1337"
"tls://yg-hkg.magicum.net:32333"
"tls://astrra.space:55535"
];
clan.meta.name = "neptune";
clan.meta.description = "Radxa SBC board for testing. (With vi)";
}

View File

@@ -0,0 +1,90 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/mmc-CUTB42_0x9d59499c";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "nofail" ];
};
};
system = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
swap = {
size = "16G";
content = {
type = "swap";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/nixos" = {
type = "zfs_fs";
options.mountpoint = "/";
mountpoint = "/";
};
"root/home" = {
type = "zfs_fs";
options.mountpoint = "/home";
mountpoint = "/home";
};
"root/tmp" = {
type = "zfs_fs";
mountpoint = "/tmp";
options = {
mountpoint = "/tmp";
sync = "disabled";
};
};
};
};
};
};
}

4160
machines/neptune/facter.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
{ self, config, ... }:
{
system.stateVersion = "25.11";
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
clan.meta.name = "ramus";
clan.meta.description = ''
A Hetzner VPS machine own by Alex.
'';
clan.core.sops.defaultGroups = [ "admins" ];
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
clan.core.vars.generators.acme = {
share = true;
files.email.secret = false;
prompts.email = {
type = "line";
description = "Email for ACME registeration";
};
script = ''
cat $prompts/email > $out/email
'';
};
users.users.nginx.extraGroups = [ "acme" ];
security.acme.acceptTerms = true;
imports = [ ./think-greater-chiangmai.nix ];
}

84
machines/ramus/disko.nix Normal file
View File

@@ -0,0 +1,84 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_107266387";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "nofail" ];
};
};
system = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/nixos" = {
type = "zfs_fs";
options.mountpoint = "/";
mountpoint = "/";
};
"root/home" = {
type = "zfs_fs";
options.mountpoint = "/home";
mountpoint = "/home";
};
"root/tmp" = {
type = "zfs_fs";
mountpoint = "/tmp";
options = {
mountpoint = "/tmp";
sync = "disabled";
};
};
};
};
};
};
}

2799
machines/ramus/facter.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
{ self, config, ... }:
let
commonSettings = {
APP_NAME = "Laravel";
APP_ENV = "local";
APP_KEY._secret = config.clan.core.vars.generators.greaterchiangmai.files.app_key.path;
APP_DEBUG = "false";
APP_URL = "http://localhost";
DB_CONNECTION = "mysql";
DB_HOST = "localhost";
DB_PORT = 3306;
DB_DATABASE = "thinkgtcm";
DB_USERNAME = "gtcm";
R2_ACCESS_KEY_ID = config.clan.core.vars.generators.greaterchiangmai-s3.files.access_key_id.value;
R2_SECRET_ACCESS_KEY._secret =
config.clan.core.vars.generators.greaterchiangmai-s3.files.secret_access_key.path;
R2_REGION = config.clan.core.vars.generators.greaterchiangmai-s3.files.region.value;
R2_BUCKET = config.clan.core.vars.generators.greaterchiangmai-s3.files.bucket.value;
R2_ENDPOINT = config.clan.core.vars.generators.greaterchiangmai-s3.files.endpoint.value;
LOG_CHANNEL = "stack";
LOG_LEVEL = "debug";
FILESYSTEM_DISK = "local";
BROADCAST_DRIVER = "log";
CACHE_DRIVER = "file";
QUEUE_CONNECTION = "sync";
SESSION_DRIVER = "file";
SESSION_LIFETIME = 120;
MEMCACHED_HOST = "127.0.0.1";
REDIS_HOST = "127.0.0.1";
REDIS_PORT = 6379;
UPLOAD_MAX_FILESIZE = "5000M";
POST_MAX_SIZE = "5000M";
TEST_LOCAL = true;
};
baseDomain = "greaterchiangmai.com";
domain = "think.${baseDomain}";
domainBackend = "think-backend.${baseDomain}";
in
{
imports = [
self.nixosModules.think-gtcm
self.nixosModules.think-backend-gtcm
];
nixpkgs.overlays = [ self.overlays.packagesOverlay ];
clan.core.vars.generators.greaterchiangmai = {
files = {
app_key = {
secret = true;
owner = config.services.think-greaterchiangmai.user;
group = config.services.think-greaterchiangmai.group;
};
};
prompts = {
app_key.persist = true;
};
script = ''
cat $prompts/app_key > $out/app_key
'';
};
clan.core.vars.generators.greaterchiangmai-s3 = {
files = {
access_key_id.secret = false;
secret_access_key = {
secret = true;
owner = config.services.think-greaterchiangmai.user;
group = config.services.think-greaterchiangmai.group;
};
endpoint.secret = false;
region.secret = false;
bucket.secret = false;
};
prompts = {
access_key_id.persist = true;
secret_access_key.persist = true;
endpoint.persist = true;
region.persist = true;
bucket.persist = true;
};
script = ''
cat $prompts/access_key_id > $out/access_key_id
cat $prompts/secret_access_key > $out/secret_access_key
cat $prompts/endpoint > $out/endpoint
cat $prompts/region > $out/region
cat $prompts/bucket > $out/bucket
'';
};
services.think-greaterchiangmai = {
enable = true;
domain = domain;
settings = commonSettings;
};
services.think-backend-greaterchiangmai = {
enable = true;
domain = domainBackend;
settings = commonSettings;
};
security.acme.certs = {
"${domain}" = {
email = config.clan.core.vars.generators.acme.files.email.value;
webroot = "/var/lib/acme/acme-challenge/${domain}";
};
"${domainBackend}" = {
email = config.clan.core.vars.generators.acme.files.email.value;
webroot = "/var/lib/acme/acme-challenge/${domainBackend}";
};
};
services.nginx.virtualHosts.${domain} = {
forceSSL = true;
useACMEHost = domain;
acmeRoot = config.security.acme.certs.${domain}.webroot;
};
services.nginx.virtualHosts.${domainBackend} = {
forceSSL = true;
useACMEHost = domainBackend;
acmeRoot = config.security.acme.certs.${domainBackend}.webroot;
};
}

View File

@@ -0,0 +1,25 @@
{
inputs,
config,
self,
...
}:
{
imports = [
self.nixosModules.common
(inputs.import-tree ./services)
];
clan.core.sops.defaultGroups = [ "admins" ];
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
networking.fqdn = config.clan.core.vars.generators.vega-internal-domain.files.name.value;
system.stateVersion = "25.11";
}

141
machines/sirius/disko.nix Normal file
View File

@@ -0,0 +1,141 @@
{ lib, ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/mmc-FIXME";
vdev = [
"/dev/disk/by-id/ata-FIXME"
"/dev/disk/by-id/ata-FIXME"
];
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
disko.devices = {
disk = {
"os-${hashDisk os}" = {
type = "disk";
device = os;
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "nofail" ];
};
};
system = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
swap = {
size = "16G";
content = {
type = "swap";
};
};
};
};
};
}
// (lib.listToAttrs (
map (disk: {
name = "data-${hashDisk disk}";
value = {
type = "disk";
device = disk;
content = {
type = "zfs";
pool = "zdata";
};
};
}) vdev
));
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/nixos" = {
type = "zfs_fs";
options.mountpoint = "/";
mountpoint = "/";
};
"root/home" = {
type = "zfs_fs";
options.mountpoint = "/home";
mountpoint = "/home";
};
"root/tmp" = {
type = "zfs_fs";
mountpoint = "/tmp";
options = {
mountpoint = "/tmp";
sync = "disabled";
};
};
};
};
zdata = {
type = "zpool";
options.ashift = "12";
rootFsOptions = {
mountpoint = "none";
compression = "lz4";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
mode = {
topology = {
type = "topology";
vdev = [
{
mode = "mirror";
members = vdev;
}
];
};
};
datasets = {
"nas" = {
type = "zfs_fs";
mountpoint = "/mnt/hdd";
mountOptions = [ "nofail" ];
};
"service-data" = {
type = "zfs_fs";
mountpoint = "/var/lib";
mountOptions = [ "nofail" ];
};
};
};
};
};
}

View File

@@ -0,0 +1,93 @@
{
config,
lib,
...
}:
let
sambaUser = lib.filterAttrs (
name: user: user.isNormalUser && builtins.elem "samba" user.extraGroups
) config.users.users;
sharedFolders = {
WhiteHouse.users = [
"w"
"kurogeek"
"berwn"
];
};
in
{
services.samba = {
enable = true;
openFirewall = true;
settings = {
global = {
security = "user";
workgroup = "WORKGROUP";
"server string" = "WhiteHouse NAS";
interfaces = "eth* en*";
"max log size" = "50";
"dns proxy" = false;
"syslog only" = true;
"map to guest" = "Bad User";
"guest account" = "nobody";
};
}
// lib.mapAttrs (share: opts: {
path = "/mnt/hdd/samba/${share}";
comment = share;
"force user" = share;
"force group" = share;
public = "yes";
"guest ok" = "yes";
"create mask" = "0640";
"directory mask" = "0750";
writable = "no";
browseable = "yes";
printable = "no";
# TODO
# "valid users" = toString opts.users;
}) sharedFolders;
};
users.users = lib.mapAttrs (share: opts: {
isSystemUser = true;
group = share;
}) sharedFolders;
users.groups = lib.mapAttrs (share: opts: { }) sharedFolders;
systemd.services.samba-smbd.postStart =
lib.concatMapStrings (
user:
let
password = config.clan.core.vars.generators."${user}-smb-password".files.password.path;
in
''
mkdir -p /mnt/hdd/samba/${user}
chown ${user}:users /mnt/hdd/samba/${user}
# if a password is unchanged, this will error
(echo $(<${password}); echo $(<${password})) | ${config.services.samba.package}/bin/smbpasswd -s -a ${user}
''
) (lib.attrNames sambaUser)
+ lib.concatMapStrings (share: ''
mkdir -p /mnt/hdd/samba/${share}
chown ${share}:${share} /mnt/hdd/samba/${share}
'') (lib.attrNames sharedFolders);
services.samba-wsdd = {
enable = true;
openFirewall = true;
};
services.avahi = {
publish.enable = true;
publish.userServices = true;
# ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
nssmdns4 = true;
# ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
enable = true;
openFirewall = true;
};
}

View File

@@ -1,10 +1,13 @@
{
inputs,
config,
self,
...
}:
{
imports = [
self.nixosModules.common
(inputs.import-tree ./services)
(import ../../lib/auto-accept-zerotier-members.nix {

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.actual-budget = {
clan.nixosTests.service-actual-budget = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/actual-budget" = module;

View File

@@ -1,4 +1,7 @@
{ inputs, lib, ... }:
{
inputs,
...
}:
{
imports =
let
@@ -16,12 +19,7 @@
# Create import paths for each valid directory
imports = (map (name: ./. + "/${name}/flake-module.nix") validModuleDirs) ++ [
(import (inputs.clan-core + "/lib/flake-parts/clan-nixos-test.nix") {
inherit lib;
flake-parts-lib = inputs.flake-parts.lib;
self = inputs.clan-core;
inputs = inputs.clan-core.clan.self.inputs;
})
inputs.clan-core.flakeModules.testModule
];
in
imports;

View File

@@ -9,7 +9,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.grafana = {
clan.nixosTests.service-grafana = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/grafana" = module;

View File

@@ -0,0 +1,133 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "jukebox";
manifest.description = "mpd server, library on removable disks";
manifest.categories = [ "System" ];
roles.default = {
interface =
{ lib, ... }:
{
options = {
baseDir = lib.mkOption {
type = lib.types.str;
default = "/mnt/jukebox";
};
binds = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
};
disks = lib.mkOption {
type =
with lib.types;
attrsOf (
submodule (
{ name, ... }:
{
options = {
name = lib.mkOption {
type = str;
default = name;
};
uuid = lib.mkOption {
type = str;
};
mountOptions = lib.mkOption {
type = listOf str;
default = [ ];
};
};
}
)
);
default = { };
description = "disks comprising library";
};
};
};
perInstance =
{
settings,
...
}:
{
nixosModule =
{
config,
lib,
pkgs,
...
}:
{
services.pulseaudio.enable = true;
# workaround cookie permissions
services.pulseaudio.tcp.enable = true;
services.pulseaudio.tcp.anonymousClients = {
allowedIpRanges = [ "127.0.0.1" ];
allowAll = true;
};
systemd.tmpfiles.rules = [
"d ${settings.baseDir} 0755 root root"
];
fileSystems =
let
disk2fs =
{
name,
uuid,
mountOptions,
...
}:
lib.nameValuePair "${settings.baseDir}/${name}" {
device = "/dev/disk/by-uuid/${uuid}";
fsType = "auto";
options = [
"noauto"
"nofail"
]
++ mountOptions;
};
in
lib.listToAttrs (lib.mapAttrsToList (_: disk2fs) settings.disks);
services.udev.extraRules =
let
translate-prefix = path: (lib.removePrefix "-" (lib.replaceStrings [ "/" ] [ "-" ] path));
mount-name = name: "${translate-prefix settings.baseDir}-${name}.mount";
disk2rule =
{ name, uuid, ... }:
lib.concatStringsSep ", " [
''ACTION=="add"''
''SUBSYSTEM=="block"''
''ENV{DEVLINKS}=="*/dev/disk/by-uuid/${uuid}*"''
''ENV{SYSTEMD_WANTS}="${mount-name name}"''
];
in
lib.concatMapStringsSep "\n" disk2rule (lib.attrValues settings.disks);
services.mpd = {
enable = true;
musicDirectory = settings.baseDir;
network.listenAddress = "any";
extraConfig = ''
audio_output {
type "pulse"
name "jukebox"
server "localhost"
}
'';
};
networking.firewall.interfaces = lib.genAttrs settings.binds (_: {
allowedTCPPorts = [ config.services.mpd.network.port ];
});
environment.systemPackages = [ pkgs.mpc ];
};
};
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{
clan.modules = {
jukebox = module;
};
}

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.nextcloud = {
clan.nixosTests.service-nextcloud = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/nextcloud" = module;

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.paperless = {
clan.nixosTests.service-paperless = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/paperless" = module;

View File

@@ -0,0 +1,353 @@
{ clanLib, ... }:
{
_class = "clan.service";
manifest.name = "phonebox";
manifest.description = "";
manifest.categories = [ "System" ];
roles.default = {
interface =
{ lib, ... }:
{
options.ata-ethernet-iface = lib.mkOption {
type = lib.types.str;
description = "An Ethernet interface that connect to ATA box.";
default = "enp2s0";
};
};
perInstance =
{
roles,
settings,
...
}:
{
nixosModule =
{
lib,
config,
pkgs,
...
}:
let
user = "asterisk";
rtpPortFrom = 10000;
rtpPortTo = 20000;
ata-interface = settings.ata-ethernet-iface;
genServerSIPEndpoint =
{ hostname, address }:
''
[${hostname}](internal_endpoint)
aors=${hostname}
[${hostname}](ip_auth)
endpoint=${hostname}
match=[${address}]
[${hostname}](dynamiic_aor)
contact=sip:[${address}]
'';
genLocalSIPEndpoint =
{ localNumber }:
''
[${localNumber}](internal_endpoint)
aors=${localNumber}
auth=${localNumber}
[${localNumber}](userpass_auth)
username=${localNumber}
password=${localNumber}
[${localNumber}](dynamiic_aor)
max_contacts=1
'';
genLocalExtenConf =
{ localNumber }:
''
exten => ${localNumber},1,Dial(PJSIP/${localNumber},20)
'';
genExtentConf =
{
prefixNumber,
hostname,
localNumber,
}:
let
replaceWithX =
ln: builtins.concatStringsSep "" (builtins.genList (_: "X") (builtins.stringLength ln));
in
''
exten => _${prefixNumber}${replaceWithX localNumber},1,Dial(PJSIP/''${EXTEN:${builtins.toString (builtins.stringLength prefixNumber)}}@${hostname},30)
'';
getYggdrasilIP =
machineName:
if config.clan.core.vars.generators.yggdrasil.files.address ? value then
clanLib.getPublicValue {
flake = config.clan.core.settings.directory;
machine = machineName;
generator = "yggdrasil";
file = "address";
default = null;
}
else
throw "clanService/yggdrasil is required";
in
{
clan.core.vars.generators.phonebox = {
files = {
server-prefix-number.secret = false;
ata-local-number.secret = false;
};
prompts = {
server-prefix-number = {
type = "line";
description = "Server prefix number: indicate server to connect to [10XX]";
};
ata-local-number = {
type = "line";
description = "Local suffix number: indicate local number on the server [XX00]";
};
};
script = ''
cat $prompts/server-prefix-number > $out/server-prefix-number
cat $prompts/ata-local-number > $out/ata-local-number
'';
};
networking.interfaces = {
${ata-interface} = {
useDHCP = false;
ipv4.addresses = [
{
address = "192.168.254.1";
prefixLength = 24;
}
];
};
};
services.dnsmasq = {
enable = true;
settings = {
bind-dynamic = true;
listen-address = "192.168.254.1";
# enable-ra = true;
domain-needed = true;
domain = "localhost";
dhcp-range = [
"192.168.254.100,192.168.254.100,255.255.255.0,3m"
];
dhcp-leasefile = "/dev/null";
dhcp-option = [
"3,192.168.254.1"
];
interface = [ ata-interface ];
};
};
services.nginx = {
enable = true;
virtualHosts = {
"_" = {
locations."/" = {
proxyPass = "http://192.168.254.100";
extraConfig = ''
client_max_body_size 100M;
'';
};
};
};
};
networking.firewall.allowedUDPPortRanges = [
{
from = rtpPortFrom;
to = rtpPortTo;
}
];
networking.firewall.allowedUDPPorts = [
53
67
5060
];
networking.firewall.allowedTCPPorts = [
53
];
networking.firewall.interfaces =
let
matchAll = if !config.networking.nftables.enable then "zt+" else "zt*";
in
{
"${matchAll}".allowedTCPPorts = [ 80 ];
};
services.asterisk = {
enable = lib.mkDefault true;
confFiles =
let
machines = lib.attrNames roles.default.machines;
nodes = builtins.foldl' (
nodes: name:
nodes
++ [
{
hostname = name;
address = getYggdrasilIP name;
prefixNumber = clanLib.getPublicValue {
flake = config.clan.core.settings.directory;
machine = name;
generator = "phonebox";
file = "server-prefix-number";
default = null;
};
localNumber = clanLib.getPublicValue {
flake = config.clan.core.settings.directory;
machine = name;
generator = "phonebox";
file = "ata-local-number";
default = null;
};
}
]
) [ ] machines;
in
{
"logger.conf" = ''
[general]
dateformat = %F %T.%3q ; ISO 8601 date format with milliseconds
use_callids = yes
appendhostname = no
queue_log = yes
queue_log_to_file = no
queue_log_name = queue_log
queue_log_realtime_use_gmt = no
rotatestrategy = rotate
exec_after_rotate=gzip -9 $\{filename\}.2
[logfiles]
console => notice,warning,error
security => security
messages => notice,warning,error
full => notice,warning,error,verbose,dtmf,fax
syslog.local0 => notice,warning,error
'';
# Dial plan config
"extensions.conf" =
let
serverConf = builtins.foldl' (
config: node:
config
+ (genExtentConf {
inherit (node) prefixNumber hostname localNumber;
})
) "" nodes;
in
''
[from-internal]
exten => 999,1,Answer()
same => n,Playback(hello-world)
same => n,Hangup()
''
+ (genLocalExtenConf {
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
})
+ serverConf;
"rtp.conf" = ''
[general]
rtpstart=${builtins.toString rtpPortFrom}
rtpend=${builtins.toString rtpPortTo}
'';
"pjsip.conf" =
let
serverConf = builtins.foldl' (
conf: node:
conf
+ (genServerSIPEndpoint {
hostname = node.hostname;
address = node.address;
})
) "" nodes;
in
''
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
[transport-udp6]
type=transport
protocol=udp
bind=::
[base_endpoint](!)
type=endpoint
disallow=all
allow=ulaw,alaw,g722,gsm
direct_media=no
[internal_endpoint](!,base_endpoint)
context=from-internal
[userpass_auth](!)
type=auth
auth_type=userpass
[ip_auth](!)
type=identify
endpoint=external
[dynamiic_aor](!)
type=aor
''
+ (genLocalSIPEndpoint {
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
})
+ serverConf;
};
};
systemd.services.asterisk-watcher = {
enable = true;
description = "Asterisk Configuration files watcher";
requires = [ "asterisk.service" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
inotify-tools
asterisk
];
script = ''
inotifywait -m -e move /etc/asterisk |
while read path action file; do
case "$file" in
pjsip.conf)
echo "restarting pjsip"
asterisk -rx "pjsip reload"
;;
esac
case "$file" in
extensions.conf)
echo "restarting core"
asterisk -rx "core restart now"
;;
esac
done
'';
};
};
};
};
}

View File

@@ -0,0 +1,23 @@
{
inputs,
self,
...
}:
let
module = ./default.nix;
in
{
clan.modules = {
phonebox = module;
};
perSystem =
{ ... }:
{
clan.nixosTests.service-phonebox = {
imports = [ ./tests/vm/default.nix ];
_module.args = { inherit self inputs; };
clan.modules."@clan/phonebox" = module;
};
};
}

View File

@@ -0,0 +1,59 @@
{
self,
hostPkgs,
config,
inputs,
lib,
...
}:
{
name = "service-phonebox";
result.update-vars =
let
relativeDir = lib.removePrefix "${self}/" (toString config.clan.directory);
in
hostPkgs.writeShellScriptBin "update-vars" ''
set -x
export PRJ_ROOT=$(git rev-parse --show-toplevel)
${
self.inputs.clan-core.packages.${hostPkgs.system}.clan-cli
}/bin/clan-generate-test-vars $PRJ_ROOT/${relativeDir} ${config.name}
'';
clan = {
directory = ./.;
inventory = {
machines.server = { };
instances = {
yggdrasil = {
module.name = "yggdrasil";
roles.default.machines.server = { };
};
phonebox-test = {
module.name = "@clan/phonebox";
module.input = "self";
roles.default.machines."server".settings = {
ata-ethernet-iface = "enp2s0";
};
};
};
};
};
nodes = {
server = {
services.asterisk = {
};
};
};
testScript = ''
start_all()
server.wait_for_unit("asterisk")
server.succeed("systemctl status asterisk")
'';
}

View File

@@ -0,0 +1,6 @@
[
{
"publickey": "age1fdkan6n20swmut0sa86g5a6gxrj8qj2sgqe8hxtw32c0u9rr4drqlyr5mf",
"type": "age"
}
]

View File

@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:t+zYfcA2f1sdUNBAl+bRGyhPEl5HZFIu+au6heH3+SoHf0zy+Deh25gaVay/oswIg2q806ozjEnTw9q2eaq0VXwMSAWOoTWteI0=,iv:sXoOmfzoCv6GHe21n2Elxr/GTdViI5vfLzMwOLvf1F4=,tag:Fmo9MjEaKV3BehR/bYRdVw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzVlo0YjZFVFF2VkFlSzBL\ndHp4YTQ5RlNxV2FWNnNScTh5d0hXR05RL0NVCnBVUENNNjg0dTFVcmx3N3djZ25l\ncmxYTVVrNGhGSkdwVThoaC9UVTQ3L2sKLS0tIG1uREdFcXNubGxzUUQ1Rkh5Wnlx\nRG5OZmJwaGh2dkt6RSttb0gxZ3FBaEkKZzwUuQmOeBk5kfRfVVdqgNvsTU1Ssb/I\nx9Iv9w/YKHDmmcFLcAbGAHbS0/Js0YqBKZonxEMDdWP/+/F+Pv8LqQ==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-31T07:08:30Z",
"mac": "ENC[AES256_GCM,data:CBuTnVIta0eFqlB7ZpDkzPOEGbIQbb5oCpksI4umscB3uE0HM3j4N5r6bCPEcYpehB3qWXuCxlj4NfH7zmp6CDq6Be6bfpB1f8MCwTlPvQUho2f31so3U/g99q6ZyWI2rJO50dzn77bdma3JXo9VQb3uRqJ0mk72IYXFwdHvO9s=,iv:9uwjtGYpqsLcXRoBFmjJjfHUq7R47ZqkrKEXMulw4OY=,tag:4Auq3mnhlusogyW44SJfqg==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -0,0 +1 @@
../../../users/admin

View File

@@ -0,0 +1,4 @@
{
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"type": "age"
}

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/server

View File

@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:L4fWAVQdQP2tgYPzUnDY5X0=,iv:fWeTc1buW/JI/8qngZwzDp+wq2OTZPGdItqG0Up5eZ8=,tag:RJt6PQdCisDhtbr3ph3fWA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fdkan6n20swmut0sa86g5a6gxrj8qj2sgqe8hxtw32c0u9rr4drqlyr5mf",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFK2x0SkcxK2RxTDRvOEpZ\nTS9QNGJxbmZIL2xtUWxyNjA2VDNVWEQzQVc4CkkxeEZDQm1mQVdUUzVIQThDVkV3\nU2lWbnlnb3lqWDZ0NWRqa2RJV0pVK1UKLS0tIHc5eUJBUUtiL0NwZDRnN004UEtr\nYnU2SWlFOGlucTdydGVmZCtGK3NjS28KkNAxwz9MesicLWtViL302AwZYdiTHmd5\nppbwelisNVlsYHSa5ybVDYER4IUz1d8AKO0jtS7qEDfT53R36swSAA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArbEpzdk0xQTFkM2V0K2xz\nRFhDak0ybkhZUjJJdVI4YTl3cStXT2hTZDBFCmRvQ3ZXYkQ1NktCb004ZE1FS1FF\naSt4RlVXSlRhYWdlQ3htTkdkb2dyVDAKLS0tIG16VFNQM0dnaGVSRmlFRFB0WWtW\nN0dPL08rV01sY3ovRnEvUnNMaWVhd2sKvQIZIo5pPMXKh9Ea3ZgHj99Dn1X3JkmB\noscG8S7HOJh/cw+uITmkuv00TyIA9pid6L1kXvfcfv+tcuY9H1Vg4w==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-31T07:08:30Z",
"mac": "ENC[AES256_GCM,data:bmvshL97XNvNYYg0EjkUAQViHpKJ/+A+CGE90/uSM2WXooJF5yzvVJSHLAuYxWM295awN8ygTRUZ2VJ0SyfjzjAyUIH4SxqlrgywqMouyVsiFDAB6R4AmMmbvvC6rSs/TSMOwYC5pchrISbpsE2kmJnAXCqcev6Q0fl8Sa1PSxo=,iv:BuBQCFb3EWfG+bPzgaRDseDq6MJQ/Fs8okvksvEj1bA=,tag:n14VDbQk+zl+XbJPkTRAGg==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
202:fe87:1ca6:3cfd:e095:c2b1:321c:c391

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/server

View File

@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:0LwQxArH6fpIYpGIEzPtjh8elyKSaed7L+KqgnIlPVneR0UbsbOM6p5kVGTRPh5LWH6jmkURCqc2c5oT5CNLBePLQcgvtfLDSFWyyPrD9WtBSPu+YGF7K51JUUIkX07//LqTQQaM5Uu/STQRIoL1BD0Tofk2woA=,iv:3rSDqBj8N9RsSLijZm7mUUjUhiLHc2yidGkil8NvCD8=,tag:F8zlw9pfMjZdJSObDePLsQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fdkan6n20swmut0sa86g5a6gxrj8qj2sgqe8hxtw32c0u9rr4drqlyr5mf",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0NnVzT0lOWnFTUU85c0d5\nZ0Q1b0xkS1NWbWVsc0o1emxLOW5NRU52REFZCjRkUGJHaDhyazdFdkxWaW1XVW53\nb25pUXlDRmhrTHlIWGl6RDJBZ2hTOW8KLS0tIGsxQmlQejkvQk9wTmtMQzZJa2dB\na0piOGlyMzVrTmNOUEhtaDNTWHdwTDAKroQG8KlnWZ6gwu1y0mr0gGezDF1jsS0Y\nC1LUAarHl+lY51sw+HJT88Y9mDfLjvYIMHKS33zdJuBXbNpoIfWyzA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqQVJhYkI1eEFYL0d5OE1F\nSStqV0xPb0pybyt2QWd4SEpiaDhlNDJicTA0CkgrVU9FcThuQktaMThTaXBmWHNV\nd2tpMVVYT21TWUFvMS9wakl4RyszTGsKLS0tIHVVeDdoMUJQaURMOGFkM3dNa3ZG\nMmVPdEg3bmdZZHgwaGpnbFFKUTdvcUUKTaxEfp19+9AJihqx51m0cLz6IuR5pvnT\nt90kZxq+BH3/6gjiDlhwzqztnMbdqQYcVuCDVp/1aVWfThABUZ92aQ==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-12-05T04:42:06Z",
"mac": "ENC[AES256_GCM,data:1/u3c/wf2Gh6PPeVTKKGBxG1FWvN7hyuzx3Qa7yU3yKCD7LDHrrzTbQkFHDo1ZrXixK5NICCw48BWA0Jao0kItu6aU1Dbk5PexZI9ls1eyaDS7nwtZuHKDSEtDYu/kx5kZgQKH9tsokLqKlcoeocS0Mp1tjvUSQsiZFaYsuKaM4=,iv:gCPqFgpwjSONn/JN3dpJOk1BXtdd9cvIAz/NKPuAdKg=,tag:j8upk9sgDiYj5lmfU4V+OQ==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAIC8cazhgQ+1Hqdm8Z43J5ooymP2ytrBEvdfXYz0ryp8=
-----END PUBLIC KEY-----

View File

@@ -0,0 +1 @@
201:b9d:4329:71c2:79ca:3648:e86d:4236

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/server

View File

@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:bAJv40t1hkgMBz4boOEIw9GIY/4UFYI2WZidRrU88ky0JDBwSmHDnxuBFRpdjfAx7sO9d6vSHxAmaf45NlTKwnkUJnaIW0NK+979lXbLV+AG0suc4Vci6fsdbAHofR//3DfTYs03HoALkRUgRemTl7kQtdMB6E6LN02OE19HSVuFjMiEipwosmFfMoR69ZZuPSzNg1uVnw==,iv:LbZZGbwkXc71PAKgjD2CvXJVtyiqgi+cNsBzbulPyIk=,tag:VPESc2Y8SdU5CovQinZleA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fdkan6n20swmut0sa86g5a6gxrj8qj2sgqe8hxtw32c0u9rr4drqlyr5mf",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2L3FzSkh3UTkvdTR6eEVE\nNnYrcnAydUl4QnJSbWJRbFFQODFmSld1YkJZCnNFUHp6YjQ3WW0vVHh5dXZBMzFJ\nYWRnZWNHN2dkL1JBWHNrZTcvTHI5S3MKLS0tIGpnekxneURuMWFkQ1RLTEszTFhi\nUUkzR2ljTXFhb1c2RDhpeFJXaXpYakUK/fLOqjNR2LML7uN3fiB9GdhWTDcr0wn4\n37ESeS1kx0EobRMaDVu8GPZovcdypFOOPiuUpEu6hIEdwvl736oDSA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjQWE2L3F1RW9oNlQzR0I3\nZDJoZ1hIM2N0Mm1aQ1IrN1gweWZxeWVGdUdzCmlIZXdKYm8rUGkvVVpiY1BJZmlD\nN0MwRHN3dUNrRU9McjNFMXpranJGU1UKLS0tIG1yMmJGaEp1cU1iZGdqdzRUTWZW\nZzZrVkRuOTBTcnFuaTE1Um1ISUxBaTQKf842rL3N7Gl1QfrIURWiu26LwO0ERkP4\nvfXN2HH1jjp2pblQF9qb+5vmUsaX1pPSY1R+YMvUK7wIwOb9zyIfmQ==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-31T07:50:02Z",
"mac": "ENC[AES256_GCM,data:/c1jqMSLd9/fxm1PxtRITr3qkjtrlJ/5wJgEVkraAqU6XTuzSyseMhdeWLL2Fy2OunT/+alM6VpaliBmcZqRkSSGngbuvMsujDiJLgUXQ8wNhMcK7ln/dqFMcA+RYGxihGEMwuPKs3yOVKj9PDzFYnm6TUCFkU/heotI/hQ1lQI=,iv:EsWsi4r4DctMLvi4WmqKe0D1NTHwYVKCcxKQ+6grzYg=,tag:28dmpzF/6dbvGIriaBVKPw==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
301:b9d:4329:71c2::/64

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.pingvin = {
clan.nixosTests.service-pingvin = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/pingvin" = module;

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.pocket-id = {
clan.nixosTests.service-pocket-id = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/pocket-id" = module;

View File

@@ -0,0 +1,42 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "pulse-stream";
manifest.description = "stream audio to attached speakers";
manifest.categories = [ "System" ];
roles.default = {
interface =
{ lib, ... }:
{
options.client-ip-ranges = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "ip subnets permitted to stream to the server";
default = [ ];
};
};
perInstance =
{
roles,
settings,
...
}:
{
nixosModule =
{ ... }:
{
services.pulseaudio = {
enable = true;
systemWide = true;
tcp = {
enable = true;
anonymousClients.allowedIpRanges = settings.client-ip-ranges;
anonymousClients.allowAll = true;
};
zeroconf.publish.enable = true;
};
networking.firewall.allowedTCPPorts = [ 4713 ];
};
};
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{
clan.modules = {
pulse-stream = module;
};
}

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.stirling-pdf = {
clan.nixosTests.service-stirling-pdf = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/stirling-pdf" = module;

View File

@@ -10,7 +10,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.victoriametrics = {
clan.nixosTests.service-victoriametrics = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/victoriametrics" = module;

View File

@@ -9,7 +9,7 @@ in
perSystem =
{ ... }:
{
clan.nixosTests.vikunja = {
clan.nixosTests.service-vikunja = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/vikunja" = module;

View File

@@ -0,0 +1,53 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "yggdrasil";
manifest.description = "An in scalable routing as an encrypted IPv6 overlay network";
manifest.categories = [ "System" ];
roles.default = {
perInstance.nixosModule =
{
lib,
config,
pkgs,
...
}:
{
clan.core.vars.generators.yggdrasil = {
files = {
yggdrasil-secret = {
secret = true;
};
yggdrasil-ip = {
secret = false;
};
yggdrasil-subnet.secret = false;
};
runtimeInputs = with pkgs; [
yggdrasil
jq
];
script = ''
yggdrasil -genconf -json | jq {PrivateKey} > $out/yggdrasil-secret
cat $out/yggdrasil-secret | yggdrasil -useconf -address | tr -d "\n" > $out/yggdrasil-ip
yggdrasil -useconffile $out/yggdrasil-secret -subnet | tr -d "\n" > $out/yggdrasil-subnet
'';
};
services.yggdrasil = {
enable = lib.mkDefault true;
configFile = config.clan.core.vars.generators.yggdrasil.files.yggdrasil-secret.path;
settings = {
Peers = [
# US Peers
"tls://ygg.jjolly.dev:3443"
"tls://[2602:fc24:18:7a42::1]:993"
"tcp://leo.node.3dt.net:9002"
"tcp://ygg-kcmo.incognet.io:8883"
];
};
};
};
};
}

View File

@@ -0,0 +1,23 @@
{
lib,
inputs,
self,
...
}:
let
module = lib.modules.importApply ./default.nix { };
in
{
clan.modules = {
yggdrasil = module;
};
perSystem =
{ ... }:
{
# clan.nixosTests.service-yggdrasil = {
# imports = [ ./tests/vm/default.nix ];
#
# clan.modules."@clan/yggdrasil" = module;
# };
};
}

View File

@@ -0,0 +1,37 @@
{
...
}:
{
name = "service-yggdrasil";
clan = {
directory = ./.;
inventory = {
machines.server = { };
instances = {
yggdrasil-test = {
module.name = "@clan/yggdrasil";
module.input = "self";
roles.default.machines."server".settings = { };
};
};
};
};
nodes = {
server = {
services.yggdrasil = {
};
};
};
testScript = ''
start_all()
server.wait_for_unit("yggdrasil")
# Check that garage is running
server.succeed("systemctl status yggdrasil")
'';
}

View File

@@ -0,0 +1,6 @@
[
{
"publickey": "age12ldrhhffl0jeteh8f0rzhezs0ulggg5jyqph6xzrgjw2dv40pqwq49lej9",
"type": "age"
}
]

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:pGMobS67sLp2GN2Xw7A/trcLYnQdVZCUbjtlwS/AShXxyXgHXzkqRee6R765GZyCpDwM8A1IuMZYctrqWxVXrpIAiJpwvwy7vDM=,iv:ysRf5xAXN+dFSx+sFHNDt1GcVQx7RLej4c12v60iSI0=,tag:yXYpWhWLdsz9BOOoKpZU4g==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2UmhPdzcrSnFhVUxRNDBL\ncXlGRzdMdWxCWmFlUkE4RnJRQ3psMlBqV0Q0CkRjTXFoQitQbjRhMlVjaDc3UDN1\nR1hBeXlCeWxvdnZoVWI1ZkcweHF5VncKLS0tIHE4YVFhYTZTNko1MnJINjFPYXh4\ndlJJZThGZ0JIaDJWRTNXbXk3alNZTnMKgd+0535zoTu6xW2778uNReu4Z7LStN6d\n1O9SXAB+s1iOZ3xGEICiQTVF/6p8RE6lheV2oXgoMiXXrFNH6INLsw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-17T05:34:34Z",
"mac": "ENC[AES256_GCM,data:YIpKJlOI6ASgOYqv9ipu+T3c+PlM5HwvdFVH8gh8hVeSbmxD1baPPmVSWlLv+u61Q1/C9PK4mczaASopaGiLoswep+Hc1Gn7sSeP9wO6Djx6fEIEyE1VUhUbTqi/nHYiB21yB/wegfpqzNYIn1nO0oFCmDmSS5qIowcT1fhYIjM=,iv:lzxll5oC7poLvC/hZPexUGAcAdf67xZGRXUpj6O3p6Y=,tag:9xu17Y5MtW5XNzGBsWwA3g==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../users/admin

View File

@@ -0,0 +1,4 @@
{
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"type": "age"
}

View File

@@ -0,0 +1 @@
204:5ce7:aa27:579b:ec90:6907:4ddc:177

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/server

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:I6yalWQ2u5hI84lJTUmh07JxUBp4EZukJrSGSN7wsGiUGlFa1v/RT1XkTiXuRjDtUVYCLmQmfSCAp/OqFscxF8KL+s24iTDrG4e3S6AeKLa3oZrNJIt1EJ06gWrPNoh1ttmwXSd4Y4Bsk4Lg8vIjH4qw3Bx+KrufxYTqe+anfMdoXKnW8wOWud5O7HMvCh+sf4dNcf6PIQ==,iv:SF5qExXNPyif+LIcNhHP0PKELUBXaFsPj9B3wvUkEp0=,tag:QEkZXDrIdcpNiZ6l2ljOPw==,type:str]",
"sops": {
"age": [
{
"recipient": "age12ldrhhffl0jeteh8f0rzhezs0ulggg5jyqph6xzrgjw2dv40pqwq49lej9",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBENDhrWVRuYjRnZktROU4v\nSHNtODFGMWl0NDRHazIwcWl0WGtNY2dYMXhnCmZQQ0doS3BTaU5hRHVsVTB5THl1\nWUNDQUNiMVJFeFZnQ1ptYmFQdTJQc28KLS0tIEtJdUQ5Y1VqSThkSVVNcVNVNEFr\nMzBCRjM1L1V5TngrZG5rR0VHY3Z6TDAKPQ6P96upDeh8xwQDrX4Zcf71Dah5zkOJ\n/F5eODEBadzQSRmJuyp3+uRMFf47eR6Q5bVah3NsVxFquXOL3CtNlw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHb0EwT2tuMVE5SE5XaWdQ\ncGR0bFFhOUQvM2dGUzdlUEFFbzRnTHBWWUVnCjFGTXcrWW1vR0x5dXBUamtkS0dF\neG9weUVwQzhhNHhPRUdqV1VnWXJyNFEKLS0tIC80b1ZqRGFOenpENDN1Vk5vRUhY\nVnJzZ1Q5VzZ6ZEZtZE13YjQ0VVhrTTAK5y0BjKBRg2AXuO416JWLMLyM/pCQChKn\nVKZMXcT6cc5hHDuqbp9qUofknF68XnzlH6nOyLB1ZtnELyeZuf29fw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-17T05:34:35Z",
"mac": "ENC[AES256_GCM,data:Y3k83RaeX64LA3rsIkQxyKw+LLUgXVsqr3F2UHkv9h73gkyChc6k1oE/FLR4CsZZWsfLNjCkPMuenqToA2mKqQK0aADwPDYo0aVm0hr1PGX5j3Py6EmP56NFvxlAQsExRWo32eqdkeCkY23hfcmUYlaB+bo/fsrRVj67zag9GYA=,iv:p18i8cV6jKXpuZ1Xd7KYCl8BMe1/8CW9YnCuVrTAqy0=,tag:IJnLzdZOn8Clu+lCKT6zvA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

10
modules/nixos/common.nix Normal file
View File

@@ -0,0 +1,10 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
screen
ncdu
vim
lshw
pciutils
];
}

View File

@@ -0,0 +1,13 @@
{
flake.nixosModules = {
common = {
imports = [ ./common.nix ];
};
think-gtcm = {
imports = [ ./think-gtcm.nix ];
};
think-backend-gtcm = {
imports = [ ./think-backend-gtcm.nix ];
};
};
}

View File

@@ -0,0 +1,292 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.think-backend-greaterchiangmai;
think-backend-gtcm = pkgs.think-backend-gtcm.override { dataDir = cfg.dataDir; };
defaultUser = "gtcm";
defaultGroup = "gtcm";
php = pkgs.php83;
artisan-be = pkgs.writeScriptBin "gtcm-be" ''
#! ${pkgs.runtimeShell}
cd ${think-backend-gtcm}
sudo() {
if [[ "$USER" != ${cfg.user} ]]; then
exec /run/wrappers/bin/sudo -u ${cfg.user} "$@"
else
exec "$@"
fi
}
sudo ${lib.getExe php} artisan "$@"
'';
in
{
options.services.think-backend-greaterchiangmai = {
enable = lib.mkEnableOption "To enable think.greaterchiangmai.com";
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/think-backend.greaterchiangmai.com";
description = ''A place where to store states'';
};
user = lib.mkOption {
type = lib.types.str;
default = defaultUser;
description = "User account under which this runs.";
};
group = lib.mkOption {
type = lib.types.str;
default = defaultGroup;
defaultText = "${defaultGroup}";
description = "Group under which the website runs.";
};
package = lib.mkPackageOption pkgs "think-backend-gtcm" { };
domain = lib.mkOption {
type = lib.types.str;
default = "think-backend.greaterchiangmai.com";
example = "forum.example.com";
description = "Domain to serve on.";
};
settings = lib.mkOption {
type =
with lib.types;
attrsOf (
nullOr (
either
(oneOf [
bool
int
port
path
str
])
(submodule {
options = {
_secret = lib.mkOption {
type = nullOr str;
description = ''
The path to a file containing the value the
option should be set to in the final
configuration file.
'';
};
};
})
)
);
default = { };
description = ''
Options for settings environment variables
'';
example = lib.literalExpression ''
{
APP_NAME = "Laravel";
APP_ENV = "local";
APP_KEY = "key";
APP_DEBUG = true;
APP_URL = "http://localhost";
LOG_CHANNEL = "stack";
LOG_DEPRECATIONS_CHANNEL = "null";
LOG_LEVEL = "debug";
DB_CONNECTION = "mysql";
DB_HOST = "127.0.0.1";
DB_PORT = "3306";
DB_DATABASE = "laravel";
DB_USERNAME = "root";
DB_PASSWORD = "";
}
'';
};
};
config = lib.mkIf cfg.enable {
users.users.${cfg.user} = lib.mkForce {
isSystemUser = true;
home = cfg.dataDir;
createHome = true;
homeMode = "755";
group = cfg.group;
};
users.groups.${cfg.group} = { };
services.phpfpm.pools.think-backend-gtcm = {
inherit (cfg) user group;
phpPackage = php;
settings = {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
"listen.mode" = "0600";
"pm" = lib.mkDefault "dynamic";
"pm.max_children" = lib.mkDefault 10;
"pm.max_requests" = lib.mkDefault 500;
"pm.start_servers" = lib.mkDefault 2;
"pm.min_spare_servers" = lib.mkDefault 1;
"pm.max_spare_servers" = lib.mkDefault 3;
};
phpOptions = ''
error_log = syslog
log_errors = on
'';
};
environment.systemPackages = [
artisan-be
];
services.think-backend-greaterchiangmai.settings = {
APP_SERVICES_CACHE = lib.mkDefault "${cfg.dataDir}/cache/services.php";
APP_PACKAGES_CACHE = lib.mkDefault "${cfg.dataDir}/cache/packages.php";
APP_CONFIG_CACHE = lib.mkDefault "${cfg.dataDir}/cache/config.php";
APP_ROUTES_CACHE = lib.mkDefault "${cfg.dataDir}/cache/routes-v7.php";
APP_EVENTS_CACHE = lib.mkDefault "${cfg.dataDir}/cache/events.php";
};
systemd.services.think-backend-gtcm-setup = {
description = "think-backend.greaterchiangmai installation";
requiredBy = [ "phpfpm-think-backend-gtcm.service" ];
before = [ "phpfpm-think-backend-gtcm.service" ];
requires = [ "mysql.service" ];
after = [ "mysql.service" ];
serviceConfig = {
type = "oneshot";
RemainAfterExit = true;
User = cfg.user;
UMask = 77;
WorkingDirectory = "${think-backend-gtcm}";
RuntimeDirectory = "think-backend-gtcm/cache";
RuntimeDirectoryMode = 700;
};
path = [ pkgs.replace-secret ];
script =
let
isSecret = v: lib.isAttrs v && v ? _secret && lib.isString v._secret;
gtcmEnvVars = lib.generators.toKeyValue {
mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
mkValueString =
v:
with builtins;
if isInt v then
toString v
else if isString v then
v
else if true == v then
"true"
else if false == v then
"false"
else if isSecret v then
hashString "sha256" v._secret
else
throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty { }) v}";
};
};
secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.settings);
mkSecretReplacement = file: ''
replace-secret ${
lib.escapeShellArgs [
(builtins.hashString "sha256" file)
file
"${cfg.dataDir}/.env"
]
}
'';
secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
filteredConfig = lib.converge (lib.filterAttrsRecursive (
_: v:
!lib.elem v [
{ }
null
]
)) cfg.settings;
gtcmEnv = pkgs.writeText "gtcm-be.env" (gtcmEnvVars filteredConfig);
in
''
# error handling
set -euo pipefail
# create .env file
install -T -m 0600 -o ${cfg.user} ${gtcmEnv} "${cfg.dataDir}/.env"
${secretReplacements}
if ! grep 'APP_KEY=base64:' "${cfg.dataDir}/.env" >/dev/null; then
sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env"
fi
# migrate & seed db
${lib.getExe php} artisan key:generate --force
${lib.getExe php} artisan migrate --force
${lib.getExe php} artisan config:cache
'';
};
systemd.tmpfiles.rules = [
"d ${cfg.dataDir} 0710 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/cache 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/public 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/public/uploads 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/app 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/fonts 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework/cache 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework/sessions 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework/views 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/logs 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/uploads 0700 ${cfg.user} ${cfg.group} - -"
];
networking.firewall.allowedTCPPorts = [
80
443
];
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedBrotliSettings = true;
recommendedProxySettings = true;
virtualHosts."${cfg.domain}" = {
root = "${think-backend-gtcm}/public";
locations = {
"/" = {
index = "index.php";
tryFiles = "$uri $uri/ /index.php?$query_string";
};
"~ \\.php$".extraConfig = ''
fastcgi_pass unix:${config.services.phpfpm.pools."think-backend-gtcm".socket};
'';
"~ \\.(js|css|gif|png|ico|jpg|jpeg)$" = {
extraConfig = "expires 365d;";
};
};
};
};
services.mysql = {
enable = true;
package = lib.mkForce pkgs.mariadb;
ensureDatabases = [ cfg.settings.DB_DATABASE ];
ensureUsers = [
{
name = cfg.settings.DB_USERNAME;
ensurePermissions = {
"${cfg.settings.DB_DATABASE}.*" = "ALL PRIVILEGES";
};
}
];
};
};
}

View File

@@ -0,0 +1,295 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.think-greaterchiangmai;
think-gtcm = pkgs.think-gtcm.override { dataDir = cfg.dataDir; };
defaultUser = "gtcm";
defaultGroup = "gtcm";
php = pkgs.php83;
artisan = pkgs.writeScriptBin "gtcm" ''
#! ${pkgs.runtimeShell}
cd ${think-gtcm}
sudo() {
if [[ "$USER" != ${cfg.user} ]]; then
exec /run/wrappers/bin/sudo -u ${cfg.user} "$@"
else
exec "$@"
fi
}
sudo ${lib.getExe php} artisan "$@"
'';
in
{
options.services.think-greaterchiangmai = {
enable = lib.mkEnableOption "To enable think.greaterchiangmai.com";
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/think.greaterchiangmai.com";
description = ''A place where to store states'';
};
user = lib.mkOption {
type = lib.types.str;
default = defaultUser;
description = "User account under which this runs.";
};
group = lib.mkOption {
type = lib.types.str;
default = defaultGroup;
defaultText = "${defaultGroup}";
description = "Group under which the website runs.";
};
package = lib.mkPackageOption pkgs "think-gtcm" { };
packageBackend = lib.mkPackageOption pkgs "think-backend-gtcm" { };
domain = lib.mkOption {
type = lib.types.str;
default = "think.greaterchiangmai.com";
example = "forum.example.com";
description = "Domain to serve on.";
};
settings = lib.mkOption {
type =
with lib.types;
attrsOf (
nullOr (
either
(oneOf [
bool
int
port
path
str
])
(submodule {
options = {
_secret = lib.mkOption {
type = nullOr str;
description = ''
The path to a file containing the value the
option should be set to in the final
configuration file.
'';
};
};
})
)
);
default = { };
description = ''
Options for settings environment variables
'';
example = lib.literalExpression ''
{
APP_NAME = "Laravel";
APP_ENV = "local";
APP_KEY = "key";
APP_DEBUG = true;
APP_URL = "http://localhost";
LOG_CHANNEL = "stack";
LOG_DEPRECATIONS_CHANNEL = "null";
LOG_LEVEL = "debug";
DB_CONNECTION = "mysql";
DB_HOST = "127.0.0.1";
DB_PORT = "3306";
DB_DATABASE = "laravel";
DB_USERNAME = "root";
DB_PASSWORD = "";
}
'';
};
};
config = lib.mkIf cfg.enable {
users.users.${cfg.user} = {
isSystemUser = true;
home = cfg.dataDir;
createHome = true;
homeMode = "755";
group = cfg.group;
};
users.groups.${cfg.group} = { };
services.phpfpm.pools.think-gtcm = {
inherit (cfg) user group;
phpPackage = php;
settings = {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
"listen.mode" = "0600";
"pm" = lib.mkDefault "dynamic";
"pm.max_children" = lib.mkDefault 10;
"pm.max_requests" = lib.mkDefault 500;
"pm.start_servers" = lib.mkDefault 2;
"pm.min_spare_servers" = lib.mkDefault 1;
"pm.max_spare_servers" = lib.mkDefault 3;
};
phpOptions = ''
error_log = syslog
log_errors = on
'';
};
environment.systemPackages = [
artisan
];
services.think-greaterchiangmai.settings = {
APP_SERVICES_CACHE = lib.mkDefault "${cfg.dataDir}/cache/services.php";
APP_PACKAGES_CACHE = lib.mkDefault "${cfg.dataDir}/cache/packages.php";
APP_CONFIG_CACHE = lib.mkDefault "${cfg.dataDir}/cache/config.php";
APP_ROUTES_CACHE = lib.mkDefault "${cfg.dataDir}/cache/routes-v7.php";
APP_EVENTS_CACHE = lib.mkDefault "${cfg.dataDir}/cache/events.php";
};
systemd.services.think-gtcm-setup = {
description = "think.greaterchiangmai installation";
requiredBy = [ "phpfpm-think-gtcm.service" ];
before = [ "phpfpm-think-gtcm.service" ];
requires = [ "mysql.service" ];
after = [ "mysql.service" ];
serviceConfig = {
type = "oneshot";
RemainAfterExit = true;
User = cfg.user;
UMask = 77;
WorkingDirectory = "${think-gtcm}";
RuntimeDirectory = "think-gtcm/cache";
RuntimeDirectoryMode = 700;
};
path = [ pkgs.replace-secret ];
script =
let
isSecret = v: lib.isAttrs v && v ? _secret && lib.isString v._secret;
gtcmEnvVars = lib.generators.toKeyValue {
mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
mkValueString =
v:
with builtins;
if isInt v then
toString v
else if isString v then
v
else if true == v then
"true"
else if false == v then
"false"
else if isSecret v then
hashString "sha256" v._secret
else
throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty { }) v}";
};
};
secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.settings);
mkSecretReplacement = file: ''
replace-secret ${
lib.escapeShellArgs [
(builtins.hashString "sha256" file)
file
"${cfg.dataDir}/.env"
]
}
'';
secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
filteredConfig = lib.converge (lib.filterAttrsRecursive (
_: v:
!lib.elem v [
{ }
null
]
)) cfg.settings;
gtcmEnv = pkgs.writeText "gtcm.env" (gtcmEnvVars filteredConfig);
in
''
# error handling
set -euo pipefail
# create .env file
install -T -m 0600 -o ${cfg.user} ${gtcmEnv} "${cfg.dataDir}/.env"
${secretReplacements}
if ! grep 'APP_KEY=base64:' "${cfg.dataDir}/.env" >/dev/null; then
sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env"
fi
# migrate & seed db
${lib.getExe php} artisan key:generate --force
${lib.getExe php} artisan migrate --force
${lib.getExe php} artisan config:cache
'';
};
systemd.tmpfiles.rules = [
"d ${cfg.dataDir} 0710 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/cache 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/public 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/public/uploads 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/app 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/fonts 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework/cache 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework/sessions 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/framework/views 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/logs 0700 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage/uploads 0700 ${cfg.user} ${cfg.group} - -"
];
networking.firewall.allowedTCPPorts = [
80
443
];
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedBrotliSettings = true;
recommendedProxySettings = true;
virtualHosts."${cfg.domain}" = {
root = "${think-gtcm}/public";
locations = {
"/" = {
index = "index.php";
tryFiles = "$uri $uri/ /index.php?$query_string";
};
"~ \\.php$".extraConfig = ''
fastcgi_pass unix:${config.services.phpfpm.pools."think-gtcm".socket};
'';
"~ \\.(js|css|gif|png|ico|jpg|jpeg)$" = {
extraConfig = "expires 365d;";
};
};
};
};
services.mysql = {
enable = true;
package = pkgs.mariadb;
ensureDatabases = [ cfg.settings.DB_DATABASE ];
ensureUsers = [
{
name = cfg.settings.DB_USERNAME;
ensurePermissions = {
"${cfg.settings.DB_DATABASE}.*" = "ALL PRIVILEGES";
};
}
];
};
};
}

6
overlays/default.nix Normal file
View File

@@ -0,0 +1,6 @@
{ ... }:
{
flake.overlays = {
packagesOverlay = import ../pkgs/overlay.nix;
};
}

4
pkgs/overlay.nix Normal file
View File

@@ -0,0 +1,4 @@
final: prev: {
think-gtcm = final.callPackage ./think-gtcm.nix { };
think-backend-gtcm = final.callPackage ./think-backend-gtcm.nix { php = final.php83; };
}

View File

@@ -0,0 +1,32 @@
{
fetchgit,
php,
dataDir ? "/var/lib/think-backend-gtcm",
}:
let
repoSrc = fetchgit {
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
rev = "7c17aa78436538241c09fc7d633904d3c063011e";
hash = "sha256-GDx0+PmuCXC+UPtsvsocCZQiTPcnOZEzJI17sxrVv7Q=";
};
src = "${repoSrc}/think-backend.greaterchiangmai.com";
in
php.buildComposerProject2 (finalAttrs: {
pname = "think-backend-gtcm";
version = "1.0.0";
inherit src;
installPhase = ''
runHook preInstall
mkdir -p $out
cp -R * $out
rm -rf $out/storage
ln -s ${dataDir}/.env $out/.env
ln -s ${dataDir}/storage $out/storage
runHook postInstall
'';
composerStrictValidation = false;
vendorHash = "sha256-eXm1x3E9KHWojaT2RU4inMdZqQVcWdLCKlvzhOlIZrc=";
})

32
pkgs/think-gtcm.nix Normal file
View File

@@ -0,0 +1,32 @@
{
fetchgit,
php,
dataDir ? "/var/lib/think-gtcm",
}:
let
repoSrc = fetchgit {
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
rev = "7c17aa78436538241c09fc7d633904d3c063011e";
hash = "sha256-GDx0+PmuCXC+UPtsvsocCZQiTPcnOZEzJI17sxrVv7Q=";
};
src = "${repoSrc}/think.greaterchiangmai.com";
in
php.buildComposerProject2 (finalAttrs: {
pname = "think-gtcm";
version = "1.0.0";
inherit src;
installPhase = ''
runHook preInstall
mkdir -p $out
cp -R * $out
rm -rf $out/storage
ln -s ${dataDir}/.env $out/.env
ln -s ${dataDir}/storage $out/storage
runHook postInstall
'';
composerStrictValidation = false;
vendorHash = "sha256-QV3hR3U3GwCqrCRxfkazmJwDpO1vFyMfA6YqUb4bjMI=";
})

View File

@@ -1,25 +1,9 @@
{ inputs, ... }:
{
flake.legacyPackages = {
qemu-router = import "${inputs.liminix}/default.nix" {
liminix-config = import "${inputs.liminix}/examples/hello-from-qemu.nix";
device = (import "${inputs.liminix}/devices/qemu-aarch64/default.nix");
};
yada-router = import "${inputs.liminix}/default.nix" {
liminix-config = import ./routers/yada-house/configuration.nix { inherit inputs; };
device = (import ./routers/yada-house/device.nix { inherit inputs; });
};
qemu-flake = import "${inputs.liminix}/default.nix" {
liminix-config = import ./routers/qemu/configuration.nix { inherit inputs; };
device = (import ./routers/qemu/device.nix { inherit inputs; });
};
vanilla = import "${inputs.liminix}/default.nix" {
liminix-config = import ./routers/vanilla/configuration.nix { inherit inputs; };
device = (import "${inputs.liminix}/devices/gl-mt300a/default.nix");
};
fax-router = import "${inputs.liminix}/default.nix" {
whitehouse-router = import "${inputs.liminix}/default.nix" {
device = (import "${inputs.liminix}/devices/gl-ar750");
liminix-config = import ./fax-router/configuration.nix { inherit inputs; };
liminix-config = import ./white-house/configuration.nix { inherit inputs; };
};
};
}

View File

@@ -1,46 +0,0 @@
# This is an example that uses the "gateway" profile to create a
# "typical home wireless router" configuration suitable for a Gl.inet
# gl-ar750 router. It should be fairly simple to edit it for other
# devices: mostly you will need to attend to the number of wlan and lan
# interfaces
{ inputs }:
{ config, pkgs, ... }:
let
inherit (pkgs.liminix.services) target;
svc = config.system.service;
in
rec {
imports = [
"${inputs.liminix}/modules/wlan.nix"
"${inputs.liminix}/modules/network"
"${inputs.liminix}/modules/ntp"
"${inputs.liminix}/modules/vlan"
];
services.dhcpv4 =
let
iface = svc.network.link.build { ifname = "eth1"; };
in
svc.network.dhcp.client.build { interface = iface; };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} ip)";
target = "default";
dependencies = [ services.dhcpv4 ];
};
services.packet_forwarding = svc.network.forward.build { };
services.ntp = config.system.service.ntp.build {
pools = {
"pool.ntp.org" = [ "iburst" ];
};
};
boot.tftp = {
serverip = "192.168.8.148";
ipaddr = "192.168.8.251";
};
defaultProfile.packages = [ pkgs.hello ];
}

View File

@@ -1,17 +0,0 @@
{
wpa_passphrase = "you bring light in";
ssid = "liminix";
l2tp = {
name = "abcde@a.1";
password = "NotMyIspPassword";
};
root = {
# mkpasswd -m sha512crypt
passwd = "$6$6pt0mpbgcB7kC2RJ$kSBoCYGyi1.qxt7dqmexLj1l8E6oTZJZmfGyJSsMYMW.jlsETxdgQSdv6ptOYDM7DHAwf6vLG0pz3UD31XBfC1";
openssh.authorizedKeys.keys = [ ];
};
lan = {
prefix = "10.8.0";
};
}

View File

@@ -1,49 +0,0 @@
{ inputs }:
{ config, pkgs, ... }:
let
svc = config.system.service;
in
rec {
imports = [
"${inputs.liminix}/modules/network"
"${inputs.liminix}/modules/dnsmasq"
"${inputs.liminix}/modules/ssh"
];
hostname = "hello";
# configure the internal network (LAN) with an address
services.int = svc.network.address.build {
interface = config.hardware.networkInterfaces.lan;
family = "inet";
address = "10.3.0.1";
prefixLength = 16;
};
services.sshd = svc.ssh.build { };
users.root = {
# the password is "secret". Use mkpasswd -m sha512crypt to
# create this hashed password string
passwd = "$6$y7WZ5hM6l5nriLmo$5AJlmzQZ6WA.7uBC7S8L4o19ESR28Dg25v64/vDvvCN01Ms9QoHeGByj8lGlJ4/b.dbwR9Hq2KXurSnLigt1W1";
};
services.dns =
let
interface = services.int;
in
svc.dnsmasq.build {
inherit interface;
ranges = [
"10.3.0.10,10.3.0.240"
"::,constructor:$(output ${interface} ifname),ra-stateless"
];
domain = "example.org";
};
defaultProfile.packages = with pkgs; [
figlet
];
}

View File

@@ -1,58 +0,0 @@
# This "device" generates images that can be used with the QEMU
# emulator. The default output is a directory containing separate
# kernel ("Image" format) and root filesystem (squashfs or jffs2)
# images
{ inputs }:
{
system = {
crossSystem = {
config = "aarch64-unknown-linux-musl";
};
};
description = ''
QEMU Aarch64
************
This target produces an image for
the `QEMU "virt" platform <https://www.qemu.org/docs/master/system/arm/virt.html>`_ using a 64 bit CPU type.
ARM targets differ from MIPS in that the kernel format expected
by QEMU is an "Image" (raw binary file) rather than an ELF
file, but this is taken care of by :command:`run.sh`. Check the
documentation for the :ref:`qemu` target for more information.
'';
# this device is described by the "qemu" device
installer = "vmroot";
module =
{ config, lim, ... }:
{
imports = [
"${inputs.liminix}/modules/arch/aarch64.nix"
"${inputs.liminix}/devices/families/qemu.nix"
];
kernel = {
config = {
VIRTUALIZATION = "y";
PCI_HOST_GENERIC = "y";
SERIAL_AMBA_PL011 = "y";
SERIAL_AMBA_PL011_CONSOLE = "y";
};
};
boot.commandLine = [
"console=ttyAMA0,38400"
];
hardware =
let
addr = lim.parseInt "0x40010000";
in
{
loadAddress = addr;
entryPoint = addr;
};
};
}

View File

@@ -1,41 +0,0 @@
{ inputs }:
{ config, pkgs, ... }:
let
inherit (pkgs.liminix.services) target;
svc = config.system.service;
in
rec {
imports = [
"${inputs.liminix}/modules/wlan.nix"
"${inputs.liminix}/modules/network"
"${inputs.liminix}/modules/ntp"
"${inputs.liminix}/modules/vlan"
];
services.dhcpv4 =
let
iface = svc.network.link.build { ifname = "eth1"; };
in
svc.network.dhcp.client.build { interface = iface; };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} ip)";
target = "default";
dependencies = [ services.dhcpv4 ];
};
services.packet_forwarding = svc.network.forward.build { };
services.ntp = config.system.service.ntp.build {
pools = {
"pool.ntp.org" = [ "iburst" ];
};
};
boot.tftp = {
serverip = "192.168.8.148";
ipaddr = "192.168.8.251";
};
defaultProfile.packages = [ pkgs.hello ];
}

View File

@@ -1,86 +0,0 @@
# This "device" generates images that can be used with the QEMU
# emulator. The default output is a directory containing separate
# kernel (uncompressed vmlinux) and initrd (squashfs) images
{ inputs }:
{
system = {
crossSystem = {
config = "mips-unknown-linux-musl";
gcc = {
abi = "32";
arch = "mips32"; # maybe mips_24kc-
};
};
};
description = ''
QEMU MIPS
*********
This target produces an image for
QEMU, the "generic and open source machine emulator and
virtualizer".
MIPS QEMU emulates a "Malta" board, which was an ATX form factor
evaluation board made by MIPS Technologies, but mostly in Liminix
we use paravirtualized devices (Virtio) instead of emulating
hardware.
Building an image for QEMU results in a :file:`result/` directory
containing ``run.sh`` ``vmlinux``, and ``rootfs`` files. To invoke
the emulator, run ``run.sh``.
The configuration includes two emulated "hardware" ethernet
devices and the kernel :code:`mac80211_hwsim` module to
provide an emulated wlan device. To read more about how
to connect to this network, refer to :ref:`qemu-networking`
in the Development manual.
'';
module =
{
config,
lib,
lim,
...
}:
{
imports = [
"${inputs.liminix}/modules/arch/mipseb.nix"
"${inputs.liminix}/devices/families/qemu.nix"
];
kernel = {
config = {
MIPS_MALTA = "y";
CPU_MIPS32_R2 = "y";
POWER_RESET = "y";
POWER_RESET_SYSCON = "y";
SERIAL_8250 = "y";
SERIAL_8250_CONSOLE = "y";
};
};
hardware =
# from arch/mips/mti-malta/Platform:load-$(CONFIG_MIPS_MALTA) += 0xffffffff80100000
let
addr = lim.parseInt "0x80100000";
in
{
loadAddress = addr;
entryPoint = addr;
# Unlike the arm qemu targets, we need a static dts when
# running u-boot-using tests, qemu dumpdtb command doesn't
# work for this board. I am not at all sure this dts is
# *correct* but it does at least boot
dts = lib.mkForce {
src = "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/mti/malta.dts";
includePaths = [
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
];
};
};
};
}

View File

@@ -0,0 +1,162 @@
{ inputs }:
{
config,
pkgs,
modulesPath,
lib,
...
}:
let
secrets = {
firewallRules = { };
}
// (import ./secrets.nix);
wirelessConfig = {
country_code = "TH";
inherit (secrets) wpa_passphrase;
wmm_enabled = 1;
};
svc = config.system.service;
in
{
imports = [
"${inputs.liminix}/modules/wlan.nix"
"${inputs.liminix}/modules/network"
"${inputs.liminix}/modules/vlan"
"${inputs.liminix}/modules/ssh"
"${inputs.liminix}/modules/bridge"
"${inputs.liminix}/modules/health-check"
"${modulesPath}/profiles/gateway.nix"
];
hostname = "whitehouse";
boot = {
tftp = {
freeSpaceBytes = 3 * 1024 * 1024;
serverip = "${secrets.lan.prefix}.148";
ipaddr = "${secrets.lan.prefix}.251";
};
};
services.sshd = svc.ssh.build {
authorizedKeys.root = secrets.root.openssh.authorizedKeys.keys;
};
users.root = secrets.root;
services.resolvconf = lib.mkForce (
pkgs.liminix.services.oneshot rec {
name = "resolvconf";
up = ''
( in_outputs ${name}
echo "nameserver 208.67.222.222" >> resolv.conf
echo "nameserver 208.67.220.220" >> resolv.conf
echo "nameserver 1.1.1.1" >> resolv.conf
echo "nameserver 1.0.0.1" >> resolv.conf
echo "nameserver 8.8.8.8" >> resolv.conf
chmod 0444 resolv.conf
)
'';
}
);
services.reAddDefaultroute =
let
threshold = 3;
healthCheck = pkgs.writeAshScript "ping-check" { } "ping 1.1.1.1";
in
pkgs.liminix.services.longrun rec {
# dependencies = [ config.services.wan ];
name = "hack-default-route";
run = ''
fails=0
while sleep 10 ; do
${healthCheck}
if test $? -gt 0; then
fails=$(expr $fails + 1)
else
fails=0
fi
echo fails $fails/${toString threshold} for ${name}
if test "$fails" -gt "${toString threshold}" ; then
echo [+] adding default route
${config.services.defaultroute4}/${config.services.defaultroute4.name}/up
${config.services.defaultroute6}/${config.services.defaultroute6.name}/up
echo bounced
fails=0
fi
done
'';
};
profile.gateway = {
lan = {
interfaces = with config.hardware.networkInterfaces; [
wlan
wlan5
lan
];
inherit (secrets.lan) prefix;
address = {
family = "inet";
address = "${secrets.lan.prefix}.1";
prefixLength = 24;
};
dhcp = {
start = 10;
end = 240;
hosts = { };
localDomain = "lan";
};
};
wan =
let
inherit (config.system.service) vlan;
wan-vlan = vlan.build {
ifname = "wan-vlan";
primary = config.hardware.networkInterfaces.wan;
vid = "10";
};
in
{
interface = svc.pppoe.build {
interface = wan-vlan;
username = secrets.l2tp.name;
password = secrets.l2tp.password;
};
dhcp6.enable = true;
};
firewall = {
enable = true;
rules = secrets.firewallRules;
};
wireless.networks = {
"${secrets.ssid}" = {
interface = config.hardware.networkInterfaces.wlan;
hw_mode = "g";
channel = "2";
ieee80211n = 1;
}
// wirelessConfig;
"${secrets.ssid}-5" = rec {
interface = config.hardware.networkInterfaces.wlan5;
hw_mode = "a";
channel = 36;
ht_capab = "[HT40+]";
vht_oper_chwidth = 1;
vht_oper_centr_freq_seg0_idx = channel + 6;
ieee80211n = 1;
ieee80211ac = 1;
}
// wirelessConfig;
};
};
defaultProfile.packages = with pkgs; [
busybox
iw
nftables
];
}

View File

@@ -0,0 +1,20 @@
{
wpa_passphrase = "";
ssid = "WhiteHouse";
l2tp = {
name = "";
password = "";
};
root = {
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAmgyEGuY/r7SDlJgrzYmQqpcWS5W+fCzRi3OS59ne4W openpgp:0xFF687387"
];
};
lan = {
prefix = "192.168.1";
};
}

View File

@@ -1,54 +0,0 @@
{ inputs }:
{
config,
pkgs,
...
}:
let
svc = config.system.service;
in
rec {
imports = [
"${inputs.liminix}/modules/network"
"${inputs.liminix}/modules/dnsmasq"
"${inputs.liminix}/modules/ssh"
];
hostname = "hello";
# configure the internal network (LAN) with an address
services.int = svc.network.address.build {
interface = config.hardware.networkInterfaces.lan2;
family = "inet";
address = "192.168.8.1";
prefixLength = 24;
};
services.sshd = svc.ssh.build { };
users.root = {
# the password is "secret". Use mkpasswd -m sha512crypt to
# create this hashed password string
passwd = "$6$y7WZ5hM6l5nriLmo$5AJlmzQZ6WA.7uBC7S8L4o19ESR28Dg25v64/vDvvCN01Ms9QoHeGByj8lGlJ4/b.dbwR9Hq2KXurSnLigt1W1";
};
services.dns =
let
interface = services.int;
in
svc.dnsmasq.build {
inherit interface;
ranges = [
"192.168.8.1,192.168.8.240"
"::,constructor:$(output ${interface} ifname),ra-stateless"
];
domain = "example.org";
};
# defaultProfile.packages = with pkgs; [
# figlet
# ];
}

View File

@@ -1,127 +0,0 @@
# GL.iNet GL-MT6000
{ inputs }:
{
system = {
crossSystem = {
config = "aarch64-unknown-linux-musl";
gcc = {
arch = "armv8-a";
};
};
};
description = ''
Device configuration for Yada/White house router.
'';
module =
{
pkgs,
config,
lib,
lim,
...
}:
let
inherit (pkgs) openwrt_24_10;
# mac80211 = pkgs.kmodloader.override {
# targets = [ "rt2800soc" ];
# inherit (config.system.outputs) kernel;
# };
in
{
imports = [
"${inputs.liminix}/modules/outputs/mtdimage.nix"
"${inputs.liminix}/modules/outputs/squashfs.nix"
"${inputs.liminix}/modules/outputs/tftpboot.nix"
"${inputs.liminix}/modules/outputs/vmroot.nix"
"${inputs.liminix}/modules/arch/aarch64.nix"
# "${inputs.liminix}/modules/base.nix"
"${inputs.liminix}/modules/vlan"
];
boot.tftp = {
serverip = "192.168.1.254";
ipaddr = "192.168.1.1";
loadAddress = lim.parseInt "0x46000000";
};
boot.imageFormat = "fit";
boot.loader.fit.enable = true;
rootfsType = "squashfs";
hardware = {
loadAddress = lim.parseInt "0x48080000";
entryPoint = lim.parseInt "0x48080000";
flash = {
address = lim.parseInt "0x41e00000";
size = lim.parseInt "0x4000";
eraseBlockSize = 65536;
};
rootDevice = "/dev/root";
dts = {
src = "${openwrt_24_10.src}/target/linux/mediatek/dts/mt7986a-glinet-gl-mt6000.dts";
includePaths = [
"${openwrt_24_10.src}/target/linux/mediatek/dts"
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
];
};
networkInterfaces =
let
inherit (config.system.service.network) link;
inherit (config.system.service) vlan;
in
rec {
eth0 = link.build { ifname = "eth0"; };
wan = link.build { ifname = "eth1"; };
lan1 = vlan.build {
ifname = "lan1@eth0";
primary = eth0;
vid = "1";
};
lan2 = vlan.build {
ifname = "lan2@eth0";
primary = eth0;
vid = "2";
};
lan3 = vlan.build {
ifname = "lan3@eth0";
primary = eth0;
vid = "3";
};
lan4 = vlan.build {
ifname = "lan4@eth0";
primary = eth0;
vid = "4";
};
lan5 = vlan.build {
ifname = "lan5@eth0";
primary = eth0;
vid = "5";
};
# wlan = link.build {
# ifname = "wlan0";
# dependencies = [ mac80211 ];
# };
};
};
kernel = {
src = openwrt_24_10.kernelSrc;
version = openwrt_24_10.kernelVersion;
extraPatchPhase = ''
echo ==================================================
ls ${openwrt_24_10.src}/config
echo ==================================================
patch ${openwrt_24_10.src}/package/boot/uboot-mediatek/patches/436-add-glinet-mt6000.patch
echo --------------------------------------------------
ls ${openwrt_24_10.src}/config
echo --------------------------------------------------
${openwrt_24_10.applyPatches.mediatek}
'';
config = {
};
};
};
}

View File

@@ -1,645 +0,0 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (C) 2021 MediaTek Inc.
* Author: Sam.Shih <sam.shih@mediatek.com>
*/
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/mt7986-clk.h>
#include <dt-bindings/reset/mt7986-resets.h>
#include <dt-bindings/phy/phy.h>
/ {
compatible = "mediatek,mt7986a";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x0>;
device_type = "cpu";
enable-method = "psci";
#cooling-cells = <2>;
};
cpu1: cpu@1 {
compatible = "arm,cortex-a53";
reg = <0x1>;
device_type = "cpu";
enable-method = "psci";
#cooling-cells = <2>;
};
cpu2: cpu@2 {
compatible = "arm,cortex-a53";
reg = <0x2>;
device_type = "cpu";
enable-method = "psci";
#cooling-cells = <2>;
};
cpu3: cpu@3 {
compatible = "arm,cortex-a53";
reg = <0x3>;
device_type = "cpu";
enable-method = "psci";
#cooling-cells = <2>;
};
};
clk40m: oscillator-40m {
compatible = "fixed-clock";
clock-frequency = <40000000>;
#clock-cells = <0>;
clock-output-names = "clkxtal";
};
psci {
compatible = "arm,psci-0.2";
method = "smc";
};
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* 192 KiB reserved for ARM Trusted Firmware (BL31) */
secmon_reserved: secmon@43000000 {
reg = <0 0x43000000 0 0x30000>;
no-map;
};
wmcpu_emi: wmcpu-reserved@4fc00000 {
no-map;
reg = <0 0x4fc00000 0 0x00100000>;
};
wo_emi0: wo-emi@4fd00000 {
reg = <0 0x4fd00000 0 0x40000>;
no-map;
};
wo_emi1: wo-emi@4fd40000 {
reg = <0 0x4fd40000 0 0x40000>;
no-map;
};
wo_ilm0: wo-ilm@151e0000 {
reg = <0 0x151e0000 0 0x8000>;
no-map;
};
wo_ilm1: wo-ilm@151f0000 {
reg = <0 0x151f0000 0 0x8000>;
no-map;
};
wo_data: wo-data@4fd80000 {
reg = <0 0x4fd80000 0 0x240000>;
no-map;
};
wo_dlm0: wo-dlm@151e8000 {
reg = <0 0x151e8000 0 0x2000>;
no-map;
};
wo_dlm1: wo-dlm@151f8000 {
reg = <0 0x151f8000 0 0x2000>;
no-map;
};
wo_boot: wo-boot@15194000 {
reg = <0 0x15194000 0 0x1000>;
no-map;
};
};
soc {
compatible = "simple-bus";
ranges;
#address-cells = <2>;
#size-cells = <2>;
gic: interrupt-controller@c000000 {
compatible = "arm,gic-v3";
reg = <0 0x0c000000 0 0x10000>, /* GICD */
<0 0x0c080000 0 0x80000>, /* GICR */
<0 0x0c400000 0 0x2000>, /* GICC */
<0 0x0c410000 0 0x1000>, /* GICH */
<0 0x0c420000 0 0x2000>; /* GICV */
interrupt-parent = <&gic>;
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <3>;
};
infracfg: infracfg@10001000 {
compatible = "mediatek,mt7986-infracfg", "syscon";
reg = <0 0x10001000 0 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
wed_pcie: wed-pcie@10003000 {
compatible = "mediatek,mt7986-wed-pcie",
"syscon";
reg = <0 0x10003000 0 0x10>;
};
topckgen: topckgen@1001b000 {
compatible = "mediatek,mt7986-topckgen", "syscon";
reg = <0 0x1001B000 0 0x1000>;
#clock-cells = <1>;
};
watchdog: watchdog@1001c000 {
compatible = "mediatek,mt7986-wdt";
reg = <0 0x1001c000 0 0x1000>;
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
#reset-cells = <1>;
status = "disabled";
};
apmixedsys: apmixedsys@1001e000 {
compatible = "mediatek,mt7986-apmixedsys";
reg = <0 0x1001E000 0 0x1000>;
#clock-cells = <1>;
};
pio: pinctrl@1001f000 {
compatible = "mediatek,mt7986a-pinctrl";
reg = <0 0x1001f000 0 0x1000>,
<0 0x11c30000 0 0x1000>,
<0 0x11c40000 0 0x1000>,
<0 0x11e20000 0 0x1000>,
<0 0x11e30000 0 0x1000>,
<0 0x11f00000 0 0x1000>,
<0 0x11f10000 0 0x1000>,
<0 0x1000b000 0 0x1000>;
reg-names = "gpio", "iocfg_rt", "iocfg_rb", "iocfg_lt",
"iocfg_lb", "iocfg_tr", "iocfg_tl", "eint";
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pio 0 0 100>;
interrupt-controller;
interrupts = <GIC_SPI 225 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
#interrupt-cells = <2>;
};
pwm: pwm@10048000 {
compatible = "mediatek,mt7986-pwm";
reg = <0 0x10048000 0 0x1000>;
#pwm-cells = <2>;
interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&topckgen CLK_TOP_PWM_SEL>,
<&infracfg CLK_INFRA_PWM_STA>,
<&infracfg CLK_INFRA_PWM1_CK>,
<&infracfg CLK_INFRA_PWM2_CK>;
clock-names = "top", "main", "pwm1", "pwm2";
status = "disabled";
};
sgmiisys0: syscon@10060000 {
compatible = "mediatek,mt7986-sgmiisys_0",
"syscon";
reg = <0 0x10060000 0 0x1000>;
#clock-cells = <1>;
};
sgmiisys1: syscon@10070000 {
compatible = "mediatek,mt7986-sgmiisys_1",
"syscon";
reg = <0 0x10070000 0 0x1000>;
#clock-cells = <1>;
};
trng: rng@1020f000 {
compatible = "mediatek,mt7986-rng",
"mediatek,mt7623-rng";
reg = <0 0x1020f000 0 0x100>;
clocks = <&infracfg CLK_INFRA_TRNG_CK>;
clock-names = "rng";
status = "disabled";
};
crypto: crypto@10320000 {
compatible = "inside-secure,safexcel-eip97";
reg = <0 0x10320000 0 0x40000>;
interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "ring0", "ring1", "ring2", "ring3";
clocks = <&infracfg CLK_INFRA_EIP97_CK>;
assigned-clocks = <&topckgen CLK_TOP_EIP_B_SEL>;
assigned-clock-parents = <&apmixedsys CLK_APMIXED_NET2PLL>;
status = "disabled";
};
uart0: serial@11002000 {
compatible = "mediatek,mt7986-uart",
"mediatek,mt6577-uart";
reg = <0 0x11002000 0 0x400>;
interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&infracfg CLK_INFRA_UART0_SEL>,
<&infracfg CLK_INFRA_UART0_CK>;
clock-names = "baud", "bus";
assigned-clocks = <&topckgen CLK_TOP_UART_SEL>,
<&infracfg CLK_INFRA_UART0_SEL>;
assigned-clock-parents = <&topckgen CLK_TOP_XTAL>,
<&topckgen CLK_TOP_UART_SEL>;
status = "disabled";
};
uart1: serial@11003000 {
compatible = "mediatek,mt7986-uart",
"mediatek,mt6577-uart";
reg = <0 0x11003000 0 0x400>;
interrupts = <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&infracfg CLK_INFRA_UART1_SEL>,
<&infracfg CLK_INFRA_UART1_CK>;
clock-names = "baud", "bus";
assigned-clocks = <&infracfg CLK_INFRA_UART1_SEL>;
assigned-clock-parents = <&topckgen CLK_TOP_F26M_SEL>;
status = "disabled";
};
uart2: serial@11004000 {
compatible = "mediatek,mt7986-uart",
"mediatek,mt6577-uart";
reg = <0 0x11004000 0 0x400>;
interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&infracfg CLK_INFRA_UART2_SEL>,
<&infracfg CLK_INFRA_UART2_CK>;
clock-names = "baud", "bus";
assigned-clocks = <&infracfg CLK_INFRA_UART2_SEL>;
assigned-clock-parents = <&topckgen CLK_TOP_F26M_SEL>;
status = "disabled";
};
i2c0: i2c@11008000 {
compatible = "mediatek,mt7986-i2c";
reg = <0 0x11008000 0 0x90>,
<0 0x10217080 0 0x80>;
interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
clock-div = <5>;
clocks = <&infracfg CLK_INFRA_I2C0_CK>,
<&infracfg CLK_INFRA_AP_DMA_CK>;
clock-names = "main", "dma";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
spi0: spi@1100a000 {
compatible = "mediatek,mt7986-spi-ipm", "mediatek,spi-ipm";
reg = <0 0x1100a000 0 0x100>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&topckgen CLK_TOP_MPLL_D2>,
<&topckgen CLK_TOP_SPI_SEL>,
<&infracfg CLK_INFRA_SPI0_CK>,
<&infracfg CLK_INFRA_SPI0_HCK_CK>;
clock-names = "parent-clk", "sel-clk", "spi-clk", "hclk";
status = "disabled";
};
spi1: spi@1100b000 {
compatible = "mediatek,mt7986-spi-ipm", "mediatek,spi-ipm";
reg = <0 0x1100b000 0 0x100>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&topckgen CLK_TOP_MPLL_D2>,
<&topckgen CLK_TOP_SPIM_MST_SEL>,
<&infracfg CLK_INFRA_SPI1_CK>,
<&infracfg CLK_INFRA_SPI1_HCK_CK>;
clock-names = "parent-clk", "sel-clk", "spi-clk", "hclk";
status = "disabled";
};
thermal: thermal@1100c800 {
compatible = "mediatek,mt7986-thermal";
reg = <0 0x1100c800 0 0x800>;
interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&infracfg CLK_INFRA_THERM_CK>,
<&infracfg CLK_INFRA_ADC_26M_CK>;
clock-names = "therm", "auxadc";
nvmem-cells = <&thermal_calibration>;
nvmem-cell-names = "calibration-data";
#thermal-sensor-cells = <1>;
mediatek,auxadc = <&auxadc>;
mediatek,apmixedsys = <&apmixedsys>;
};
auxadc: adc@1100d000 {
compatible = "mediatek,mt7986-auxadc";
reg = <0 0x1100d000 0 0x1000>;
clocks = <&infracfg CLK_INFRA_ADC_26M_CK>;
clock-names = "main";
#io-channel-cells = <1>;
status = "disabled";
};
ssusb: usb@11200000 {
compatible = "mediatek,mt7986-xhci",
"mediatek,mtk-xhci";
reg = <0 0x11200000 0 0x2e00>,
<0 0x11203e00 0 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 173 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&infracfg CLK_INFRA_IUSB_SYS_CK>,
<&infracfg CLK_INFRA_IUSB_CK>,
<&infracfg CLK_INFRA_IUSB_133_CK>,
<&infracfg CLK_INFRA_IUSB_66M_CK>,
<&topckgen CLK_TOP_U2U3_XHCI_SEL>;
clock-names = "sys_ck",
"ref_ck",
"mcu_ck",
"dma_ck",
"xhci_ck";
phys = <&u2port0 PHY_TYPE_USB2>,
<&u3port0 PHY_TYPE_USB3>,
<&u2port1 PHY_TYPE_USB2>;
status = "disabled";
};
mmc0: mmc@11230000 {
compatible = "mediatek,mt7986-mmc";
reg = <0 0x11230000 0 0x1000>,
<0 0x11c20000 0 0x1000>;
interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
assigned-clocks = <&topckgen CLK_TOP_EMMC_416M_SEL>,
<&topckgen CLK_TOP_EMMC_250M_SEL>;
assigned-clock-parents = <&apmixedsys CLK_APMIXED_MPLL>,
<&topckgen CLK_TOP_NET1PLL_D5_D2>;
clocks = <&topckgen CLK_TOP_EMMC_416M_SEL>,
<&infracfg CLK_INFRA_MSDC_HCK_CK>,
<&infracfg CLK_INFRA_MSDC_CK>,
<&infracfg CLK_INFRA_MSDC_133M_CK>,
<&infracfg CLK_INFRA_MSDC_66M_CK>;
clock-names = "source", "hclk", "source_cg", "bus_clk",
"sys_cg";
status = "disabled";
};
pcie: pcie@11280000 {
compatible = "mediatek,mt7986-pcie",
"mediatek,mt8192-pcie";
reg = <0x00 0x11280000 0x00 0x4000>;
reg-names = "pcie-mac";
ranges = <0x82000000 0x00 0x20000000 0x00
0x20000000 0x00 0x10000000>;
device_type = "pci";
#address-cells = <3>;
#size-cells = <2>;
interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
clocks = <&infracfg CLK_INFRA_IPCIE_PIPE_CK>,
<&infracfg CLK_INFRA_IPCIE_CK>,
<&infracfg CLK_INFRA_IPCIER_CK>,
<&infracfg CLK_INFRA_IPCIEB_CK>;
clock-names = "pl_250m", "tl_26m", "peri_26m", "top_133m";
phys = <&pcie_port PHY_TYPE_PCIE>;
phy-names = "pcie-phy";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0x7>;
interrupt-map = <0 0 0 1 &pcie_intc 0>,
<0 0 0 2 &pcie_intc 1>,
<0 0 0 3 &pcie_intc 2>,
<0 0 0 4 &pcie_intc 3>;
status = "disabled";
pcie_intc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
};
};
pcie_phy: t-phy {
compatible = "mediatek,mt7986-tphy",
"mediatek,generic-tphy-v2";
ranges;
#address-cells = <2>;
#size-cells = <2>;
status = "disabled";
pcie_port: pcie-phy@11c00000 {
reg = <0 0x11c00000 0 0x20000>;
clocks = <&clk40m>;
clock-names = "ref";
#phy-cells = <1>;
};
};
efuse: efuse@11d00000 {
compatible = "mediatek,mt7986-efuse", "mediatek,efuse";
reg = <0 0x11d00000 0 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
thermal_calibration: calib@274 {
reg = <0x274 0xc>;
};
};
usb_phy: t-phy@11e10000 {
compatible = "mediatek,mt7986-tphy",
"mediatek,generic-tphy-v2";
ranges = <0 0 0x11e10000 0x1700>;
#address-cells = <1>;
#size-cells = <1>;
status = "disabled";
u2port0: usb-phy@0 {
reg = <0x0 0x700>;
clocks = <&topckgen CLK_TOP_DA_U2_REFSEL>,
<&topckgen CLK_TOP_DA_U2_CK_1P_SEL>;
clock-names = "ref", "da_ref";
#phy-cells = <1>;
};
u3port0: usb-phy@700 {
reg = <0x700 0x900>;
clocks = <&topckgen CLK_TOP_USB3_PHY_SEL>;
clock-names = "ref";
#phy-cells = <1>;
};
u2port1: usb-phy@1000 {
reg = <0x1000 0x700>;
clocks = <&topckgen CLK_TOP_DA_U2_REFSEL>,
<&topckgen CLK_TOP_DA_U2_CK_1P_SEL>;
clock-names = "ref", "da_ref";
#phy-cells = <1>;
};
};
ethsys: syscon@15000000 {
compatible = "mediatek,mt7986-ethsys",
"syscon";
reg = <0 0x15000000 0 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
wed0: wed@15010000 {
compatible = "mediatek,mt7986-wed",
"syscon";
reg = <0 0x15010000 0 0x1000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>;
memory-region = <&wo_emi0>, <&wo_ilm0>, <&wo_dlm0>,
<&wo_data>, <&wo_boot>;
memory-region-names = "wo-emi", "wo-ilm", "wo-dlm",
"wo-data", "wo-boot";
mediatek,wo-ccif = <&wo_ccif0>;
};
wed1: wed@15011000 {
compatible = "mediatek,mt7986-wed",
"syscon";
reg = <0 0x15011000 0 0x1000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>;
memory-region = <&wo_emi1>, <&wo_ilm1>, <&wo_dlm1>,
<&wo_data>, <&wo_boot>;
memory-region-names = "wo-emi", "wo-ilm", "wo-dlm",
"wo-data", "wo-boot";
mediatek,wo-ccif = <&wo_ccif1>;
};
eth: ethernet@15100000 {
compatible = "mediatek,mt7986-eth";
reg = <0 0x15100000 0 0x80000>;
interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 199 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ethsys CLK_ETH_FE_EN>,
<&ethsys CLK_ETH_GP2_EN>,
<&ethsys CLK_ETH_GP1_EN>,
<&ethsys CLK_ETH_WOCPU1_EN>,
<&ethsys CLK_ETH_WOCPU0_EN>,
<&sgmiisys0 CLK_SGMII0_TX250M_EN>,
<&sgmiisys0 CLK_SGMII0_RX250M_EN>,
<&sgmiisys0 CLK_SGMII0_CDR_REF>,
<&sgmiisys0 CLK_SGMII0_CDR_FB>,
<&sgmiisys1 CLK_SGMII1_TX250M_EN>,
<&sgmiisys1 CLK_SGMII1_RX250M_EN>,
<&sgmiisys1 CLK_SGMII1_CDR_REF>,
<&sgmiisys1 CLK_SGMII1_CDR_FB>,
<&topckgen CLK_TOP_NETSYS_SEL>,
<&topckgen CLK_TOP_NETSYS_500M_SEL>;
clock-names = "fe", "gp2", "gp1", "wocpu1", "wocpu0",
"sgmii_tx250m", "sgmii_rx250m",
"sgmii_cdr_ref", "sgmii_cdr_fb",
"sgmii2_tx250m", "sgmii2_rx250m",
"sgmii2_cdr_ref", "sgmii2_cdr_fb",
"netsys0", "netsys1";
assigned-clocks = <&topckgen CLK_TOP_NETSYS_2X_SEL>,
<&topckgen CLK_TOP_SGM_325M_SEL>;
assigned-clock-parents = <&apmixedsys CLK_APMIXED_NET2PLL>,
<&apmixedsys CLK_APMIXED_SGMPLL>;
#address-cells = <1>;
#size-cells = <0>;
mediatek,ethsys = <&ethsys>;
mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
mediatek,wed-pcie = <&wed_pcie>;
mediatek,wed = <&wed0>, <&wed1>;
status = "disabled";
};
wo_ccif0: syscon@151a5000 {
compatible = "mediatek,mt7986-wo-ccif", "syscon";
reg = <0 0x151a5000 0 0x1000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>;
};
wo_ccif1: syscon@151ad000 {
compatible = "mediatek,mt7986-wo-ccif", "syscon";
reg = <0 0x151ad000 0 0x1000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 212 IRQ_TYPE_LEVEL_HIGH>;
};
wifi: wifi@18000000 {
compatible = "mediatek,mt7986-wmac";
reg = <0 0x18000000 0 0x1000000>,
<0 0x10003000 0 0x1000>,
<0 0x11d10000 0 0x1000>;
resets = <&watchdog MT7986_TOPRGU_CONSYS_SW_RST>;
reset-names = "consys";
clocks = <&topckgen CLK_TOP_CONN_MCUSYS_SEL>,
<&topckgen CLK_TOP_AP2CNN_HOST_SEL>;
clock-names = "mcu", "ap2conn";
interrupts = <GIC_SPI 213 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 216 IRQ_TYPE_LEVEL_HIGH>;
memory-region = <&wmcpu_emi>;
};
};
thermal-zones {
cpu_thermal: cpu-thermal {
polling-delay-passive = <1000>;
polling-delay = <1000>;
thermal-sensors = <&thermal 0>;
trips {
cpu_trip_crit: crit {
temperature = <125000>;
hysteresis = <2000>;
type = "critical";
};
cpu_trip_hot: hot {
temperature = <120000>;
hysteresis = <2000>;
type = "hot";
};
cpu_trip_active_high: active-high {
temperature = <115000>;
hysteresis = <2000>;
type = "active";
};
cpu_trip_active_med: active-med {
temperature = <85000>;
hysteresis = <2000>;
type = "active";
};
cpu_trip_active_low: active-low {
temperature = <60000>;
hysteresis = <2000>;
type = "active";
};
};
};
};
timer {
compatible = "arm,armv8-timer";
interrupt-parent = <&gic>;
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
};
};

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More