Skip to content

fix+feat: site mail log, phantom staging, sidebar status, ClamAV niceness - #91

Merged
nechodom merged 1 commit into
mainfrom
feat/round-four
Aug 12, 2026
Merged

fix+feat: site mail log, phantom staging, sidebar status, ClamAV niceness#91
nechodom merged 1 commit into
mainfrom
feat/round-four

Conversation

@nechodom

Copy link
Copy Markdown
Owner

First tranche of the latest round. Each diagnosis below was verified against the code, then adversarially re-checked before anything was changed.

"Mail sent by this site" — reported twice as still broken

Neither the wrapper nor the mail path was at fault. The wrapper writes JSON keys from and to (site-mail-wrapper.sh:171); SiteEmailLogEntry declared from_address / to_address with no alias. serde therefore failed on every line — and the reader parses with filter_map(… .ok()), turning each failure into a silently dropped record. The panel said "No outbound mail captured yet" forever, however much mail went out.

Fixed with alias (so the RPC wire and templates keep the long names, and the jsonl already on every node stays readable) plus #[serde(default)], so one odd record loses a column instead of hiding every record around it. Three tests: the wrapper's exact output, a missing field, and the unchanged wire format.

Phantom staging card

"ačkoliv to nemám vytvořené, tak tam vidím preview staging, co je jako?"

The card rendered unconditionally. "Open staging ↗" linked to a computed staging.<domain> that had never been provisioned, and "Push staging → production" offered to overwrite the live site from a source that did not exist. Both are now behind an existence check.

ACME directory was empty

Nothing ever populated it — directory_url: String::new(), // not stored here. Worse, the key was writable but read by nobody: issuance hardcodes the Let's Encrypt endpoints, so saving it persisted a value into agent.toml that reads as authoritative and changes nothing. A knob that lies is worse than no knob — the field is gone and the key is no longer accepted; the panel states the endpoint it actually uses.

Sidebar status dots

Monitoring, WordPress updates and Certificates carry a dot: green when nothing needs attention, a red count when something does. One request feeds all three so they cannot disagree, and nothing is painted until a response arrives — a green dot nobody checked would be worse than no dot.

ClamAV niceness — with recommended defaults

clamscan is single-threaded but CPU-bound and will saturate a core for minutes; on a small VPS that's the difference between a site that responds and one that doesn't. Scans now run under nice, plus ionice best-effort (starving nginx of disk is the other half of "the site went slow during a scan").

The default is a recommendation derived from core count, not 0 — 1 core → 19, 2-3 → 15, 4-7 → 10, 8+ → 5 — because a default of "no limit" means every box has the problem until someone goes looking for the switch. Override with [integrity] scan_niceness. nice beats a hard percentage cap here: it costs nothing on an idle box and yields instantly when a web request wants the CPU.

Care packages

Edit is a button, not a text link — it's the row's primary action and sat next to real buttons looking like a caption.

Tests

cargo test --workspace green, cargo fmt --all clean.

🤖 Generated with Claude Code

… niceness

MAIL SENT BY THIS SITE — reported twice as "still doesn't work", and the
cause was neither the wrapper nor the mail path. The wrapper writes JSON
keys `from` and `to`; SiteEmailLogEntry declared `from_address` and
`to_address` with no alias, so serde failed on EVERY line — and the
reader parses with `filter_map(.. .ok())`, which turned each failure into
a silently dropped record. The panel then said "No outbound mail captured
yet" forever, however much mail the site sent. Aliases fix it for the
jsonl already on disk; `#[serde(default)]` means one odd record loses a
column instead of hiding every record around it. Three tests pin the
wrapper's exact output, a missing field, and the unchanged wire format.

PHANTOM STAGING. The staging card rendered unconditionally: "Open staging
↗" linked to a computed `staging.<domain>` that had never been
provisioned, and "Push staging → production" offered to overwrite the
live site from a source that did not exist. Both are now behind an
existence check, so the card shows either the create form or the live
actions, never both.

ACME DIRECTORY was empty because nothing ever populated it — and worse,
the key was writable while being read by nobody: issuance hardcodes the
Let's Encrypt endpoints, so saving it wrote a value into agent.toml that
reads as authoritative and changes nothing. A knob that lies is worse
than no knob, so the field is gone from the form and the key is no longer
accepted; the panel states the endpoint it actually uses.

SIDEBAR STATUS. Monitoring, WordPress updates and Certificates now carry
a dot: green when nothing needs attention, a red count when something
does. One request feeds all three, so they cannot disagree with each
other, and nothing is painted until a response arrives — a green dot
nobody checked would be worse than no dot.

CLAMAV NICENESS. clamscan is single-threaded but CPU-bound and will
saturate a core for minutes, which on a small VPS is the difference
between a site that responds and one that does not. Scans now run under
`nice` (+ `ionice` best-effort, since starving nginx of disk is the other
half of "the site went slow during a scan"). The default is a
RECOMMENDATION derived from core count rather than 0 — one core gets 19,
many cores get 5 — because a default of "no limit" means every box has
the problem until someone goes looking for the switch. Overridable via
`[integrity] scan_niceness`.

Care packages: Edit is a button rather than a text link. It is the row's
primary action and sat next to real buttons looking like a caption.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nechodom
nechodom merged commit 001661f into main Aug 12, 2026
1 check passed
@nechodom
nechodom deleted the feat/round-four branch August 12, 2026 10:48
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