Commit 065bd20
Changed files (13)
modules
desktop
game
disk
preservation
hosts
services
forgejo
headscale
mihomo
users
modules/desktop/game/ludusavi.nix
@@ -7,8 +7,7 @@
};
hjem = {pkgs, ...}: {
- # Use ludusavi to avoid add every Linux native game's
- # data folder into persistence.
+ # Ludusavi backs up game data instead of persisting every game folder.
packages = [
pkgs.ludusavi
];
modules/disk/preservation/default.nix
@@ -3,9 +3,8 @@
lib,
...
}: let
- # A single preserved file or directory entry, shaped like
- # `preservation.preserveAt."/…".files` / `.directories` entries.
- # Declared fields are checked, unknown fields pass through.
+ # One `preserveAt` files/directories entry; declared fields are
+ # checked, unknown ones pass through.
mkEntryType = kind:
lib.types.submodule {
freeformType = lib.types.attrsOf lib.types.anything;
@@ -70,8 +69,7 @@ in {
persistHome = homeFiles;
provides.to-users = {
- # Re-emit persistHome at user scopes: the aspect itself only runs at
- # host scope, so its own `persistHome` attribute alone would not land.
+ # Re-emit persistHome at user scopes; the host-scope aspect cannot.
persistHome = homeFiles;
hjem = {lib, ...}: {
modules/hosts/tribios/file-system.nix
@@ -4,9 +4,7 @@
};
den.aspects.tribios = {
- # Manually manage file system.
- # Since tribios has edk2-rk3588 firmware in main storage.
- # Using disko will break this.
+ # Disko would break the edk2-rk3588 firmware in main storage.
includes = [
# den.aspects.disk.btrfs-disko
den.aspects.disk.btrfs
modules/hosts/tribios/networking.nix
@@ -65,10 +65,8 @@
};
};
- # NAT-PMP for Tailscale NAT traversal.
- # Tailscale clients behind this router will auto-request
- # endpoint-independent port mappings, enabling direct
- # connections from the company network (192.168.100.x).
+ # NAT-PMP enables direct connections from the company network
+ # (192.168.100.x) to Tailscale clients behind this router.
services.miniupnpd = {
enable = true;
externalInterface = "enP3p49s0";
modules/services/caddy/default.nix
@@ -52,10 +52,7 @@
templateName
;
- # Resolve how Caddy on the current host should reach the host that
- # submitted the reverse proxy (the source host).
- #
- # Priority:
+ # Priority for reaching the source host:
# 1. Same host -> loopback.
# 2. Source has a clear text -> use it directly.
# 3. Source has a secret IP -> import it from a vaultix template.
modules/services/caddy/quirks.nix
@@ -3,8 +3,7 @@
lib,
...
}: let
- # Tailnet domain (base domain of Headscale MagicDNS). Tailnet reverse proxy
- # entries are recognized by this suffix.
+ # Tailnet MagicDNS base domain; reverse proxy entries use it as a suffix.
tailnetDomain = "net.trin.one";
in {
den.quirks.reverseProxy.description = ''
modules/services/caddy/tailscale.nix
@@ -30,8 +30,7 @@
enabled = builtins.length entries > 0;
- # Wildcard site: only obtains the *.net.trin.one certificate via DNS-01.
- # Concrete subdomain sites below reuse it automatically (Caddy 2.10+).
+ # Wildcard DNS-01 cert; subdomain sites below reuse it (Caddy 2.10+).
certificateVirtualHost = {
hostName = "*.${tailnetDomain}";
extraConfig = ''
modules/services/forgejo/runner.nix
@@ -82,16 +82,14 @@
then "podman*"
else "podman+";
in {
- # If you would like to use docker runners in combination with cache actions,
- # be sure to add docker bridge interfaces “br-*” to the firewalls’ trusted interfaces.
+ # Docker runners with cache actions need their "br-*" bridges trusted.
# See https://forgejo.org/docs/next/admin/actions/runner-installation/#nixos
networking.firewall.trustedInterfaces =
if (config.networking.nftables.enable)
then ["br-*"]
else ["br-+"];
- # The cache proxy is bound on the host side of the podman bridge networks
- # the job containers run on, so it must be reachable from them.
+ # The cache proxy must be reachable from job containers on podman bridges.
networking.firewall.interfaces."${matchPodmanNetwork}".allowedTCPPorts = cacheProxyPorts;
services.forgejo-runner.instances =
modules/services/forgejo/server.nix
@@ -61,9 +61,7 @@
DOMAIN = hostCfg.domain;
HTTP_ADDR = hostCfg.address;
HTTP_PORT = hostCfg.port;
- # Tailnet deployments (domain under the tailnet domain) use the
- # tailnet SSH daemon on 22; public deployments use the openssh
- # server port.
+ # Tailnet deployments use port 22, public ones the openssh port.
SSH_PORT =
if lib.hasSuffix ".${den.aspects.services.caddy.tailnetDomain}" hostCfg.domain
then 22
modules/services/headscale/_lib.nix
@@ -52,9 +52,7 @@
reverseProxy
);
- # Inject the implicit dst (the emitting host's tailscale addresses) into
- # provenance-wrapped grants and dedupe identical entries so that multiple
- # hosts sharing an explicit dst (e.g. a tag) collapse into one rule.
+ # Dedupe grants; dst defaults to the emitting host's tailscale addresses.
mkPolicyGrants = grants:
lib.unique (
map (grant: let
@@ -87,8 +85,7 @@
grants
);
- # Tailnet reverse-proxied domains are served by the host's caddy on the
- # tailnet, so each host with such entries gets a grant for its caddy ports.
+ # Hosts serving tailnet reverse-proxy entries get a grant for caddy's ports.
mkCaddyGrants = reverseProxy:
lib.unique (
map (r: let
modules/services/mihomo/tproxy.nix
@@ -7,14 +7,10 @@
}: let
cfg = host.settings.services.mihomo;
tproxyMark = "666";
- # Marks WAN-bound local traffic in the output chain. Must differ
- # from tproxyMark and its policy rule must stay un-qualified:
- # fib_validate_source() reverse lookups always run with
- # flowi4_iif = lo and (src_valid_mark=1, set by tailscaled) keep
- # the packet mark, so reusing mark 666 or adding "iif lo" would
- # route LAN reverse lookups into table 100 again and drop them
- # as martians. Looped-back self packets skip source validation
- # because their output dst survives loopback_xmit.
+ # Must differ from tproxyMark with an unqualified policy rule:
+ # fib_validate_source() reverse lookups (iif lo, mark kept via
+ # src_valid_mark) would otherwise re-route LAN packets into
+ # table 100 and drop them as martians.
selfMark = "667";
tproxyRules = pkgs.writeText "mihomo-tproxy.nft" ''
table inet mihomo {
@@ -59,10 +55,8 @@
}
set bypass-udp-sports {
type inet_service
- # Tailscale WireGuard sockets on LAN clients. Proxying them
- # breaks NAT endpoint discovery, and the router's own
- # tailscaled already occupies UDP 41641, so mihomo cannot
- # even bind its transparent reply socket (EADDRINUSE).
+ # LAN WireGuard sockets: proxying breaks NAT endpoint
+ # discovery and tailscaled already holds UDP 41641 (EADDRINUSE).
elements = { 41641 }
}
set outbounds {
@@ -117,17 +111,14 @@
"net.ipv4.conf.default.rp_filter" = 0;
};
- # The nftables firewall's strict rpfilter chain (priority mangle + 10)
- # runs right after tproxy-prerouting (priority mangle) and does
- # `fib saddr . mark . iif oif` with the tproxy mark set: the lookup
- # hits table 100 (local default dev lo), oif=lo never equals iif,
- # so every tproxied packet would be dropped without this exemption.
+ # The rpfilter chain's `fib saddr . mark` with the tproxy mark hits
+ # table 100 (dev lo) where oif never equals iif; exempt marked
+ # packets or every tproxied packet gets dropped.
networking.firewall.extraReversePathFilterRules = "meta mark { ${tproxyMark}, ${selfMark} } accept";
services.mihomo = {
- # tunMode grants CAP_NET_ADMIN + PrivateUsers=false needed for
- # IP_TRANSPARENT. tun.enable = false keeps the actual TUN
- # device off — traffic interception is done via nftables instead.
+ # tunMode grants CAP_NET_ADMIN (no PrivateUsers) for IP_TRANSPARENT;
+ # the TUN device stays off — nftables intercepts traffic.
tunMode = true;
config = {
tun.enable = lib.mkForce false;
@@ -138,14 +129,10 @@
systemd.services.mihomo = {
serviceConfig = let
ip = lib.getExe' pkgs.iproute2 "ip";
- # tailscaled sets net.ipv4.conf.all.src_valid_mark=1, making
- # fib_validate_source() keep the fwmark during its reverse
- # lookup. Without an iif qualifier that lookup would also hit
- # table 100 (local default dev lo -> RTN_LOCAL, not
- # RTN_UNICAST) and every marked packet would be dropped as a
- # martian. The reverse lookup runs with flowi4_iif = lo, so
- # restricting the rule to LAN ingress keeps it out of source
- # validation while forward lookups from LAN still match.
+ # fib_validate_source() keeps the fwmark (src_valid_mark), so
+ # reverse lookups (iif lo) would hit table 100 (RTN_LOCAL) and
+ # drop as martians; qualifying the rule by LAN ingress keeps
+ # forward lookups matching while reverse ones miss it.
ipRulesAdd =
lib.concatMapStrings (ifname: ''
${ip} rule add fwmark ${tproxyMark} iif ${ifname} lookup 100 pref 5000 2>/dev/null || true
modules/users/schema.nix
@@ -3,8 +3,7 @@
config,
...
}: {
- # Share a portion of the user schema across every host that declares
- # the same user. Hosts without the user stay unaffected.
+ # Shared user schema across every host
options.den.users = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule ({name, ...}: {
@@ -69,9 +68,7 @@
};
config.den.schema.user = {
- # `config` referencing the instance's own args are forced while the
- # fixpoint is still being built and recurse, so the per-user name can only
- # be recovered from the freeform merge's option path.
+ # Per-user name is only recoverable from the freeform merge's option path.
options._module.freeformType = lib.mkOption {
apply = t:
if t == null
@@ -80,9 +77,7 @@
lib.mkOptionType {
name = "den-shared-user-freeform";
check = builtins.isAttrs;
- # If without any freeform definitions, the module system will skips
- # the freeform merge entirely, so the shared registry would never
- # reach hosts that declare `users.<name> = {}`.
+ # Freeform merge never runs without any definitions.
merge = loc: defs:
lib.removeAttrs (
lib.recursiveUpdate (config.den.users.${lib.last loc} or {}) (t.merge loc defs)
modules/users/theme.nix
@@ -1,17 +1,10 @@
-# User color theme control system.
-#
-# Users pick a theme via `den.users.<name>.system.settings.themes.theme`.
-# Any aspect may register themed content as `den.aspects.<path>.themes.<name>`
-# sub-aspects (the `themes` key is reserved so the registry is never treated
-# as pipeline content or settings). When the configured theme matches a
-# registered theme name, the controller automatically imports that theme
-# aspect into the user scope.
+# Auto collect all registered theme config from `den.aspects.<path>.themes.<name>`
+# by `den.users.<name>.system.settings.themes.theme`.
{
den,
lib,
...
}: let
- # Keys the scan must not descend into.
scanSkip = [
"_"
"_module"
@@ -26,10 +19,8 @@
"themes"
];
- # Normalize a navigated aspect ref into `{ name, meta.provider }` form so
- # guard-time hasAspect (identity.key) matches the identity the walker
- # assigns to the same node (top-level aspects already carry name/meta;
- # nested content wrappers only carry __provider).
+ # Navigated refs lack name/meta; derive them from __provider so
+ # guard-time hasAspect matches what the walker assigns.
normalizeRef = v:
if
builtins.isAttrs v
@@ -48,8 +39,7 @@
}
else v;
- # Collect every `themes.<name>` aspect in the tree. One scan at module
- # evaluation, reused by every user scope.
+ # Collect every `themes.<name>` aspect in the tree.
collectThemes = path: node:
if !(builtins.isAttrs node)
then []
@@ -78,9 +68,8 @@
candidates = collectThemes [] den.aspects;
- # Fire at every user (and home) scope. Reads the user's theme choice and
- # re-emits the matching registrations as guarded aspects — an aspect only
- # activates when its owner (`xyz`) is part of the user's or host's tree.
+ # Re-emit the user's matching theme registrations as aspects guarded
+ # on their owner.
controller = {
host,
user,