Compare commits

...
213 Commits
Author SHA1 Message Date
kurogeek d233798113 default_thai_company: bump to 6dd557e 2026-11-04 10:01:26 +00:00
kurogeek 8eb4f40a36 bosona: repoint ERPNext site from tempoerp to b4lerp.newedge.house 2026-11-03 17:03:14 +07:00
kurogeek f121a3ec30 stellio: drop mt7530 get_stats64 kernel patch, in 6.18.58
Upstream 07d9958739 was backported to linux-6.18.y as af98611ad109
(2026-10-15) and is in the 6.18.58 nixpkgs now builds for stellio.
Applying the patch on top fails: patch(1) rejects all 10 hunks as
already applied, so the kernel build has been broken since the nixpkgs
bump brought 6.18.58.
2026-11-03 06:39:35 +00:00
kurogeek 570cc7c74a router: pin stellio mgmt ipv6SubnetId to 1
Restores the mgmt subnet id the dropped mkForce used, via cnx-network's
new per-VLAN option instead of overriding networkd config. Still skipped
on AIS's bare /64; unlike the VLAN-id default (10) it also fits a /61-/63
delegation.
2026-11-03 06:34:17 +00:00
kurogeek e66589a654 stellio: drop IPv6 workarounds now upstream in cnx-network
Both interim blocks are in cnx-network b2b5daa:

- 27c697a adds vlans.<name>.ipv6SubnetId (lan -> 0, others -> VLAN id),
  replacing the SubnetId=auto race the mkForce pins worked around. mgmt
  moves from 0x1 to 0xA; on AIS's bare /64 both are skipped alike.
- 45643d6 is the router-radv-deprecate unit this file carried a copy
  of. Keeping both concatenated the two scripts into one unit running
  two ip-monitor loops.
2026-11-03 06:24:48 +00:00
kurogeek bb3bdc9e2b flake.lock: bump cnx-network to b2b5daa
b2749dc -> b2b5daa (rev 203 -> 497). Pulls in cnx-network's new
hermes-agent input (uv2nix, pyproject-nix, ...) and its nixos-mailserver
pin d357b9f -> b3e735a.

All nixosConfigurations, checks, packages and the devShell evaluate.
2026-11-03 06:24:48 +00:00
kurogeek 68475ab2ba vars: update via generator data-mesher-node-identity (machine: vega) 2026-11-02 16:43:55 +07:00
kurogeek c0c83bee1c vars: update via generator data-mesher-node-identity (machine: tangra) 2026-11-02 16:43:55 +07:00
kurogeek 7ecb9d0a2b vars: update via generator data-mesher-node-identity (machine: stellio) 2026-11-02 16:43:55 +07:00
kurogeek 7b505fdcfa vars: update via generator data-mesher-node-identity (machine: sirius) 2026-11-02 16:43:55 +07:00
kurogeek f13f513c1d vars: update via generator data-mesher-node-identity (machine: rigel) 2026-11-02 16:43:55 +07:00
kurogeek c9b8541224 vars: update via generator data-mesher-node-identity (machine: rana) 2026-11-02 16:43:55 +07:00
kurogeek 4bb3fe1efe vars: update via generator data-mesher-node-identity (machine: ramus) 2026-11-02 16:43:55 +07:00
kurogeek caeaafe664 vars: update via generator data-mesher-node-identity (machine: procyon) 2026-11-02 16:43:55 +07:00
kurogeek 548558da9e vars: update via generator data-mesher-node-identity (machine: petra) 2026-11-02 16:43:55 +07:00
kurogeek e2bc58b7ee vars: update via generator data-mesher-node-identity (machine: neptune) 2026-11-02 16:43:55 +07:00
kurogeek 4c5248669f vars: update via generator data-mesher-node-identity (machine: mirach) 2026-11-02 16:43:55 +07:00
kurogeek 5b973c3e51 vars: update via generator data-mesher-node-identity (machine: hadar) 2026-11-02 16:43:55 +07:00
kurogeek 9a4da3ae26 vars: update via generator data-mesher-node-identity (machine: deneb) 2026-11-02 16:43:55 +07:00
kurogeek 56e397286d vars: update via generator data-mesher-node-identity (machine: cursa) 2026-11-02 16:43:55 +07:00
kurogeek 87ca5e8349 vars: update via generator data-mesher-node-identity (machine: canopus) 2026-11-02 16:43:55 +07:00
kurogeek 5247afa512 vars: update via generator data-mesher-node-identity (machine: buna) 2026-11-02 16:43:55 +07:00
kurogeek c9869083f5 vars: update via generator data-mesher-node-identity (machine: bosona) 2026-11-02 16:43:55 +07:00
kurogeek 50a01ba7fc vars: update via generator data-mesher-node-identity (machine: alpheratz) 2026-11-02 16:43:55 +07:00
kurogeek b3523208f7 vars: update via generator data-mesher-node-identity (machine: almach) 2026-11-02 16:43:55 +07:00
kurogeek c9c967f111 vars: update via generator data-mesher-node-identity (machine: alasia) 2026-11-02 16:43:55 +07:00
kurogeek 58f7189702 vars: update via generator data-mesher-node-identity (machine: adhil) 2026-11-02 16:43:55 +07:00
kurogeek 7cdbd12835 inventory/data-mesher: sign node identity certs for 10 years
All data-mesher-node-identity certs were signed on 2026-07-22 with the
default 90-day validity and expired on 2026-10-20, taking the whole mesh
down ("identity certificate not signed by network certificate authority"
is actually an expiry failure). Nothing rotates them automatically.

Requires `clan vars generate --generator data-mesher-node-identity
--regenerate` and a redeploy to take effect.
2026-11-02 16:43:55 +07:00
kurogeek 7abdbfec85 inventory/borgbackup: fix ssh key path for cursa, hadar, procyon, bosona, canopus, deneb
The sops vars backend deploys generator files at
/run/secrets/vars/per-machine/<machine>/<generator>/<file>; the hard-coded
rsh still pointed at /run/secrets/vars/borgbackup/borgbackup.ssh, so
borgbackup-job-alex could not find its key. Align with alasia's entry.
2026-10-27 06:15:06 +00:00
kurogeek 63614d824a inventory.json: update install time of alasia 2026-10-27 12:01:54 +07:00
kurogeek cc7cb5aae9 machines/alasia/facter.json: update hardware configuration 2026-10-27 12:01:53 +07:00
kurogeek 2f36a34a95 gtcm-file-uploader: bump to deadb17 (1.1.6)
upload-large-file untouched upstream; rev pinned in lockstep.
2026-10-27 02:20:53 +00:00
kurogeek 85d1591f05 think-gtcm, think-backend-gtcm: bump to deadb17 (1.1.6)
composer.lock unchanged; vendorHash moves with the version (embedded in
vendor/composer/installed.*).
2026-10-27 02:20:53 +00:00
kurogeek 9f76f7ddc2 gtcm-file-uploader: bump to c274e6d (1.1.5)
upload-large-file untouched upstream; rev pinned in lockstep.
2026-10-27 02:04:04 +00:00
kurogeek ee3059a8d7 think-gtcm, think-backend-gtcm: bump to c274e6d (1.1.5)
composer.lock unchanged; vendorHash moves anyway because the vendor FOD
embeds the root package version (vendor/composer/installed.*).
2026-10-27 02:04:04 +00:00
kurogeek 37496b974e modules/prometheus: run alertmanager as a static user
nixpkgs' alertmanager uses DynamicUser, so the `alertmanager` user only
exists while the service is running. The matrix-alertmanager-urlfile
secret is owned by that user; at boot sops-install-secrets cannot resolve
it and rejects the whole manifest, leaving /run/secrets empty (no ssh
host key, yggdrasil key, tor key, ...). Deploys masked it because the
dynamic user was resolvable via nss-systemd at switch time.

Create a static alertmanager user/group and disable DynamicUser.
2026-10-26 09:27:42 +00:00
kurogeek 452bd5f686 flake.lock: bump frappix to dbfa76d
frappe v15.113.3 -> v15.120.1, erpnext v15.115.0 -> v15.121.2,
insights v3.0.24 -> v3.12.6.
2026-10-26 07:54:10 +00:00
kurogeek d8193f0652 bosona, canopus, deneb: apply frappix fixups overlay 2026-10-26 07:54:10 +00:00
kurogeek ac2b1873b9 overlays: add frappix fixups for nixos-unstable (sql_metadata, rq_1)
Building the frappix hosts on nixos-unstable 3ec6363 fails in frappix's
python overlay:

- sql_metadata 2.12.0 pins sqlparse<0.6, nixpkgs ships 0.6.0. frappe
  itself needs 0.6 (MAX_GROUPING_TOKENS), and sql_metadata 3.x needs
  sqlglot >= 30 which nixpkgs lacks, so relax the pin. frappe only feeds it
  SELECT statements; the only regressions on 0.6 are CREATE TABLE IF NOT
  EXISTS name extraction.
- rq 1.16.2 test_parse_connection fails against redis-py 8.1 (extra
  himport_registry kwarg). Test-only; disable it.

nix flake check does not catch these: it only evaluates hosts.
2026-10-26 07:54:10 +00:00
kurogeek 466dcba07c gtcm-file-uploader: bump to fdb71d6 (1.1.4) 2026-10-26 07:09:32 +00:00
kurogeek 136daea65d think-gtcm, think-backend-gtcm: bump to fdb71d6 (1.1.4)
composer.lock unchanged; vendorHash moved with the php/composer bump in
nixos-unstable 3ec6363.
2026-10-26 07:09:32 +00:00
kurogeek 835373fa06 flake.lock: bump clan-core to 9486215
Also moves clan-core/{disko,nix-darwin,sops-nix}.
2026-10-26 06:53:54 +00:00
kurogeek d63c87d0bf ramus, tangra, flake, overlays: drop php backport (nixpkgs#576853 landed)
nixos-unstable 3ec6363 ships php83 8.3.36 / php85 8.5.12; the overlay
was warning that it had become a no-op.
2026-10-26 06:53:54 +00:00
kurogeek 477af55f8c flake: switch nixpkgs to nixos-unstable, bump to 3ec6363
nixos-unstable dropped nixVersions.nix_2_31, which clan-core still pins
for clan-cli (pkgs/clan-cli/flake-module.nix); override it to pkgs.nix in
the devshell.
2026-10-26 06:53:45 +00:00
kurogeek c909e483b8 bosona, canopus, deneb: apply posthog workaround overlay 2026-10-26 06:53:24 +00:00
kurogeek 70be800f77 overlays: work around nixpkgs#578265 (posthog broken with mcp 2)
nixpkgs#575004 bumped mcp 1.29 -> 2.2 and marked python3Packages.posthog
broken; only one optional MCP test fails. frappe needs posthog at runtime.
Skip that test and clear meta.broken until the upstream unbreak lands;
the overlay warns at eval time once it does.
2026-10-26 06:53:24 +00:00
kurogeek e04805f889 gtcm-file-uploader: bump to 225578a (1.1.3) 2026-10-23 09:31:23 +00:00
kurogeek 2b69d0d7c4 think-gtcm, think-backend-gtcm: bump to 225578a (1.1.3) 2026-10-23 09:31:23 +00:00
kurogeek 3612bb2d22 ramus, tangra, flake: apply php backport overlay
Covers every PHP consumer: think/think-backend (php83) on ramus,
wordpress pools (pkgs.php) on tangra, and the perSystem think packages.
2026-10-23 02:30:28 +00:00
kurogeek d13c64f314 overlays: backport nixpkgs#576853 (php 8.3.36/8.4.27/8.5.12)
Temporary overlay until the PR reaches nixpkgs-unstable. Fixes
CVE-2026-109618 and CVE-2026-109619. Becomes a no-op and warns at eval
time once the locked nixpkgs ships these versions; delete it then.
2026-10-23 02:30:27 +00:00
kurogeek 32b1d76dbf flake.lock: bump clan-core to a566329
Fixes devShell build on nixpkgs 5adc508: makeWrapper no longer word-splits
makeWrapperArgs, so clan-cli's "--unset LD_LIBRARY_PATH" single-string
args failed with "makeWrapper doesn't understand the arg". Upstream
clan-core main splits the tokens.
2026-10-22 11:12:07 +00:00
kurogeek 51d7b7eef0 Revert "personal-computer: vendor ipp-usb module with fixed avahi options"
This reverts commit d41fa2210a.
2026-10-22 17:32:15 +07:00
kurogeek a48f1c75c5 personal-computer: vendor ipp-usb module with fixed avahi options
nixpkgs' services/printing/ipp-usb.nix still sets the obsolete
services.avahi.publish.{enable,userServices}, warning on every machine
that enables ipp-usb. Disable the upstream module and carry a copy that
uses services.avahi.settings.publish; only that block differs.
2026-10-22 17:32:15 +07:00
kurogeek 8769b83d42 prometheus: write README
manifest.readme read an empty file, triggering a missing-readme warning.
2026-10-22 17:32:15 +07:00
kurogeek d19cd6c51e emmie: fix hunspell and firefox deprecation warnings
hunspellWithDicts -> hunspell.withDicts; pin programs.firefox.configPath
to the legacy .mozilla/firefox so the profile stays put when stateVersion
crosses 26.05.
2026-10-22 17:32:15 +07:00
kurogeek 862d46b157 samba: migrate obsolete services.avahi.publish options
Use services.avahi.settings.publish as nixpkgs now expects.
2026-10-22 17:32:15 +07:00
kurogeek 7d3dc6c1ee think-gtcm: pin to php83
Default php is now 8.5; composer.lock pins nette/* to php <= 8.4.
Matches think-backend-gtcm and the runtime module.
2026-10-22 17:32:15 +07:00
kurogeek 57e03a0557 emmie: remove wasabiwallet
Removed from nixpkgs (vendored ancient openssl, unmaintained).
2026-10-22 17:32:15 +07:00
kurogeek 47ce38ab4b stellio: drop image.repart.enable shim
nixpkgs declares the option again.
2026-10-22 17:32:15 +07:00
kurogeek e1e4687168 nextcloud: create admin user via occ
nixpkgs removed services.nextcloud.config.{adminuser,adminpassFile} and
now installs with --disable-admin-user. Add a nextcloud-admin-user oneshot
that creates the admin account from the vars secret after nextcloud-setup.
2026-10-22 17:32:15 +07:00
kurogeek 78d4960301 flake.lock: bump nixpkgs to 5adc508, clan-core to 86d72f3
clan-core bumped alongside: the previous rev used lib.licenses.apsl20,
which nixpkgs renamed to apple-psl20.
2026-10-22 17:32:15 +07:00
kurogeek ea9529aef6 gtcm-file-uploader: bump to 895092d (1.1.2) 2026-10-22 09:45:30 +00:00
kurogeek 9a2f3e1ada think-gtcm, think-backend-gtcm: bump to 895092d (1.1.2) 2026-10-22 09:45:30 +00:00
kurogeek a460f345d8 gtcm-file-uploader: bump to 421ed1f (1.1.1) 2026-10-21 09:45:39 +00:00
kurogeek e4133ad242 think-gtcm, think-backend-gtcm: bump to 421ed1f (1.1.1) 2026-10-21 09:45:39 +00:00
kurogeek 759e3ec61b default_thai_company: bump to c8fb983 2026-10-21 03:42:43 +00:00
kurogeek b99cb381c4 gtcm-file-uploader: bump to b67934a (1.1.0) 2026-10-20 08:38:30 +00:00
kurogeek 5540557413 think-gtcm, think-backend-gtcm: bump to b67934a (1.1.0) 2026-10-20 08:33:59 +00:00
kurogeek 50d923442a gtcm-file-uploader: bump to 0658ed2 2026-10-20 03:48:00 +00:00
kurogeek 243c66e0ab think-gtcm, think-backend-gtcm: bump to 0658ed2 2026-10-19 10:57:43 +00:00
kurogeek 72a17fea6e yggdrasil: replace dead public peers, add Asia/US peers
ygg.jjolly.dev:3443 has been offline 21 weeks and
[2602:fc24:18:7a42::1]:993 is unreachable (dropped from the public
peer list); every node was retrying both. Add measured-reachable
peers near the Asian (SG/JP/HK) and US nodes so tree parents are no
longer forced through two US-Midwest relays.
2026-10-09 09:35:15 +00:00
kurogeek 81f4e393e3 vars: update via generator zerotier-ip-mirach-b4l-network (shared) 2026-10-09 14:45:19 +07:00
kurogeek ff88a93280 vars: update via generator zerotier-ip-alpheratz-b4l-network (shared) 2026-10-09 14:44:45 +07:00
kurogeek 9abafc50aa vars: update via generator zerotier-ip-almach-b4l-network (shared) 2026-10-09 14:44:45 +07:00
kurogeek 08f87d630a vars: update via generator zerotier-identity-mirach (shared) 2026-10-09 14:44:44 +07:00
kurogeek b848f709fa vars: update via generator zerotier-ip-adhil-b4l-network (shared) 2026-10-09 14:44:44 +07:00
kurogeek 5174234e95 vars: update via generator zerotier-identity-mirach (shared) 2026-10-09 14:44:44 +07:00
kurogeek 3dcc71de4b vars: update via generator zerotier-identity-buna (shared) 2026-10-09 14:44:44 +07:00
kurogeek 04e1fc2f8e vars: update via generator zerotier-identity-alpheratz (shared) 2026-10-09 14:44:43 +07:00
kurogeek 6350dff658 vars: update via generator zerotier-identity-buna (shared) 2026-10-09 14:44:43 +07:00
kurogeek 5bad8fba97 vars: update via generator zerotier-identity-almach (shared) 2026-10-09 14:44:42 +07:00
kurogeek 9a861fa294 vars: update via generator zerotier-identity-alpheratz (shared) 2026-10-09 14:44:42 +07:00
kurogeek 4536b97366 vars: update via generator zerotier-identity-adhil (shared) 2026-10-09 14:44:42 +07:00
kurogeek 2d13588f77 vars: update via generator zerotier-identity-almach (shared) 2026-10-09 14:44:41 +07:00
kurogeek 46ad94c4b9 vars: update via generator zerotier-identity-adhil (shared) 2026-10-09 14:44:40 +07:00
kurogeek c915103634 phonebox: open port 80 on the yggdrasil interface so the ATA web UI proxy is reachable over ygg too 2026-10-09 07:02:35 +00:00
kurogeek 597f0df6b3 phonebox: move hello-world test extension from 999 to 99 so it is reachable cross-node (e.g. 2099 → rigel) 2026-10-09 03:27:17 +00:00
kurogeek 7f3457859e machines/stellio: stop deprecating a PD prefix the ISP re-delegates (sync with cnx-network 45643d6)
The ISP modem restart at 08:19 re-delegated the same /64 five seconds
after the PPPoE drop. networkd re-announced it at once, but the fixed
7 s radvd run kept sending preferred-lifetime-0 RAs for it, so clients
sat on a deprecated prefix until networkd's next periodic RA and IPv6
stayed broken for minutes.

Send one RA per radvd start, up to three 3 s apart, and skip each one if
the /64 is already back on the interface. Verified on stellio with the
rendered script: a prefix gone for good -> three RAs; the PD address
removed and re-added 4 s later -> two RAs, then "is back, leaving it
announced", client preferred again and pinging over v6.
2026-10-08 08:36:59 +00:00
kurogeek 31ebf2d081 machines/stellio: deprecate withdrawn DHCPv6-PD prefixes (interim copy of cnx-network fa11327)
After the reboot vlan-lan got its /64 at boot, but IPv6 still failed for
clients: the first PPPoE session lands on another AIS BNG, is torn down
minutes later, and the reconnect brings a different /64 (07:53
2405:9800:b911:91e3 -> 07:57 b910:1f5d). networkd announces the new
prefix at once but never deprecates the old one (systemd#29651), so
clients keep it preferred for up to 30 minutes and source selection
sends every connection from a dead address.

router-radv-deprecate.service watches vlan-* for networkd removing a
dynamic address and sends RAs with that /64 at preferred lifetime 0 via
a short-lived radvd (SIGKILLed: no shutdown RA). Identical script to the
upstream commit; drop once the cnx-network input is bumped past it.

Verified on stellio: the stale prefix flipped to deprecated on the
client at the first RA and pings resumed; a synthetic dynamic address
removed from vlan-lan produced the same RA end to end.
2026-10-08 08:13:23 +00:00
kurogeek b5077983ea machines/stellio: give the AIS /64 to vlan-lan, not whichever VLAN came up first
Clients on stellio's lan had no global IPv6. AIS delegates a single /64
over PPPoE; cnx-network's router service carves the VLAN prefixes with
SubnetId=auto, so vlan-mgmt took the whole prefix and vlan-lan logged
"Couldn't find a suitable prefix. Ran out of address space" -- no RA
prefix on lan.

Pin SubnetId: 0x0 on vlan-lan (the whole /64), 0x1 on vlan-mgmt (out of
range on a /64, harmless; works unchanged if AIS ever delegates wider).
Verified live via runtime drop-ins: vlan-lan got 2405:9800:b910:1f5d::/64
and a LAN client pinged 2606:4700:4700::1111 over v6.

Drop once cnx-network carries the per-VLAN `ipv6SubnetId` option
(cnx-network-clan commit 27c697a) and the input is bumped.
2026-10-08 07:54:39 +00:00
kurogeek 0a87664afd default_thai_company: bump version 2026-10-05 15:49:34 +07:00
kurogeek 7469976750 stellio: allow ssh over yggdrasil
The router service only opens SSH to the mgmt VLAN, the staging port
and the ZeroTier mesh. Also admit it on the yggdrasil interface (`ygg`,
as named by clan-core's service) so the box stays reachable when
ZeroTier is down. Interface-scoped, so a spoofed 200::/7 source on the
WAN never matches.
2026-10-02 07:53:47 +00:00
kurogeek c95f74be25 default_thai_company: e7d0a54 -> c6e6c12
Default Standard Sales Order as Sales Order default print format;
position line under signatures on print formats.
2026-10-01 04:51:37 +00:00
kurogeek 2e56bfc54c default_thai_company: 68f0817 -> e7d0a54
Picks up print formats for Purchase Invoice/Order and Sales Order,
Withholding Tax Certificate print format and button, DRAFT watermark
on unsubmitted certificates, Type of Income Paid on Tax Withholding
Category.
2026-10-01 04:19:20 +00:00
kurogeek b4c197e64c w-inventory: allow phone to access 2026-09-30 13:56:52 +07:00
kurogeek 0dc7595f43 poy-inventory -> w-inventory 2026-09-29 18:25:07 +07:00
kurogeek 92a3676b83 remove break 2026-09-29 15:42:11 +07:00
kurogeek 6039edfac6 phonebox: stop dnsmasq trying to write leases to /dev/null
dhcp-leasefile=/dev/null logs 'failed to write /dev/null: Invalid argument'
every lease; leasefile-ro is the supported way to run without a lease file.
2026-09-29 07:46:41 +00:00
kurogeek 5066d4ee46 phonebox: arm hardware watchdog and auto-reboot on panic
mirach and almach have each hung at least twice since July with nothing in
the journal or pstore, and stayed down until someone power-cycled the board.
RuntimeWatchdogSec was 0 and kernel.panic was 0, so a hung kernel/PID1 or a
panic could never self-recover.

Arm the systemd hardware watchdog (30s) and reboot 10s after a panic on all
phonebox nodes. Watchdog resets will show up as extra entries in
journalctl --list-boots.
2026-09-29 07:46:41 +00:00
kurogeek a80202e419 flake.lock: cnx-network (ppp0 KeepConfiguration=yes) 2026-09-28 15:17:32 +07:00
kurogeek 9b0368edc6 machines/stellio: backport mt7530 .get_stats64 atomic-context fix
The MT7531 on the Flint 2 is MDIO-attached and shares the SoC MDIO bus
with the 2.5G WAN PHY. On 6.18.49 mt7530_get_stats64 takes the MDIO
mutex under the RCU read lock that /proc/net/dev readers hold; when the
WAN PHY is renegotiating (PPPoE flap) the mutex is contended, the read
sleeps in atomic context and the box oopses:

  BUG: scheduling while atomic: zerotier-one/1748/0x00000002
  mt7530_get_stats64 -> dsa_user_get_stats64 -> dev_seq_show
  Unable to handle kernel paging request at 0000afa547cef5f0

Vendor upstream 07d99587396024932e02474c3a5bede71d108454 (mainline
2026-05-04, absent from linux-6.18.y). Applies to v6.18.49 with one
fuzz-1 hunk (6.18 still exports mt7530_switch_ops).
2026-09-24 09:21:59 +00:00
kurogeek d525e63050 machines/stellio: persist the journal
Storage=volatile left nothing behind for the 2026-09-23/24 reboots: the
last persisted entry is journald stopping during the 2026-09-22 switch,
and /sys/fs/pstore is empty too. Keep the eMMC-wear intent with a 256M
cap and a 1 min sync interval matching the ext4 commit=60.
2026-09-24 09:21:46 +00:00
kurogeek 9b8d15d583 machines/stellio: a new whitehouse router that uses clanService/router from cnx-network 2026-09-23 15:31:57 +07:00
kurogeek fa95098b7e frappix: bump default_thai_company to 68f0817 (main) 2026-09-17 10:55:54 +00:00
kurogeek 0906700ad6 deneb: add default_thai_company frappe app to poyerp
Package git.b4l.co.th/newedge/default_thai_company as a frappix app
    (pkgs/frappix/default-thai-company.nix) and expose it through a new
    frappixAppsOverlay that extends the pkgs.frappix scope. Install it on
    the poyerp.newedge.house site on deneb.
2026-09-15 16:18:30 +07:00
kurogeek b9302e6192 clanService/prometheus: add mirach and almach 2026-09-11 10:53:00 +07:00
kurogeek 5cb262857e inventory/wifi: retry autoconnect and auth indefinitely
Set connection.autoconnect-retries=0 and connection.auth-retries=0 on the
NetworkManager profiles generated by the clan wifi module for buna, so a
flaky AP or slow auth never leaves the connection permanently blocked.
2026-09-09 04:15:18 +00:00
kurogeek 47e536540f flake: bump nixpkgs to fix broken prettier build
prettier 3.9.6 in nixpkgs e8be781 failed to build (stale pnpm lockfile in
binding-wasm32-wasi), breaking nix fmt. Fixed upstream in 58973d7.
2026-09-08 08:35:21 +00:00
kurogeek 282a8f8287 clanService/personal-computer: supportedFilesystems ntfs 2026-09-04 14:06:30 +07:00
kurogeek a9254c424d machines/sirius: buildPlatform.system = x86_64-linux 2026-09-04 13:43:04 +07:00
kurogeek 5961254878 machines/sirius: rm common module 2026-09-04 13:42:10 +07:00
kurogeek 9403ddbf08 clanService/headscale: adjust magic dns settings 2026-09-03 17:17:36 +07:00
kurogeek 37ff958560 inventory/auto-pull-update: limited to selected machines until a build machine is present 2026-09-03 13:58:29 +07:00
kurogeek efa4b39782 libreoffice-fresh -> libreoffice-stable 2026-09-03 12:22:15 +07:00
kurogeek e2341b7c79 inputs/nixpkgs,home-manager,clan-core,frappix, bump version 2026-09-03 12:21:00 +07:00
kurogeek dc1c94796a machines/sirius: reduce write on disk 2026-09-02 11:34:40 +07:00
kurogeek 5b07b98f40 inventory/prometheus: add buna to monitored machines 2026-08-21 12:03:34 +07:00
kurogeek 88ac5cbd83 machines/buna: use wifi 2026-08-19 14:34:34 +07:00
kurogeek 969c006f97 machines/deneb: support Thai characters 2026-08-16 14:02:41 +07:00
kurogeek 1c7d0bb388 hm/emmie: librewolf -> firefox 2026-08-05 09:07:15 +07:00
kurogeek 84c3bd92bb hm/emmie: enable librewolf 2026-08-03 13:51:12 +07:00
kurogeek 3340731c8d flake/packages/installer: init 2026-07-30 17:55:41 +07:00
kurogeek cd979fb771 inventory/borgbackup: init 2026-07-29 12:38:12 +07:00
kurogeek 7f6abc1d04 clanService/apple-network: init 2026-07-23 17:33:04 +07:00
kurogeek 0946de0e46 vars: update prometheus/matrix-alertmanager-token for machine cursa 2026-07-23 15:12:16 +07:00
kurogeek a29e4611b2 inventory/auto-pull-update: init 2026-07-23 14:12:34 +07:00
kurogeek 7246ab1437 vars: secret rotation 2026-07-21 18:15:45 +07:00
kurogeek f8193425af drop vi 2026-07-20 10:31:42 +07:00
kurogeek 3c54e8d6ef inventory/internet: add tangra machine 2026-07-17 15:41:59 +07:00
kurogeek 4a7d5340f3 inventory/internet: add ramus machine 2026-07-17 15:41:44 +07:00
kurogeek 458265f96b inventory/yggdrasil-global-network: allow kurogeek laptop as extra ip 2026-07-17 15:01:09 +07:00
kurogeek 702ef6ab86 inventory/yggdrasil-global-network: enable on all machines 2026-07-17 14:55:06 +07:00
kurogeek ea4e2f03a7 nix fmt 2026-07-16 16:34:46 +07:00
kurogeek e738692558 clanService/phonebox: allow multi-clients to exist on one node 2026-07-16 16:25:51 +07:00
kurogeek ef698f8ad3 inventory/prometheus: add sirius to monitor machines 2026-07-13 10:28:47 +07:00
kurogeek 824b099ad6 Merge pull request 'clanService/wordpress: allow mutable at wp-content/ai1wm-backups' (#2) from mooyai/infra:mooyai-patch-1 into main
Reviewed-on: #2
2026-07-08 16:41:27 +07:00
mooyai 651240f5a1 clanService/wordpress: symlink upgrade-temp-backup and ai1wm-backups to state dir
WordPress writes into wp-content/upgrade-temp-backup (core rollback on
theme/plugin updates) and wp-content/ai1wm-backups (All-in-One WP
Migration). Both sit directly under wp-content, which is the read-only
Nix store path, so creation fails.

Symlink both dirs out to the writable state dir and add matching
tmpfiles rules, same pattern as themes/plugins/upgrade.

Fixes "Could not create the upgrade-temp-backup directory" on Bricks
theme update, and the ai1wm-backups permission errors.
2026-07-08 16:18:17 +07:00
kurogeek 3b8c11b096 inputs/frappix: bump version 2026-07-02 11:19:35 +07:00
kurogeek 982c6c23ca machines/deneb: frappe add posprinter app 2026-07-01 17:05:40 +07:00
kurogeek e55bbaaa6b inputs/frappix: bump version 2026-07-01 17:05:11 +07:00
kurogeek 53c98dcba8 Merge pull request 'Update modules/clan/wordpress/default.nix' (#1) from mooyai/infra:main into main
Reviewed-on: #1
2026-06-23 16:59:11 +07:00
mooyai ce5f4ff43f Update modules/clan/wordpress/default.nix
make dir to support plugin update temp dir
2026-06-23 16:44:52 +07:00
kurogeek dbb3e55cad init cursa as a prometheus server, vega and rigel are monitored 2026-06-23 15:28:35 +07:00
kurogeek d09f67a757 clanService/prometheus: init monitoring system 2026-06-23 15:27:31 +07:00
kurogeek 77b487a709 clancore bumped, zerotier migration 2026-06-16 15:01:21 +07:00
kurogeek 2b239eb162 hm/emmie: use hunspell dictionary with systemd user session variables, libreoffice -> libreoffice-qt 2026-06-16 11:20:05 +07:00
kurogeek 8e64e88d8f inputs: bump home-manager 2026-06-16 11:18:34 +07:00
kurogeek 6276d9aee0 inputs: bump nixpkgs, frappix. migrate inventree 2026-06-15 16:36:18 +07:00
kurogeek 9471d1a4e6 machines/bosona: nix fmt 2026-06-15 15:55:04 +07:00
kurogeek 77d8e42ec2 hm/emmie: add hunspellDicts en and th 2026-06-15 13:28:58 +07:00
kurogeek 0dafb8cd52 clanService/wordpress: nginx clientMaxBodySize is 128m 2026-06-12 12:22:27 +07:00
kurogeek 8b12656149 clanService/wordpress: phpfpm options and wp-config.php are configurable from inventory interface 2026-06-12 12:09:56 +07:00
kurogeek d622040d30 hm/emmie: rm DnsOverHttpsMode and DnsOverHttpsTemplates 2026-06-11 13:54:17 +07:00
kurogeek 2bc05c2d6d clanService/wordpress: test vars 2026-06-10 18:59:30 +07:00
kurogeek 5fa8444112 clanService/wordpress: security.acme.acceptTerms = true and nginx is in acme group 2026-06-10 18:59:01 +07:00
kurogeek 8874b33a5d greaterchiangmai: nodejs_20 -> nodejs 2026-06-10 18:56:48 +07:00
kurogeek 521ccdc886 clanService/personal-computer: rm services.displayManager.gdm.wayland 2026-06-10 18:53:46 +07:00
kurogeek 07b648db9a clanService/headscale: magic_dns is disabled 2026-06-10 15:11:26 +07:00
kurogeek da6be4946f machines/tangra: poyfestival.com website 2026-06-01 10:42:03 +07:00
kurogeek bc16c72707 machines/canopus: localhost -> sitename 2026-05-25 14:20:44 +07:00
kurogeek 3a26403590 inventory/tags: add alasia to b4l and global-network tags 2026-05-20 14:05:29 +07:00
kurogeek 5681a65c6f Update vars via generator zerotier (machine: alasia) 2026-05-20 12:49:31 +07:00
kurogeek 175087e9fd Update vars via generator zerotier (machine: alasia) 2026-05-20 12:49:17 +07:00
kurogeek 0b82ffa913 Update vars via generator yggdrasil (machine: alasia) 2026-05-20 12:48:43 +07:00
kurogeek 5d216dd56f Update vars via generator yggdrasil (machine: alasia) 2026-05-20 12:48:27 +07:00
kurogeek 48ea7cb6ea machines/deneb: unauthorized dbe44c0287 and e3d6559697 2026-05-20 10:45:13 +07:00
kurogeek 0f79022907 machines/bosona: ERPNext machine for Tempo 2026-05-19 17:11:55 +07:00
kurogeek c15ac6fca5 inputs/frappix: bump version 2026-05-19 14:41:52 +07:00
kurogeek e9ae808a7d machines/deneb: frappe add module crm 2026-05-18 12:22:27 +07:00
kurogeek 8e085211f7 machines/alasia: headscale server 2026-05-15 18:00:31 +07:00
kurogeek 47c4f3143e rm b4l 2026-05-12 17:21:37 +07:00
kurogeek f1e914b927 Revert "rm b4l"
This reverts commit 06f1f990f0.
2026-05-12 17:16:23 +07:00
kurogeek 210a3101de specify boot.zfs.forceImportRoot 2026-05-12 16:49:03 +07:00
kurogeek 06f1f990f0 rm b4l 2026-05-12 16:48:30 +07:00
kurogeek 781e70287d inputs: bump nixpkgs, frappix, treefmt-nix 2026-05-12 16:46:07 +07:00
kurogeek c030b045a2 routers/whitehouse-2: init whitehouse-2 as an AP 2026-05-07 10:52:05 +07:00
kurogeek 6a29794575 vars/canopus: rm zerotier/zerotier-network-id 2026-05-06 16:32:12 +07:00
kurogeek da9ad2523c Update vars via generator zerotier (machine: canopus) 2026-05-06 15:47:51 +07:00
kurogeek 052082d631 revert: clan-core and zerotier update 2026-05-06 15:42:44 +07:00
kurogeek 1aab77c302 machines/deneb: localhost -> poyerp.newedge.house 2026-05-04 16:59:31 +07:00
kurogeek f146eaf0a5 machines/deneb: allow Alex gateway 2026-05-04 16:59:02 +07:00
kurogeek 0aabd4f527 inventory/poy-network: add 2026-05-04 15:05:09 +07:00
kurogeek a22582502b vars: update via generator zerotier (machine: deneb) 2026-05-04 14:29:27 +07:00
kurogeek 0c7c791f19 vars: update via generator zerotier (machine: canopus) 2026-05-04 14:13:25 +07:00
kurogeek 87a1ebba2c machines/canopus: rm netbird service, disconnect from the internet 2026-05-04 11:42:40 +07:00
kurogeek bc04896b63 machines/all: machine description 2026-05-01 11:13:53 +07:00
kurogeek 1d823afe58 inputs/frappix: bump version 2026-05-01 09:58:26 +07:00
kurogeek 898f9f9320 machines/canopus: netbird service 2026-04-30 17:10:19 +07:00
kurogeek a89a98d233 vars: update via generator netbird (machine: canopus) 2026-04-30 15:47:46 +07:00
kurogeek 1923c096aa inputs/clan-core: bump clan-core 2026-04-28 15:59:32 +07:00
kurogeek 1e72ccb391 vars: update via generator zerotier (machine: vega) 2026-04-28 14:50:57 +07:00
kurogeek cbcc9e74c0 vars: update via generator zerotier (machine: sirius) 2026-04-28 14:50:56 +07:00
kurogeek 83e366a2b8 vars: update via generator zerotier (machine: rigel) 2026-04-28 14:50:56 +07:00
kurogeek 85333e6a3e vars: update via generator zerotier (machine: rana) 2026-04-28 14:50:55 +07:00
kurogeek 6af195a3fd vars: update via generator zerotier (machine: ramus) 2026-04-28 14:50:55 +07:00
kurogeek 85497d5777 vars: update via generator zerotier (machine: petra) 2026-04-28 14:50:53 +07:00
kurogeek 753e264c89 vars: update via generator zerotier (machine: neptune) 2026-04-28 14:50:52 +07:00
kurogeek 10a5a2265c vars: update via generator zerotier-controller (shared) 2026-04-28 14:50:51 +07:00
kurogeek 29a09c53a6 machines/b4l: nextcloud32 -> nextcloud33 2026-04-28 13:56:54 +07:00
kurogeek c608c2f856 clanService/admin: migrated due to deprecated 2026-04-28 13:44:37 +07:00
kurogeek 86b7d4bda5 inputs/liminix: bump version 2026-04-27 14:01:23 +07:00
kurogeek e573bfd98a inventree services are running on procyon and hadar machines 2026-04-24 11:35:50 +07:00
kurogeek 94901da15e test.useContainers = false for all tests 2026-04-23 18:08:35 +07:00
kurogeek ecbd5e00a6 erpnext services are running on deneb and canopus machines 2026-04-23 15:26:18 +07:00
kurogeek d5b70b050c inventory/emmie: add syncthing path 2026-04-21 09:29:31 +07:00
kurogeek 79eeb2a491 machines/sirius: add monitoring system 2026-04-11 15:19:03 +07:00
kurogeek f5930ae9e6 flake/inputs: bump liminix 2026-04-06 10:15:20 +07:00
kurogeek aa3db27c77 machines/rana: hardcode kernel to version 6.12 to fix freezing issue 2026-04-02 17:53:37 +07:00
kurogeek 44210e6bbb machines/rana: boot.kernelParams adjusted 2026-04-02 17:16:30 +07:00
kurogeek 984d587d00 machines/rana: update facter.json 2026-04-02 11:20:24 +07:00
kurogeek 79be24d581 machines/rana: fixed randomly freeze 2026-04-01 10:49:48 +07:00
kurogeek 1518cd7db6 inputs/nixpkgs: bump version 2026-03-31 13:45:49 +07:00
kurogeek ac7117c13d clanService/personal-computer: added more drivers 2026-03-31 13:45:23 +07:00
1410 changed files with 35478 additions and 4660 deletions

No files matched your search

+1
View File
@@ -3,3 +3,4 @@
result result
result-* result-*
run-vm-* run-vm-*
.nixos-test-history
Generated
+1128 -34
View File
File diff suppressed because it is too large. Load diff
+50 -1
View File
@@ -7,6 +7,12 @@
inputs.treefmt-nix.follows = "treefmt-nix"; inputs.treefmt-nix.follows = "treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
clan-community = {
url = "git+https://git.clan.lol/clan/clan-community";
inputs.clan-core.follows = "clan-core";
inputs.flake-parts.follows = "flake-parts";
inputs.treefmt-nix.follows = "treefmt-nix";
};
devshell = { devshell = {
url = "github:numtide/devshell"; url = "github:numtide/devshell";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -25,7 +31,7 @@
inputs.home-manager.follows = "home-manager"; inputs.home-manager.follows = "home-manager";
}; };
import-tree.url = "github:vic/import-tree"; import-tree.url = "github:vic/import-tree";
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
treefmt-nix = { treefmt-nix = {
url = "github:numtide/treefmt-nix"; url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -34,6 +40,34 @@
url = "git+https://gti.telent.net/dan/liminix"; url = "git+https://gti.telent.net/dan/liminix";
flake = false; flake = false;
}; };
frappix = {
url = "github:kurogeek/frappix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.devshell.follows = "devshell";
};
nixos-images = {
url = "github:nix-community/nixos-images";
inputs.nixos-unstable.follows = "nixpkgs";
inputs.nixos-stable.follows = "nixpkgs";
};
matthew-hardware = {
url = "git+https://codeberg.org/matthewcroughan/matthew-hardware?ref=mc/wip";
inputs.flake-parts.follows = "flake-parts";
inputs.nixpkgs.follows = "nixpkgs";
inputs.disko.follows = "clan-core/disko";
};
# `router` clan service (site gateway: PPPoE, VLAN bridge, Kea/Blocky,
# nftables). Consumed via `module.input = "cnx-network"` in inventories/router.nix.
cnx-network = {
url = "git+https://git.b4l.co.th/B4L/cnx-network-clan";
inputs.clan-core.follows = "clan-core";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-parts.follows = "flake-parts";
inputs.treefmt-nix.follows = "treefmt-nix";
};
}; };
outputs = outputs =
{ {
@@ -50,6 +84,7 @@
./fmt.nix ./fmt.nix
./shell.nix ./shell.nix
./overlays
./machines ./machines
./routers ./routers
./inventories ./inventories
@@ -71,6 +106,20 @@
packages.think = pkgs.think-gtcm; packages.think = pkgs.think-gtcm;
packages.think-be = pkgs.think-backend-gtcm; packages.think-be = pkgs.think-backend-gtcm;
packages.file-uploader = pkgs.gtcm-file-uploader; packages.file-uploader = pkgs.gtcm-file-uploader;
packages.installer =
(pkgs.nixos [
inputs.nixos-images.nixosModules.image-installer
{
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek"
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOJDRQfb1+7VK5tOe8W40iryfBWYRO6Uf1r2viDjmsJtAAAABHNzaDo="
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDgsWq+G/tcr6eUQYT7+sJeBtRmOMabgFiIgIV44XNc6AAAABHNzaDo="
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJMi3TAuwDtIeO4MsORlBZ31HzaV5bji1fFBPcC9/tWuAAAABHNzaDo="
];
}
]).config.system.build.isoImage;
}; };
} }
); );
+376 -91
View File
@@ -2,6 +2,7 @@
imports = [ imports = [
./personal-computer.nix ./personal-computer.nix
./emmie.nix ./emmie.nix
./router.nix
]; ];
clan = { clan = {
inventory = { inventory = {
@@ -10,16 +11,23 @@
glom = [ glom = [
"vega" "vega"
"ramus" "ramus"
"canopus"
];
poy = [
"deneb"
"bosona"
];
w = [
"sirius"
"stellio"
]; ];
w = [ "sirius" ];
b4l = [ b4l = [
"rigel" "rigel"
"neptune"
"rana" "rana"
"petra" "petra"
"alasia"
]; ];
phonebox = [ phonebox = [
"neptune"
"rigel" "rigel"
"almach" "almach"
"alpheratz" "alpheratz"
@@ -27,32 +35,196 @@
"adhil" "adhil"
"buna" "buna"
]; ];
global-network = [
prometheus = [
"cursa"
"rigel"
"vega"
"buna"
];
dm-bootstrapper = [
"rigel"
"cursa"
"deneb"
"bosona"
"canopus"
];
dm-pull-deploy = [
"rana" "rana"
"sirius" "rigel"
"vega"
]; ];
}; };
instances = { instances = {
admin = {
borgbackup = {
module = { module = {
name = "admin"; name = "borgbackup";
input = "clan-core";
};
roles.client.machines."cursa".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/cursa/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/cursa/borgbackup/borgbackup.ssh";
};
};
roles.client.machines."hadar".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/hadar/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/hadar/borgbackup/borgbackup.ssh";
};
};
roles.client.machines."procyon".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/procyon/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/procyon/borgbackup/borgbackup.ssh";
};
};
roles.client.machines."bosona".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/bosona/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/bosona/borgbackup/borgbackup.ssh";
};
};
roles.client.machines."canopus".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/canopus/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/canopus/borgbackup/borgbackup.ssh";
};
};
roles.client.machines."deneb".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/deneb/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/deneb/borgbackup/borgbackup.ssh";
};
};
roles.client.machines."alasia".settings.destinations = {
alex = {
repo = "ssh://borg@10.0.10.225:2222/backup/alasia/backup";
rsh = "ssh -i /run/secrets/vars/per-machine/alasia/borgbackup/borgbackup.ssh";
};
};
};
data-mesher = {
module = {
name = "data-mesher";
input = "clan-core";
};
roles.bootstrap.tags = [ "dm-bootstrapper" ];
roles.default.tags = [ "all" ];
roles.default.settings.interfaces = [ "ygg" ];
# default 2160h (90d) expired cluster-wide on 2026-10-20; node certs
# are only re-signed when `clan vars generate --regenerate` is run.
roles.default.settings.certificateValidity = "87600h";
};
auto-pull-update = {
module = {
name = "dm-pull-deploy";
input = "clan-community";
};
roles.push.machines."rigel".settings = {
gitUrl = "https://git.b4l.co.th/newedge/infra.git";
branch = "main";
};
roles.push.extraModules = [
(
{ pkgs, config, ... }:
{
# work around until upstream is fixed
environment.systemPackages = [
(pkgs.writeShellApplication {
name = "custom-dm-send-deploy";
runtimeInputs = [
config.services.data-mesher.package
pkgs.git
pkgs.nix
pkgs.jq
];
text =
let
settings.gitUrl = "https://git.b4l.co.th/newedge/infra.git";
settings.branch = "main";
in
''
if [ $# -gt 1 ]; then
echo "Usage: dm-send-deploy [<flake-ref>]"
echo "Without arguments, sends the latest commit on '${settings.branch}' from ${settings.gitUrl}"
echo "Example: dm-send-deploy git+https://example.com/repo.git?rev=abc123..."
exit 1
fi
KEY="${config.clan.core.vars.generators.dm-pull-deploy-signing-key.files."signing.key".path}"
if [ ! -r "$KEY" ]; then
echo "Error: cannot read signing key at $KEY (are you root?)"
exit 1
fi
if [ $# -eq 1 ]; then
FLAKE_REF="$1"
else
REV=$(git ls-remote "${settings.gitUrl}" "refs/heads/${settings.branch}" | cut -f1)
if [ -z "$REV" ]; then
echo "Error: could not determine latest commit on ${settings.branch} from ${settings.gitUrl}"
exit 1
fi
FLAKE_REF="git+${settings.gitUrl}?rev=$REV"
fi
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
printf '%s' "$FLAKE_REF" > "$TMPFILE"
NETWORK_ID="${config.clan.core.vars.generators.data-mesher-network.files."network.pub".path}"
data-mesher file update "$TMPFILE" \
--url http://localhost:7331 \
--network-id "$NETWORK_ID" \
--key "$KEY" \
--name "dm_pull_deploy/target"
echo "Deployment target pushed: $FLAKE_REF"
'';
})
];
}
)
];
roles.default.tags = [ "dm-pull-deploy" ];
roles.default.settings.action = "switch";
};
sshd = {
roles.server.tags."all" = { };
roles.server.settings = {
authorizedKeys = {
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f";
"davhau" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk";
"kurogeek" =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
"matthewcroughan" =
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOJDRQfb1+7VK5tOe8W40iryfBWYRO6Uf1r2viDjmsJtAAAABHNzaDo=";
"matthewcroughan-1" =
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDgsWq+G/tcr6eUQYT7+sJeBtRmOMabgFiIgIV44XNc6AAAABHNzaDo=";
"matthewcroughan-2" =
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJMi3TAuwDtIeO4MsORlBZ31HzaV5bji1fFBPcC9/tWuAAAABHNzaDo=";
};
};
};
root-password = {
module = {
name = "users";
input = "clan-core"; input = "clan-core";
}; };
roles.default.tags."all" = { }; roles.default.tags."all" = { };
roles.default.settings.allowedKeys = { roles.default.settings = {
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f"; user = "root";
"davhau" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk";
"vi" =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAmgyEGuY/r7SDlJgrzYmQqpcWS5W+fCzRi3OS59ne4W openpgp:0xFF687387";
"kurogeek" =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
"matthewcroughan" =
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOJDRQfb1+7VK5tOe8W40iryfBWYRO6Uf1r2viDjmsJtAAAABHNzaDo=";
"matthewcroughan-1" =
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDgsWq+G/tcr6eUQYT7+sJeBtRmOMabgFiIgIV44XNc6AAAABHNzaDo=";
"matthewcroughan-2" =
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJMi3TAuwDtIeO4MsORlBZ31HzaV5bji1fFBPcC9/tWuAAAABHNzaDo=";
}; };
}; };
@@ -83,7 +255,13 @@
name = "zerotier"; name = "zerotier";
input = "clan-core"; input = "clan-core";
}; };
roles.controller.machines."vega" = { }; roles.controller.machines."vega" = {
settings.allowedIds = [
"dbe44c0287" # Alex-gateway
"b0e0b84fd3" # Alex
"2bd36db8cc" # kurogeek-thinkpad
];
};
roles.peer.tags.glom = { }; roles.peer.tags.glom = { };
}; };
@@ -92,36 +270,93 @@
name = "zerotier"; name = "zerotier";
input = "clan-core"; input = "clan-core";
}; };
roles.controller.machines."rigel" = { }; roles.controller.machines."rigel" = {
settings.allowedIds = [
"dbe44c0287" # Alex-gateway
"b0e0b84fd3" # Alex
"2bd36db8cc" # kurogeek-thinkpad
];
};
roles.peer.tags.b4l = { }; roles.peer.tags.b4l = { };
}; };
poy-network = {
module = {
name = "zerotier";
input = "clan-core";
};
roles.controller.machines."deneb" = {
settings.allowedIps = [
#kurogeek
"fdfe:7bf:a795:4524:4c99:932b:d36d:b8cc"
];
};
roles.peer.tags."poy" = { };
};
internet = {
module.name = "internet";
roles.default.machines = {
ramus.settings.host = "5.223.63.55";
tangra.settings.host = "5.223.65.50";
};
};
yggdrasil-global-network = { yggdrasil-global-network = {
module = { module = {
name = "yggdrasil"; name = "yggdrasil";
input = "clan-core"; input = "clan-core";
}; };
roles.default.tags."global-network" = { }; roles.default.tags."all" = { };
roles.default.settings.extraYggdrasilIPs = [
# kurogeek's laptop
"200:c8db:ea9b:5bdc:44ed:ad87:462a:6bd0"
];
roles.default.settings.extraPeers = [ roles.default.settings.extraPeers = [
"tls://ygg.jjolly.dev:3443" "tls://133.18.201.69:54232"
"tls://[2602:fc24:18:7a42::1]:993" "tls://153.120.42.137:54232"
"tcp://yg-sin.magicum.net:23901"
"tls://asia.deinfra.org:15015"
"tls://ygg5.mk16.de:1338?key=0000009611ae5391dc0aceea9f3fa6a0dc1279f4306059339e84bfb8b74d2f9b"
"tcp://leo.node.3dt.net:9002" "tcp://leo.node.3dt.net:9002"
"tcp://ygg-kcmo.incognet.io:8883" "tcp://ygg-kcmo.incognet.io:8883"
"tls://23.184.48.86:1443"
"tls://192.3.13.16:8118"
"tls://ygg-pa.incognet.io:8884"
"tls://mo.us.ygg.triplebit.org:993"
]; ];
}; };
yggdrasil-phone-network = { newedge-headscale = {
module = { module = {
name = "yggdrasil"; name = "headscale";
input = "clan-core"; input = "self";
};
roles.server.machines."alasia".settings = {
public_url = "tailvpn.public.newedge.house";
advertise_routes = [ "10.0.10.0/24" ];
dns = {
magic_dns = true;
base_domain = "tailvpn.newedge.house";
nameservers = [
"10.0.10.82"
"1.1.1.1"
"8.8.8.8"
];
extra_records = [
{
name = "poyerp.newedge.house";
type = "A";
value = "10.0.10.1";
}
{
name = "glomerp.newedge.house";
type = "A";
value = "10.0.10.1";
}
];
};
}; };
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 = { phonebox = {
@@ -133,64 +368,15 @@
roles.default.machines."adhil".settings = { roles.default.machines."adhil".settings = {
ata-ethernet-iface = "end0"; ata-ethernet-iface = "end0";
}; };
roles.default.machines."rigel".settings = {
extraClientNumbers = [
"01"
"02"
];
extraFixedIPClient = { };
};
}; };
pocket-id = {
module = {
name = "pocket-id";
input = "self";
};
roles.default.machines.b4l = { };
};
nextcloud = {
module = {
name = "nextcloud";
input = "self";
};
roles.default.machines.b4l = { };
};
stirling-pdf = {
module = {
name = "stirling-pdf";
input = "self";
};
roles.default.machines.b4l = { };
};
actual-budget = {
module = {
name = "actual-budget";
input = "self";
};
roles.default.machines.b4l = { };
};
victoriametrics = {
module = {
name = "victoriametrics";
input = "self";
};
roles.default.machines.b4l = { };
};
vikunja = {
module = {
name = "vikunja";
input = "self";
};
roles.default.machines.b4l = { };
};
grafana = {
module = {
name = "grafana";
input = "self";
};
roles.default.machines.b4l = { };
};
paperless = {
module = {
name = "paperless";
input = "self";
};
roles.default.machines.b4l = { };
};
pulse-stream = { pulse-stream = {
module = { module = {
name = "pulse-stream"; name = "pulse-stream";
@@ -283,6 +469,105 @@
dataDir = "/mnt/hdd/samba"; dataDir = "/mnt/hdd/samba";
}; };
}; };
wordpress = {
module = {
name = "wordpress";
input = "self";
};
roles.server.machines."tangra".settings = {
tenants = [
"poyfestival.com"
];
phpfpmOptions = ''
upload_max_filesize=64M
post_max_size=128M
'';
wpExtraConfig = ''
define('WP_MEMORY_LIMIT', '256M');
define('WP_DEBUG', false);
define('WP_DEBUG_DISPLAY', false);
define('WP_DEBUG_LOG', false);
'';
};
};
prometheus-monitoring = {
module = {
name = "prometheus";
input = "self";
};
roles.server.machines."cursa".settings = {
matrix-alertmanager = {
enable = true;
homeserverUrl = "https://matrix-client.matrix.org";
matrixUser = "@nixapollo:matrix.org";
matrixRooms = [
{
receivers = [
"matrix"
];
roomId = "!rqIrWqPvsXqMgYpcNZ:matrix.org";
}
];
};
};
roles.nodes.machines = {
vega.settings = {
exporters.smartctl = { };
exporters.zfs = { };
};
rigel.settings = {
exporters.smartctl = { };
};
sirius.settings = {
exporters.smartctl = { };
exporters.zfs = { };
};
buna.settings = {
exporters.smartctl = { };
};
mirach.settings = {
exporters.smartctl = { };
};
almach.settings = {
exporters.smartctl = { };
};
stellio.settings.exporters.smartctl = { };
};
};
wifi =
let
networks = {
home = { };
glom = { };
};
in
{
module.name = "wifi";
module.input = "clan-community";
roles.default = {
machines."buna".settings = { inherit networks; };
extraModules = [
(
{ lib, ... }:
{
# profile names match the network attr names above;
# 0 = retry forever for both (defaults: 4 autoconnect attempts, 3 auth attempts)
networking.networkmanager.ensureProfiles.profiles = lib.mapAttrs (_: _: {
connection.autoconnect-retries = 0;
connection.auth-retries = 0;
}) networks;
}
)
];
};
};
}; };
}; };
}; };
+2 -3
View File
@@ -18,9 +18,8 @@ in
roles.peer.tags.emmie = { }; roles.peer.tags.emmie = { };
roles.peer.settings = { roles.peer.settings = {
folders = { folders = {
Syncthing = { Syncthing.path = "${userhome}/Share/Syncthing";
path = "${userhome}/Share/Syncthing"; Desktop.path = "${userhome}/Desktop";
};
}; };
extraDevices = { extraDevices = {
pixel7a = { pixel7a = {
+97
View File
@@ -0,0 +1,97 @@
# Site gateways: the `router` clan service from cnx-network-clan (see
# flake.nix input `cnx-network`, and that repo's modules/clan/router/README.md).
# One instance for the fleet; each site adds its gateway machine with the
# site's port / VLAN layout. `clan vars generate <gw>` then prompts for the
# PPPoE username/password.
{ lib, ... }:
let
# Admin mesh = the site's ZeroTier network. The service only opens SSH,
# metrics and iperf3 to this prefix, so the gateway must also be a peer of
# the same network (tag membership in default.nix). Prefix derivation as in
# cnx-network's modules/mesh-hosts.nix: fd + 8-byte network id + 0x9993,
# /88 covers every peer.
meshSubnet =
instance:
let
networkId = lib.fileContents ../vars/shared/zerotier-network-${instance}/network-id/value;
full = "fd" + networkId + "9993";
hextet = i: builtins.substring (i * 4) 4 full;
in
"${hextet 0}:${hextet 1}:${hextet 2}:${hextet 3}:${hextet 4}:${builtins.substring 20 2 full}00::/88";
in
{
clan.inventory.instances.router = {
module = {
name = "router";
input = "cnx-network";
};
roles.default.settings.mesh.subnet = meshSubnet "w-network";
# stellio: GL.iNet Flint 2 (GL-MT6000, MT7986a). Ports as named by the
# device tree in matthew-hardware: eth1 = the 2.5G "WAN" port, lan1 = the
# 2.5G LAN port, lan2-lan5 = the 1G LAN ports (DSA on the MT7531 switch).
roles.default.machines.stellio.settings = {
site = "white-house";
siteId = 8;
# PPPoE on the ISP's VLAN 10, as on the liminix router it replaces
# (routers/white-house); set to null if the ISP runs PPPoE untagged.
wan.interface = "end1";
wan.vlanId = 10;
# lan1 (2.5G): untagged mgmt port for on-site recovery. lan2-lan4 (1G):
# untagged client ports on lan. lan5 is the pre-cutover staging uplink:
# DHCP client into the old LAN (routers/white-house), SSH only, no VLAN
# membership; set stagingPort = null at cutover.
accessPorts = {
lan1 = "mgmt";
lan2 = "lan";
lan3 = "lan";
lan4 = "lan";
};
stagingPort = "lan5";
# Both MT7976 radios serve lan as an untagged access port, one SSID per
# band named like the GL-AR750 it stands in for ("GL-AR750" on 2.4 GHz,
# "GL-AR750-5" on 5 GHz) so clients roam over unchanged. Passphrases
# are the `wifi-GL-AR750-passphrase` / `wifi-GL-AR750-5-passphrase`
# vars prompts. Channels as on the old router. Check the band mapping
# with `iw phy` (mt7986: phy0/wlan0 = 2.4 GHz, phy1/wlan1 = 5 GHz).
wifi = {
enable = true;
countryCode = "TH";
networks = {
"GL-AR750".vlan = "lan";
"GL-AR750-5".vlan = "lan";
};
radios = {
wlan0 = {
band = "2g";
channel = 2;
wifi6 = true;
networks = [ "GL-AR750" ];
};
wlan1 = {
band = "5g";
channel = 36;
wifi6 = true;
networks = [ "GL-AR750-5" ];
};
};
};
# 10.1.<id>.0/24, router .1, DHCP pool .100-.199.
# ipv6SubnetId: which /64 of the DHCPv6-PD prefix the VLAN announces.
# lan defaults to 0 (whole prefix on AIS's bare /64). mgmt would default
# to its VLAN id (10); keep it at 1 so it also fits a /61-/63 delegation.
vlans = {
mgmt = {
id = 10;
ipv6SubnetId = 1;
};
lan.id = 20;
};
};
};
}
+25 -1
View File
@@ -19,7 +19,7 @@
"installedAt": 1765277591 "installedAt": 1765277591
}, },
"buna": { "buna": {
"installedAt": 1765343708 "installedAt": 1787123510
}, },
"rana": { "rana": {
"installedAt": 1773134236 "installedAt": 1773134236
@@ -29,6 +29,30 @@
}, },
"sirius": { "sirius": {
"installedAt": 1770974584 "installedAt": 1770974584
},
"deneb": {
"installedAt": 1775718970
},
"canopus": {
"installedAt": 1775793532
},
"hadar": {
"installedAt": 1774427255
},
"procyon": {
"installedAt": 1775458442
},
"alasia": {
"installedAt": 1793077313
},
"bosona": {
"installedAt": 1779098893
},
"tangra": {
"installedAt": 1779958921
},
"cursa": {
"installedAt": 1782187627
} }
} }
} }
+2 -1
View File
@@ -8,6 +8,7 @@
# clan.core.networking.targetHost = "root@"; # clan.core.networking.targetHost = "root@";
clan.core.settings.name = "adhil"; clan.core.settings.name = "adhil";
# clan.meta.description = "Raspberry Pi 4 SBC board for one of w phone network. (With w office)"; clan.core.settings.machine.description =
"Raspberry Pi 4 SBC board for one of w phone network. (With w office)";
} }
+15
View File
@@ -0,0 +1,15 @@
{
clan.core.settings.machine.description = "VM machine to host headplane instance";
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
networking.firewall.allowedTCPPorts = [
80
443
];
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = false;
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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -8,6 +8,6 @@
# clan.core.networking.targetHost = "root@"; # clan.core.networking.targetHost = "root@";
clan.core.settings.name = "almach"; clan.core.settings.name = "almach";
# clan.meta.description = "Radxa X4 SBC board for one of w phone network."; clan.core.settings.machine.description = "Radxa X4 SBC board for one of w phone network.";
} }
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+1 -1
View File
@@ -8,6 +8,6 @@
# clan.core.networking.targetHost = "root@"; # clan.core.networking.targetHost = "root@";
clan.core.settings.name = "alpheratz"; clan.core.settings.name = "alpheratz";
# clan.meta.description = "Radxa X4 SBC board for one of w phone network."; clan.core.settings.machine.description = "Radxa X4 SBC board for one of w phone network.";
} }
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
-36
View File
@@ -1,36 +0,0 @@
{ config, ... }:
let
abDomain = "${config.clan.core.vars.generators.b4l-actual-budget.files.subdomain.value}.${config.networking.fqdn}";
in
{
clan.core.vars.generators.b4l-actual-budget = {
files.subdomain.secret = false;
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Actual Budget app. Default:(budget)";
};
};
script = ''cat $prompts/subdomain || echo -n "budget" > $out/subdomain'';
};
services.actual = {
settings = {
allowedLoginMethods = [
"password"
"openid"
];
trustedProxies = [ "127.0.0.1" ];
};
};
services.nginx.virtualHosts."${abDomain}" = {
useACMEHost = "${config.networking.fqdn}";
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.actual.settings.port}";
};
};
}
-79
View File
@@ -1,79 +0,0 @@
{
pkgs,
config,
lib,
...
}:
with lib;
let
serviceName = "${config.networking.hostName}-grafana";
gfDomain = "${
config.clan.core.vars.generators."${serviceName}".files.subdomain.value
}.${config.networking.fqdn}";
settingsFormatIni = pkgs.formats.ini {
listToValue = concatMapStringsSep " " (generators.mkValueStringDefault { });
mkKeyValue = generators.mkKeyValueDefault {
mkValueString = v: if v == null then "" else generators.mkValueStringDefault { } v;
} "=";
};
configFile = settingsFormatIni.generate "config.ini" config.services.grafana.settings;
in
{
clan.core.vars.generators."${serviceName}" = {
files = {
adminpassword.secret = true;
subdomain.secret = false;
};
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Grafana. Default:(grafana)";
};
adminpassword = {
persist = true;
type = "hidden";
description = "Password for the admin user. Leave empty to auto-generate.";
};
};
runtimeInputs = [
pkgs.xkcdpass
pkgs.coreutils
];
script = ''
prompt_domain=$(cat "$prompts"/subdomain)
if [[ -n "''${prompt_domain-}" ]]; then
echo $prompt_domain | tr -d "\n" > "$out"/subdomain
else
echo -n "grafana" > "$out"/subdomain
fi
prompt_password=$(cat "$prompts"/adminpassword)
if [[ -n "''${prompt_password-}" ]]; then
echo "$prompt_password" | tr -d "\n" > "$out"/adminpassword
else
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminpassword
fi
'';
};
systemd.services.grafana.serviceConfig.ExecStartPre = [
"+${pkgs.writeShellScript "grafana-set-password" ''
${pkgs.grafana}/bin/grafana cli --homepath ${config.services.grafana.dataDir} --config ${configFile} admin reset-admin-password $(cat ${
config.clan.core.vars.generators."${serviceName}".files.adminpassword.path
})
''}"
];
services.nginx.virtualHosts."${gfDomain}" = {
forceSSL = true;
useACMEHost = "${config.networking.fqdn}";
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.grafana.settings.server.http_port}";
};
};
}
-35
View File
@@ -1,35 +0,0 @@
{ config, pkgs, ... }:
let
ncDomain = "${config.clan.core.vars.generators.b4l-nextcloud.files.subdomain.value}.${config.networking.fqdn}";
in
{
clan.core.vars.generators.b4l-nextcloud = {
files.subdomain.secret = false;
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Nextcloud app. Default:(cloud)";
};
};
script = ''cat $prompts/subdomain || echo -n "cloud" > $out/subdomain'';
};
services.nextcloud = {
hostName = ncDomain;
package = pkgs.nextcloud32;
settings = {
overwriteprotocol = "https";
trusted_domains = [ ];
trusted_proxies = [ ];
};
};
services.nginx.virtualHosts."${ncDomain}" = {
useACMEHost = "${config.networking.fqdn}";
forceSSL = true;
};
}
-67
View File
@@ -1,67 +0,0 @@
{ config, pkgs, ... }:
let
serviceName = "${config.networking.hostName}-paperless";
domain-name = "${
config.clan.core.vars.generators."${serviceName}".files.subdomain.value
}.${config.networking.fqdn}";
in
{
clan.core.vars.generators."${serviceName}" = {
files = {
subdomain.secret = false;
adminpassword = {
secret = true;
owner = config.services.paperless.user;
group = config.services.paperless.user;
};
};
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Paperless. Default:(paperless)";
};
adminpassword = {
persist = true;
type = "hidden";
description = "Password for the admin user. Leave empty to auto-generate.";
};
};
runtimeInputs = [
pkgs.xkcdpass
pkgs.coreutils
];
script = ''
prompt_domain=$(cat "$prompts"/subdomain)
if [[ -n "''${prompt_domain-}" ]]; then
echo $prompt_domain | tr -d "\n" > "$out"/subdomain
else
echo -n "paperless" > "$out"/subdomain
fi
prompt_password=$(cat "$prompts"/adminpassword)
if [[ -n "''${prompt_password-}" ]]; then
echo "$prompt_password" | tr -d "\n" > "$out"/adminpassword
else
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminpassword
fi
'';
};
environment.systemPackages = [ pkgs.toybox ];
services.paperless = {
passwordFile = config.clan.core.vars.generators."${serviceName}".files.adminpassword.path;
};
services.nginx.virtualHosts."${domain-name}" = {
forceSSL = true;
useACMEHost = "${config.networking.fqdn}";
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.paperless.port}";
};
};
}
-35
View File
@@ -1,35 +0,0 @@
{ config, ... }:
let
pidDomain = "${config.clan.core.vars.generators.b4l-pocket-id.files.subdomain.value}.${config.networking.fqdn}";
in
{
clan.core.vars.generators.b4l-pocket-id = {
files.subdomain.secret = false;
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Pocket-ID app. Default:(auth)";
};
};
script = ''cat $prompts/subdomain || echo -n "auth" > $out/subdomain'';
};
services.pocket-id = {
settings = {
APP_ENV = "production";
APP_URL = "https://${pidDomain}";
TRUST_PROXY = true;
};
};
services.nginx.virtualHosts."${pidDomain}" = {
useACMEHost = "${config.networking.fqdn}";
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.pocket-id.settings.PORT}";
};
};
}
-26
View File
@@ -1,26 +0,0 @@
{ config, ... }:
let
stDomain = "${config.clan.core.vars.generators.b4l-stirling-pdf.files.subdomain.value}.${config.networking.fqdn}";
in
{
clan.core.vars.generators.b4l-stirling-pdf = {
files.subdomain.secret = false;
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Stirling PDF app. Default:(pdf)";
};
};
script = ''cat $prompts/subdomain || echo -n "pdf" > $out/subdomain'';
};
services.nginx.virtualHosts."${stDomain}" = {
forceSSL = true;
useACMEHost = "${config.networking.fqdn}";
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.stirling-pdf.environment.SERVER_PORT}";
};
};
}
-73
View File
@@ -1,73 +0,0 @@
{ config, pkgs, ... }:
let
vmDomain = "${config.clan.core.vars.generators.b4l-victoriametrics.files.subdomain.value}.${config.networking.fqdn}";
in
{
clan.core.vars.generators.b4l-victoriametrics = {
files.subdomain.secret = false;
files.adminuser.secret = false;
files.adminpassword.secret = true;
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Victoria Metrics app. Default:(metrics)";
};
adminuser = {
persist = true;
type = "line";
description = "Username for an admin user. Default:(admin)";
};
adminpassword = {
persist = true;
type = "hidden";
description = "Password for the admin user. Leave empty to auto-generate.";
};
};
runtimeInputs = [
pkgs.xkcdpass
pkgs.coreutils
];
script = ''
prompt_domain=$(cat "$prompts"/subdomain)
if [[ -n "''${prompt_domain-}" ]]; then
echo $prompt_domain | tr -d "\n" > "$out"/subdomain
else
echo -n "metrics" > "$out"/subdomain
fi
prompt_adminuser=$(cat "$prompts"/adminuser)
if [[ -n "''${prompt_adminuser-}" ]]; then
echo $prompt_adminuser | tr -d "\n" > "$out"/adminuser
else
echo -n "admin" > "$out"/adminuser
fi
prompt_password=$(cat "$prompts"/adminpassword)
if [[ -n "''${prompt_password-}" ]]; then
echo "$prompt_password" | tr -d "\n" > "$out"/adminpassword
else
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminpassword
fi
'';
};
services.victoriametrics = {
extraOptions = [
"-httpAuth.username=file://${config.clan.core.vars.generators.b4l-victoriametrics.files.adminuser.path}"
"-httpAuth.password=file://${config.clan.core.vars.generators.b4l-victoriametrics.files.adminpassword.path}"
];
};
services.nginx.virtualHosts."${vmDomain}" = {
forceSSL = true;
useACMEHost = "${config.networking.fqdn}";
locations."/" = {
proxyPass = "http://localhost${builtins.toString config.services.victoriametrics.listenAddress}";
};
};
}
-26
View File
@@ -1,26 +0,0 @@
{ config, ... }:
{
clan.core.vars.generators.b4l-vikunja = {
files.subdomain.secret = false;
prompts = {
subdomain = {
persist = true;
type = "line";
description = "Sub-domain for Vikunja todo app. Default:(todo)";
};
};
script = ''cat $prompts/subdomain || echo "todo" > $out/subdomain'';
};
services.vikunja = {
frontendHostname = "${config.clan.core.vars.generators.b4l-vikunja.files.subdomain.value}.${config.networking.fqdn}";
};
services.nginx.virtualHosts."${config.services.vikunja.frontendHostname}" = {
useACMEHost = "${config.networking.fqdn}";
forceSSL = true;
locations."/" = {
proxyPass = "${config.services.vikunja.frontendScheme}://${config.services.vikunja.frontendHostname}:${builtins.toString config.services.vikunja.port}";
};
};
}
+108
View File
@@ -0,0 +1,108 @@
{
inputs,
pkgs,
config,
...
}:
let
sitename = "b4lerp.newedge.house";
in
{
clan.core.settings.machine.description = "VM machine that host ERPNext for Buildforlife";
imports = [
inputs.frappix.nixosModules.x86_64-linux.frappix
];
nixpkgs.overlays = [
inputs.self.overlays.frappixFrappeOverlay
inputs.self.overlays.frappixLibsOverlay
inputs.self.overlays.frappixPythonOverlay
inputs.self.overlays.frappixToolsOverlay
inputs.self.overlays.frappixAppsOverlay
inputs.self.overlays.posthogPr578265Overlay # TEMPORARY, see overlays/nixpkgs-pr-578265-posthog.nix
inputs.self.overlays.frappixFixupsOverlay
];
clan.core.vars.generators.frappix = {
files = {
sslCertificate.secret = false;
sslCertificateKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
adminPassword.secret = true;
};
runtimeInputs = with pkgs; [
openssl
xkcdpass
];
script = ''
openssl req -x509 -newkey rsa:4096 -keyout $out/sslCertificateKey -out $out/sslCertificate -sha256 -days 3650 -nodes -subj "/C=TH/ST=ChiangMai/L=ChiangMai/O=kurogeek/CN=kurogeek.home"
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminPassword
'';
};
services.frappe = {
enable = true;
project = "b4lerp";
gunicorn_workers = 2;
adminPassword = config.clan.core.vars.generators.frappix.files.adminPassword.path;
apps = [
pkgs.frappix.erpnext
pkgs.frappix.hrms
pkgs.frappix.default_thai_company
];
sites = {
"${sitename}" = {
domains = [ sitename ];
apps = [
"frappe"
"erpnext"
"hrms"
"default_thai_company"
];
};
};
};
services.nginx.virtualHosts."${sitename}" = {
sslCertificate = config.clan.core.vars.generators.frappix.files.sslCertificate.path;
sslCertificateKey = config.clan.core.vars.generators.frappix.files.sslCertificateKey.path;
};
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
clan.core.vars.generators.nginx = {
files = {
sslCert = {
owner = "nginx";
group = "nginx";
secret = true;
};
sslKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
};
runtimeInputs = [
pkgs.openssl
];
script = ''
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout $out/sslKey \
-out $out/sslCert \
-subj "/CN=localhost"
'';
};
networking.firewall.allowedTCPPorts = [
80
443
];
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -8,6 +8,7 @@
# clan.core.networking.targetHost = "root@"; # clan.core.networking.targetHost = "root@";
clan.core.settings.name = "buna"; clan.core.settings.name = "buna";
# clan.meta.description = "Radxa X4 SBC board for one of w phone network. (With w whitehouse)"; clan.core.settings.machine.description =
"Radxa X4 SBC board for one of w phone network. (With w whitehouse)";
} }
+728 -606
View File
File diff suppressed because it is too large. Load diff
+111
View File
@@ -0,0 +1,111 @@
{
inputs,
pkgs,
config,
...
}:
let
sitename = "glomerp.newedge.house";
nbClientName = "netbird-b4l";
in
{
clan.core.settings.machine.description = "VM machine that host ERPNext for glomglom";
imports = [ inputs.frappix.nixosModules.x86_64-linux.frappix ];
nixpkgs.overlays = [
inputs.self.overlays.frappixFrappeOverlay
inputs.self.overlays.frappixLibsOverlay
inputs.self.overlays.frappixPythonOverlay
inputs.self.overlays.frappixToolsOverlay
inputs.self.overlays.posthogPr578265Overlay # TEMPORARY, see overlays/nixpkgs-pr-578265-posthog.nix
inputs.self.overlays.frappixFixupsOverlay
];
clan.core.vars.generators.frappix = {
files = {
sslCertificate.secret = false;
sslCertificateKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
adminPassword.secret = true;
};
runtimeInputs = with pkgs; [
openssl
xkcdpass
];
script = ''
openssl req -x509 -newkey rsa:4096 -keyout $out/sslCertificateKey -out $out/sslCertificate -sha256 -days 3650 -nodes -subj "/C=TH/ST=ChiangMai/L=ChiangMai/O=kurogeek/CN=kurogeek.home"
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminPassword
'';
};
services.frappe = {
enable = true;
project = "glomerp";
gunicorn_workers = 2;
adminPassword = config.clan.core.vars.generators.frappix.files.adminPassword.path;
apps = [
pkgs.frappix.erpnext
pkgs.frappix.hrms
];
sites = {
"${sitename}" = {
domains = [ sitename ];
apps = [
"frappe"
"erpnext"
"hrms"
];
};
};
};
services.nginx.virtualHosts."${sitename}" = {
sslCertificate = config.clan.core.vars.generators.frappix.files.sslCertificate.path;
sslCertificateKey = config.clan.core.vars.generators.frappix.files.sslCertificateKey.path;
};
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
clan.core.vars.generators.nginx = {
files = {
sslCert = {
owner = "nginx";
group = "nginx";
secret = true;
};
sslKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
};
runtimeInputs = [
pkgs.openssl
];
script = ''
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout $out/sslKey \
-out $out/sslCert \
-subj "/CN=localhost"
'';
};
networking.firewall.allowedTCPPorts = [
80
443
];
# services.nginx.virtualHosts."${domain}" = {
# forceSSL = true;
# sslCertificate = config.clan.core.vars.generators.nginx.files.sslCert.path;
# sslCertificateKey = config.clan.core.vars.generators.nginx.files.sslKey.path;
# };
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+14
View File
@@ -0,0 +1,14 @@
{
...
}:
{
clan.core.settings.machine.description =
"VM machine for collecting prometheus metrics and fire alerts";
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+14 -2
View File
@@ -1,11 +1,23 @@
{ inputs, self, ... }: {
inputs,
self,
...
}:
{ {
imports = [ imports = [
inputs.clan-core.flakeModules.default inputs.clan-core.flakeModules.default
]; ];
clan = { clan = {
meta.name = "NewEdgeClan"; meta.name = "NewEdgeClan";
machines = { }; machines = {
cursa = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
hadar = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
procyon = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
bosona = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
canopus = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
deneb = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
alasia = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
};
secrets.age.plugins = [ secrets.age.plugins = [
"age-plugin-yubikey" "age-plugin-yubikey"
"age-plugin-fido2-hmac" "age-plugin-fido2-hmac"
+128
View File
@@ -0,0 +1,128 @@
{
inputs,
pkgs,
config,
...
}:
let
sitename = "poyerp.newedge.house";
in
{
clan.core.settings.machine.description = "VM machine that host ERPNext for poysound";
imports = [
inputs.frappix.nixosModules.x86_64-linux.frappix
# (import ../../lib/auto-accept-zerotier-members.nix {
# memberIds = [
# # Alex Caddy gateway
# "dbe44c0287"
# "e3d6559697"
# ];
# })
];
nixpkgs.overlays = [
inputs.self.overlays.frappixFrappeOverlay
inputs.self.overlays.frappixLibsOverlay
inputs.self.overlays.frappixPythonOverlay
inputs.self.overlays.frappixToolsOverlay
inputs.self.overlays.frappixAppsOverlay
inputs.self.overlays.posthogPr578265Overlay # TEMPORARY, see overlays/nixpkgs-pr-578265-posthog.nix
inputs.self.overlays.frappixFixupsOverlay
];
fonts.packages = [ pkgs.tlwg ];
clan.core.vars.generators.frappix = {
files = {
sslCertificate.secret = false;
sslCertificateKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
adminPassword.secret = true;
};
runtimeInputs = with pkgs; [
openssl
xkcdpass
];
script = ''
openssl req -x509 -newkey rsa:4096 -keyout $out/sslCertificateKey -out $out/sslCertificate -sha256 -days 3650 -nodes -subj "/C=TH/ST=ChiangMai/L=ChiangMai/O=kurogeek/CN=kurogeek.home"
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminPassword
'';
};
services.frappe = {
enable = true;
project = "poyerp";
gunicorn_workers = 2;
adminPassword = config.clan.core.vars.generators.frappix.files.adminPassword.path;
apps = [
pkgs.frappix.erpnext
pkgs.frappix.hrms
pkgs.frappix.crm
pkgs.frappix.posprinter
pkgs.frappix.default_thai_company
];
sites = {
"${sitename}" = {
domains = [ sitename ];
apps = [
"frappe"
"erpnext"
"hrms"
"crm"
"posprinter"
"default_thai_company"
];
};
};
};
services.nginx.virtualHosts."${sitename}" = {
sslCertificate = config.clan.core.vars.generators.frappix.files.sslCertificate.path;
sslCertificateKey = config.clan.core.vars.generators.frappix.files.sslCertificateKey.path;
};
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
clan.core.vars.generators.nginx = {
files = {
sslCert = {
owner = "nginx";
group = "nginx";
secret = true;
};
sslKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
};
runtimeInputs = [
pkgs.openssl
];
script = ''
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout $out/sslKey \
-out $out/sslCert \
-subj "/CN=localhost"
'';
};
networking.firewall.allowedTCPPorts = [
80
443
];
# services.nginx.virtualHosts."${domain}" = {
# forceSSL = true;
# sslCertificate = config.clan.core.vars.generators.nginx.files.sslCert.path;
# sslCertificateKey = config.clan.core.vars.generators.nginx.files.sslKey.path;
# };
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+87
View File
@@ -0,0 +1,87 @@
{
inputs,
config,
pkgs,
...
}:
let
domain = "w-inventory.newedge.house";
in
{
clan.core.settings.machine.description = "VM machine that host Inventree system for Poy";
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
clan.core.vars.generators.inventree = {
files = {
secret-key = {
owner = "inventree";
group = "inventree";
secret = true;
};
oidc-key = {
owner = "inventree";
group = "inventree";
secret = true;
};
admin-password = {
owner = "inventree";
group = "inventree";
secret = true;
};
};
runtimeInputs = [
pkgs.pwgen
pkgs.xkcdpass
];
script = ''
pwgen -s 32 1 > $out/secret-key
pwgen -s 32 1 > $out/oidc-key
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/admin-password
'';
};
clan.core.vars.generators.nginx = {
files = {
sslCert = {
owner = "nginx";
group = "nginx";
secret = true;
};
sslKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
};
runtimeInputs = [
pkgs.openssl
];
script = ''
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout $out/sslKey \
-out $out/sslCert \
-subj "/CN=localhost"
'';
};
networking.firewall.allowedTCPPorts = [
80
443
];
services.inventree = {
enable = true;
inherit domain;
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
settings.INVENTREE_SITE_URL = "https://${domain}";
settings.INVENTREE_FRONTEND_SETTINGS = ''{"mobile_mode": "allow-ignore"}'';
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -8,6 +8,6 @@
# clan.core.networking.targetHost = "root@"; # clan.core.networking.targetHost = "root@";
clan.core.settings.name = "mirach"; clan.core.settings.name = "mirach";
# clan.meta.description = "Radxa X4 SBC board for one of w phone network."; clan.core.settings.machine.description = "Radxa X4 SBC board for one of w phone network.";
} }
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+1 -2
View File
@@ -10,7 +10,6 @@
}; };
system.stateVersion = "25.11"; system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ]; 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 networking.interfaces.enx00e04c106368.useDHCP = true; # recovery
@@ -57,6 +56,6 @@
]; ];
clan.core.settings.name = "neptune"; clan.core.settings.name = "neptune";
# clan.meta.description = "Radxa SBC board for testing. (With vi)"; clan.core.settings.machine.description = "Radxa SBC board for testing. (With vi)";
} }
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+1
View File
@@ -9,4 +9,5 @@
}; };
system.stateVersion = "25.11"; system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ]; clan.core.sops.defaultGroups = [ "admins" ];
clan.core.settings.machine.description = "A personal computer for Chocolate Shop";
} }
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+86
View File
@@ -0,0 +1,86 @@
{
inputs,
config,
pkgs,
...
}:
let
domain = "glom-inventory.newedge.house";
in
{
clan.core.settings.machine.description = "VM machine that host Inventree system for Glom";
nixpkgs.hostPlatform = {
system = "x86_64-linux";
};
clan.core.vars.generators.inventree = {
files = {
secret-key = {
owner = "inventree";
group = "inventree";
secret = true;
};
oidc-key = {
owner = "inventree";
group = "inventree";
secret = true;
};
admin-password = {
owner = "inventree";
group = "inventree";
secret = true;
};
};
runtimeInputs = [
pkgs.pwgen
pkgs.xkcdpass
];
script = ''
pwgen -s 32 1 > $out/secret-key
pwgen -s 32 1 > $out/oidc-key
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/admin-password
'';
};
clan.core.vars.generators.nginx = {
files = {
sslCert = {
owner = "nginx";
group = "nginx";
secret = true;
};
sslKey = {
owner = "nginx";
group = "nginx";
secret = true;
};
};
runtimeInputs = [
pkgs.openssl
];
script = ''
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout $out/sslKey \
-out $out/sslCert \
-subj "/CN=localhost"
'';
};
networking.firewall.allowedTCPPorts = [
80
443
];
services.inventree = {
enable = true;
inherit domain;
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
settings.INVENTREE_SITE_URL = "https://${domain}";
};
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
}
+85
View File
@@ -0,0 +1,85 @@
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+1 -4
View File
@@ -6,12 +6,9 @@
}; };
clan.core.settings.name = "ramus"; clan.core.settings.name = "ramus";
# clan.meta.description = '' clan.core.settings.machine.description = "A Hetzner VPS machine own by Alex.";
# A Hetzner VPS machine own by Alex.
# '';
clan.core.sops.defaultGroups = [ "admins" ]; 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 = { clan.core.vars.generators.acme = {
share = true; share = true;
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+3 -1
View File
@@ -54,7 +54,9 @@ in
self.nixosModules.think-gtcm self.nixosModules.think-gtcm
self.nixosModules.think-backend-gtcm self.nixosModules.think-backend-gtcm
]; ];
nixpkgs.overlays = [ self.overlays.packagesOverlay ]; nixpkgs.overlays = [
self.overlays.packagesOverlay
];
clan.core.vars.generators.greaterchiangmai = { clan.core.vars.generators.greaterchiangmai = {
files = { files = {
+11 -1
View File
@@ -1,5 +1,7 @@
{ self, ... }: { self, pkgs, ... }:
{ {
clan.core.settings.machine.description = "Emmie personal computer";
imports = [ imports = [
self.nixosModules.common self.nixosModules.common
]; ];
@@ -9,4 +11,12 @@
}; };
system.stateVersion = "25.11"; system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ]; clan.core.sops.defaultGroups = [ "admins" ];
boot.kernelParams = [
"amdgpu.dcdebugmask=0x10"
"amdgpu.cwsr_enable=0"
"amdgpu.gpu_recovery=1"
];
boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_12;
} }
+2
View File
@@ -14,6 +14,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+779 -681
View File
File diff suppressed because it is too large. Load diff
+2 -11
View File
@@ -1,16 +1,7 @@
{ config, ... }: { config, ... }:
{ {
imports = [
(import ../../lib/auto-accept-zerotier-members.nix {
memberIds = [
"dbe44c0287" # Alex-gateway
"b0e0b84fd3" # Alex
"2bd36db8cc" # kurogeek-thinkpad
];
})
];
system.stateVersion = "25.11"; system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ]; clan.core.sops.defaultGroups = [ "admins" ];
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
clan.core.settings.machine.description = "Zima board computer for testing in B4L";
} }
+43 -8
View File
@@ -1,11 +1,10 @@
{ {
self, self,
pkgs,
... ...
}: }:
{ {
imports = [ imports = [
self.nixosModules.common
./hardware-configuration.nix ./hardware-configuration.nix
]; ];
@@ -15,20 +14,19 @@
]; ];
}; };
clan.core.settings.machine.description = "w NAS";
clan.core.sops.defaultGroups = [ "admins" ]; clan.core.sops.defaultGroups = [ "admins" ];
nixpkgs.hostPlatform = { nixpkgs.hostPlatform = {
system = "aarch64-linux"; system = "aarch64-linux";
}; };
nixpkgs.buildPlatform = {
system = "x86_64-linux";
};
system.stateVersion = "25.11"; system.stateVersion = "25.11";
services.journald.extraConfig = ''
Storage=volatile
RuntimeMaxUse=30M
RuntimeMaxFileSize=10M
'';
services.udisks2.enable = false; services.udisks2.enable = false;
nix.settings.log-lines = 25; nix.settings.log-lines = 25;
@@ -42,6 +40,8 @@
boot.loader.grub.enable = false; boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = true; boot.loader.generic-extlinux-compatible.enable = true;
boot.zfs.forceImportRoot = false;
boot.zfs.extraPools = [ "zdata" ]; boot.zfs.extraPools = [ "zdata" ];
boot.supportedFilesystems = [ "zfs" ]; boot.supportedFilesystems = [ "zfs" ];
boot.initrd.availableKernelModules = [ boot.initrd.availableKernelModules = [
@@ -49,6 +49,23 @@
"sd_mod" "sd_mod"
]; ];
boot = {
consoleLogLevel = 0;
kernel.sysctl = {
"fs.suid_dumpable" = 0;
"kernel.core_pattern" = "/dev/null";
};
tmp = {
useTmpfs = true;
};
};
services.journald.settings.Journal.Storage = "none";
systemd = {
coredump.enable = false;
};
fileSystems."/mnt/hdd" = { fileSystems."/mnt/hdd" = {
device = "zdata/nas"; device = "zdata/nas";
fsType = "zfs"; fsType = "zfs";
@@ -70,4 +87,22 @@
]; ];
}; };
systemd.services.heartbeat-push = {
description = "Heartbeat push to uptime monitor";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.curl}/bin/curl -s -o /dev/null https://uptime.b4l.co.th/api/push/X0WCHAcY5gPf1U8If7BT1FLjpacZqGZu?status=up&msg=OK&ping=";
};
};
systemd.timers.heartbeat-push = {
description = "Heartbeat push timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "60s";
OnUnitActiveSec = "60s";
Unit = "heartbeat-push.service";
};
};
} }
+92
View File
@@ -0,0 +1,92 @@
{
config,
inputs,
lib,
pkgs,
...
}:
{
imports = [
inputs.matthew-hardware.nixosModules.mt7986a-glinet-gl-mt6000
];
config = {
hardware.mt7986a-glinet-gl-mt6000.enable = true;
hardware.mt7986a-glinet-gl-mt6000.zealous = true;
hardware.mt7986a-glinet-gl-mt6000.image.repart.enable = true;
nixpkgs.hostPlatform.system = "aarch64-linux";
nixpkgs.buildPlatform.system = "x86_64-linux";
system.stateVersion = "26.11";
clan.core.sops.defaultGroups = [ "admins" ];
clan.core.settings.name = "stellio";
clan.core.settings.machine.description = "Flint-2 router";
# Root is ext4 on the Flint 2's eMMC: keep steady-state writes off it.
# The journal is persistent but bounded (256M) and synced once a minute
# (the router needs logs for pppd/kea debugging and, after the Sep 2026
# unlogged reboots, for post-mortem; volatile storage left nothing).
# /tmp is tmpfs, no core dumps, and ext4 skips atime updates and
# flushes its journal every 60 s instead of 5 s so the small periodic
# writes (Kea leases, timesyncd clock, speedtest textfile, journal)
# coalesce. A power cut loses at most a minute of that state.
fileSystems."/".options = [
"noatime"
"commit=60"
];
boot.tmp.useTmpfs = true;
services.journald.settings.Journal = {
Storage = "persistent";
SystemMaxUse = "256M";
SyncIntervalSec = "1m";
};
systemd.coredump.enable = false;
# nixpkgs' services.blocky check runs the aarch64 blocky in a
# system.checks derivation, which an x86_64 builder without binfmt cannot
# execute (`--max-jobs 0` -> Exec format error). Validate the same YAML
# with the build host's blocky instead. Drop once cnx-network's router
# service carries this itself (router/dns: validate the Blocky config
# with the build host's binary).
services.blocky.enableConfigCheck = false;
system.checks = [
(pkgs.runCommand "check-blocky-config" { } ''
${lib.getExe pkgs.buildPackages.blocky} --config ${
(pkgs.formats.yaml { }).generate "blocky-config.yaml" config.services.blocky.settings
} validate && touch $out
'')
];
# The router service opens SSH only to the mgmt VLAN, the staging port
# and the ZeroTier mesh (inventories/router.nix). Also admit it over
# Yggdrasil (fleet-wide instance in inventories/default.nix) so the box
# stays reachable when ZeroTier's controller/relays are down. Scoped to
# the tunnel interface (clan-core's yggdrasil service names it `ygg`):
# only decrypted 200::/7 traffic arrives there, so a spoofed source on
# the WAN never matches.
networking.firewall.interfaces.${config.services.yggdrasil.settings.IfName}.allowedTCPPorts = [
22
];
systemd.services.heartbeat-push = {
description = "Heartbeat push to uptime monitor";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.curl}/bin/curl -s -o /dev/null https://uptime.b4l.co.th/api/push/X0WCHAcY5gPf1U8If7BT1FLjpacZqGZu?status=up&msg=OK&ping=";
};
};
systemd.timers.heartbeat-push = {
description = "Heartbeat push timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "60s";
OnUnitActiveSec = "60s";
Unit = "heartbeat-push.service";
};
};
};
}
+13
View File
@@ -0,0 +1,13 @@
{ inputs, ... }:
{
disko = {
imageBuilder = {
# Avoid double emulation to significantly speed up image building process.
# Update the system according to your host system.
# See https://github.com/nix-community/disko/issues/856
qemu =
inputs.nixpkgs.legacyPackages.x86_64-linux.qemu
+ "/bin/qemu-system-aarch64 -M virt -cpu cortex-a57";
};
};
}
@@ -1,24 +1,15 @@
{ inputs, config, ... }: { ... }:
{ {
imports = [ system.stateVersion = "25.11";
(inputs.import-tree ./services)
];
nixpkgs.hostPlatform = { nixpkgs.hostPlatform = {
system = "x86_64-linux"; system = "x86_64-linux";
}; };
boot.loader.grub.devices = [ "/dev/disk/by-id/FIXME" ];
fileSystems = {
"/".device = "/dev/FIXME";
};
networking.fqdn = "b4l.co.th";
system.stateVersion = "25.11";
clan.core.sops.defaultGroups = [ "admins" ];
security.acme.defaults.email = "admin@b4l.co.th"; clan.core.settings.name = "tangra";
security.acme.acceptTerms = true; clan.core.settings.machine.description =
services.nginx.virtualHosts."${config.networking.fqdn}" = { "A Hetzner VPS machine own by Alex. This is a machine for poyfestival.com";
enableACME = true;
}; clan.core.sops.defaultGroups = [ "admins" ];
clan.core.vars.generators.acme = { clan.core.vars.generators.acme = {
share = true; share = true;
@@ -33,4 +24,10 @@
cat $prompts/email > $out/email cat $prompts/email > $out/email
''; '';
}; };
users.users.nginx.extraGroups = [ "acme" ];
security.acme.acceptTerms = true;
imports = [ ];
} }
+86
View File
@@ -0,0 +1,86 @@
{ ... }:
let
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_119349241";
in
{
boot.loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
boot.zfs.forceImportRoot = 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";
};
};
};
};
};
};
}
File diff suppressed because it is too large. Load diff
+2 -8
View File
@@ -10,17 +10,11 @@
(inputs.import-tree ./services) (inputs.import-tree ./services)
(import ../../lib/auto-accept-zerotier-members.nix {
memberIds = [
"dbe44c0287" # Alex-gateway
"b0e0b84fd3" # Alex
"2bd36db8cc" # kurogeek-thinkpad
];
})
]; ];
clan.core.sops.defaultGroups = [ "admins" ]; clan.core.sops.defaultGroups = [ "admins" ];
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
clan.core.settings.machine.description = "Glom NAS";
nixpkgs.hostPlatform = { nixpkgs.hostPlatform = {
system = "x86_64-linux"; system = "x86_64-linux";
+2
View File
@@ -18,6 +18,8 @@ in
}; };
}; };
boot.zfs.forceImportRoot = true;
disko.devices = { disko.devices = {
disk = { disk = {
"os-${hashDisk os}" = { "os-${hashDisk os}" = {
+5 -2
View File
@@ -82,8 +82,11 @@ in
}; };
services.avahi = { services.avahi = {
publish.enable = true; settings.publish = {
publish.userServices = true; disable-publishing = false;
disable-user-service-publishing = false;
publish-addresses = true;
};
# ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile` # ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
nssmdns4 = true; nssmdns4 = true;
# ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it # ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
@@ -5,6 +5,7 @@
name = "service-actual-budget"; name = "service-actual-budget";
clan = { clan = {
test.useContainers = false;
directory = ./.; directory = ./.;
inventory = { inventory = {
machines.server = { }; machines.server = { };
+3
View File
@@ -0,0 +1,3 @@
# Apple Network
This service allows atalkd instances to connect to each other over vxlan over Yggdrasil. We need to use vxlan due to the support of broadcasting it provides. The service will form a network of star-topology vxlan using the underlying Yggdrasil network. With this vxlan network, we can have a private Apple Talk network over the internet.
+139
View File
@@ -0,0 +1,139 @@
{ clanLib, ... }:
{
_class = "clan.service";
manifest.name = "apple-network";
manifest.description = "This service will create an instance of `atalkd` for each peer to talk to Apple machines over Apple Talk protocol and automatically connect each peer using vxlan over Yggdrasil network to achieve Apple Talk over internet experience";
manifest.readme = builtins.readFile ./README.md;
manifest.categories = [ "Network" ];
roles.peer = {
description = "A gateway machine that allow classic Apple machines to connect to other Apple machines over the internet";
interface =
{ lib, ... }:
{
options = {
zone_name = lib.mkOption {
type = with lib.types; str;
description = "Zone name for Apple Talk protocol";
default = "Default";
};
};
};
perInstance =
{ roles, settings, ... }:
{
nixosModule =
{
lib,
config,
pkgs,
...
}:
let
vxlanPort = 4789;
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";
noSelfPeers = builtins.filter (peerName: peerName != config.clan.core.settings.machine.name) (
lib.mapAttrsToList (machineName: _: machineName) roles.peer.machines
);
sortedPeers = builtins.sort (x: y: x < y) (
lib.mapAttrsToList (machineName: _: machineName) roles.peer.machines
);
getMachineIndex = machineName: lib.lists.findFirstIndex (x: x == machineName) null sortedPeers;
selfVXAddress = "192.168.254.${
lib.toString ((getMachineIndex config.clan.core.settings.machine.name) + 1)
}/24";
in
{
services.atalkd = {
enable = true;
interfaces = {
vxlan.config = ''
-router -phase 2 -net 1 -zone "${settings.zone_name}"
'';
};
};
networking.useNetworkd = true;
networking.firewall.interfaces."ygg".allowedUDPPorts = [ vxlanPort ];
boot.kernelModules = [ "vxlan" ];
systemd.network.netdevs =
builtins.listToAttrs (
map (
peerName:
(lib.nameValuePair "10-apl-vxlan-${peerName}" {
enable = true;
netdevConfig = {
Name = "vxlan-${peerName}";
Kind = "vxlan";
};
vxlanConfig = {
VNI = (getMachineIndex config.clan.core.settings.machine.name) + (getMachineIndex peerName);
Remote = getYggdrasilIP peerName;
DestinationPort = vxlanPort;
Independent = true;
};
})
) noSelfPeers
)
// {
"10-apl-vxlan" = {
enable = true;
netdevConfig = {
Kind = "bridge";
Name = "vxlan";
};
bridgeConfig = {
STP = true;
};
};
};
systemd.network.networks =
builtins.listToAttrs (
map (
peerName:
(lib.nameValuePair "10-apl-vxlan-${peerName}" {
enable = true;
matchConfig.Name = "vxlan-${peerName}";
networkConfig.Bridge = "vxlan";
})
) noSelfPeers
)
// {
"10-apl-vxlan" = {
enable = true;
matchConfig.Name = "vxlan";
address = [ selfVXAddress ];
};
};
environment.systemPackages = [
pkgs.netatalk
pkgs.bridge-utils
];
};
};
};
}
@@ -0,0 +1,19 @@
{ inputs, self, ... }:
let
module = ./default.nix;
in
{
clan.modules = {
apple-network = module;
};
perSystem =
{ ... }:
{
clan.nixosTests.service-apple-network = {
imports = [ ./tests/vm/default.nix ];
_module.args = { inherit self inputs; };
clan.modules."@clan/apple-network" = module;
};
};
}
@@ -0,0 +1,76 @@
{
self,
lib,
config,
hostPkgs,
...
}:
{
name = "service-apple-network";
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 = ./.;
test.useContainers = false;
inventory = {
meta.domain = "test.clan";
machines.peer1 = { };
machines.peer2 = { };
machines.peer3 = { };
instances = {
apple-network = {
module.name = "@clan/apple-network";
module.input = "self";
roles.peer.machines = {
peer1 = { };
peer2 = { };
peer3 = { };
};
};
yggdrasil = {
module.name = "yggdrasil";
roles.default.tags.all = { };
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"
];
};
};
};
};
nodes = {
peer1 = { };
peer2 = { };
peer3 = { };
};
testScript = _: ''
# cannot test connectivity due to Yggdrasil's establishment
start_all()
peer1.wait_for_unit("atalkd")
peer1.succeed("systemctl status atalkd")
peer2.wait_for_unit("atalkd")
peer2.succeed("systemctl status atalkd")
peer3.wait_for_unit("atalkd")
peer3.succeed("systemctl status atalkd")
'';
}
@@ -0,0 +1,6 @@
[
{
"publickey": "age1f92zl8y70z5w9gn0l5leg2fhyvxy6m93dfh7n6ltgvuu9nyg6epsrw6t8x",
"type": "age"
}
]
@@ -0,0 +1,6 @@
[
{
"publickey": "age1u36fr87cru90vfznf5szasya9nhvvseh4atk8zsdj0rrsu0nnvwqsyt8g6",
"type": "age"
}
]
@@ -0,0 +1,6 @@
[
{
"publickey": "age1rka3368ltqc787xw99uu762lqfk7qxld9265u2t62fpxnp9xhufsc2q5rw",
"type": "age"
}
]
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:hC4Lle6mXuTFq//wBHUTyH3X778u/gYJSXJOiQ7xUb+gnC8beH1olxL2ZhtUiMkktzhsPM/j6cxhltPp25UTeJnUOcoYesqZcp8=,iv:WOx2P2Cu4v58xM9ePjAw1gdA0fFnGG2UqEXH2iksGoo=,tag:43n1ibVSy2AeS9f5qQBKNQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtT244N1Q3Zk9pRjFFQlUr\ncDExSTBwSWk5UXVyMjhWclQwSFZaYnpTRFY0CnE5YStVdzMrS3BKYzYvc3V3QVFY\nbDVwVytoNERveU1xQllqNUFGV2hYbGMKLS0tIDJSUmkwMUQzL3Y0MU5qZTJ3eFN3\nNGdwamM1UDBPdzhvSWsyZzR6anZ0eFEKKnr1/7rf3fH1i7KUoZdilLDgb44K2qzn\nI9Y+7FbV4gzQUzKWjAQeFN37Z4Qiuy0xJVA0lw8KvM+NCxSZDfh3kw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
}
],
"lastmodified": "2026-07-03T10:46:56Z",
"mac": "ENC[AES256_GCM,data:hmpbOtra6EyNiPg7EsA3F4elHEO87JPUr4VKVppvzUwLX1HSj67WrU6k3rEoJT+k7Hq1lxnAzDEckSG1fUOap11rk8ksp1IAGR0+yqykC/Qj/Nl8wXqP84gr49bJK6xE/DDdCRCcBff2d6IpnehyEr8fGFEZbWGygblec8U8GPo=,iv:+M1C3rL22foowr0VD80jwrNFb5xz4k5JQ/DJm4ThKHk=,tag:wabFAC5NTCVGfIQQzm0yGg==,type:str]",
"version": "3.13.1"
}
}
@@ -0,0 +1 @@
../../../users/admin
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:ndWIEOqP2ybCN7Ak5g+8q3YG5rLQ7OuSv/vCbnMA8AK6Lby2JMGRkRGysqJdyEF7eh4w7PpZ/AtKPtPbUucjeTB5+8g76PPJTBE=,iv:EYy0GGqEBmIc5rSWP7Zleb5eQ0JzTz84HqeJPpCSpxk=,tag:iMgTUb9+wxE9S2kKoOEvsQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQTEF6OXN4K0dHdXhWWmt2\neG5wdkNIc2F0UnFEeHFLQkdTWkFpUWhTa0NBCmw2N2J1T0FJVE9JR3VBRWV0THh5\nVEZlbVZrcXAwK3BsYXFOZUF6c3lRSkUKLS0tIFRCQzNUaGFHdERWNWR4enVWRGpx\nbDEzRU9jWHgxWHovaENjbkFlZERIbkkKvAW31gTJg9izrWuAMeDHS9SWPQYtxRBY\necDCn8QORFrnDdxEusCnGJrAU4kmS3Y3CwSHCtEijs3JdIgg9wl/MA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
}
],
"lastmodified": "2026-07-03T10:47:04Z",
"mac": "ENC[AES256_GCM,data:y1U2vsdqR97fPAnp6iZiLY1KbWYmgDqgZ5bwIkWPCr7Gjjrn+qV+sc2T1BTVz3Zo0JOz4ScCc+f6snVsFKdCDU/NmDVFt/sqyH4NHUPRQ3StsQim8C7IkSwUtm3EhrqpLYFD21MEjrYYdHMHbWybxrOI0qI6mDwZymHJFeCLdQA=,iv:lVvPn69eQE/huwDmwePepfpi2l9ZOPQO7W20o4AJBFQ=,tag:7VPfjKZsBVJTZxNwTcI6sg==,type:str]",
"version": "3.13.1"
}
}
@@ -0,0 +1 @@
../../../users/admin
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:JPdetkhtF9eSmgEfCO2exXlGQ5KWLeIM3mnW5BfyTY8r0PxWSEGAwBskC1SiDdRERWB2VEbPiPgCNXMnGaYlDqSdZN6R9wSFvsQ=,iv:qGveNhUtgxvKnmdsPoPpffKyi5srlq0Pd6aW283B5C0=,tag:TcJsCrBlzfjXvztcRaxYFQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvdjNHa1RlcFJHazB5TXFu\nOUQ4REpCZm5Yc3ZhcVhwT1FqRHlvbnoxZWlFCnNrVUEyK2ZYSmlacmJ2b2QrclZl\nSDV1eWxXVmJxZlhMMXgrTWtjcjVhQlkKLS0tIHZPN3pCUVBLQmpRVGx6SDBQNDZo\nSitheGhXNGl5QW9HTGNsTTdTdWVwaE0KfuM5xZOPbR1lxkJPJUOVrtlW5Ujobemp\nid40reDqxKQxP3khJv8lf9ZoN8LAx2Iwh0DeAl+UJpsoVWgS7uI2qQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
}
],
"lastmodified": "2026-07-07T04:54:10Z",
"mac": "ENC[AES256_GCM,data:Tt8yqzfAnSDe2n7KDt1Z/D73hkS1w7DsqIh+s5WljLzar9PEt5vUCLb8DIvBwITyzGAoHH1ym3v3NrnJWCdxajKExTMBf4aY3MJCtykeCG37701S/KxumjTW9/fJw74gcET5I6NafTWb6H5XHsrRaQX1Xnspcs8B7uLGZZ28dSM=,iv:Fo5UPVhnjuOx6Cp4grofHOiD2pm0TXuRRuoTwvxKn1s=,tag:VQ/TF1TiyJADj7/3RwPCAg==,type:str]",
"version": "3.13.1"
}
}
@@ -0,0 +1 @@
../../../users/admin
@@ -0,0 +1,4 @@
{
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"type": "age"
}
@@ -0,0 +1 @@
200:e6cc:c86:f02d:b0d5:fba9:9b2c:eb77
@@ -0,0 +1 @@
../../../../../../sops/machines/peer1
@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:6YDp5S2TzEnhDgGhcz8cA3uBsfDt0Oc4JpztB1JUcg/+tDBbeaQN4MLxuZKstXn2Hb1mYtBLlX0vHw++T181Js2sIznyfC1lr68GOvQAp4NYgf23dXpdZk/CMPNUMiGOYVwKWQ/Z0/p0azJ6xV29dYQcM4Q9hys=,iv:Ijzz8hm0looCWoSpjg/rYKp9jhvzRuLboKWHrlwVeFA=,tag:kJcH/1Pu+9q9f2J68LETug==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYNDNEL3kvdEpEMjYyOFJG\nMk5adU04M3hxa21jTTZNM2dQREpmUlVES1NvCjh6eDNFZ1hzUVlTY3Nidnp3TkYr\nRVl4TFlrNUE3dVIzZzg2VWMwTFBYUXMKLS0tIFE0dVFQQmxwOFJ6YkdaQlh6dUlW\nTi8rdXdQbGR1eXpLcTdOandqQmxka3cKImbmJQo2YMYMSQD8kNTgol3VJ4YGIaoz\nl3AY4u/fobg5qlzVQwV33G0/Wf72Z2aTjSLnP4thnasQ89PdYvs7hA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1f92zl8y70z5w9gn0l5leg2fhyvxy6m93dfh7n6ltgvuu9nyg6epsrw6t8x"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiaWw5SU9KWmJnVVVmTVpr\nL1V6RnRYUGg2UnhXR1BnVTh5eFhnNFpidGdjCmdMeU1nZkZmTTVwbkNaVElFa0Z4\nVkdGdjc4c2lPWGNrVGxocE9iQUZ3QTAKLS0tIHIzTzRRbXVkM3I4ZVRxeSs5eHV5\nTUxOaytzb2MxQ2UybkVCYWVWVmVEcUEKPeK+CC7r3CP5BJqg9thWNHWjPBp/ueHU\nRif6T79kqf+c3B34qXwR0zfyM1+a0r9GAVVC2/q+Ha/As5sPJU39ww==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
}
],
"lastmodified": "2026-07-03T10:46:56Z",
"mac": "ENC[AES256_GCM,data:PffiPU1GR9DojTb7TxzBkpivKV2ybmmq9h4pOOyq7p5BAIPF+JSe+BDtbItQ+Gn5LnitQRUVvhat8E2iYfz3JgNxNKcsNFU/mWtCz3PlqyiuRzUBtEL3zYfhdMi8hYYlVdfMumaK3VMk+b4uDGExR+MpURL97TFku47qRbzbILs=,iv:3//fj7WtIX2QNYB9ub1JKAFIn5vJeuZl6rqaEsX0oQc=,tag:njU+WAVq7xBgE0sna2dMKA==,type:str]",
"version": "3.13.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/admin
@@ -0,0 +1 @@
8c99f9bc87e92795022b32698a440d6f9fe01d1110f867de8176bb190b92acab
@@ -0,0 +1 @@
200:eb95:7d1b:3ac3:62e7:24af:2274:3771
@@ -0,0 +1 @@
../../../../../../sops/machines/peer2
@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:vOwMqT12A5ap5hRvCGTjTUX4sXKO3N7KE8eQ+/SCcBWndU9/b2AehiNgplWWMGcYdmeDL9qI8V2pPkgBkQ/tWH6N8DccSM/zQzrBUkXvRApl6hvNZAWuBVf/P4MW4/EbRGtMg2tEXkaPDtRn2TsDK7ygmXmqArI=,iv:FkxFx/ClAftLu85S6Uq3ZOom+YDOpeWGlYLOFBmFfnk=,tag:zprw62qiu+hmLZEZbnqYXA==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0WVB1M2ttd0FocGhTSjNo\nUDFIcmpsL051TndWbkJVakJHbUxjY3Q3NjM0CnkrbFlHS1E4bkZJQTIvMDJ3VDFv\nNW1OdmwvalBEelN2d3YwbHVqTW94OFEKLS0tIGtpOWxCM29xdXhQL2Yzb094OG1n\nT21mc25yU1dJL29vekRHdldzdDR3dEkKoZ1dPRaPg8iVsuec6IhV3cLPchokV2wv\nu6qb92hsLQuyPHDA01Oey0jxmaYMQGtAVjo2zEM+E+zrKY+f2pHIUQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArcmo5d2x6c3J0YTkwMGJE\nanphd2FiUjdGdmdNeW0rL0FpcTVSM3dublZ3CnphajNVV2YrTGl2a3ZqMTJKeU4v\nTzZRS2F5MlA5Ny84cFFoNWZQZnpBZTgKLS0tIHVKQUJWcmlPdy9GNXNBYnpJcEVC\nNmJVMlpvSzZPdk1JY2hIZVEvc3cwV2MKE/cMJU/j5g62njJ49AfnLutmxkpz/gxX\nIqWe1m7Uxl/awVFa7F8N7TUkd0jLbAGXZaonDjiBxOTwg/jr/apnuQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1u36fr87cru90vfznf5szasya9nhvvseh4atk8zsdj0rrsu0nnvwqsyt8g6"
}
],
"lastmodified": "2026-07-03T10:47:04Z",
"mac": "ENC[AES256_GCM,data:wiIkwEDwGJZe0u8qLHthPkCXubeIGyHV8yBo8CsSengLVTOtRZ877zTrFvE74ATF1pGE3DFJ5qGRrOGgKJ4VOl043LbEBs0bxZC495pPMPYszimp2UAf/XE3QWlNi9p4Ce/kbvUDd0W3SRt4hIv6HOLWApiRoqfS2qSMUnBQeW0=,iv:Hs7EysUnje/iWZRhJLlqf8wuFJnIMpWfXWHrilfmM5I=,tag:Rgj9x4ZOM2fihKKNQ4ux3g==,type:str]",
"version": "3.13.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/admin
@@ -0,0 +1 @@
8a354172629e4e8c6da86ec5e447411e4238cf7bb439b798fae56509a82d7472
@@ -0,0 +1 @@
200:3dcd:9b0d:103c:f953:1e8:e3a2:ed90
@@ -0,0 +1 @@
../../../../../../sops/machines/peer3
@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:7no9ZnfatNLEykz+JOUExxmaRzBr7B5PEcevETySJWHTCCjSULdRb2KLxbXP5OKWSuJbR8pJ+p+nZuyr7aOTyXrU+fgwf/Pff4KGaZbKf1t3HJun8wJ2q62uaFYu8ACbNFfwIzJ51SPGcO7IHxfjWn5k9zG7av4=,iv:+VNvFKAJ+HDCGZciug8SKHOcM1JZQUAbZpRT5en4xcU=,tag:3kcDHWIs9ITzTKDiyyYhnQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvZ0VFcGhPR0NrMGU4dStU\nZGpnMHZmbUF0R0NFNWQzNGVpZXhYQ24xNXk0CnJIYmJYMnBmeURLZytBcXNRNG5u\nZzk4cXdPdnJIU0JVam1NWFA3TkhuWFkKLS0tIDZYUEVGMUtCQWdSM3k4WEZhdUt3\naElkc3o5SXlFYTJLNUpGZ0ptNm96M2MKYKW5emktK2M+IYX0v/swCV1fgrQXQVlb\nmz11rN5fJWV5wvLcTtyU9imgZLB9MfRMyIYlHzD2HPbW+zr8Fe+bBQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZVDVxY3JrWk1TY3ZsazNz\naVl4Vk1vRUdkRTdlaDU1TE15dC96ZHlxQ1ZBCmYvSmh1QlBNRnJyRy9kcDR0cXhY\neDdPUXZ6b2FkWnBXbzA4bzU1aFo3bEUKLS0tIHMrb05Oa3FrRzh5VHN1RWRmUU9W\ndGZUYTVVQkIzM3FRRHp6NXlEdEU0VTgK7ibTaEcvbVTQjcbgeylu2s5tmI5kAadM\nFGiOgJhDFuzbHxXIEQlsCBIaXe6LlGhBo41VZbMxme5py6nJFTBHKg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1rka3368ltqc787xw99uu762lqfk7qxld9265u2t62fpxnp9xhufsc2q5rw"
}
],
"lastmodified": "2026-07-07T04:54:10Z",
"mac": "ENC[AES256_GCM,data:gonhuGY61G3Gdtj7q23aTNYU95fjLMz2uVTsSX+Hw10LGUTEMM8LF/Phae9kr5BZKD8BYaIgH32As2+n0B9jZ4WV77A5hUe2NJze9d0A40P2MtMh9xFWWh7yEXXnx5dy1UDrthL2LBa7ebPspuaC0mFhRHdoJK3J2W1m41PGjZc=,iv:H4yNuE8i3UxVjAUIpnaFX6i8/DaO4uIlHrEY9VaSiRs=,tag:9lJtixSNv7tpox0V3ZoNnA==,type:str]",
"version": "3.13.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/admin
@@ -0,0 +1 @@
e119327977e183567f0b8e2e8937c9d04708e296beed57792a1b6935eacf69d9
@@ -20,6 +20,7 @@
''; '';
clan = { clan = {
test.useContainers = false;
directory = ./.; directory = ./.;
inventory = { inventory = {
machines.server = { }; machines.server = { };
Loaded 100 of 1410 files, more files were not shown because too many files have changed in this diff. Show more