fix(cuprate): gate package.restart and package.update too (review)

Restart and update are stop + recreate — a fresh start by another name —
but only start carried the disk gate, so on a disk that shrank below the
floor after install, either action silently resumed the unprunable
Monero sync: the exact failure the gate exists to close.

Both now call check_cuprate_disk_compatibility after validate_app_id and
BEFORE any state mutation (user-stopped clear / Restarting / Updating
flip), matching handle_package_start's fail-clean contract.
This commit is contained in:
2026-09-12 16:14:55 -04:00
committed by archipelago
parent 7c0ba14a00
commit c681472e15
2 changed files with 11 additions and 0 deletions
@@ -295,6 +295,12 @@ impl RpcHandler {
.ok_or_else(|| anyhow::anyhow!("Missing package id"))?
.to_string();
super::validation::validate_app_id(&package_id)?;
// Update is stop → pull → remove → recreate, i.e. a fresh start by
// another name: on a disk that shrank since install it would resume
// cuprate's unprunable sync unchecked. Same gate as install and
// start, run BEFORE the Updating flip so a refusal leaves the app
// cleanly in its previous state.
super::dependencies::check_cuprate_disk_compatibility(&package_id).await?;
// Reject if already in a transitional lifecycle.
{
@@ -257,6 +257,11 @@ impl RpcHandler {
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing package id"))?;
validate_app_id(package_id)?;
// Restart is stop + recreate, so on a disk that shrank below the cuprate
// minimum after install it resumes the doomed unprunable sync just like
// start would — same gate, same "fail before clearing user-stopped /
// flipping state" contract (see handle_package_start).
super::dependencies::check_cuprate_disk_compatibility(package_id).await?;
let single_orchestrator_app =
self.orchestrator.is_some() && uses_single_orchestrator_app(package_id);