Contract-Neutral: Issue #1408 keeps release-local signed-installer guidance; update APIs and deployment behavior are unchanged, with direct model coverage.
Use a native absolute path in the SMART override test so Windows verifies the same contract as Unix. Move the metrics concurrency proof into the canonical verification artifact and document that live ingestion remains independent from lifecycle maintenance.
Do not let a short per-request connection timeout shrink the whole inventory cycle below its 90-second default. The seven-node support bundle for #1437 showed hundreds of successful guest reads followed by exact 30-second cancellation and dead-lettering, preventing a coherent generation from publishing. Larger request timeouts can still expand the cycle up to MAX_POLL_TIMEOUT.\n\nFixes #1437
Run startup maintenance, rollups, and retention on a lifecycle-owned worker separate from buffered metric ingestion. SQLite still serializes write transactions, while maintenance CPU/read work can no longer stop writeCh from draining until it drops live batches. Join both workers before closing the shared database.\n\nFixes #1601
When remote-config signature enforcement is disabled, an unsigned response is an expected compatibility state rather than a warning. Keep its structured diagnostic at debug while preserving warning/error behavior for required, expired, future, or invalid signatures.\n\nFixes #1538
Fourth candidate on the v6.2.0 line, superseding v6.2.0-rc.3 and still following stable v6.1.2. This hardening cut includes action recovery, kiosk containment, alert and notification accounting, node-restricted Proxmox storage, container-runtime selection, and host CPU sampling fixes that landed after rc.3. Version pins move to 6.2.0-rc.4 across the repo root, Docker bootstrap defaults, and Helm metadata; stable install pointers remain on v6.1.2 until governed promotion.
The Build and Test workflow ran everything in one serial job — frontend
lint/tests/build, the full go test -race sweep, and benchmarks — for a
37-55 minute wall clock on every push. The job now splits into parallel
jobs: frontend (lint, unit tests, type-check, bundle build and size gate),
backend tests sharded three ways with internal/api on its own shard since
it alone takes ~10m under -race, script smoke tests with the backend
build, and benchmarks with the existing baseline cache flow. Backend jobs
satisfy the frontend embed with a stub index.html, which unit tests do
not assert against; the real bundle is still built and size-gated by the
frontend job and by release builds. Expected wall clock is roughly the
slowest single job, 10-15 minutes.
A new changes job skips the build/test jobs when a push touches only
docs/ and Markdown, mirroring the path filters Core E2E already has. The
gitleaks secret scan deliberately stays unconditional since this workflow
is the only per-push scan and docs can leak secrets too.
scripts/dev-prepush.sh runs the checks that most often turn main red,
scoped to the outgoing commits: the canonical completion guard in CI
mode, the registry snapshot tests when registry.json changed, the
mutation registry audits, build plus tests for touched Go packages, and
the frontend type-check. It exists because CI verdicts arrive well after
the push; today's unclassified force-fail route would have failed it
locally in about a minute.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Kiosk mode is frontend-only and a kiosk API token carries monitoring:read
alone, but the shell treated kiosk as a display preference rather than an
authority fact. Pressing Escape on a kiosk display revealed the full header,
the navigation, and a security-warning banner offering links into Settings.
Three separate bugs produced that:
Escape called toggleKioskMode() unconditionally. setKioskMode(false) writes
'false' to sessionStorage, so the exit was sticky for the rest of the browser
session and the auto-enable effect never re-armed. Escape now exits kiosk only
for a session that can reach settings; a scope-limited session gets the
temporary header reveal the hover and touch affordances already provide.
The redirect that keeps sessions off /settings, /patrol and the
alert-configuration tabs ran only while kiosk was on, so once Escape had turned
kiosk off a kiosk-token session could navigate into Settings. It now also keys
off whether the session's token can read settings, so it holds with kiosk off.
The panels themselves already 403 server-side; this is UI containment, not an
auth change.
/api/security/status truncates its payload by authority and names the tier in
detailLevel. A token without settings:read gets an 'authenticated' payload with
no posture fields at all, which the banner read as disabled controls and turned
into a false security score with an "Enable Security" link. The visibility
helper now refuses to assert anything the payload does not carry: an
authenticated payload raises nothing, a public payload is authoritative for
hasAuthentication only so a genuinely unauthenticated instance is still warned
about, and a privileged payload keeps the full assessment. The remaining global
banners deep-link into settings too, so that block is gated on the same
settings-access predicate.
Scope membership is now decided once by sessionHasSettingsAccess in
AppLayout.tsx and shared with App.tsx instead of being re-derived inline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Canonical Governance mutation registry audit scans the real router
sources and fails closed on any infrastructure route it cannot resolve to
a registry disposition, so the new POST /api/actions/{id}/force-fail
route broke the audit on main. Register it as the lifecycle entry
action.api.force-fail, executed by internal/actionlifecycle.Service.ForceFail
under the same execute_action capability, admin approval floor, and
committed-lifecycle delivery as execute, and classify the route in the
runtime surface catalogue.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>