Compare commits

...
5 Commits
Author SHA1 Message Date
hektor 5c587dc5a7 feat(reference-manager): replace 'Zotero' with 'papis'
check / lint (push) Successful in 6s
2026-11-08 22:55:01 +01:00
hektor fce7a277a7 feat(lynis): declare 'lynis' module (with profile + skips) 2026-11-08 22:55:01 +01:00
hektor 21e5b62dfa feat(hecuba): switch to UEFI (and modularize)
check / lint (push) Successful in 9s
2026-11-08 13:41:38 +01:00
hektor e794e6c0e2 feat(hardening): declare hardening module using 'nix-mineral' 2026-11-08 13:41:38 +01:00
hektor 209ab8ba30 feat(reference-manager): nixify Zotero Better BibTeX
check / lint (push) Successful in 17s
2026-11-06 21:04:16 +01:00
16 changed files with 960 additions and 364 deletions

No files matched your search

-101
View File
@@ -1,101 +0,0 @@
{
"config": {
"options": {},
"preferences": {
"ascii": "",
"asciiBibLaTeX": false,
"asciiBibTeX": true,
"autoAbbrev": false,
"autoExport": "immediate",
"autoExportDelay": 5,
"autoExportIdleWait": 10,
"autoExportPathReplaceDiacritics": false,
"autoExportPathReplaceDirSep": "-",
"autoExportPathReplaceSpace": " ",
"automaticTags": true,
"auxImport": false,
"baseAttachmentPath": "/home/h/doc/readings",
"biblatexExtendedDateFormat": true,
"biblatexExtendedNameFormat": true,
"biblatexExtractEprint": true,
"biblatexUsePrefix": true,
"bibtexEditionOrdinal": false,
"bibtexParticleNoOp": false,
"bibtexURL": "off",
"cache": true,
"cacheDelete": false,
"charmap": "",
"chinese": false,
"chineseSplitName": true,
"citeCommand": "cite",
"citekeyCaseInsensitive": true,
"citekeyFold": true,
"citekeyFormat": "zotero.clean",
"citekeyFormatEditing": "zotero.clean",
"citekeySearch": false,
"citekeyUnsafeChars": "\\\"#%'(),={}~",
"csquotes": "",
"DOIandURL": "both",
"exportBibTeXStrings": "off",
"exportBraceProtection": true,
"exportSort": "citekey",
"exportTitleCase": true,
"extraMergeCitekeys": false,
"extraMergeCSL": false,
"extraMergeTeX": false,
"fillKeyAfter": 1,
"git": "config",
"import": true,
"importBibTeXStrings": true,
"importCaseProtection": "as-needed",
"importCitationKey": true,
"importDetectURLs": true,
"importExtra": true,
"importJabRefAbbreviations": true,
"importJabRefStrings": true,
"importNoteToExtra": "",
"importPlaceEvent": "inproceedings,conference,presentation,talk",
"importSentenceCase": "on+guess",
"importSentenceCaseQuoted": true,
"importUnknownTexCommand": "ignore",
"itemObserverDelay": 5,
"jabrefFormat": 0,
"japanese": false,
"keyScope": "global",
"language": "langid",
"logEvents": false,
"mapMath": "",
"mapText": "",
"packages": "",
"parseParticles": true,
"patchDates": "dateadded=dateAdded, date-added=dateAdded, datemodified=dateModified, date-modified=dateModified",
"platform": "lin",
"postscript": "",
"postscriptOverride": "",
"preferencesOverride": "",
"qualityReport": false,
"quickCopyEta": "",
"quickCopyMode": "citekeys",
"quickCopyOrgMode": "zotero",
"quickCopyPandocBrackets": false,
"quickCopySelectLink": "zotero",
"rawImports": false,
"rawLaTag": "#LaTeX",
"relativeFilePaths": false,
"remigrate": false,
"resetKeyOnChange": false,
"scrubDatabase": false,
"separatorList": "and",
"separatorNames": "and",
"skipFields": "",
"skipWords": "a,ab,aboard,about,above,across,after,against,al,along,amid,among,an,and,anti,around,as,at,before,behind,below,beneath,beside,besides,between,beyond,but,by,d,da,das,de,del,dell,dello,dei,degli,della,dell,delle,dem,den,der,des,despite,die,do,down,du,during,ein,eine,einem,einen,einer,eines,el,en,et,except,for,from,gli,i,il,in,inside,into,is,l,la,las,le,les,like,lo,los,near,nor,of,off,on,onto,or,over,past,per,plus,round,save,since,so,some,sur,than,the,through,to,toward,towards,un,una,unas,under,underneath,une,unlike,uno,unos,until,up,upon,versus,via,von,while,with,within,without,yet,zu,zum",
"startupProgress": "popup",
"strings": "",
"stringsOverride": "",
"testing": false,
"verbatimFields": "url,doi,file,pdf,ids,eprint,/^verb[a-z]$/,groups,/^citeulike-linkout-[0-9]+$/, /^bdsk-url-[0-9]+$/",
"warnBulkModify": 10,
"warnTitleCased": false
}
}
}
Generated
+71 -1
View File
@@ -226,6 +226,20 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat_3": {
"locked": {
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"revCount": 69,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
}
},
"flake-parts": { "flake-parts": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
@@ -248,6 +262,24 @@
} }
}, },
"flake-parts_2": { "flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_3": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"stylix", "stylix",
@@ -417,6 +449,28 @@
"type": "github" "type": "github"
} }
}, },
"nix-mineral": {
"inputs": {
"flake-compat": "flake-compat_3",
"flake-parts": "flake-parts_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1792339378,
"narHash": "sha256-2GSp02yOdG9BSvb0c9SW1nsMnUpjR1/WBKskGz0D+8g=",
"owner": "cynicsketch",
"repo": "nix-mineral",
"rev": "bcd361bb18fccc4297d668aa5e0d5e4ebd4918ce",
"type": "github"
},
"original": {
"owner": "cynicsketch",
"repo": "nix-mineral",
"type": "github"
}
},
"nix-secrets": { "nix-secrets": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -503,6 +557,21 @@
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
} }
}, },
"nixpkgs-lib": {
"locked": {
"lastModified": 1777168982,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1792210252, "lastModified": 1792210252,
@@ -639,6 +708,7 @@
"git-hooks": "git-hooks", "git-hooks": "git-hooks",
"hecuba-services": "hecuba-services", "hecuba-services": "hecuba-services",
"home-manager": "home-manager", "home-manager": "home-manager",
"nix-mineral": "nix-mineral",
"nix-secrets": "nix-secrets", "nix-secrets": "nix-secrets",
"nixgl": "nixgl", "nixgl": "nixgl",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
@@ -714,7 +784,7 @@
"base16-helix": "base16-helix", "base16-helix": "base16-helix",
"base16-vim": "base16-vim", "base16-vim": "base16-vim",
"firefox-gnome-theme": "firefox-gnome-theme", "firefox-gnome-theme": "firefox-gnome-theme",
"flake-parts": "flake-parts_2", "flake-parts": "flake-parts_3",
"gnome-shell": "gnome-shell", "gnome-shell": "gnome-shell",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
+4
View File
@@ -6,6 +6,10 @@
nixos-hardware = { nixos-hardware = {
url = "github:NixOS/nixos-hardware/master"; url = "github:NixOS/nixos-hardware/master";
}; };
nix-mineral = {
url = "github:cynicsketch/nix-mineral";
inputs.nixpkgs.follows = "nixpkgs";
};
disko = { disko = {
url = "github:nix-community/disko/latest"; url = "github:nix-community/disko/latest";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
+1 -1
View File
@@ -47,6 +47,7 @@
nfc.enable = true; nfc.enable = true;
nvim.enable = true; nvim.enable = true;
pandoc.enable = true; pandoc.enable = true;
reference-manager.enable = true;
secrets.enable = true; secrets.enable = true;
shell = { shell = {
enable = true; enable = true;
@@ -56,7 +57,6 @@
deploy.enable = true; deploy.enable = true;
taskwarrior.enable = true; taskwarrior.enable = true;
terminal.enable = true; terminal.enable = true;
reference-manager.enable = true;
programs = { programs = {
home-manager.enable = true; home-manager.enable = true;
+5
View File
@@ -20,6 +20,11 @@
default = config.home.username; default = config.home.username;
}; };
readings.path = lib.mkOption {
default = "${config.home.homeDirectory}/doc/readings";
type = lib.types.str;
};
nixgl.wrap = lib.mkOption { nixgl.wrap = lib.mkOption {
type = lib.types.functionTo lib.types.package; type = lib.types.functionTo lib.types.package;
default = if config.lib ? nixGL then config.lib.nixGL.wrap else lib.id; default = if config.lib ? nixGL then config.lib.nixGL.wrap else lib.id;
+16 -33
View File
@@ -1,37 +1,20 @@
{ config, lib, ... }:
{ {
config, options.reference-manager.enable = lib.mkOption { default = false; };
inputs,
lib,
pkgs,
...
}:
let config = lib.mkIf config.reference-manager.enable {
cfg = config.reference-manager; programs.papis = {
enable = true;
hasBrowser = libraries.papers = {
name: isDefault = true;
lib.elem name [ settings.dir = "${config.readings.path}/papis";
config.browser.primary };
config.browser.secondary settings = {
]; unique-document-keys = ''["ref"]'';
bibtex-ignore-keys = ''["abstract", "note", "csl_type", "collections", "tags", "files"]'';
zoteroConnector = add-folder-name = "{doc[ref]}";
inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}.zotero-connector; };
zoteroAddonId = "zotero@chnm.gmu.edu"; };
connectorExtension = {
profiles.default.extensions.packages = [ zoteroConnector ];
policies.ExtensionSettings.${zoteroAddonId}.default_area = "navbar";
};
in
{
options.reference-manager.enable = lib.mkEnableOption "reference manager (Zotero)";
config = lib.mkIf cfg.enable {
home.packages = [ pkgs.zotero ];
programs.firefox = lib.mkIf (hasBrowser "firefox") connectorExtension;
programs.librewolf = lib.mkIf (hasBrowser "librewolf") connectorExtension;
}; };
} }
+12 -11
View File
@@ -24,7 +24,11 @@ in
inherit (meta) host; inherit (meta) host;
hardware.facter.reportPath = ./facter.json; hardware = {
facter.reportPath = ./facter.json;
cpu.intel.updateMicrocode = true;
hackrf.enable = true;
};
"ai-tools".enable = true; "ai-tools".enable = true;
anki.enable = true; anki.enable = true;
@@ -43,6 +47,7 @@ in
}; };
keyboard.enable = true; keyboard.enable = true;
localization.enable = true; localization.enable = true;
lynis.enable = true;
memory.zram.enable = true; memory.zram.enable = true;
my = { my = {
fonts.enable = true; fonts.enable = true;
@@ -56,6 +61,7 @@ in
desktop.ly.enable = true; desktop.ly.enable = true;
docker.enable = true; docker.enable = true;
k8s.enable = true; k8s.enable = true;
hardening.enable = true;
hcloud.enable = true; hcloud.enable = true;
networking = { networking = {
enable = true; enable = true;
@@ -76,15 +82,12 @@ in
}; };
ssh.enable = true; ssh.enable = true;
storage.enable = true; storage.enable = true;
syncthing = { readings = {
enable = true; enable = true;
folders.readings = { peers = [
path = "/home/${config.host.username}/doc/readings"; "astyanax"
devices = [ "boox"
"astyanax" ];
"boox"
];
};
}; };
tailscale.enable = true; tailscale.enable = true;
taskwarrior.enable = true; taskwarrior.enable = true;
@@ -111,8 +114,6 @@ in
}; };
}; };
hardware.cpu.intel.updateMicrocode = true;
boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
services.locate = { services.locate = {
+7 -8
View File
@@ -40,8 +40,10 @@ in
model = "arc-140t"; model = "arc-140t";
}; };
}; };
hardening.enable = true;
keyboard.enable = true; keyboard.enable = true;
localization.enable = true; localization.enable = true;
lynis.enable = true;
memory = { memory = {
oom.enable = true; oom.enable = true;
zram.enable = true; zram.enable = true;
@@ -60,15 +62,12 @@ in
secrets.enable = true; secrets.enable = true;
ssh.enable = true; ssh.enable = true;
storage.enable = true; storage.enable = true;
syncthing = { readings = {
enable = true; enable = true;
folders.readings = { peers = [
path = "/home/${config.host.username}/doc/readings"; "andromache"
devices = [ "boox"
"andromache" ];
"boox"
];
};
}; };
taskwarrior.enable = true; taskwarrior.enable = true;
wol.wakes = [ "andromache" ]; wol.wakes = [ "andromache" ];
+1
View File
@@ -14,6 +14,7 @@ in
inherit (meta) host; inherit (meta) host;
lynis.enable = true;
ssh.enable = true; ssh.enable = true;
tailscale.enable = true; tailscale.enable = true;
wol.wakes = [ "andromache" ]; wol.wakes = [ "andromache" ];
+1
View File
@@ -14,6 +14,7 @@ in
inherit (meta) host; inherit (meta) host;
lynis.enable = true;
ssh.enable = true; ssh.enable = true;
tailscale.enable = true; tailscale.enable = true;
+54 -15
View File
@@ -1,5 +1,6 @@
{ {
inputs, inputs,
lib,
pkgs, pkgs,
config, config,
... ...
@@ -9,6 +10,8 @@
let let
meta = import ./meta.nix; meta = import ./meta.nix;
bios = config.bootloader.firmware == "bios";
uefi = config.bootloader.firmware == "uefi";
in in
{ {
imports = [ imports = [
@@ -23,7 +26,13 @@ in
hardware.facter.reportPath = ./facter.json; hardware.facter.reportPath = ./facter.json;
bootloader = {
enable = true;
firmware = "uefi";
};
docker.enable = true; docker.enable = true;
hardening.enable = true;
lynis.enable = true;
secrets.enable = true; secrets.enable = true;
ssh.enable = true; ssh.enable = true;
tailscale.enable = true; tailscale.enable = true;
@@ -57,22 +66,56 @@ in
networking.hostName = config.host.name; networking.hostName = config.host.name;
fileSystems."/" = { boot.loader.grub.device = lib.mkIf bios "/dev/sda";
device = "/dev/disk/by-label/nixos"; boot.loader.systemd-boot.bootCounting.enable = lib.mkIf uefi true;
fsType = "ext4";
fileSystems = lib.mkIf bios {
"/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "ext4";
};
}; };
fileSystems."/boot" = { swapDevices = lib.mkIf bios [
device = "/dev/disk/by-label/boot";
fsType = "ext4";
};
swapDevices = [
{ {
device = "/dev/disk/by-label/swap"; device = "/dev/disk/by-label/swap";
} }
]; ];
boot.loader.grub.enable = true; disko.devices.disk.main = lib.mkIf uefi {
boot.loader.grub.device = "/dev/sda"; type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
swap = {
size = "4G";
content.type = "swap";
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
users.users = { users.users = {
root.hashedPassword = "!"; root.hashedPassword = "!";
@@ -84,11 +127,7 @@ in
security.sudo.wheelNeedsPassword = false; security.sudo.wheelNeedsPassword = false;
nix.gc = { nix.gc.options = "--delete-older-than 14d";
automatic = true;
dates = "weekly";
options = "-d";
};
virtualisation.docker.autoPrune = { virtualisation.docker.autoPrune = {
enable = true; enable = true;
+531 -183
View File
File diff suppressed because it is too large. Load diff
+25 -11
View File
@@ -1,17 +1,31 @@
{ lib, config, ... }: { lib, config, ... }:
{ {
options.bootloader.enable = lib.mkOption { options.bootloader = {
default = false; enable = lib.mkOption {
type = lib.types.bool; default = false;
}; type = lib.types.bool;
};
config = lib.mkIf config.bootloader.enable { firmware = lib.mkOption {
boot = { default = "uefi";
loader = { type = lib.types.enum [
systemd-boot.enable = true; "bios"
efi.canTouchEfiVariables = true; "uefi"
}; ];
}; };
}; };
config = lib.mkIf config.bootloader.enable (
lib.mkMerge [
(lib.mkIf (config.bootloader.firmware == "uefi") {
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
})
(lib.mkIf (config.bootloader.firmware == "bios") {
boot.loader.grub.enable = true;
})
]
);
} }
+124
View File
@@ -0,0 +1,124 @@
{
inputs,
config,
lib,
...
}:
let
unusedProtocols = [
"dccp"
"sctp"
"rds"
"tipc"
];
wakeOnLan = lib.any (iface: iface.wakeOnLan.enable) (lib.attrValues config.networking.interfaces);
randomMac = config.host.role == "laptop" && !wakeOnLan;
desktop = {
preset = [
"compatibility"
]
++ lib.optional config.gaming.enable "performance"; # prioritized (order matters) <https://github.com/cynicsketch/nix-mineral#configuring-options>
settings = {
debug.debugfs = true;
etc.harden-bluetooth = false;
};
};
profiles = {
inherit desktop;
laptop = lib.recursiveUpdate desktop {
preset = [
"compatibility"
"performance"
];
settings.kernel.iommu-passthrough = false;
};
server = {
preset = [
"compatibility"
"maximum" # prioritized (order matters) <https://github.com/cynicsketch/nix-mineral#configuring-options>
];
settings = {
debug.quiet-boot = false;
network = {
ipv6-tempaddr = false;
max-addresses = false;
neighbor-solicitations = false;
router-solicitations = false;
};
system = {
lower-address-mmap = false;
multilib = false;
yama = "admin-only";
};
};
extras = {
kernel.warn-panic = false;
system.secure-chrony = null;
network = {
tcp-window-scaling = true;
};
};
};
};
in
{
imports = [ inputs.nix-mineral.nixosModules.nix-mineral ];
options.hardening = {
enable = lib.mkOption {
default = false;
type = lib.types.bool;
};
profile = lib.mkOption {
default = if profiles ? ${config.host.role} then config.host.role else null;
type = lib.types.nullOr (lib.types.enum (lib.attrNames profiles));
};
};
config = lib.mkIf config.hardening.enable {
assertions = [
{
assertion = config.hardening.profile != null;
message = "no 'hardening.profile' for `${config.host.role}` host role";
}
];
boot.blacklistedKernelModules = unusedProtocols;
boot.extraModprobeConfig = lib.concatMapStrings (m: "install ${m} /bin/false\n") unusedProtocols;
hardware.bluetooth.settings.General = lib.mkIf config.hardware.bluetooth.enable {
PairableTimeout = 30;
DiscoverableTimeout = 30;
TemporaryTimeout = 0;
};
networking.wireless.iwd.settings.General.AddressRandomization = lib.mkIf randomMac "network";
nix-mineral = lib.mkMerge [
{
enable = true;
filesystems.normal = {
"/tmp".enable = !config.boot.tmp.useTmpfs;
# only needed for FAT
"/boot".options.umask = lib.mkIf (
!lib.elem (config.fileSystems."/boot".fsType or null) [
"vfat"
"msdos"
"exfat"
]
) (lib.mkForce false);
};
settings = {
etc.harden-gitconfig = false;
kernel.cpu-mitigations = "smt-on";
network = {
ip-forwarding = config.virtualisation.docker.enable || config.services.k3s.enable;
random-mac = randomMac;
router-advertisements = "on";
tcp-sack = true;
};
};
}
(lib.optionalAttrs (config.hardening.profile != null) profiles.${config.hardening.profile})
];
};
}
+63
View File
@@ -0,0 +1,63 @@
{
lib,
config,
pkgs,
...
}:
let
serverRoles = [
"server"
"embedded"
];
server = lib.elem config.host.role serverRoles;
role = if server then "server" else "personal";
forwarding = lib.elem (config.boot.kernel.sysctl."net.ipv4.conf.all.forwarding" or false) [
true
1
"1"
];
in
{
options.lynis = {
enable = lib.mkOption {
default = false;
type = lib.types.bool;
};
skipTests = lib.mkOption {
default = [
"AUTH-9230"
"AUTH-9262"
"AUTH-9284"
"AUTH-9286"
"FILE-6310"
"FILE-6410"
"FINT-4350"
"KRNL-5830"
"NAME-4028"
"PKGS-7398"
"SSH-7408"
"TOOL-5002"
]
++ lib.optionals (!server) [
"ACCT-9622"
"ACCT-9626"
"ACCT-9628"
"HRDN-7222"
"HRDN-7230"
"KRNL-6000:kernel.modules_disabled"
"LOGG-2154"
"USB-1000"
]
++ lib.optional forwarding "KRNL-6000:net.ipv4.conf.all.forwarding";
type = lib.types.listOf lib.types.str;
};
};
config = lib.mkIf config.lynis.enable {
environment.systemPackages = [ pkgs.lynis ];
environment.etc."lynis/custom.prf".text = lib.concatLines (
[ "machine-role=${role}" ] ++ map (t: "skip-test=${t}") config.lynis.skipTests
);
};
}
+45
View File
@@ -0,0 +1,45 @@
{ config, lib, ... }:
let
inherit (config.host) username;
home = config.users.users.${username}.home;
devices = builtins.attrNames (import ../syncthing/devices.nix);
in
{
options.readings = {
enable = lib.mkOption {
default = false;
type = lib.types.bool;
};
path = lib.mkOption {
default = "${home}/doc/readings";
type = lib.types.str;
};
peers = lib.mkOption {
type = lib.types.nonEmptyListOf (lib.types.enum devices);
};
};
config = lib.mkIf config.readings.enable {
assertions = [
{
assertion = !builtins.elem config.host.name config.readings.peers;
message = "'readings.peers' for ${config.host.name} should not include '${config.host.name}'.";
}
];
syncthing = {
enable = true;
folders.readings = {
inherit (config.readings) path;
devices = config.readings.peers;
};
};
home-manager.sharedModules = [
{
readings.path = lib.mkDefault config.readings.path;
}
];
};
}