process_treepid=4312How it works
Install it, connect it, ask it. Then check its work.
A read-only agent on each machine opens one stream out. Over it, the analyst calls the checks it needs and answers with what came back attached, including what it could not see. This is the whole loop, and the things it will never do.
Three steps
Three steps, and the third one is the product.
The first two take about a minute. Then you just ask.
Install the agent
One static binary with no dependencies, for macOS, Linux or Windows. The installer checks its published SHA-256. Enrolment needs a token you paste yourself, so a leaked install command cannot add machines to your fleet.
$ curl -fsSL https://guardyn.dev/install.sh | sh $ guardyn-agent enroll \ --server https://guardyn.dev \ --token gdn_enrol_… enrolled build-runner-03 (linux/amd64)
Connect it
The agent dials out and holds one long-lived stream. It never listens on a port, so there is no inbound firewall rule. When the stream closes, the console says offline instead of guessing.
$ guardyn-agent connect connected as build-runner-03 29 tools offered, elevated=true waiting for jobs
Ask
Pick a machine, pick a model, ask in plain language. It calls the checks it needs in parallel, reads what comes back, and answers with the evidence attached.
> What is listening on build-runner-03? ▸ list_sockets state=listening 0.4s Four ports. Three expected. One is not: 8443, held by a process running from /tmp.
An investigation
The evidence stays attached to the answer.
One question, five checks sent at once, one follow-up. Every result is kept with the turn, including the check that could not run. Save what matters as a finding, and decide how serious it is yourself.
list_processesunsigned_only=trueon finance-mbp-14outbound_connectionson finance-mbp-14list_autorunssuspicious_only=trueon finance-mbp-14recent_changeshours=24executable_only=trueevent_logon finance-mbp-14process_treepid=4312on finance-mbp-14Suspicious. Investigate. An unsigned binary /tmp/.cache/nodeh (pid 4312, started 02:03) holds an established connection to 203.0.113.5:443. A launch agent that starts it was written at 01:23, forty minutes earlier. Three facts that are individually ordinary, and together are not.
Confidence: high. The plist, the binary and the socket agree on timing.
Not checked: event_log needs root, so tonight's system events were not read. needs root, so last night's system events were not read.
Do now: copy /tmp/.cache/nodeh somewhere safe before it is removed, then kill 4312. Check whether any other machine has talked to 203.0.113.5.
- 02:1409:14One question about one laptop.
- Five checks go out at once. The hairline under each is time against that tool’s real limit.
- Results land in the order they finish. One could not run, and the answer says so.
- The answer quotes the pid, the path and the address.
- Illustrative data. Fictional machines, people and addresses.
outbound_connections| Process | Remote | Signed |
|---|---|---|
| nodeh 4312 | 203.0.113.5:443 | unsigned |
| Safari 811 | 198.51.100.24:443 | system |
| nsurlsessiond 402 | 198.51.100.77:443 | system |
| Mail 790 | 192.0.2.41:993 | system |
list_autorunssuspicious_only=true- Location
- ~/Library/
LaunchAgents/ com.cache.nodeh.plist - Target
- /tmp/.cache/nodeh
- Signed
- unsigned
- Modified
- 01:23 tonight01:23 last night
- Command
- */30 * * * * ~/bin/sync-notes.sh
- Modified
- 6 days ago
Then it is yours to rank.
- Severity is your call, not the model’s.
Save an answer as a finding and you choose critical, high, medium or low. The title starts from the verdict line, and you can change it.
- One problem, one entry.
Raise the same thing twice and it stays a single finding with a seen count.
- A work queue, not a feed.
Assign it, acknowledge it, resolve it, or mark it not an issue.
No open findings means nothing in the evidence gathered so far looked wrong. That is not the same as nothing being wrong.The empty state of the findings queue
pid 4312 runs /tmp/.cache/nodeh and holds 203.0.113.5:443. A launch agent written at 01:23 starts it.
Hard limits
Read-only is a property of the binary.
The tool set is compiled into the agent. The server can send a tool name and typed arguments, checked on both ends. It has no way to send a command.
It cannot run commands
Hard limitThere is no shell tool, no script tool and no allowlisted-command tool.
If this server were compromised tomorrow, the worst it could do to your fleet is read from it.
It cannot change anything
Nothing writes, deletes, moves, kills or quarantines. Every remediation is yours to run, spelled out precisely enough to paste.
It only dials out
The agent opens one outbound stream and never listens on a port. The server pings every 20 seconds, and after 70 seconds of silence the agent reconnects. It runs at most three jobs at a time.
It keeps its own record
Every run is written to an audit log on the machine itself, so the endpoint has its own account of what was looked at.
It tells you what it did not check
Truncated lists, skipped privileged checks and a machine that went offline mid-scan are all said out loud. Silence about a gap reads as completeness, and that is how people get hurt.
No arbitrary file reads. File contents come back only for a short allowlist of forensic paths. A deny list for keys, .env files and credential stores always wins, matched against the canonicalised path so a symlink cannot walk around it. An investigation console that can read any file is a data exfiltration tool with a nice interface.
Find out what is actually running on your machines.
One binary, one question, one honest answer, including the parts it could not see.
curl -fsSL https://guardyn.dev/install.sh | shKeep it running with launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.guardyn.agent.plist
irm https://guardyn.dev/install.ps1 | iexNeeds no administrator rights. It installs under your own AppData.
curl -fsSL https://guardyn.dev/install.sh | shKeep it running with sudo systemctl enable --now guardyn-agent
guardyn-agent enroll --server https://guardyn.dev --token gdn_enrol_…