Nix Observability Daemon
observability nix
at master 133 lines 4.0 kB view raw
1{ 2 description = "A simple self-contained daemon to gather nix statistics"; 3 4 inputs = { 5 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 rust-overlay.url = "github:oxalica/rust-overlay"; 7 rust-overlay.inputs.nixpkgs.follows = "nixpkgs"; 8 }; 9 10 outputs = { 11 self, 12 nixpkgs, 13 rust-overlay, 14 }: let 15 systems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; 16 forAllSystems = f: 17 nixpkgs.lib.genAttrs systems (system: 18 f (import nixpkgs { 19 inherit system; 20 overlays = [(import rust-overlay)]; 21 })); 22 in { 23 packages = forAllSystems (pkgs: { 24 default = pkgs.rustPlatform.buildRustPackage { 25 pname = "nod"; 26 version = "0.1.0"; 27 src = ./.; 28 cargoLock.lockFile = ./Cargo.lock; 29 30 nativeBuildInputs = [pkgs.pkg-config]; 31 buildInputs = 32 [pkgs.sqlite] 33 ++ ( 34 if pkgs.stdenv.isDarwin 35 then [pkgs.iconv] 36 else [] 37 ); 38 }; 39 }); 40 devShells = forAllSystems (pkgs: { 41 default = pkgs.mkShell { 42 buildInputs = 43 [ 44 pkgs.rust-bin.stable.latest.default 45 pkgs.pkg-config 46 pkgs.sqlite 47 ] 48 ++ ( 49 if pkgs.stdenv.isDarwin 50 then [pkgs.iconv] 51 else [] 52 ); 53 }; 54 }); 55 56 nixosModules.default = { 57 config, 58 lib, 59 pkgs, 60 ... 61 }: let 62 cfg = config.services.nod; 63 in { 64 options.services.nod = { 65 enable = lib.mkEnableOption "Nix Observability Daemon"; 66 package = lib.mkOption { 67 type = lib.types.package; 68 default = self.packages.${pkgs.system}.default; 69 description = "The nod package to use."; 70 }; 71 user = lib.mkOption { 72 type = lib.types.str; 73 default = "nod"; 74 description = "User to run the nod daemon as."; 75 }; 76 group = lib.mkOption { 77 type = lib.types.str; 78 default = "nod"; 79 description = '' 80 Group for the nod daemon. Other services that need read access to the database (e.g. a monitoring agent) should be added to this group. 81 ''; 82 }; 83 84 socketPath = lib.mkOption { 85 type = lib.types.path; 86 default = "/run/nod/nod.sock"; 87 description = "Path to the Unix socket. Exposed via NOD_SOCKET in the session environment."; 88 }; 89 databasePath = lib.mkOption { 90 type = lib.types.path; 91 default = "/var/lib/nod/nod.db"; 92 description = "Path to the SQLite database"; 93 }; 94 }; 95 96 config = lib.mkIf cfg.enable { 97 users.users.${cfg.user} = { 98 isSystemUser = true; 99 group = cfg.group; 100 description = "Nix Observability Daemon"; 101 }; 102 users.groups.${cfg.group} = {}; 103 104 # Tell nix to forward its internal JSON log to the socket 105 nix.settings.json-log-path = cfg.socketPath; 106 107 # Make the socket path available to interactive shells so users 108 # can run `nod stats` without passing --socket explicitly. 109 environment.sessionVariables.NOD_SOCKET = cfg.socketPath; 110 111 systemd.services.nod = { 112 description = "Nix Observability Daemon"; 113 wantedBy = ["multi-user.target"]; 114 after = ["network.target"]; 115 116 serviceConfig = { 117 User = cfg.user; 118 Group = cfg.group; 119 ExecStart = "${cfg.package}/bin/nod daemon --db ${cfg.databasePath} --socket ${cfg.socketPath}"; 120 Restart = "always"; 121 StateDirectory = "nod"; 122 StateDirectoryMode = "0750"; 123 # /run/nod must be world-searchable so nix (running as any user) can reach the socket. 124 RuntimeDirectory = "nod"; 125 RuntimeDirectoryMode = "0755"; 126 # SQLite WAL mode requires write access to the -shm file even for read-only connections. 127 UMask = "0117"; 128 }; 129 }; 130 }; 131 }; 132 }; 133}