[Unit] Description=Sentinel agent — AI/GPU server monitoring collector Documentation=file:///usr/share/doc/sentinel-agent/README.md After=network-online.target Wants=network-online.target [Service] Type=simple User=sentinel Group=sentinel ExecStart=/usr/local/bin/sentinel-agent run --config /etc/sentinel/agent.yaml Restart=on-failure RestartSec=5s # Unbuffered stdout so journald sees lines immediately. Environment=PYTHONUNBUFFERED=1 # --- hardening --- # v2 update commands deliberately DROP NoNewPrivileges (blocks sudo's setuid # transition) and ProtectSystem=strict (its read-only /usr namespace is # inherited by sudo'd children, so apt could never install). Compensating # controls: the root-owned sentinel-update-helper's fixed case arms + the 4 # exact-argv sudoers lines (project/v2/protocol.md §7). ProtectHome=true PrivateTmp=true # NVML needs the GPU character devices; keep device access on. PrivateDevices=false # DeviceAllow globs do not cross "/" (and on some systemd versions do not match # at all), so "/dev/nvidia*" silently blocked /dev/nvidia-caps/* and NVML init # failed with "Unknown Error". List literal nodes instead and adjust to the # host's GPUs: ls /dev/nvidia* /dev/nvidia-caps/* DeviceAllow=/dev/nvidiactl DeviceAllow=/dev/nvidia-modeset DeviceAllow=/dev/nvidia-uvm DeviceAllow=/dev/nvidia-uvm-tools DeviceAllow=/dev/nvidia0 DeviceAllow=/dev/nvidia1 DeviceAllow=/dev/nvidia-caps/nvidia-cap1 DeviceAllow=/dev/nvidia-caps/nvidia-cap2 # The buffer lives under /var/lib/sentinel. ReadWritePaths stays listed so the # unit keeps working unchanged if ProtectSystem is ever re-added. ReadWritePaths=/var/lib/sentinel StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target