The signed manifest gains a "ui" object {version, sha256, size, url, min_engine, signature}; the entry's own Ed25519
signature (the release key, manifest::ui_sign_bytes) and the manifest's cover it. src/uiota.rs: the hourly check hands
the entry over; a bundle for this engine is downloaded, checked (size, sha256, signature), unpacked next to the current
one and swapped by an atomic pointer; the server serves the bundle's fixed file names in place of the embedded ones;
the first page load starts a 10 s wait for the page's health ping, and silence, a first-paint error, a missing
index.html or a renamed bundle rolls back to the embedded interface and marks the version bad for good. No "ui"
object retires the bundle (the kill switch). settings.ui_builtin and state.ui carry Settings > Interface.
igneum-ota-sign gains sign-ui and verify-ui. Tests: a good bundle applies, a bad signature is refused, a too-new
min_engine is ignored, a broken bundle rolls back, every bad manifest field fails.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 3417f1e1fa)