Skip to content

v0.1.0b2 — beta 2: the reliability + evidence-harness release

Pre-release
Pre-release

Choose a tag to compare

@DustinTrap DustinTrap released this 12 Jul 14:39
e835ebb

Beta 2 — the reliability + evidence-harness release. Two hardening
batches in one: the transports can no longer double-fire a destructive action,
MCP approvals became signed single-use receipts with an audit trail, GLKVM
snapshots work headless at native resolution, evidence learned to carry the
conditions it was observed under — and kvm-pilot test-report turns the whole
per-device intake into one command. Everything in this release is
unit/emulator-verified; the first live fleet run of the new paths is the next
milestone (the harness exists precisely to record it honestly). 18 issues
closed.

Added

  • kvm-pilot test-report (#99) — the live-test harness: probes the
    device's capabilities and appends one evidence row to the run ledger
    (--ledger > $KVM_PILOT_TEST_LEDGER > ~/.config/kvm-pilot/test_runs.jsonl;
    never the installed package data). Read-only probes (info, snapshot —
    stamped with the #156 conditions on pass AND fail — healthcheck, logs,
    power_state) run every invocation; destructive probes run only via
    --include virtual_media,power,firmware_update with a recorded --attest
    operator statement, still routed through the normal safety gates. Pass =
    assertion + observed effect (power must flip the read-back and restore;
    media must report online and the eject must land; a flash passes only via
    the driver's #94 verified-state contract) — an unobserved effect records an
    honest FAIL. Fake-driver runs (and --synthetic) record
    source="synthetic", which never promotes maturity; there is no flag to
    force real.

  • The generated wiki Hardware-Compatibility page now carries a Maturity
    column
    — the #98-derived level per (device, firmware) read from the shipped
    registry ( when no live-derived rating exists), completing #103.

  • New GLKVM quirk firmware-flash-webui-only (#177): the GL web console is
    the only known-good RM1PE upgrade path (V1.5.1→V1.9.1 live-verified); the
    /api/upgrade/* flash was observed to no-op (#94/#95) and no API-driven
    flash has ever been verified on any release. firmware-update output now
    steers to the web console on affected devices (it never blocks — the driver
    already reports a no-op flash honestly), and
    firmware-update/driver-features/firmware-registry docs carry the guidance.

  • GLKVM headless JPEG snapshot at native resolution (#187): when the
    snapshot bytes fail the JPEG guard (H.264 at native/high res, #107) and the
    firmware exposes params.video_format (V1.9.1+), the driver flips the
    encoder to MJPEG (POST /api/streamer/set_params?video_format=1), retries,
    and restores the prior format — no EDID change, no H.264 decoder, no
    browser. Inside streamer_warm() the flip is held for the whole block and
    restored once at exit. Composes with the #142 offline-streamer recovery
    (offline → warm → H.264 → flip). V1.5.1 doesn't expose the switch and keeps
    the honest SnapshotFormatError (upgrade via the web UI, #177). Mechanism
    live-proven on V1.9.1; the auto-path is emulator-verified and awaits its
    first live ledger row. New quirk snapshot-h264-at-native-res.

  • A firmware change now invalidates the device assessment: preflight
    remembers the firmware each device was last assessed at, forces the stable
    checks to re-run live when it differs (out-of-band web-UI flashes included),
    and emits a firmware-delta finding diffing what cleared / regressed /
    stayed open (#180).

  • Run-ledger capability rows can now record the conditions a result was
    observed under
    (conditions: {resolution, encoder_format, snapshot_cached, jpeg_sink_clients}) — the axes that actually decide a GL snapshot outcome —
    and the support-matrix rollup surfaces them as pass_conditions /
    fail_conditions, so field reports at different operating points reconcile
    from data instead of reading as contradictions. Optional; pre-existing rows
    and derived maturity are unaffected (#156).

  • CI docs-parity guard: build_wiki.py --check fails when a docs/*.md page is
    not registered in the wiki PAGES allowlist, so a new page can no longer
    silently go unpublished; the MCP README tool table gained the missing
    access_paths row (#175).

Added (MCP)

  • Signed, expiring, single-use approval receipts + audit trail for the
    destructive act tools (#72): every approval mints an HMAC-signed receipt
    bound to the exact invocation (host, tool, effect, args-hash, dry-run,
    approver), re-verified immediately before dispatch and consumed on use —
    a bound field changing after approval, an expired receipt
    (KVM_PILOT_MCP_RECEIPT_TTL, default 60 s), or a replay fails closed as a
    denial-shaped result. Approved results carry receipt: {id, state} and a
    real approval.expires. Every destructive-invocation terminal (approved,
    denied, consumed, expired, mismatched, replayed, dispatch-exception) emits
    one JSON audit record on the kvm_pilot.mcp.audit logger.

  • New file_firmware_report tool — the MCP twin of CLI firmware-check's
    auto-filing (#189), completing the registry telemetry loop for agent
    sessions. Filing a GitHub issue is a new external-write effect class
    with its own operator gate (KVM_PILOT_MCP_ALLOW_EXTERNAL_WRITE, off by
    default) plus the usual per-invocation approval; the shared helper moved to
    firmware_registry.file_firmware_report (#190).

Changed

  • The healthcheck's support-evidence finding now labels ledger history as
    recorded (vs this run's own tested-now probes), renders the #156
    conditions each result was observed under, and flags condition-blind
    snapshot passes ("verified only under unknown resolution/encoder") instead
    of overclaiming a bare verified (n=N) — the honesty regression that gave
    #180 its false confidence (#180).

Changed (MCP)

  • Act results now carry a typed outcome field (approved / denied /
    cancelled / not_confirmed / gate_closed / invalidated) so agents
    branch on data instead of matching the human-facing denied_reason strings —
    a cancelled elicitation (benign, retryable) is now typed apart from an
    explicit denial. The KVM_PILOT_MCP_ELICIT=off escape-hatch hint moved from
    every client-side denial to a one-time hint after ≥2 consecutive
    client-side approval kills on the same host — a security trade-off shouldn't
    be advertised on a one-off mis-click (#149).
  • The power tool now returns a structured result instead of a sentence: it
    bumps the frame generation (a stale mouse click can no longer anchor to a
    pre-reboot snapshot within the reboot window) and verifies the effect via
    the driver's trustworthy signal — Redfish PowerState or a wired ATX LED —
    reporting verified: true/false with the observed state, or an honest
    verified: null + the reason and remedy when no trustworthy signal exists
    (GL units: ATX sensing lies; verify visually) (#168).

Fixed

  • The transports no longer auto-retry a 409/503 that answers a state-changing
    request
    — re-firing a POST whose 409/503 arrived after the device began
    acting (e.g. a BMC perturbed by the ComputerSystem.Reset it just accepted)
    could double-fire a destructive action. Reads (GET/HEAD) keep the bounded
    retry; the #164 breaker semantics are unchanged; the Redfish driver's own
    at-target reconciliation now sees the surfaced error instead of being
    pre-empted by a transport re-POST (#167).

  • A JSON endpoint answering with a non-JSON body (e.g. truncated at the
    content boundary) now raises a typed ProtocolError with a redacted preview
    instead of leaking raw bytes into dict-expecting callers as an opaque
    AttributeError; an empty 2xx body returns None (#170).

  • Redfish mount_iso now verifies the medium actually landed (polls the
    VirtualMedia slot for Inserted=true, raising MediaOfflineError on a
    silent no-op — the #78 trap, Redfish edition; verify=False opts out), and
    a mid-flight 401 re-auth DELETEs the old session before re-login so a
    spurious 401 can't strand a live session slot on session-capped BMCs; a
    login that yields no session URI now logs the future leak (#169).

  • The MCP effect gate now fails closed for an effect class with no
    registered enable-flag instead of silently borrowing the CONFIG gate;
    APPLIANCE_RESET is now explicitly mapped to
    KVM_PILOT_MCP_ALLOW_APPLIANCE (#190).


Install: pip install --pre kvm-pilot
Full diff: v0.1.0b1...v0.1.0b2