Skip to content

Agent Payload Signing

Since the hardening round of May 2026, each agent signs its ingest payloads with an own Ed25519 keypair. The hub-side bearer-token alone is not sufficient to push data on behalf of an agent — the attacker also needs the private signing key of the host.

How it works

Agent (first start)
├─ generates Ed25519 keypair
├─ writes secret to /var/lib/monsys/agent-signing.key (mode 0600)
└─ sends public key to hub via /api/v1/agents/register
Hub pins the key in
agents.signing_pubkey
(trust-on-first-use)
Agent (each ingest)
body = "[{...metrics...}]"
sig = base64(Ed25519(SHA256(body)))
HTTP POST /api/v1/ingest
Authorization: Bearer ms_...
X-Monsys-Signature: <sig>
Hub
pubkey = SELECT signing_pubkey FROM agents WHERE token_hash = …
if pubkey NULL → accept + log "unsigned_payload" (medium)
if sig missing → 403 + log "unsigned_payload" (high)
if sig invalid → 403 + log "signature_invalid" (critical)
otherwise → ✓ process

File Location

PlatformPath
Linux/var/lib/monsys/agent-signing.key
WindowsC:\ProgramData\monsys\agent-signing.key

Permissions on Linux: mode 0600, owner = the user running the agent (user/monsys depending on your install). Windows: ACL restricted to SYSTEM + Administrators.

Content is 32 bytes (raw Ed25519 secret seed). No header, no PEM — keep the file exactly that size.

Trust-on-first-use semantics

The first time an agent sends a pubkey in register, the hub pins it and sets signing_set_at = NOW(). From then on, the pin is immutable from the agent — a new pubkey will be rejected and generates a signature_invalid integrity-anomaly.

To rotate legitimately, an admin must explicitly unset the pin via:

POST /api/v1/agents/<id>/rotate-signing-key

(see Token rotation)

What an attacker with only the bearer token cannot do

AttackBlocked by
Fake metrics push under same agentsignature mismatch → 403
Honeypot trip falsificationsignature mismatch → 403
Process DNA hash replacementsignature mismatch → 403
Version downgrade in reportingsignature mismatch → 403, plus version_downgrade integrity check

What an attacker with token + key file can do

If both are stolen, the attacker can fully report on behalf of the agent. The key file is however outside /etc/ (where system-config backups typically go), and is mode 0600. Concrete measures to prevent both from leaking together:

  • Keep the key file out of backups (backup_exclude in restic / borg)
  • Rotate the signing-key after a confirmed security event
  • Monitor changes on /var/lib/monsys/agent-signing.key with the host’s own FIM (auditd, auditctl -w /var/lib/monsys/agent-signing.key -p wa)

Migration of existing installations

An older agent without signing module will continue to work: the hub accepts the unsigned payload but makes a unsigned_payload-anomaly every hour so it doesn’t go unnoticed.

Upgrade path:

Terminal window
# On the host
sudo apt update && sudo apt upgrade monsys-agent
# Or via install script:
curl -fsSL https://get.monsys.ai/install.sh | sudo bash
sudo systemctl restart monsys-agent

The agent then generates a keypair, registers the pubkey, and the unsigned_payload-anomalies are automatically closed in the next integrity cycle (10 min).