Skip to content

Keep a failed Studio restart from reading as an operator stop - #95

Open
mattmacrocket wants to merge 1 commit into
mainfrom
fix/deep-decoy-stop-audit
Open

mattmacrocket wants to merge 1 commit into
mainfrom
fix/deep-decoy-stop-audit

Conversation

@mattmacrocket

Copy link
Copy Markdown
Collaborator

Why

Diagnosing the production Studio Build Mac "unavailable" at every sensor start took two database reads, because the sensor could not say whether the host was stopped on purpose or had failed, and a failed restart leaves the same persisted state as a deliberate stop.

What changes

  • Startup retries persisted degraded hosts. Only a stopped row is operator intent. A degraded row goes through the same fail-closed activation path as a normal start.
  • A failed restart is saved as degraded, not stopped. Restart is disable then enable, and disable persists stopped so a crash between the halves fails closed. When the enable half fails, the five rows return to degraded with failure_count and last_failure_at recorded, and the orchestrator's diagnostics carry the reason. The next startup or reconcile retries. Enable of an already stopped host keeps its contract and stays stopped on failure.
  • Startup log says which state it is. An operator stop logs at INFO and points at the Decoys tab. A failure logs at WARNING with the diagnostics reason.
  • Lifecycle audit line. Enable, disable, and restart on any decoy type log the paired client name, certificate fingerprint, decoy identity, and outcome. Value-free, same shape as the config audit line.

Deception integrity

Control surface only (api/, __main__.py, the deep orchestrator's persistence). Nothing attacker-visible changes: no decoy response content, banners, timing, or port set are touched.

Verification

  • New tests: degraded-row retry at startup, failed restart persisted as degraded then recovered by reconcile, failed enable keeps stopped, both startup log shapes, route audit line for success and for a 409 failure. All eight behavioral tests were run red against the unchanged source before the fix.
  • Full sensor suite: 2627 passed, 6 skipped. ruff check src tests clean.

Operator note

The production rows were written stopped on 2026-09-03, which matches the early-2.1 test build issue already described in the user guide. This PR does not bulk-enable anything; the host is enabled from the Decoys tab.

A production install showed the Studio Build Mac "unavailable" at every
sensor start with no failure in the log. Its rows had been saved as
stopped weeks earlier, and the startup path treats any non-active row
as an operator stop, so nothing retried and nothing explained why.

Three changes close that gap:

- Startup only honors a persisted "stopped" row as intent. A persisted
  "degraded" host is one that should be running and failed, so it is
  retried through the same fail-closed activation path.
- Restart is disable then enable, and disable persists "stopped" so a
  crash between the halves fails closed. When the enable half fails the
  rows are returned to "degraded" with failure_count and last_failure_at
  recorded, so the next startup or reconcile retries them instead of
  reading the interrupted restart as a deliberate stop. Enable of an
  already stopped host keeps its existing contract and stays stopped.
- The startup log distinguishes the two states: an operator stop is an
  INFO line that points at the Decoys tab; a failure is a WARNING that
  carries the diagnostics reason. The enable, disable, and restart
  routes now log the paired client, fingerprint, decoy, and outcome, so
  an operator action on any decoy leaves a trail in the sensor log.

Tests cover the degraded-row retry, the failed-restart persistence and
its later recovery, the unchanged enable contract, both startup log
shapes, and the route audit line for success and failure.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant