Installing the agent
One static binary, no dependencies, on macOS, Linux and Windows. It makes outbound connections only and never listens on a port, so no inbound firewall rule is needed and there is nothing on the endpoint for anyone to attack.
Four steps: install, enrol, connect, and keep it running. Each one says which account to run it as, because that matters more than it looks.
1. Install#
macOS#
curl -fsSL https://guardyn.dev/install.sh | shIt checks the published SHA-256 before installing and asks for your password only to write to /usr/local/bin.
Windows#
irm https://guardyn.dev/install.ps1 | iexWindows has curl.exe but no sh, so the one-line installer used on macOS and Linux fails at the pipe. This one checks the published SHA-256 too, needs no administrator, installs under your own AppData and adds itself to your PATH. Open a new terminal afterwards so the PATH change is picked up.
Linux#
curl -fsSL https://guardyn.dev/install.sh | shIt checks the published SHA-256 before installing and uses sudo only to write to /usr/local/bin. Without sudo it installs to ~/.local/bin and says so.
None of the installers enrol. Enrolment needs a token you paste yourself, so a leaked install command cannot add machines to anyone’s fleet.
Without the installer#
Every build is published, with a checksum list:
- macOS: Apple silicon · Intel
- Linux: x86-64 · ARM64
- Windows: x86-64 · ARM64
- SHA256SUMS, which every installer checks against
On macOS or Linux, from the folder you downloaded it to. Set f to the file you downloaded; on Linux use sha256sum -c if shasum is missing.
f=guardyn-agent-darwin-arm64curl -fsSLO https://guardyn.dev/agent/latest/SHA256SUMSgrep " $f\$" SHA256SUMS | shasum -a 256 -cchmod +x "$f"xattr -d com.apple.quarantine "$f" 2>/dev/nullsudo mv "$f" /usr/local/bin/guardyn-agentThe shasum line must print OK; if it does not, delete the file and download it again. The xattr line clears the macOS download quarantine and does nothing on Linux.
On Windows, in PowerShell:
$f = "$HOME\Downloads\guardyn-agent-windows-amd64.exe"(Get-FileHash $f -Algorithm SHA256).Hash.ToLower()$dir = "$env:LOCALAPPDATA\Programs\guardyn"New-Item -ItemType Directory -Force $dir | Out-NullUnblock-File $fMove-Item $f "$dir\guardyn-agent.exe" -ForceCompare the hash it prints with the matching line in SHA256SUMS before you move the file. This puts it where the installer would, so the service step below works unchanged; run it by full path, or add that folder to your PATH.
2. Enrol#
Issue an enrolment token from Fleet in the console. It starts with gdn_enrol_. Put it in place of PASTE_YOUR_gdn_enrol_TOKEN below, and give the machine a label you will recognise.
The agent keeps its enrolment per account, so enrol as the same account that will run it.
On macOS, enrol as yourself, without sudo. Your enrolment lives in ~/.guardyn-agent, and the launchd agent in step 4 runs as you too.
guardyn-agent enroll --server https://guardyn.dev --token PASTE_YOUR_gdn_enrol_TOKEN --label "build-01"On Linux, enrol with sudo. As root the enrolment lives in /var/lib/guardyn-agent, and the service in step 4 runs as root, so it only sees an enrolment made with sudo. Leave sudo out only if you will run the agent in your own terminal and never as a service.
sudo guardyn-agent enroll --server https://guardyn.dev --token PASTE_YOUR_gdn_enrol_TOKEN --label "build-01"If sudo answers command not found, the installer put the binary in ~/.local/bin. Move it with sudo mv ~/.local/bin/guardyn-agent /usr/local/bin/ and enrol again; the service expects it in /usr/local/bin.
On Windows, enrol in an ordinary PowerShell. State lives in %ProgramData%\GuardynAgent whichever account runs the agent, so there is no account to match.
guardyn-agent enroll --server https://guardyn.dev --token PASTE_YOUR_gdn_enrol_TOKEN --label "build-01"The shared enrolment token is exchanged for a token unique to this machine, and only that per-machine token is used afterwards. So the shared secret is used once per endpoint and can be revoked without touching machines already enrolled. Re-enrolling the same machine rotates its token rather than creating a duplicate entry.
3. Connect#
To try it, run it in a terminal, as the same account you enrolled as:
guardyn-agent connectsudo guardyn-agent connectguardyn-agent connectThis holds one outbound stream open. While it is open the machine is inspectable in real time; when it closes, the console says offline instead of guessing. It reconnects with backoff on its own, so a closed laptop lid or a dropped network needs no intervention. A reboot or a closed terminal does: that is what step 4 is for. Press Ctrl-C to stop it before you set up the service, so only one copy is connected.
4. Keep it running after a reboot#
For a machine you want permanently inspectable, run the agent as a service.
macOS#
A launchd agent that runs as you. Write the plist, then load it. It starts when you sign in and restarts the agent if it ever exits.
mkdir -p ~/Library/LaunchAgentstee ~/Library/LaunchAgents/dev.guardyn.agent.plist >/dev/null <<'EOF'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>Label</key> <string>dev.guardyn.agent</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/guardyn-agent</string> <string>connect</string> <string>--quiet</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>ThrottleInterval</key> <integer>10</integer> <key>StandardOutPath</key> <string>/tmp/guardyn-agent.log</string> <key>StandardErrorPath</key> <string>/tmp/guardyn-agent.log</string></dict></plist>EOFlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.guardyn.agent.plistIts output goes to /tmp/guardyn-agent.log. To stop it: launchctl bootout gui/$(id -u)/dev.guardyn.agent.
Linux#
A systemd unit, running as root. Write the unit, then enable it:
sudo tee /etc/systemd/system/guardyn-agent.service >/dev/null <<'EOF'[Unit]Description=guardyn.dev endpoint agentDocumentation=https://guardyn.dev/docs/agentAfter=network-online.targetWants=network-online.target[Service]Type=simpleUser=rootExecStart=/usr/local/bin/guardyn-agent connect --quietRestart=alwaysRestartSec=10StandardOutput=journalStandardError=journalSyslogIdentifier=guardyn-agentNoNewPrivileges=truePrivateTmp=trueProtectSystem=strictReadWritePaths=/var/lib/guardyn-agentRestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINKRestrictRealtime=trueLockPersonality=true[Install]WantedBy=multi-user.targetEOFsudo systemctl daemon-reloadsudo systemctl enable --now guardyn-agentjournalctl -u guardyn-agent -fIf the journal says this machine is not enrolled, it was enrolled without sudo: run the enrol command from step 2 again, with sudo. The unit is sandboxed read-only apart from /var/lib/guardyn-agent, which that sudo enrolment creates.
Windows#
A scheduled task that starts the agent each time you sign in, running as you and unelevated. It starts at sign-in, not when the machine starts: after a reboot the machine stays offline until someone signs in to that account. Register it in an ordinary PowerShell:
$exe = "$env:LOCALAPPDATA\Programs\guardyn\guardyn-agent.exe"$action = New-ScheduledTaskAction -Execute $exe -Argument "connect --quiet"$trigger = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit 0Register-ScheduledTask -TaskName guardyn-agent -Action $action -Trigger $trigger -Settings $settings -RunLevel Limited -ForceTask Scheduler’s defaults would stop the agent after 72 hours and would not start it, or keep it running, on battery. The settings line turns all three off. On some machines only an administrator may create a task that runs at sign-in; if Register-ScheduledTask answers Access is denied, that is why, and until an administrator allows it the agent runs only while guardyn-agent connect is open in a terminal.
To start it now without signing out:
Start-ScheduledTask guardyn-agentIt opens a console window when it starts; minimise it rather than closing it, because closing it disconnects the agent until you next sign in.
Privileges#
Without root or administrator rights the privileged collectors — the operating system event log, package and kernel-module integrity — are skipped, and their areas are unexamined rather than clear. The console says so on the machine page and the analyst says so in its answers, but the gap is real.
On Linux the service above already runs as root. On macOS the launchd agent runs as you, the account the console enrols, so the checks that need administrator rights are reported as unexamined. On Windows the installer and the scheduled task stay per-user on purpose: the installer writes into your own profile, so running it from an administrator’s PowerShell installs into that account instead. When you want the checks that need administrator rights, stop the running agent (Stop-ScheduledTask guardyn-agent, or close its window) and start it from an elevated PowerShell by its full path, replacing <you> with your Windows user name:
& "C:\Users\<you>\AppData\Local\Programs\guardyn\guardyn-agent.exe" connectScreen capture#
The agent can photograph the machine’s main display, for an investigation where what is on screen is the evidence. It is off until someone at the machine turns it on, and the console cannot turn it on:
guardyn-agent screenshots on # or off; no argument shows the settingsudo guardyn-agent screenshots on # or off; no argument shows the setting- Run it as the account you enrolled as (plain on macOS and Windows, with sudo on Linux), or pass
--allow-screenshotswhen you enrol. - The image goes to the machine’s page in the console, for a person to look at. The model never receives it, only its size and time. One frame per machine is kept, the latest.
- Every capture, and every refused request, is written to the machine’s own audit log before anything is sent.
- Capturing on a timer, with nobody asking, is a second decision made the same way:
guardyn-agent capture-schedule --every 2m --for 8h, andcapture-schedule offto stop. Turning screen capture off stops it too. - A background service has no screen to see: under systemd or the launchd daemon above, a capture fails and says why rather than returning a blank picture.
Other commands#
| Command | What it does |
|---|---|
guardyn-agent tools | Every tool this binary offers, with its arguments. |
guardyn-agent collectors | Every collector compiled in. |
guardyn-agent scan | One full collection, uploaded as a snapshot. Works without a live connection. |
guardyn-agent audit | The local log of everything this agent has done. |
guardyn-agent screenshots [on|off] | Whether the console may photograph this screen. Off unless set here. |
guardyn-agent capture-schedule | Whether this machine photographs its own screen on a timer. Off unless set here. |
guardyn-agent version | Version, OS and architecture. |
Where it keeps state#
- macOS, as you:
~/.guardyn-agent/ - Linux, as root (the service):
/var/lib/guardyn-agent/ - Linux, as anyone else:
~/.guardyn-agent/ - Windows, whichever account runs it:
%ProgramData%\GuardynAgent\
Two files: state.json (mode 0600 — it holds the machine token) and audit.log, one JSON line per action. That audit log is the machine owner’s copy: a tool that reads a machine and reports upstream has to be auditable from the machine’s own side, or it is indistinguishable from spyware.
Removing it#
First revoke the machine from its page in the console, which invalidates its token and drops any live connection immediately. The machine’s history is kept as evidence; the seat is freed. Then, on the machine:
macOS#
launchctl bootout gui/$(id -u)/dev.guardyn.agent 2>/dev/nullrm -f ~/Library/LaunchAgents/dev.guardyn.agent.plist /tmp/guardyn-agent.logsudo rm -f /usr/local/bin/guardyn-agent; rm -f ~/.local/bin/guardyn-agentrm -rf ~/.guardyn-agentLinux#
sudo systemctl disable --now guardyn-agent 2>/dev/nullsudo rm -f /etc/systemd/system/guardyn-agent.service && sudo systemctl daemon-reloadsudo rm -f /usr/local/bin/guardyn-agent ~/.local/bin/guardyn-agentsudo rm -rf /var/lib/guardyn-agent ~/.guardyn-agentWindows#
Unregister-ScheduledTask -TaskName guardyn-agent -Confirm:$false -ErrorAction SilentlyContinueGet-Process guardyn-agent -ErrorAction SilentlyContinue | Stop-Process$dir = "$env:LOCALAPPDATA\Programs\guardyn"Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue$path = ([Environment]::GetEnvironmentVariable('Path', 'User') -split ';' | Where-Object { $_ -and $_ -ne $dir }) -join ';'[Environment]::SetEnvironmentVariable('Path', $path, 'User')Remove-Item "$env:ProgramData\GuardynAgent" -Recurse -Force -ErrorAction SilentlyContinueIf the last line is refused, the state folder was created from an administrator’s PowerShell; run that line from one too.