Vigilo
An OS-level security daemon for validators, signers, and bridge nodes: a one-second alert path for single hostile events, and a five-minute LLM pass that correlates sequences into attack patterns.
Vigilo
latin — I watch. I am vigilant.
A daemon that watches validators, signers, and bridge nodes for the ten seconds before a compromise turns into a loss — and says plainly what it cannot do.
the wager
A validator or signer node getting compromised isn't usually a single dramatic event. It's a private key file getting read by a process that's never read it before. A shell spawning from inside the application, which application code has no legitimate reason to do. An outbound connection to a destination nobody configured. Individually, each of these is a whisper. Together, in sequence, over a few minutes, they're an attack in progress — and the whole value of catching it is the minutes between the first whisper and the theft.
Vigilo's wager is that those minutes are recoverable, if something is actually watching the OS-level signals continuously and can tell a routine event from an attack pattern fast enough to matter.
two speeds of watching
immediate — ~1s
Any single high or critical event — a keystore read, a suspicious outbound connection — pushes straight to Slack, Telegram, or email. No waiting for corroboration.
correlated — ~5m
A language model reviews the full event window on a schedule, looking for the pattern a single event can't show — an env dump followed by an outbound connection, the same sequence appearing on more than one server at once.
The split matters because the two failure modes are different. Miss the immediate tier and a key access goes unnoticed for five minutes. Skip the correlated tier and every alert is a single fact with no story — a legitimate backup job and a real exfiltration chain can look identical one event at a time.
what a single event actually means
Severity is not a vibe. It is a function of how much damage the event can do alone, and how much time you have before that damage becomes irreversible. The multi-server correlation entry is the one that justifies the whole architecture over a simple per-host script — a single compromised signer looks like noise; the same shell-spawn pattern showing up on three signers within the same hour is a campaign, and only something aggregating across hosts can see the difference.
| Severity | Event pattern | What it means |
|---|---|---|
| critical | Private key or keystore file read by an unfamiliar process | The most direct path to loss. Immediate alert; no waiting for corroboration. |
| critical | Secret dump followed by an outbound connection | Exfiltration shape. Even small outbound transfers after a secret read are worth treating as compromise. |
| critical | Same attack sequence observed across more than one server | Campaign indicator. Correlation across hosts is the signal a single daemon cannot see. |
| high | Shell spawned from a node or python process | RCE indicator. The app has no legitimate reason to spawn an interactive shell. |
| high | Package install initiated from a running app process | Supply-chain shape. Runtime installs from inside the app are rarely intentional. |
| high | Privilege escalation attempted from an application process | Lateral-movement prep. The process is trying to become something it is not supposed to be. |
what it refuses to claim
The most credible thing in the project isn't a feature — it's the explicit list of what it doesn't do, published next to what it does, not buried.
stated plainly, not discovered the hard way
Vigilo is an observation and alerting daemon. It does not block filesystem access or network connections, does not prevent privilege escalation, does not stop an attack already in progress, cannot protect against a compromised kernel, and cannot guarantee an alert fires if the process is killed before it does.
It raises the cost of an attack and shortens the detection window. One layer, not a complete answer.
The deployment guidance carries the same honesty. Bind every port to 127.0.0.1 and reach it over Tailscale or WireGuard, never the public interface — the README states flatly that opening the ports publicly is unsupported. Secrets go in environment variables, never config.yaml, because that file tends to end up committed or baked into a container image whether anyone means it to or not. The included systemd unit runs as a dedicated low-privilege user with ProtectSystem=strict and an empty capability set — hardening that's shipped as the default, not left as an exercise for whoever deploys it.
the interesting borrow
The correlation agent's own notes credit patterns proven in goose — context compaction so a long event window doesn't blow the model's context, and a preamble-and-retry shape for keeping a long-running analyst loop honest across restarts. It's a small, quiet instance of exactly the thing worth doing more of: don't re-invent a durable-loop pattern a general-purpose agent framework already solved, borrow the shape and spend the actual engineering effort on the part that's genuinely novel — knowing what a keystore read next to an outbound connection means.