Skip to content

v1.9.4 — the scanner daemon's calls are audited, the TTL delete first

Choose a tag to compare

@zw008 zw008 released this 15 Sep 08:56
· 7 commits to main since this release

A family survey on 2026-09-15 found that the scanner daemon deleted VMs whose TTL had expired with no row in
either audit trail, on success or failure, and without the guard() that stops the same deletion over MCP or the
CLI. Its scan cycles against vCenter and its webhook sends left no row either, and daemon start was marked
@cli_local, so the family's CLI gate could not see it.

  • TTL delete. Before deleting, the daemon passes guard() as vm_delete (risk critical), so a deny rule on
    vm_delete now stops an expiry too; the entry is kept for when the rule is lifted. Each attempt writes one
    vm_delete row with trigger: ttl_expiry: ok when deleted, denied when refused, error when the delete
    failed (entry kept for retry) or the VM was already gone (stale entry dropped). A policy check that fails is an
    error and skips the delete.
  • Scan cycle. One daemon_scan row per cycle, error when a connection or a pass failed, with the failed
    passes in the result.
  • Webhook. One webhook_send row per send, error when it was not delivered. The URL is not recorded — it can
    carry a token.
  • No row flood. The TTL check runs every minute, so a refused or failing entry records its outcome once and
    again only when it changes (a different status, refusing rule or error class); every successful delete is
    recorded. Retries still happen every minute. Without this, an entry refused by a deny rule wrote 1,440 rows a day.
  • Interrupts are interrupted. Ctrl+C during a scan or a webhook send (the first scan runs before the signal
    handlers are installed) records interrupted instead of ok.
  • daemon start is @audited (daemon_start). Its row is written when the daemon stops.

Tests: tests/eval/regression/test_daemon_calls_are_audited.py (12 tests, each red before its fix; suite 594 passed). Works with
vmware-policy 1.15.0: the daemon passes each status explicitly.