diff --git a/.sops.yaml b/.sops.yaml index 9b0bac5..63161c3 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,9 +1,11 @@ keys: - &xps13 age1x8qsd7kxxjvan4psvnvua3r0emljsnq07agxnu6jqw56ky8z6faqyjq0e3 - &pi age1y2s7ah49jmhd8n05q7tw0gjcnv3390s0uxp3ewjqueekq7a7rvdqzytgd2 + - &server age107mmu7nkjfpm7ygp25zpj69m06ftckc9gh7a37umkjq0y7ac34msd6uj3u creation_rules: - path_regex: secrets/secrets.yaml$ key_groups: - age: - *xps13 - *pi + - *server diff --git a/devices/server/configuration.nix b/devices/server/configuration.nix index a5612a8..c38eeff 100644 --- a/devices/server/configuration.nix +++ b/devices/server/configuration.nix @@ -1,23 +1,17 @@ -{ - config, - pkgs, - ... -}: let - user = "polen"; +{ config, pkgs, ... }: +let user = "polen"; in { - imports = [ - ./hardware-configuration.nix - ]; + imports = [ ./hardware-configuration.nix ]; boot.loader = { grub = { enable = true; - devices = ["/dev/sda"]; + devices = [ "/dev/sda" ]; }; }; #boot.kernelModules = ["msr"]; - boot.binfmt.emulatedSystems = ["aarch64-linux"]; + boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; virtualisation.docker.enable = true; @@ -70,10 +64,7 @@ in { openFirewall = true; # 11434 host = "0.0.0.0"; syncModels = true; - loadModels = [ - "mistral:7b" - "phi3.5:3.8b" - ]; + loadModels = [ "mistral:7b" "phi3.5:3.8b" ]; }; }; @@ -90,29 +81,19 @@ in { }; prometheus = { enable = true; - exporters = { - node.enable = true; - }; - scrapeConfigs = [ - { - job_name = "node-exporters-lan"; - static_configs = [ - { - targets = ["127.0.0.1:9100"]; - labels = { - instance = "server"; - }; - } - ]; - } - ]; + exporters = { node.enable = true; }; + scrapeConfigs = [{ + job_name = "node-exporters-lan"; + static_configs = [{ + targets = [ "127.0.0.1:9100" ]; + labels = { instance = "server"; }; + }]; + }]; }; }; systemd.services.jellyfin = { - environment = { - DOTNET_SYSTEM_IO_DISABLEFILELOCKING = "1"; - }; + environment = { DOTNET_SYSTEM_IO_DISABLEFILELOCKING = "1"; }; }; # media @@ -158,13 +139,13 @@ in { fileSystems."/mnt/latoure-data" = { device = "latoure.local:/data"; fsType = "nfs"; - options = ["x-systemd.automount" "noauto" "x-systemd.idle-timeout=600"]; + options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ]; }; fileSystems."/mnt/latoure-data1" = { device = "latoure.local:/data1"; fsType = "nfs"; - options = ["_netdev"]; + options = [ "_netdev" ]; }; networking = { @@ -189,12 +170,43 @@ in { time.timeZone = "America/Toronto"; users.users."${user}" = { - extraGroups = ["wheel" "transmission" "jellyfin" "polensky" "docker"]; + extraGroups = [ "wheel" "transmission" "jellyfin" "polensky" "docker" ]; shell = pkgs.zsh; openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC6O2MJqR+P/FwRyVSz1HWYhMtIwh16ozBU71Y2vf0oNDQ6DZ5T8Bvp5/4uSJgS8lOl3qYyNy0e0zJMIyfFVJnu89ycKBEdixA4HqWOUQGiyvn1C4s740jHolOzN1xNB24PDXFz0vHcVb+G5nU/xeKeaq0vrszrkK2zctqXshw94/x3ah0m3fr5CwM4S2RY/VODOdt11fllFEvN8HGE2mQTPn5sJzwtGW20npQ5iJ7ShugPbC4D1G2JU1R7MqkvWEpq9OFVb1prTpJM+i/lcqCn3lBv8XxpKKnD3q+48eeO1geosAsG/kgUWPDildbzcSfytgj7/TCTujx2ow4ZUfS4kWUrNaXM3M99SG61rFN7zLMAv14SOSsgegmX3q0ZAwOieUhCifqIqdfFr5QjEUP11ALofYRC6567X1YrEVXZFFnZSXMKGkBKpTxx0jaTTGnFSd6F49kDlI30cKJnVUgAK5nESissdEFn3UGRSFfxmjZkYvhY5l3LqtbO3kEutJU= polen@polen-xps" ]; }; + + # Luna user for OpenClaw AI assistant + users.users.luna = { + isSystemUser = true; + group = "luna"; + home = "/var/lib/luna"; + createHome = true; + shell = pkgs.bash; + }; + users.groups.luna = { }; + + # Enable lingering for luna's systemd user services (runs at boot without login) + system.activationScripts.enableLingeringLuna = '' + ${pkgs.systemd}/bin/loginctl enable-linger luna || true + ''; + + # SOPS secrets for Luna (OpenClaw) + sops = { + defaultSopsFile = ../../secrets/secrets.yaml; + age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + secrets = { + luna-telegram-token = { + owner = "luna"; + group = "luna"; + }; + luna-gateway-token = { + owner = "luna"; + group = "luna"; + }; + }; + }; environment.systemPackages = with pkgs; [ neovim htop-vim @@ -218,8 +230,8 @@ in { nixpkgs.config.allowUnfree = true; nix = { - settings.experimental-features = ["nix-command" "flakes"]; - settings.trusted-users = ["polen"]; + settings.experimental-features = [ "nix-command" "flakes" ]; + settings.trusted-users = [ "polen" ]; # settings.extra-platforms = config.boot.binfmt.emulatedSystems; gc = { automatic = true; diff --git a/devices/server/luna-documents/AGENTS.md b/devices/server/luna-documents/AGENTS.md new file mode 100644 index 0000000..4673ad7 --- /dev/null +++ b/devices/server/luna-documents/AGENTS.md @@ -0,0 +1,3 @@ +# Luna Agent + +Luna is a homelab assistant. diff --git a/devices/server/luna-documents/SOUL.md b/devices/server/luna-documents/SOUL.md new file mode 100644 index 0000000..a392e27 --- /dev/null +++ b/devices/server/luna-documents/SOUL.md @@ -0,0 +1 @@ +# Soul diff --git a/devices/server/luna-documents/TOOLS.md b/devices/server/luna-documents/TOOLS.md new file mode 100644 index 0000000..e2e6fbb --- /dev/null +++ b/devices/server/luna-documents/TOOLS.md @@ -0,0 +1,3 @@ +# Tools + +Luna can help answer questions about the homelab infrastructure. diff --git a/devices/server/luna.nix b/devices/server/luna.nix new file mode 100644 index 0000000..64f559d --- /dev/null +++ b/devices/server/luna.nix @@ -0,0 +1,43 @@ +{ inputs, config, pkgs, ... }: + +{ + imports = [ inputs.nix-openclaw.homeManagerModules.openclaw ]; + + home.username = "luna"; + home.homeDirectory = "/var/lib/luna"; + home.stateVersion = "25.05"; + + programs.openclaw = { + enable = true; + documents = ./luna-documents; + + config = { + # Use local Ollama - auto-detected at 127.0.0.1:11434 + agents.defaults.model = { + primary = "ollama/mistral:7b"; + fallbacks = [ "ollama/phi3.5:3.8b" ]; + }; + + gateway = { + mode = "local"; + auth = { tokenFile = "/run/secrets/luna-gateway-token"; }; + }; + + channels.telegram = { + tokenFile = "/run/secrets/luna-telegram-token"; + allowFrom = [ + 1268580775 + ]; + groups = { "*" = { requireMention = true; }; }; + }; + }; + + # Plugins useful for homelab Q&A + bundledPlugins = { + summarize.enable = true; # Summarize docs/web pages + oracle.enable = true; # Web search + }; + }; + + programs.home-manager.enable = true; +} diff --git a/flake.lock b/flake.lock index 26645d0..0ab50e3 100644 --- a/flake.lock +++ b/flake.lock @@ -2,7 +2,9 @@ "nodes": { "disko": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { "lastModified": 1769524058, @@ -13,8 +15,9 @@ "type": "github" }, "original": { - "id": "disko", - "type": "indirect" + "owner": "nix-community", + "repo": "disko", + "type": "github" } }, "flake-compat": { @@ -73,6 +76,65 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770586272, + "narHash": "sha256-Ucci8mu8QfxwzyfER2DQDbvW9t1BnTUJhBmY7ybralo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "b1f916ba052341edc1f80d4b2399f1092a4873ca", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_2": { + "inputs": { + "nixpkgs": [ + "nix-openclaw", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767909183, + "narHash": "sha256-u/bcU0xePi5bgNoRsiqSIwaGBwDilKKFTz3g0hqOBAo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "cd6e96d56ed4b2a779ac73a1227e0bb1519b3509", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, "mnw": { "locked": { "lastModified": 1767030222, @@ -113,7 +175,7 @@ }, "nix-darwin": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs" }, "locked": { "lastModified": 1770184146, @@ -129,13 +191,54 @@ "type": "github" } }, + "nix-openclaw": { + "inputs": { + "flake-utils": "flake-utils", + "home-manager": "home-manager_2", + "nix-steipete-tools": "nix-steipete-tools", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770607161, + "narHash": "sha256-D9axajTuNJq1EckiMNfAOEir7z8lbLUGpMmf/UMZvwM=", + "owner": "openclaw", + "repo": "nix-openclaw", + "rev": "4d01743f028500544e814712949e1a658196d27e", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-openclaw", + "type": "github" + } + }, + "nix-steipete-tools": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1770240566, + "narHash": "sha256-fY8t41kMSHu2ovf89mIdvC7vkceroCwKxw/MKVn4rsE=", + "owner": "openclaw", + "repo": "nix-steipete-tools", + "rev": "983210e3b6e9285780e87f48ce9354b51a270e95", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-steipete-tools", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1769330179, - "narHash": "sha256-yxgb4AmkVHY5OOBrC79Vv6EVd4QZEotqv+6jcvA212M=", + "lastModified": 1765934234, + "narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "48698d12cc10555a4f3e3222d9c669b884a49dfe", + "rev": "af84f9d270d404c17699522fab95bbf928a2d92f", "type": "github" }, "original": { @@ -162,11 +265,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1765934234, - "narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=", + "lastModified": 1767364772, + "narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "af84f9d270d404c17699522fab95bbf928a2d92f", + "rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa", "type": "github" }, "original": { @@ -178,11 +281,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1770197578, - "narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=", + "lastModified": 1770562336, + "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", "owner": "nixos", "repo": "nixpkgs", - "rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2", + "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", "type": "github" }, "original": { @@ -194,11 +297,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1769740369, - "narHash": "sha256-xKPyJoMoXfXpDM5DFDZDsi9PHArf2k5BJjvReYXoFpM=", + "lastModified": 1770380644, + "narHash": "sha256-P7dWMHRUWG5m4G+06jDyThXO7kwSk46C1kgjEWcybkE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "6308c3b21396534d8aaeac46179c14c439a89b8a", + "rev": "ae67888ff7ef9dff69b3cf0cc0fbfbcd3a722abe", "type": "github" }, "original": { @@ -247,11 +350,11 @@ ] }, "locked": { - "lastModified": 1770304450, - "narHash": "sha256-Z4CCgseJdBkl5xdb/MZw5MmRkw1/CmIsQR0KasHktwE=", + "lastModified": 1770607512, + "narHash": "sha256-+h2O7BfXDMZqYdB8nQoeJcxLgEib5FDaC223hOEqphE=", "owner": "noctalia-dev", "repo": "noctalia-shell", - "rev": "20a1b115d72d6f906414930cb9cfeb98362d8948", + "rev": "c39e200d54c6e8ebf43a6a91e1ef984135fd8318", "type": "github" }, "original": { @@ -267,7 +370,7 @@ "mnw": "mnw", "ndg": "ndg", "nixpkgs": "nixpkgs_6", - "systems": "systems" + "systems": "systems_2" }, "locked": { "lastModified": 1768464392, @@ -286,7 +389,9 @@ "root": { "inputs": { "disko": "disko", + "home-manager": "home-manager", "nix-darwin": "nix-darwin", + "nix-openclaw": "nix-openclaw", "nixpkgs": "nixpkgs_3", "noctalia": "noctalia", "sops-nix": "sops-nix", @@ -298,11 +403,11 @@ "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1770145881, - "narHash": "sha256-ktjWTq+D5MTXQcL9N6cDZXUf9kX8JBLLBLT0ZyOTSYY=", + "lastModified": 1770526836, + "narHash": "sha256-xbvX5Ik+0inJcLJtJ/AajAt7xCk6FOCrm5ogpwwvVDg=", "owner": "Mic92", "repo": "sops-nix", - "rev": "17eea6f3816ba6568b8c81db8a4e6ca438b30b7c", + "rev": "d6e0e666048a5395d6ea4283143b7c9ac704720d", "type": "github" }, "original": { @@ -326,6 +431,21 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "vimix": { "inputs": { "flake-parts": "flake-parts", diff --git a/flake.nix b/flake.nix index 5ac8689..f4f9c3a 100644 --- a/flake.nix +++ b/flake.nix @@ -10,76 +10,82 @@ url = "github:noctalia-dev/noctalia-shell"; inputs.nixpkgs.follows = "nixpkgs"; }; + disko = { + url = "github:nix-community/disko"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nix-openclaw = { + url = "github:openclaw/nix-openclaw"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { - nixpkgs, - nix-darwin, - sops-nix, - disko, - ... - } @ inputs: { - nixosConfigurations = { - default = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs;}; - system = "x86_64-linux"; - modules = [ - ./devices/xps13/configuration.nix - ./modules - ]; + outputs = { nixpkgs, nix-darwin, sops-nix, disko, home-manager, nix-openclaw + , ... }@inputs: { + nixosConfigurations = { + default = nixpkgs.lib.nixosSystem { + specialArgs = { inherit inputs; }; + system = "x86_64-linux"; + modules = [ ./devices/xps13/configuration.nix ./modules ]; + }; + latoure = nixpkgs.lib.nixosSystem { + specialArgs = { inherit inputs; }; + system = "x86_64-linux"; + modules = [ ./devices/latoure/configuration.nix ./modules ]; + }; + asus = nixpkgs.lib.nixosSystem { + specialArgs = { inherit inputs; }; + system = "x86_64-linux"; + modules = [ ./devices/asus/configuration.nix ./modules ]; + }; + server = nixpkgs.lib.nixosSystem { + specialArgs = { inherit inputs; }; + system = "x86_64-linux"; + modules = [ + disko.nixosModules.disko + sops-nix.nixosModules.sops + home-manager.nixosModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.extraSpecialArgs = { inherit inputs; }; + home-manager.users.luna = import ./devices/server/luna.nix; + } + ./devices/server/configuration.nix + ./modules + ]; + }; + pi = nixpkgs.lib.nixosSystem { + specialArgs = { inherit inputs; }; + system = "aarch64-linux"; + modules = [ + "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix" + ./devices/pi/configuration.nix + sops-nix.nixosModules.sops + { + sdImage.compressImage = false; + nixpkgs.overlays = [ + (final: super: { + makeModulesClosure = x: + super.makeModulesClosure (x // { allowMissing = true; }); + }) + ]; + } + ]; + }; }; - latoure = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs;}; - system = "x86_64-linux"; - modules = [ - ./devices/latoure/configuration.nix - ./modules - ]; - }; - asus = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs;}; - system = "x86_64-linux"; - modules = [ - ./devices/asus/configuration.nix - ./modules - ]; - }; - server = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs;}; - system = "x86_64-linux"; - modules = [ - disko.nixosModules.disko - ./devices/server/configuration.nix - ./modules - ]; - }; - pi = nixpkgs.lib.nixosSystem { - specialArgs = {inherit inputs;}; - system = "aarch64-linux"; - modules = [ - "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix" - ./devices/pi/configuration.nix - sops-nix.nixosModules.sops - { - sdImage.compressImage = false; - nixpkgs.overlays = [ - (final: super: { - makeModulesClosure = x: - super.makeModulesClosure (x // {allowMissing = true;}); - }) - ]; - } - ]; - }; - }; - darwinConfigurations = { - "mbp-m4" = nix-darwin.lib.darwinSystem { - modules = [./devices/macbook/configuration.nix]; - specialArgs = { - inherit inputs; - system = "aarch64-darwin"; + darwinConfigurations = { + "mbp-m4" = nix-darwin.lib.darwinSystem { + modules = [ ./devices/macbook/configuration.nix ]; + specialArgs = { + inherit inputs; + system = "aarch64-darwin"; + }; }; }; }; - }; } diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index 31da3d2..0426777 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -1,9 +1,7 @@ pi_user_pass: ENC[AES256_GCM,data:X5u07UvEov5eYWks,iv:SPDFU01/5WThCSZjj1pExNZENhmIG2W6LvHfpPH5TS0=,tag:z5bhJ2TrX6Bevd40O1nPxg==,type:str] +luna_telegram_token: ENC[AES256_GCM,data:LWzEamz5SFK4HC+zR+6seTrVsTR0kQGETD6DSHMW3fMeFbUQm6/K+d8mc6Wg7w==,iv:LqvyTIkniNiqEAK76+Uqq4cYHnddmjMId+HQBjHk68o=,tag:knP6aqkLwGeEIAYMyET3Xg==,type:str] +luna_gateway_token: ENC[AES256_GCM,data:NV13qS8Vj0/HcvPM34Z90kFBoezpKeyhYKIWfU8zkHbOK7pHkl8yNACmUBszgjhbP4baqmO61isd94TxN4AjoA==,iv:j+9SfQoRMWeZbCjAm6/D5hwDBLl/0IOyu34DYphWz2o=,tag:od78mpifFMajv9eQfmnp1w==,type:str] sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] age: - recipient: age1x8qsd7kxxjvan4psvnvua3r0emljsnq07agxnu6jqw56ky8z6faqyjq0e3 enc: | @@ -14,8 +12,7 @@ sops: b3AxOTd2VmdHdXd5c3NNTkJoYW12bUUKbX199Z7jI6nornm0erzm7dSQ+XuxAnXb glw60TnUSnLUWIHTTx/jVSRR4uO5I6FzxUUfVJ2BMOn/eUNa5BJ70A== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-11-07T04:04:03Z" - mac: ENC[AES256_GCM,data:7UGKhfZg3SNg1f74nQiax4F7CB8NC12uIpTlQDtb8d1iiu5AdPZHwzlkpXbzkIp26g61pI8qXcvdjmToWjaWzsbUZ2Mo8/HEzOtV8HzxAeQFAyYBhIFAS0q0WzN/yijI7fQeHKnhZ/YCUuHQAZ94bBBSnkVTVOKf6mR7Pu1klr4=,iv:DzOwKxrcJse6yyOw+l7+wgEGBI36HWnebLE7js4VRiE=,tag:BIR67kZzZJZo+Kfie4wIvw==,type:str] - pgp: [] + lastmodified: "2026-02-09T03:39:35Z" + mac: ENC[AES256_GCM,data:lVU+w4EOpNACzz1CjtBGwGcoB/huatCa97sJ4irzX/dNP8LYwMiRvm6axsyqNiBns76WyKHAS9r+drByQzNAFAIh+2EwJCk7Mm7Njy3+kL0GNd0RanMhI51WSHTbjHIqrGC81jgS4ydcsGDMDmZBOZHL5t/uTcrdn/SRjgDvHTA=,iv:F4kbFHvTFmG4mhvMIAUtNq6WCwXyILOlbYvIaFno6BI=,tag:JSYzsKF86cuJtApPeTVDTA==,type:str] unencrypted_suffix: _unencrypted - version: 3.8.1 + version: 3.11.0