The 24/7 watch
An investigation answers the question you asked. The watch answers the one nobody thought to ask: did anything about this machine change that should not have? A new administrator, a new thing that starts at boot, a port newly open to the network, a program running from a temporary folder. Those are what an intruder leaves behind, and they show up as changes long before they show up as damage.
It works in real time. The agent, running as a service, asks the operating system to tell it when the places that matter change, and reports each change as it happens: in the console within a few seconds, with an email for anything serious.
What it hears as it happens#
Each of these is a notification from the operating system, or a cheap check every few seconds where no notification exists. When one fires, the agent re-reads only that part of the machine, compares it with its baseline and sends what changed.
| macOS | Linux | Windows | |
|---|---|---|---|
| Things that start automatically | LaunchDaemons, LaunchAgents (every user’s), crontabs, shell profiles | systemd units (system and every user’s), cron, profiles | Run keys (the machine’s and every signed-in user’s), Startup folders, scheduled tasks, services, Winlogon, IFEO; service and task events in the log |
| Accounts and administrators | the local directory’s user records and admin group | passwd, group, shadow, sudoers | account and group-membership events in the Security log |
| New processes | the process table, every 2 seconds | the kernel’s exec events: every process as it starts, however briefly it runs | the process table every 2 seconds, and process-creation events (4688) where that auditing is on |
| Ports open to the network | every 5 seconds | every 3 seconds | every 3 seconds |
| Hosts file, DNS, proxy, SSH keys | /etc/hosts, network settings, every user’s authorized_keys | /etc/hosts, resolv.conf, every user’s authorized_keys | hosts, DNS and proxy settings, OpenSSH’s keys |
| Trusted root certificates | the System keychain and its trust settings | the local CA folders | the machine’s root store |
| Protections | the firewall’s settings | SSH server settings, ufw | Defender and firewall settings and events; Defender detections; the audit policy; a cleared log |
Changes that land together, such as an installer writing a dozen files, are sent together, so one install is one report. The machine’s page lists what its agent is subscribed to, and guardyn-agent service status prints the same on the machine.
Who signs in, and how hard the machine is working#
Besides what changes, the watch reports what happens: each one an event on the machine’s page, sent once, and something a hook in Settings → Alerts can send on.
| Event | When | Severity |
|---|---|---|
| Sign-in and sign-out | Someone signs in over SSH, Remote Desktop or at the console, with the account, the address and, for SSH, the key; and how long the session lasted when it ends. On Linux from systemd-logind, on macOS from the login records, on Windows from the Security log, with the name of the computer a Remote Desktop session came from and whether it has administrator rights. Opening a terminal on a desktop already signed in is not a sign-in, and neither is a program checking a password. | low; medium as root or Administrator, or from an address the account has not used before (after the first three days); high, and a finding, straight after five or more failed attempts from the same address |
| Failed sign-ins | Ten or more failed attempts in five minutes, with the addresses, the accounts tried, how they tried (Remote Desktop, SSH, Windows file sharing, a website on IIS, WinRM, SQL Server, mail, FTP, Cockpit) and why each failed (wrong password, no such account, disabled or locked out); then at most one more every fifteen minutes while it goes on. Linux (SSH, and mail, FTP, Cockpit, console and xrdp sign-ins from the journal) and Windows (the Security log, and Remote Desktop’s own log for the addresses the Security log leaves out). Agent 0.12.0 and later say how and why. | medium; high at a hundred in five minutes |
| CPU | Over 90% for two minutes, with the processes that used it over the whole time; again if it is still that busy after fifteen minutes, and when it has been under 70% for a minute. | medium; high still busy after fifteen minutes; low back to normal |
| Memory | Over 92% in use for a minute (on macOS, the kernel’s own memory pressure), with the processes using the most; and on Linux, any process the kernel or systemd-oomd stopped because memory ran out. | medium; high over 97%, and for a process stopped |
| Disk | A disk over 90% full with less than 50 GB left (a big disk at 90% still has room); again at 97% or under 5 GB left, and when it is back under 85%. | medium; high at 97% or under 5 GB |
Load is measured every ten seconds and disks every minute, from the same readings the analyst’s resource tool uses; a spike that is over in a minute is not an event. Agent 0.10.0 and later.
Every address in an alert says where it is: the country and whose network it belongs to (a hosting company, a home provider), or that it is on the machine’s own local network. That comes from files kept on guardyn’s server and refreshed monthly, so no address is ever sent to anyone to be looked up. IP locations by DB-IP (CC BY 4.0).
Privilege, accounts, services, files and deploys#
Agent 0.11.0 and later also report these, each an event a hook can send on. Most are read from the system’s own logs as they are written (the journal on Linux, the event logs on Windows), so nothing new runs on the machine to produce them.
| Event | When | Severity |
|---|---|---|
| Privilege use | Linux: someone becomes another account with sudo, su, pkexec or polkit, with the command they ran (anything shaped like a password or token masked, and cut at 160 characters); a wrong password; a refused command. The same person doing the same thing again within a quarter of an hour is counted into one event, and an Ansible task is named as one. | low; medium for a wrong password or a refused command; high at three wrong passwords; high, and a finding, for an account that may not use sudo trying to |
| Second factor | Windows, where guardyn access (the second factor for Windows sign-in) runs too: each prompt’s answer (approved in Telegram, Slack, Discord, WhatsApp or the authenticator app; refused; no answer; let in by audit mode or because its cloud was unreachable; an offline code; a blocked or exempt account). guardyn access asks only after Windows has accepted the password, so a refusal means someone had it. | low for an approval; medium for no answer, audit mode or an offline code; high when let in without a second factor; high, and a finding, for a refusal or a blocked account; critical, and a finding, for a forged answer |
| Accounts | Linux: a password changed, and whether by its owner; an account unlocked; an account added to a group as good as root (docker, lxd, disk, shadow, libvirt). Windows: a password reset by someone else or changed by its owner, an account’s flags changed, locked out, unlocked or renamed. | low for your own password; otherwise medium; high for renaming an administrator; high, and a finding, for root’s password, an administrator’s password reset by someone else, or a group as good as root |
| Restarts, and the agent itself | When the agent starts it says what happened while it was not running: the machine restarted (and who restarted it, when that was logged), went down without shutting down (a power cut, a crash, a reset), the agent was updated, or the agent was stopped or killed while the machine stayed up. A different agent binary with the same version is a signal. | low for a restart or an update; medium for the agent killed; high, and a finding, for an unclean shutdown, and for the agent stopped or killed for two minutes or more |
| Services | Linux: a service failing, restarting in a loop or given up on by systemd; a process dumping core; a protection (the firewall, auditd, the journal, AppArmor, fail2ban and others) still stopped a minute after it stopped. Windows: a service stopping unexpectedly, and how many times; Defender failing to remove what it found, or its engine failing. | medium; high for a loop or systemd giving up; high, and a finding, for a protection stopped (Defender, the event log, the firewall, Sysmon, Remote Desktop) |
| Disks | Linux: the kernel reporting read or write errors, a filesystem switched to read-only or found corrupt, a RAID array running without one of its disks (and whole again), and a disk running out of inodes, the room for files, while it still has space. | high; critical, and a finding, for read-only, corruption or a degraded array; inodes as disk space |
| Kernel, logs and hiding | Linux: a kernel module loaded while the machine runs; the kernel running a module that is unsigned or not in its list; SELinux or AppArmor switched off; a log in /var/log or a shell history cut short or deleted; a program disguised as a kernel thread, or a process hidden by mounting over it. | always a finding: medium for a module or a shell history; high for a log or a protection; critical for an unsigned module and for hiding |
| Files | Linux: system binaries, PAM modules and sign-in configuration, boot files, library search paths and web folders (/var/www, /srv), as they change, with the file’s owner, mode and SHA-256. A package update is one event saying how many files it changed, not one per file. | high, and a finding, for system binaries, sign-in files and boot files changed outside a package update, and for a new script in a web folder; medium for a script changed in a web folder; low for other web files (more than ten at once is one event) |
| Deploys | Linux: an app’s code changing, for apps that systemd runs from /srv, /opt, /var/www or /home: the branch and the commits before and after (or the release it switched to), who did it (a GitHub Actions run, or whoever had signed in just before) and which of its services restarted. | low |
| CI jobs | Linux: a self-hosted GitHub Actions runner finishing a job, with the repository, workflow, run, who started it, the commit and branch, the result and how long it took, and a link to the run. | low |
| Remote-control tools and tunnels | Every system: AnyDesk, TeamViewer, RustDesk, ScreenConnect and other remote-control tools, and tunnels such as ngrok, Cloudflare Tunnel, frp and chisel, as they start. | high, and a finding: admins use some of them too, so it is one to confirm rather than an alarm |
The full check#
On start, and every hour after, the agent also runs every check it has and compares the whole machine with its baseline. It is the backstop: it covers what no notification reports (disk encryption, SIP, Secure Boot), anything that changed while the machine was off or the service stopped, and anything a notification missed. A change made while the agent was not running is not lost: it is still a difference from the baseline.
How it decides#
With rules and a baseline, never a model, so it spends no credits. The agent reduces what it reads to facts (one per thing that matters, named the same way every time) and compares them with the facts from its last accepted report. It sends what changed, and the signals its rules raised. The server keeps track of which signals each machine has already shown, so you hear about a signal once, when it first appears.
New processes get the rules the full process check uses: an executable in a folder anyone can write to, one that is not validly signed (checked once it has run for a couple of seconds, so a compiler’s helpers are not each a signature check), a command line shaped like a reverse shell or an encoded payload, and a document, browser or web server starting a shell. The same program from the same place is reported once an hour at most, and no machine sends more than 30 process alerts an hour, the last of them saying that more are being held back: a build that starts a thousand helpers is not a thousand alerts. Critical ones always go.
The first full check takes the baseline. Whatever it finds is the machine as it was when the watch started: it is listed on the machine’s page, and only what is high or critical becomes a finding. You do not get fifty alerts for the state the machine was already in.
Turning it on#
Install the agent as a service on the machine (the installation guide has all three platforms):
sudo guardyn-agent service installOn a paid plan the watch starts on its own once the service connects, and changing plan turns it on or off with nothing to reinstall. Real time needs agent 0.9.0 or later; an older agent checks on an interval until it is updated.
| Plan | The watch |
|---|---|
| Free | no watch; investigations on demand |
| Starter | real time, with a full check every hour |
| Team | real time, with a full check every hour |
| Enterprise | real time, with a full check every hour |
On every plan, Free included, the service keeps the machine connected, so it can be investigated at any time without anyone at it.
What each change is worth#
| What | Added | Changed | Gone |
|---|---|---|---|
| An administrator | high | — | low |
| A local account | medium | low | low |
| Something that starts automatically | medium | high | low |
| A port open to the network | medium | low | low |
| A trusted root certificate | high | — | low |
| A browser extension | low | low | low |
| A hosts-file entry | medium | — | low |
| The DNS servers | medium | medium | low |
| The proxy settings | high | high | low |
| An SSH authorized key | high | — | low |
Something that starts automatically and points at an unsigned binary is high, whatever else is true of it. Protections switching off is high: antivirus, realtime protection, firewall, disk encryption, secure boot, SIP, Gatekeeper, automatic updates. Ways in switching on is medium: remote login, remote desktop, screen sharing.
Some things are left out on purpose, because they change on their own and would bury everything else. macOS’s own launch jobs on the sealed system volume are left out, and SIP being on is what protects them. So are listeners on loopback, which nothing on the network can reach, and the file hashes of programs signed by Apple or Microsoft, which change on every patch day. Their signature is still checked, so one swapped for an unsigned binary still shows up.
Where you hear about it#
- In the console, as it happens: everything at medium and above becomes a finding, marked as raised by the 24/7 watch, with what changed and what the agent noticed. Open pages update without a reload, and anything high or critical also shows as a notice wherever you are in the console. On a machine that allows emergency actions, a finding or an event on its timeline offers an owner or admin the ones that fit it.
- By email: the workspace’s owners and admins get a message when something at high or above is new (one per report, so changes that land together are one email), and one when a machine’s watch starts.
- In Slack: paste your Slack app’s bot token in Settings → Alerts and save (a link there creates the app with its permissions filled in). Events then go to the channel for their kind (
#guardyn-security,#guardyn-signins,#guardyn-health,#guardyn-load,#guardyn-deploys,#guardyn-files), made the first time something goes there or joined if your team made it first. The people you list (by the emails they use in Slack), or else the workspace’s owners and admins, are added to each channel so it shows in their Slack; that needs the app’s users:read.email permission, which the link includes. Each alert comes with a picture card that says at a glance how bad it is and what happened, drawn by guardyn and uploaded into your Slack, shared in no channel; that needs files:write, which the link includes too, and without it alerts come as words only. Every value worth copying stays in the words. Nobody is mentioned, and rules choose which events go there, as for any other place. On a plan with guardyn actions, once the app’s signing secret is pasted in too, an alert about a machine that allows emergency actions carries a button for each one that fits: see From Slack. - Through your own mail or WhatsApp: in Settings → Alerts, an owner or admin adds an SMTP server (TLS required, the password encrypted at rest), a SendGrid account (an API key allowed only to send mail, encrypted at rest) or a WhatsApp number (a phone linked once by QR code, like WhatsApp Web, and the numbers to alert), and rules that choose which events go to each, from what severity, and from which machines. Different people can get different things: one place per group of people, each with its own rules. If a place stops delivering, owners and admins are told by email, once, and what its rules held is sent once it works again.
- About WhatsApp: it works the way WhatsApp Web does, which WhatsApp does not officially support for automated messages and may answer by blocking the number. Link a number kept for alerts, not a personal one; the link can read that account’s chats, and it is kept on guardyn’s server, readable only by the service that sends. Messages are paced and capped at 120 an hour.
- A finding someone resolved that happens again is reopened and sent again. One marked a false positive stays closed.
What leaves the machine#
Only what changed, the signals raised, and a line naming what the agent noticed (the path that changed, or the event in the log): a few kilobytes per report. The baseline, which is the machine’s whole inventory, stays on the machine in watch-baseline.json in the agent’s state folder. Every report is written to the machine’s own audit log, which guardyn-agent audit prints. Nothing it finds is acted on by itself: where a machine allows emergency actions, an owner or admin decides each one. See guardyn actions.
The events above carry what they need and no more: a command run with sudo, with anything shaped like a password or token masked and at most 160 characters; a changed file’s path, owner and SHA-256, never its contents; commit ids and branch names; and for a GitHub Actions job the repository, workflow, run, actor, commit and branch. Of a job’s environment only those GITHUB_ values are read, never its secrets.
When it goes quiet#
A machine where nothing happens sends nothing but its hourly full check, so silence is normal. What the console watches for is the service itself, and the fleet and the machine’s page say the same thing about it: watching live while the service is connected, and reconnecting for the few minutes after its connection drops. Six minutes without it, a server says stopped reporting and its owners and admins are emailed; a laptop or desktop, which sleeps, says asleep or offline, noted on its timeline only. A machine that is connected but whose full check is overdue says watch stalled. On the machine itself:
guardyn-agent service statusIt prints whether the service is running, what the watch is subscribed to, the last full check, the last thing it reported and the last error, if there was one. A report that could not be delivered is not lost: the baseline only moves once the server has accepted a report, so the agent keeps trying, and the next attempt carries the same changes.