diff --git a/Plugin/unraid/Tools/auth_sweep.sh b/Plugin/unraid/Tools/auth_sweep.sh index b587e67..f9fd5bd 100755 --- a/Plugin/unraid/Tools/auth_sweep.sh +++ b/Plugin/unraid/Tools/auth_sweep.sh @@ -28,6 +28,38 @@ # finding for every hostname on the machine. # # ============================================================================================== +# DESIGN PRINCIPLES +# ============================================================================================== +# +# It answers the questions nobody was going to ask. +# Both checks were already possible from the Auth tab, and both required somebody to open it +# and press a button on the right row. That is why one host here returned nothing but 5xx for +# months. A check that depends on being run is a check that is not running. +# +# Files findings; never applies remedies. +# The fixes are "start a container", "edit a rule", "change a default policy". Each of those +# is a judgement about intent, and a sweep that guessed at intent would be repairing a +# deliberate configuration as often as a broken one. +# +# Down is measured in time, not in samples. +# A host must have been failing for longer than AUTH_SWEEP_DOWN_MIN before anything is filed. +# Counting failed samples instead would file a finding for every hostname on the machine every +# time it reboots, and a report that cries wolf after every restart stops being read. +# +# ============================================================================================== +# OPERATIONAL SAFEGUARDS +# ============================================================================================== +# +# Read-only against the auth stack. Nothing here starts a container, edits a proxy host, changes +# a rule or alters a default policy — the two questions are answered by observation alone. +# +# --dry-run reports what it would file and writes no finding. --report only reads what is +# already filed, and is silent when clean, so it cannot manufacture noise for the Sunday report. +# +# Findings are filed, not notified. A sweep that raised an alert per host would be its own +# incident every time the array restarted. +# +# ============================================================================================== # RUNTIME MODES # ============================================================================================== # diff --git a/Plugin/unraid/Tools/cert_triage.php b/Plugin/unraid/Tools/cert_triage.php index 93e0619..ac51f4f 100644 --- a/Plugin/unraid/Tools/cert_triage.php +++ b/Plugin/unraid/Tools/cert_triage.php @@ -40,6 +40,21 @@ // downstream of something else here, and listing it alongside its own cause invites fixing the // symptom. // +// OPERATIONAL SAFEGUARDS +// Read-only. Opens certbot's logs and nothing else — no certificate is requested, renewed or +// deleted here, and no log is rotated or truncated. +// +// Bounded by file count and by bytes per file, so a directory that has grown to 639 MB across +// 1001 files cannot turn a page load into an unbounded read. Only the tail of each log is +// examined, because a run explains its failure at the end rather than the beginning. +// +// An unrecognised failure is reported as unclassified, never folded into the nearest category. +// A tidy chart that is wrong sends the operator to fix a domain that was never broken. +// +// The log directory is discovered from the NPM container rather than assumed, so a container +// path change surfaces as "no logs found" instead of an empty triage that reads as "no +// failures". +// // RUNTIME MODES // cert_triage.php summary — categories, affected domains, and the causal reading // cert_triage.php --json the same as JSON, for the Certs tab diff --git a/Plugin/unraid/Tools/cert_triage.sh b/Plugin/unraid/Tools/cert_triage.sh index d964267..57cece5 100755 --- a/Plugin/unraid/Tools/cert_triage.sh +++ b/Plugin/unraid/Tools/cert_triage.sh @@ -30,6 +30,42 @@ # fails renewals for domains that have nothing wrong with them. # # ============================================================================================== +# DESIGN PRINCIPLES +# ============================================================================================== +# +# One invocation is one data point. +# A single failure writes its reason three times — the ACME response, the traceback, and +# certbot's summary. Counting lines would report it as three and inflate whichever category +# happens to be the most verbose, which is the opposite of what triage is for. +# +# Causes are separated from consequences. +# Rate limiting is nearly always downstream: retries against a hostname with no DNS record +# exhaust the allowance, which then fails renewals for domains that have nothing wrong with +# them. Reporting the rate limit as the problem sends the operator to fix the wrong domains. +# +# Rotation suffix is the clock, not mtime. +# Every file in this directory carries the same mtime because they arrive as a synced set, so +# ordering by mtime returns an arbitrary answer that looks authoritative. +# +# Bounded by design, not by hope. +# 639 MB across 1001 files cannot be read in a page load. Only CERT_TRIAGE_FILES logs are +# opened, and only CERT_TRIAGE_MAX_BYTES from the end of each, because a failure explains +# itself at the end of the run rather than the start. +# +# ============================================================================================== +# OPERATIONAL SAFEGUARDS +# ============================================================================================== +# +# Read-only throughout. It opens certbot's logs and nothing else — no certificate is requested, +# renewed or deleted, and no log is rotated or truncated by anything here. +# +# Diagnosis only. This names why renewals failed; acting on that is the operator's, and +# cert_history.sh remains the thing that records what happened. +# +# The log directory is normally discovered from the NPM container rather than hardcoded, so a +# container path change surfaces as "no logs found" rather than as a silently empty triage. +# +# ============================================================================================== # RUNTIME MODES # ============================================================================================== #