Ed25519, TOFU-pinning en offline verificatie: hoe monsys evidence packs werken
Dashboards zijn mooi voor operators. Auditors willen een artifact dat ze offline kunnen verifiëren, zonder jouw systeem te vertrouwen. Zo werkt de monsys signing chain.
Een auditor vraagt bewijs dat je server op 14 maart geen ongeautoriseerde wijzigingen had. Je stuurt een screenshot van een dashboard. De auditor vraagt: hoe weet ik dat dit niet is aangepast?
Dat is het kernprobleem van elke monitoring-tool die compliance-bewijs wil leveren. Dashboards zijn mooi voor operators. Auditors willen iets anders: een artifact dat ze offline kunnen verifiëren, zonder jouw systeem te hoeven vertrouwen.
Zo werkt de monsys signing chain.
De architectuur in één overzicht
Elke payload die de agent naar de hub stuurt, is gesigneerd met een Ed25519-keypair. Dat keypair is uniek per agent — de private key wordt gegenereerd op de host tijdens installatie en verlaat de host nooit. De hub kent alleen de publieke key.
Agent (host) Hub (monsys.ai BE)
───────────────── ──────────────────────────────
Ed25519 private key Ed25519 public key (TOFU-pinned)
│ │
▼ ▼
payload → sign(payload, privkey) → verify(sig, pubkey) → store
De hub pinnt de publieke key bij de eerste verbinding (TOFU). Daarna wordt elke payload geverifieerd. Een aanvaller die de hub-token steelt, kan geen valse telemetrie injecteren — hij heeft de private key op de host nodig om een geldige handtekening te produceren.
Hoe een evidence pack gebouwd wordt
Wanneer je op "Genereer evidence pack" klikt voor een periode (bv. april 2026):
Stap 1: Query De hub haalt alle relevante events op voor jouw tenant in die periode:
- Agent inventory snapshots
- Alert-events (open, gesloten, geacknowledged)
- Detection events
- Compliance control evaluaties
- Audit log entries (wie deed wat, wanneer)
- AI observability traces (indien actief)
Stap 2: Normalisatie Elke event-rij wordt geserialiseerd naar canonical JSON (gesorteerde sleutels, geen whitespace-variaties). Dit is belangrijk: twee identieke datasets moeten identieke bytes produceren, anders klopt de handtekening niet.
Stap 3: Manifest Het manifest is een JSON-bestand dat alle bestanden in de tarball opsomt met hun SHA256-hashes:
{
"tenant_id": "acme-bv",
"period_start": "2026-04-01T00:00:00Z",
"period_end": "2026-04-30T23:59:59Z",
"generated_at": "2026-05-01T08:03:14Z",
"signing_key_id": "sk_2024_q1",
"files": [
{
"path": "agents/web-edge-01/inventory_snapshots.ndjson",
"sha256": "b4c2f1a3...",
"record_count": 2880
},
{
"path": "alerts/april_2026.ndjson",
"sha256": "e7d9c2b1...",
"record_count": 47
}
],
"inputs_hash": "sha256:a1b2c3d4..."
}
De inputs_hash is de SHA256 van alle sha256-waarden geconcateneerd — een vingerafdruk van de hele bundle.
Stap 4: Signing De hub signt het manifest met zijn tenant-Ed25519 signing key:
signature = Ed25519.sign(manifest_bytes, tenant_signing_privkey)
De signature en de publieke key worden mee in de tarball gestopt.
Stap 5: Packaging
evidence_acme-bv_april-2026.tar.gz
├── manifest.json
├── manifest.sig ← Ed25519 signature (hex)
├── signing_key.pub ← publieke key (PEM)
├── verify.py ← standalone verificatiescript
└── data/
├── agents/
├── alerts/
├── detections/
├── compliance/
└── audit_log/
Offline verificatie: verify.py
Het verify.py-script dat in de tarball zit, heeft geen externe dependencies behalve de Python stdlib (3.8+) en cryptography (pip-installeerbaar). Een auditor die monsys niet kent, kan het uitvoeren:
pip install cryptography
python verify.py evidence_acme-bv_april-2026.tar.gz
Output bij succes:
✓ Signature valid (Ed25519)
✓ Signing key matches expected fingerprint: 4a:b3:c2:...
✓ All 12 files intact (SHA256 match)
✓ inputs_hash consistent
✓ Period: 2026-04-01 → 2026-04-30
✓ Tenant: acme-bv
exit 0
Output bij manipulatie:
✗ File tampered: data/alerts/april_2026.ndjson
Expected SHA256: e7d9c2b1...
Actual SHA256: f8e0d3c2...
exit 1
De auditor hoeft geen account bij monsys. Het script haalt niets op van het internet. De verificatie is volledig offline.
Key rotation: de signing-key lineage
Signing keys worden periodiek geroteerd (aanbevolen: jaarlijks, of bij vermoeden van compromittering). Maar wat gebeurt er met evidence packs die onder een oude key gesigneerd zijn?
monsys houdt een key lineage bij. Elke key heeft:
key_id: unieke identifier (bv.sk_2024_q1)public_key: PEM-formaatactive_from/active_untilsuperseded_by: verwijzing naar de opvolger-key
Bij verificatie checkt verify.py niet alleen of de signature geldig is, maar ook of de gebruikte key actief was op het moment van generatie (generated_at in het manifest). Een key die op 1 januari 2025 geroteerd werd, is nog steeds geldig voor evidence packs die vóór die datum gegenereerd werden.
# Vereenvoudigd uit verify.py
def verify_key_was_active(key_id: str, generated_at: datetime, lineage: list) -> bool:
for key in lineage:
if key["key_id"] == key_id:
active_from = datetime.fromisoformat(key["active_from"])
active_until = datetime.fromisoformat(key["active_until"]) if key.get("active_until") else datetime.max
return active_from <= generated_at <= active_until
return False
De lineage-snapshot zit ook in de tarball, zodat verificatie zonder netwerktoegang kan.
Bearer-token theft: wat het wél en niet kan
Een veelgestelde vraag: als een aanvaller de agent-token steelt (de token die de agent gebruikt om telemetrie naar de hub te sturen), wat kan hij dan?
Kan de aanvaller:
- Valse telemetrie sturen als die agent → de hub accepteert de request
- Maar: de telemetrie mist de Ed25519-handtekening van de agent-private-key
- De hub slaat ongesigneerde telemetrie op met een
signature_missingflag - Evidence packs voor die periode worden gemarkeerd als
integrity_warning
Kan de aanvaller niet:
- Een geldig gesigneerde payload sturen zonder de private key op de host
- Bestaande evidence packs retroactief aanpassen (de signature klopt dan niet meer)
- De signing-key op de hub roteren (dat vereist TOTP + admin-rechten)
Dit is waarom token rotation regelmatig aanbevolen wordt — maar de signing chain garandeert dat gestolen tokens geen historisch bewijs kunnen vervuilen.
mTLS: de tweede beveiligingslaag
Bovenop Ed25519-signing gebruikt de hub-agent-verbinding mTLS. De agent heeft een client-certificaat dat bij de eerste verbinding wordt uitgegeven en gepinnd. Dit betekent:
- De hub weet zeker dat de verbinding van de geregistreerde agent komt (niet van iemand met alleen het token)
- Een man-in-the-middle kan geen valse agent simuleren
De combinatie: mTLS authenticeert wie de verbinding maakt; Ed25519 authenticeert de inhoud van elke payload.
NIS2 en AI Act: wat dit concreet betekent
Artikel 21 van NIS2 vereist "appropriate technical measures" voor logging en incident response. "Appropriate" is vaag, maar een getekende, offline verifieerbare audit trail scoort aanzienlijk beter bij een toezichthouder dan screenshots of CSV-exports.
Voor AI Act artikel 12 (logging van AI-systeem-gebeurtenissen): de AI observability module gebruikt dezelfde signing chain als de rest van monsys. Elke trace — prompt, completion, tokens, PII-hits — zit in de tarball, getekend, verifieerbaar door een DPA-inspecteur zonder monsys-account.
Inspecteur DPA: "Bewijs dat jullie AI-systeem op 3 april geen IBAN-nummers
heeft doorgestuurd naar OpenAI."
Operator: python verify.py evidence_april-2026.tar.gz
✓ Signature valid
grep -r "pii_hits_count" data/ai_traces/ | grep "2026-04-03"
→ 0 results voor IBAN-hits op die datum
Verifieerbaar. Reproduceerbaar. Zonder jouw vertrouwen te vragen.
De signing chain is gedocumenteerd in docs.monsys.ai/nl/security/agent-signing en docs.monsys.ai/nl/security/signing-keys-rotation. Eerste vijf servers gratis: monsys.ai/nl/signup.