go-webauthn refuses an assertion whose Backup Eligible flag differs from
the stored credential's. The flag was never stored, so it compared against
false and every synced passkey (iCloud Keychain, Google Password Manager,
1Password) failed with "Backup Eligible flag inconsistency" - in
passwordless sign-in, second-factor sign-in and step-up alike.
Registration now stores BackupEligible and BackupState. Rows registered
before this have no baseline, so their first verified assertion adopts the
signed flag and records it; a recorded value always stands, so a genuine
change is still refused.
Every WebAuthn refusal answered "that passkey could not be verified" and
discarded the library's error, leaving a misconfigured relying party
undiagnosable. Log the stage, the derived RP ID and expected origin, the
request's Origin and X-Forwarded-Proto, and the library error with its
DevInfo. None of it is secret.
The MFA routes were missing from routeScopes and serverScopedRoutes, and both
boot assertions refused to start the server. They are deliberately not
reachable by API tokens, so they go in a new sessionOnlyRoutes set rather than
receiving a scope, and are declared exempt from server scoping.
Adds a test that registers the real routes and runs both boot assertions, so
an undeclared route fails CI instead of production startup.
- OIDC sessions minted through saveSignIn with AMR oidc and StepUpAt
- passwordless passkey uses ValidateDiscoverableLogin with owner-handle check
- TOTP replay guard burns the matched step, not the current one
- enrol-only tickets refused once the user already has a factor
- bootstrap owner session minted through mintSession
The missing /me/step-up/webauthn/begin and /finish were a plan defect,
not an acceptable gap: a user whose only factor is a passkey was
offered only recovery codes for step-up, burning one every ten
minutes. Adds the handlers in package auth (session-authenticated,
not themselves behind RequireStepUp, modeled on
HandleMFAWebAuthnBegin/finishAssertion) and registers both routes
behind the same RateLimitAuth() as /me/step-up. StepUpModal now offers
"Use passkey" when the server names webauthn and the browser supports
WebAuthn.
Adds the global re-authentication modal for guarded routes and the
owner/admin MFA controls on the settings page.
request() in lib/api.ts now intercepts a 403 step_up_required response,
awaits re-authentication through a callback registered by StepUpModal
(lib/stepup.ts), and retries the original request exactly once. The
modal offers TOTP, recovery code and password, since the webauthn
step-up routes (/me/step-up/webauthn/begin and /finish) are not
registered server-side yet; it omits the passkey option rather than
calling a route that does not exist.
me.stepUp posts one factor to /api/me/step-up. The settings page gains
an owner-only "Require MFA" toggle and the members table gains an MFA
column and a "Reset MFA" action, both routed through the existing
PUT /api/settings and DELETE /api/org/users/:id/mfa.
apt lists phased updates as upgradable while an upgrade defers them until
Ubuntu selects the host, so a freshly patched server kept reporting pending
updates. The agent now flags them; the server stores the flag and leaves them
out of patch run counts, and the server page shows them in their own section.
Windows reports boot time as now minus uptime, which drifts by a second or so
between reports. A static report sent in the grace period before the reboot
could read as a changed boot and mark a server still owing a reboot as failed.
- no dispatch in the last 15 minutes of a window; no-result timeout from dispatch time
- per-server output moves to patch_run_outputs (16MB document limit)
- reboot proven by a changed boot time; RebootTimeout 45m, ResultGrace 20m
- window update and delete are server-scoped against the policies using them
- scheduler puts the claim back on an error after it, so the next tick retries
- cancelled runs with failures alert; MCP apply_updates audits per server
- apply-updates 503 body documented; openapi regenerated
- web: cleared numeric fields no longer save as 0; Run now asks for confirmation
Fix round 1 review findings on the patch run service:
- advanceRun no longer runs every server's dispatch claim and failed-send
reset on the caller's shared short context; each gets its own fresh
patchCtx(), and a failed reset write is logged instead of discarded.
- The dispatch claim (queued/waiting_offline -> patching) now also requires
the run to still be status running with no cancelled_at, closing a race
where a tick that loaded the run just before CancelPatchRun wrote
cancelled_at could still dispatch.
- RecordPatchResult's write is now guarded on command_id too, so a late
result for a superseded command cannot land on a re-dispatched attempt.
Named patch_window.go (not patch_windows.go) since the _windows.go
suffix is Go's implicit GOOS build constraint and would silently
exclude the file on non-Windows builds.
Implements the patchrun package with a pure functional state machine for managing
patch runs. Contains no database dependencies - the services layer loads a run,
asks this package what should change, and writes changes guarded by expected status.
All 14 test cases pass, covering:
- Agent version parsing and support detection
- Concurrency limits and queueing
- Window deadlines and offline handling
- Result timeouts (ResultGrace, ManualTimeout, RebootTimeout)
- Reboot verification with boot time proof
- Run finalization logic
- Summary generation for alerts