Compare commits

..
Author SHA1 Message Date
finn.markwitzandClaude Sonnet 5 202eca2d03 fix(homepage): merge service groups by name to stop manual entries being dropped
homepage-dashboard resolves services.yaml groups by name using first-match
lookup, so the module-contributed "Services" group and manual-services.nix's
"Services" group (two separate top-level YAML list entries) caused the
second one to be silently discarded. Merge by group name before handing the
list to services.homepage-dashboard.services.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbLevsLozTCcCWe1SoUGe1
2026-09-21 15:27:07 +02:00
finn.markwitz 2ff3042cd9 Merge pull request 'feat(mibook): add swapfile and nodejs dev tooling' (#13) from feat/mibook-swapfile into main
Reviewed-on: #13
2026-09-21 15:21:22 +02:00
finn.markwitzandClaude Sonnet 5 d219f2b61c feat(mibook): add swapfile and nodejs dev tooling
Add an 8G swapfile for headroom during nix builds (7.7G RAM, no prior
swap) and bring nodejs (bundles npm) into the development profile.
Also picks up latest flake.lock input updates.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5fRUDLRzDhoKhEqa69cyR
2026-09-21 15:13:03 +02:00
finn.markwitz 5a2a649244 feat: Sops for Jupiter Setup 2026-09-19 18:48:53 +02:00
finn.markwitz 4691eb1aa4 Merge pull request 'Feat/sops nix' (#12) from feat/sops-nix into main
Reviewed-on: #12
2026-09-14 13:06:45 +02:00
finn.markwitz 9dbd6ff71d Merge branch 'main' into feat/sops-nix 2026-09-14 13:06:33 +02:00
finn.markwitz b96d30edd7 config: Futher Poweroptions to deny hibernate 2026-09-03 08:19:07 +02:00
finn.markwitz a7ffcc4648 feat: Enable Claude on Jupiter 2026-09-03 08:18:39 +02:00
finn.markwitz 652630cfe4 config: Jupiter Domain 2026-08-13 17:22:58 +02:00
finn.markwitz abe2431dbb config: Local DNS for Homepage Dashboard 2026-08-13 17:21:08 +02:00
8 changed files with 146 additions and 108 deletions
+7 -5
View File
@@ -1,7 +1,7 @@
keys: keys:
- &admin_finn age1a0v442ct2qfjcdhyktn25spw02yq4fl08plrrvuradwwxzsle4mq8thln4 - &admin_finn age1a0v442ct2qfjcdhyktn25spw02yq4fl08plrrvuradwwxzsle4mq8thln4
- &host_mibook age1n3uqtee5ymykrls6k04dznq50cwmj99hw7ny83yxjndt4y2ncf5qhjwa2p - &host_mibook age1n3uqtee5ymykrls6k04dznq50cwmj99hw7ny83yxjndt4y2ncf5qhjwa2p
# - &host_jupiter age1... # TODO: Phase 9, see docs/secrets.md - &host_jupiter age15uxuaxhn2hr4ypmgll36fwnkdkvv6gynvlxuc2p8ek0enmxmr5rsvlq0r2
creation_rules: creation_rules:
- path_regex: ^secrets/mibook\.yaml$ - path_regex: ^secrets/mibook\.yaml$
@@ -9,9 +9,11 @@ creation_rules:
- age: - age:
- *admin_finn - *admin_finn
- *host_mibook - *host_mibook
# - path_regex: ^secrets/jupiter\.yaml$ # TODO: Phase 9 - path_regex: ^secrets/jupiter\.yaml$
# key_groups: key_groups:
# - age: [ *admin_finn, *host_jupiter ] - age:
# - path_regex: ^secrets/common\.yaml$ # TODO: Phase 9 - *admin_finn
- *host_jupiter
# - path_regex: ^secrets/common\.yaml$ # TODO: only if/when a secret needs to be shared across both hosts
# key_groups: # key_groups:
# - age: [ *admin_finn, *host_mibook, *host_jupiter ] # - age: [ *admin_finn, *host_mibook, *host_jupiter ]
Generated
+36 -36
View File
@@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1785627969, "lastModified": 1788450739,
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=", "narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a", "rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -63,11 +63,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1782949081, "lastModified": 1785627969,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=", "narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e", "rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -87,11 +87,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1783008725, "lastModified": 1784288435,
"narHash": "sha256-jGiy6+sxjNWXSjp25uoJuNfyH9zBK1PEDY0lVoL4ibQ=", "narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "bca82caa46d5ec0f5d422c61fb1e30bc51313cbe", "rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -113,11 +113,11 @@
"uv2nix": "uv2nix" "uv2nix": "uv2nix"
}, },
"locked": { "locked": {
"lastModified": 1788877844, "lastModified": 1789361774,
"narHash": "sha256-9Ik9egIxYhW/IDw1mz/dYN0IqLTP3bEe+LdEFtP9FWM=", "narHash": "sha256-ashkMxyQelHq7T4AcJ0Zwuzm9/jMR3hTCWW+2FPwlDg=",
"owner": "NousResearch", "owner": "NousResearch",
"repo": "hermes-agent", "repo": "hermes-agent",
"rev": "c8aa5608c24e3636e77c267650c0f1f52e44adb0", "rev": "5eb99eb2844b22ebb723711b8e6a0bbb80bb5f04",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -157,11 +157,11 @@
"nixpkgs-regression": "nixpkgs-regression" "nixpkgs-regression": "nixpkgs-regression"
}, },
"locked": { "locked": {
"lastModified": 1787274306, "lastModified": 1789352761,
"narHash": "sha256-Qg9f9td5iphUWSQS6zmvyZWO1F+D7j8Z3U6dGyUTg08=", "narHash": "sha256-jAcDj4iXkbxa5AlooMRZj7H3E81Cz/q7yN/bX7GPFSU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nix", "repo": "nix",
"rev": "649e823fb24ed118d72e613be35fa8ea1b64afe7", "rev": "920f6956361496578b8a97ae04ec5f400d729ab4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -211,11 +211,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1787144466, "lastModified": 1789127271,
"narHash": "sha256-HHfv2/HkNSKbbSyU9iD/g8lbP6r4tl33sSw1W4rXCk0=", "narHash": "sha256-vL/Fpo7Zxa7wvO8V5FAToUBvP+K3XsMB1wUglTtd/J0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "0471accf8d0a8210b31d947497d179ecc99e0021", "rev": "24cfdc1f9344b90a1eee329a3906e2f39f4d0f1e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -226,11 +226,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1783148766, "lastModified": 1788921488,
"narHash": "sha256-H9+N+GFtsbVC8ZniHliChM7ndizxtqVZs6bnGOLM3WQ=", "narHash": "sha256-JLopN/XSodSsJIZ7v7tEjpPZ+QXc883s3HoUIxHD8Io=",
"rev": "a50de1b7d8a586adc18d2395c19de7d6058e6030", "rev": "6aefcda9401be8acc2b74244fb3b37520ea1f0a8",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.4193.a50de1b7d8a5/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.9440.6aefcda9401b/nixexprs.tar.xz"
}, },
"original": { "original": {
"type": "tarball", "type": "tarball",
@@ -255,11 +255,11 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1785031560, "lastModified": 1788057806,
"narHash": "sha256-OmshNvn2vupOFpYinLUu+1Dnpu4n7Q5N3ggGVNHpkUI=", "narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "0e79af5e3d4dcfcd676ab5ba3f95d2e3352e078c", "rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -286,11 +286,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1787135253, "lastModified": 1789286504,
"narHash": "sha256-M5/r2v++FjVhdsxXYMb4BDJ5YLAdCWFt3aZotcshocA=", "narHash": "sha256-3i80JnZ9pZ7tWReb/Sqpc32cfzffCu8TWLnbnH0hN9c=",
"rev": "ffb3c9b700e759be2ef13237c9d8f953b32a1e46", "rev": "ef34387ddd751e1ab8857adf4676492d32eb24ec",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1058091.ffb3c9b700e7/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1073009.ef34387ddd75/nixexprs.tar.xz"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
@@ -313,11 +313,11 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1787101114, "lastModified": 1789114715,
"narHash": "sha256-BA7sSNjLDuPGSOYBGpr6WQjke1MQ8AZpJ8GlYZM/mOc=", "narHash": "sha256-eeH+/1GxEjw8NCObjaHVIy3YvCQ4Qckrx4LZj5xBaU4=",
"rev": "b18a4b905f8d028dc4476412e6d6891728695379", "rev": "21a67dc470149f337cecafbe965d8d252a390518",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.8045.b18a4b905f8d/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.9592.21a67dc47014/nixexprs.tar.xz"
}, },
"original": { "original": {
"id": "nixpkgs", "id": "nixpkgs",
@@ -415,11 +415,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788337237, "lastModified": 1788914643,
"narHash": "sha256-gkSH8VUtCo6hnysNmb9DbTuDepH2t5pv+QWjP75xKAk=", "narHash": "sha256-4GuMPW90JSxXWDPUB9M+1m7fYbe3H0apOd86/zBQ2Kw=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "fbf759290e0cb0a98dfc813a4eb7d53ad1dacb57", "rev": "13616fff713a9f94055c66f15687ebdc17a335df",
"type": "github" "type": "github"
}, },
"original": { "original": {
+12 -1
View File
@@ -49,11 +49,22 @@
# Try fix wifi disconnect # Try fix wifi disconnect
networking.networkmanager.wifi.powersave = false; networking.networkmanager.wifi.powersave = false;
# Disable hibernate completely # Disable hibernate and suspend completely - this is a headless server.
# The board exposes a phantom ACPI lid switch (PNP0C0D) that fires a
# "closed" event when the HDMI cable is unplugged, which used to trigger
# a real suspend via logind's default HandleLidSwitch=suspend.
powerManagement.enable = true; powerManagement.enable = true;
systemd.targets."hibernate".enable = false; systemd.targets."hibernate".enable = false;
systemd.targets."hybrid-sleep".enable = false; systemd.targets."hybrid-sleep".enable = false;
systemd.targets."suspend-then-hibernate".enable = false; systemd.targets."suspend-then-hibernate".enable = false;
systemd.targets."suspend".enable = false;
systemd.targets."sleep".enable = false;
services.logind.settings.Login = {
HandleLidSwitch = "ignore";
HandleLidSwitchExternalPower = "ignore";
HandleLidSwitchDocked = "ignore";
};
# Optional: kernel parameter to fully disable hibernation # Optional: kernel parameter to fully disable hibernation
boot.kernelParams = [ "nohibernate" ]; boot.kernelParams = [ "nohibernate" ];
+1
View File
@@ -10,6 +10,7 @@ _: {
}; };
actual.enable = false; actual.enable = false;
audiobookshelf.enable = true; audiobookshelf.enable = true;
claude-code.enable = true;
kde-desktop.enable = false; kde-desktop.enable = false;
radarr.enable = true; radarr.enable = true;
docker.enable = true; docker.enable = true;
+1 -1
View File
@@ -4,7 +4,7 @@ _: {
]; ];
networking = { networking = {
domain = "jupiter.solar.internal"; domain = "jupiter";
search = [ "jupiter.solar.internal" ]; search = [ "jupiter.solar.internal" ];
}; };
} }
+7
View File
@@ -16,6 +16,13 @@
]; ];
networking.hostName = "mibook"; networking.hostName = "mibook";
swapDevices = [
{
device = "/var/lib/swapfile";
size = 8 * 1024; # 8G, headroom for nix builds (7.7G RAM, no prior swap)
}
];
boot.loader.grub = { boot.loader.grub = {
enable = true; enable = true;
device = "/dev/nvme0n1"; device = "/dev/nvme0n1";
@@ -21,6 +21,7 @@ in
neovim neovim
# jetbrains.idea-ultimate # jetbrains.idea-ultimate
go go
nodejs # bundles npm
(python3.withPackages ( (python3.withPackages (
ps: with ps; [ ps: with ps; [
jupyter # notebooks jupyter # notebooks
+31 -15
View File
@@ -12,21 +12,15 @@ let
manualServices = import ./manual-services.nix; manualServices = import ./manual-services.nix;
manualWidgets = import ./manual-widgets.nix; manualWidgets = import ./manual-widgets.nix;
groupedServices = groupedServices = lib.foldl' (
lib.foldl'
(
acc: entry: acc: entry:
acc acc
// { // {
${entry.group} = (acc.${entry.group} or [ ]) ++ [ entry ]; ${entry.group} = (acc.${entry.group} or [ ]) ++ [ entry ];
} }
) ) { } config.my.homepage.services;
{ }
config.my.homepage.services;
homepageServices = lib.mapAttrsToList ( homepageServices = lib.mapAttrsToList (group: entries: {
group: entries:
{
${group} = map ( ${group} = map (
entry: entry:
let let
@@ -39,12 +33,30 @@ let
${entry.name} = lib.filterAttrs (_: value: value != null) service; ${entry.name} = lib.filterAttrs (_: value: value != null) service;
} }
) entries; ) entries;
} }) groupedServices;
) groupedServices;
# homepage-dashboard keeps only the first services.yaml list entry with a
# given group name (first match wins), so module-contributed and manual
# groups sharing a name (e.g. "Services") must be merged here rather than
# simply concatenated, or the later ones are silently dropped.
mergeGroups =
lists:
let
merged = lib.foldl' (
acc: groupAttrs:
let
groupName = builtins.head (builtins.attrNames groupAttrs);
in
acc // { ${groupName} = (acc.${groupName} or [ ]) ++ groupAttrs.${groupName}; }
) { } lists;
in
lib.mapAttrsToList (name: entries: { ${name} = entries; }) merged;
in in
{ {
options.my.homepage.services = with lib; mkOption { options.my.homepage.services =
with lib;
mkOption {
type = types.listOf ( type = types.listOf (
types.submodule { types.submodule {
options = { options = {
@@ -80,7 +92,9 @@ in
description = "Merged homepage service metadata contributed by repo modules."; description = "Merged homepage service metadata contributed by repo modules.";
}; };
options.my.homepage.widgets = with lib; mkOption { options.my.homepage.widgets =
with lib;
mkOption {
type = types.listOf types.attrs; type = types.listOf types.attrs;
default = [ ]; default = [ ];
description = "Widget definitions passed directly to services.homepage-dashboard.widgets. Each entry is an attrset like { resources = { cpu = true; memory = true; }; }."; description = "Widget definitions passed directly to services.homepage-dashboard.widgets. Each entry is an attrset like { resources = { cpu = true; memory = true; }; }.";
@@ -91,12 +105,14 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ dashboardPort ];
services.homepage-dashboard = { services.homepage-dashboard = {
enable = true; enable = true;
listenPort = dashboardPort; listenPort = dashboardPort;
allowedHosts = "${dashboardHost}:${toString dashboardPort},localhost:${toString dashboardPort},127.0.0.1:${toString dashboardPort},jupiter.solar.internal:${toString dashboardPort}"; allowedHosts = "${dashboardHost}:${toString dashboardPort},192.168.178.65:${toString dashboardPort},localhost:${toString dashboardPort},127.0.0.1:${toString dashboardPort},jupiter:${toString dashboardPort},jupiter.solar.internal:${toString dashboardPort}";
bookmarks = import ./bookmarks.nix; bookmarks = import ./bookmarks.nix;
services = homepageServices ++ manualServices; services = mergeGroups (homepageServices ++ manualServices);
widgets = config.my.homepage.widgets ++ manualWidgets; widgets = config.my.homepage.widgets ++ manualWidgets;
}; };