github LanternOps/breeze v0.112.0

latest release: v0.113.0
2 days ago

Breeze RMM v0.112.0 — the Windows MSI installs again, a security remediation wave that changes who can do what, and bare-metal recovery that actually restores the machine.

If you enroll Windows devices with the MSI, take this release now. Every fresh MSI install on Windows 10/11 has been refused since v0.110.0 (#5525, with a WiX build fix that re-cut the tag, #5571). The rest of the release is 87 commits and 34 migrations since v0.111.1: a security program covering 42 SEC-tracked findings across auth, remote access, PAM, accounting, billing/Stripe reconciliation, disaster recovery, fleet logging, ticketing, patch installs, backup/vault access, AI budget enforcement, OAuth grant revocation, enrollment, invitations, portal identity, software deployment and the installer trust chain; two live backup-assurance campaigns that fixed 26 defects on real Windows, Linux and macOS hosts and proved bare-metal recovery end to end, followed by a server-side backup-reclamation wave (base-pin, retirements, lineage) and the first wave of bare-metal boot-media recovery (recovery codes, state machine, heartbeat check-in); and the first wave of a new service-deliverables / org key-dates feature for MSP contract work.

⚠️ No breaking API changes, but this release removes implicit access and pauses some scheduled work on upgrade. Read "Security — action required" before you pull: pre-existing PAM auto-approve rules, recurring sensitive-data scans and recurring network-baseline scans all pause until a human re-enables them after the migration runs (PAM: Re-approve button; scans: re-save/re-activate the policy); QuickBooks, Workspace, connected-apps and PAM approval now need dedicated permissions that only Partner Admin and the built-in Org Admin hold; Office add-in technician bindings are revoked; outstanding installer bootstrap tokens are invalidated; and remote desktop sessions need an updated agent and are capped at 12 hours.

Summary

  • Windows MSI installs again — the OS floor now reads the registry instead of Windows Installer's shimmed VersionNT, which reports the Windows 8.1 value on every Windows 10+ host (#5525, WiX property-visibility fix #5571).
  • Security remediation wave, 42 findings — session-transition enforcement on every issuance path with no opt-out flag (#5475); a fail-closed revocation lease and 12 h hard cap on remote desktop (#5481); dedicated pam:approve / pam:manage_policy (#5480), accounting:read / accounting:manage (#5517) and workspace:* / connected_apps:* (#5472) permissions; site-restricted users fenced out of org-wide governance objects (#5482) and out of DR, log, ticketing, AI-agent and patch-compliance reads (#5539, #5542, #5546, #5547, #5563); creator-bound authority for recurring scans (#5359, #5521); installer bootstrap trust chain verified end to end (#5469, #5446); durable Stripe refund/dispute reconciliation (#5503); atomic AI budget reservations that close a check-then-spend overspend window (#5555); durable OAuth grant revocation (#5553); the unaudited remote-desktop file-drop channel removed (#5559); Windows patch installs bound to what the device actually observed (#5561).
  • Backup assurance, two phases — 18 + 8 defects fixed against real rigs, including silent data loss on colliding object keys, incremental dedupe pulling another device's base snapshot, Windows VSS restore/browse landing under the shadow-copy path, retention GC aborting on the first restored snapshot, and public recovery routes that rejected every valid token (#5418, #5461).
  • Bare-metal recovery that restores system state — system-state artifacts are published under their own snapshot prefix and verified by checksum on recovery; the Linux restorer applies packages, services, firewall, crontabs and /etc with an exclude policy (#5445, #5458, #5444, #5455). File backups now capture symlinks, directories, ownership and setuid/setgid/sticky bits (#5520), every system_image run records a disk-layout manifest with a restorability verdict, and a "Whole machine" profile is now a single snapshot with files, layout and state instead of two disjoint ones (#5523, #5572).
  • Backup storage reclamation and boot-media recovery, server side — the server (not the agent) now picks the incremental dedupe base and publishes it under a fixed lease, expired snapshots retire into a tombstone table instead of a bare delete, and full lineage tracks parent/incremental/late-result fencing (#5483); the boot-media recovery flow gained recovery codes, a bare_metal_recoveries state machine and agent heartbeat check-in (#5569); three live-rig defects found on the W03 boot proof were fixed — excluded mount points (/proc, /sys, /dev, /tmp) surviving a rebuild, resumed rebuilds reusing restore progress instead of re-restoring everything, and manifest checksums matching the bytes actually uploaded for files that changed mid-backup (#5587, #5584, #5583).
  • Service deliverables and org key dates (new, W01 of a multi-wave feature) — recurring/one-off MSP contract deliverables with a pure recurrence engine, and a typed deliverable ticket work-kind and organization key-dates tracker (insurance renewals, vendor contracts, compliance deadlines) as building blocks for the org record page (#5580).
  • Lenovo warranty lookup works for the first time, with two configurable providers (#5477).
  • Mobile — findings list and detail with acknowledge/dismiss/reopen (#5375); Stop timer opens the ticket with the note composer focused (#5372); requester contact on new tickets (#5377); one top-anchored toast host (#5378); Home fleet strip counts findings (#5371).

Added

  • Service deliverables and org key dates (W01, feature #5573) — tenant schema, a pure recurrence + occurrence state machine, core services, REST routes and org-record web surfaces for recurring/one-off MSP contract deliverables; tickets gain a typed work_kind (support / deliverable / project_task); organizations gain a key-dates tracker (insurance renewal, vendor contract end, compliance deadline, audit, other) with optional reminder tickets. Backend/data foundation for later waves — not yet a full portal-facing feature (#5580).
  • Whole-machine backup profile — a profile combining file: {paths:["/"]} with system_image: {enabled:true} now fans out into ONE system_image job with wholeMachine: true; the server (not the client) picks the OS root from the device's osType, producing a single snapshot with files, layout.json and system state together instead of two disjoint snapshots (#5572).
  • Bare-metal boot-media recovery, server + helper (W04a of feature #5493)bare_metal_recoveries state machine, recovery codes, agent heartbeat check-in, and a token-driven breeze-backup rebuild mode for the boot-media console; devices gain recovered_at / recovered_from_snapshot_id stamps (#5569).
  • Bare-metal rebuild engine (Linux, W03)breeze-backup rebuild provisions GPT partitions from a snapshot's layout manifest onto a disk or raw image, restores the whole-machine snapshot, applies system state offline, installs the bootloader and validates, with a resumable state file and --dry-run. Groundwork for boot media and restore-as-VM; not yet wired into the UI (#5529)
  • Bare-metal recovery — system-state published to snapshots/<id>/system-state/ with a schema-versioned manifest, per-artifact sha256, requiredSteps, and GC awareness (#5445); the recovery consumer verifies checksum and size on every artifact, fails on missing required steps, and no longer reports completed when advertised state was never applied (#5458); the Linux restorer reads the collector's real layout (packages/, services/systemd.txt, firewall/iptables.rules, crontabs/spool/) and restores /etc with fstab, machine-id, hostname, netplan and NetworkManager connections excluded (#5444); an integration cell and the Windows offline-hive decision doc (#5455).
  • File-backup fidelity — symlinks, empty and non-default directories, uid/gid and full mode bits are captured and restored; manifests carrying them stamp formatVersion: 3, plain-file manifests are byte-identical to before; ownership and special bits apply only when the restore runs as root, with one summary warning otherwise (#5520).
  • Layout manifest + restorability guard — every system_image run writes snapshots/<id>/layout.json, the snapshot row carries bareMetalRestorable and reasons (UEFI+GPT single disk with ext4/xfs/ntfs root is supported; LVM, LUKS, RAID, BIOS/MBR, multi-disk, btrfs and ZFS are named at backup time), the snapshot browser shows the verdict, and two "Whole machine" profile templates (Linux/Windows) ship with root-anchored excludes (#5523).
  • Backup exclude patterns are root-anchored — a leading / now anchors the pattern to the selection root (gitignore semantics) instead of matching at any depth (#5523).
  • Lenovo warranty — official Lenovo Warranty API via LENOVO_API_KEY, or the credential-free pcsupport endpoint via LENOVO_WARRANTY_ENABLED=true; DELL_CLIENT_ID / DELL_CLIENT_SECRET and the Lenovo knobs are now mapped through every compose file and documented (#5477).
  • Permissionspam:approve, pam:manage_policy (#5480); accounting:read, accounting:manage (#5517); workspace:read / write / credentials / execute and connected_apps:read / manage (#5472). All appear in the role editor automatically.
  • PAM — an "Auto-approve suspended — re-approve" badge and Re-approve action on quarantined rules; PAM controls hidden or disabled client-side for callers without the new permissions (#5480).
  • Remote desktopPOST /remote/sessions/:id/lease/renew and the viewer-token equivalent; "Remote desktop needs an agent update on this device" as a distinct state from "device offline" (#5481).
  • Network baselines — a "Recurring scan paused: re-approval required" banner, translated in all 8 locales (#5521).
  • TicketsrequesterContactId on POST /tickets and PATCH /tickets/:id (nullable to unlink), with name/email backfilled from the contact so reply mail keeps working; a searchable contact picker on the mobile New Ticket screen (#5377).
  • Mobile — findings list and detail from the Systems tab with Acknowledge, Dismiss (note required) and Reopen, gated exactly as the web drawer (#5375); Stop on the timer bar opens the ticket with the Internal note composer focused (#5372); Home fleet strip includes open findings and shares the hero's bar colours (#5371); a single top-anchored toast host replaces eight per-screen toasts (#5378).
  • Opsscripts/backup.sh --data backs up the api_data volume; BackupRestoreTestFailed / Stale / MetricsMissing alert rules; the npm audit gate now fails on HIGH as well as CRITICAL (#5490).
  • Release tooling — signed, digest-pinned release image inventory (release-artifact-manifest.json + scripts/release/verify-release-images.sh), macOS pkg attestation, and a Swift test job for the macOS installer (#5469).

Improved

  • Devices list — a refresh button refetches devices, assets and filters in place without swapping to the skeleton, and switching org context now uses a soft navigation instead of a full page reload (#5526)
  • Webhook delivery — one HTTP attempt per invocation with retries left to BullMQ; permanent failures (bad config, SSRF-blocked URL, ordinary 4xx) dead-letter immediately instead of burning the retry budget; retryCount is bounded to 0–2 (#5468). The delivery queue no longer carries decrypted webhook secrets; the worker decrypts once, immediately before the send (#5482).
  • Software deployments pin each approved dependency's package identity and version and refuse to install anything else; deleting a catalog entry is fenced against live deployments and removes its uploaded artifact from storage (#5473).
  • Backup engine — concurrent file + system_image jobs on one device no longer collide; the watchdog no longer restarts the agent mid-backup; long source paths no longer truncate the staging filename; selective restore matches exact paths, not string prefixes; system_image failures surface their real reason; restore progress and recovery downloads back off and honour SIGINT/SIGTERM; Windows read-only files are restored (#5418, #5461).
  • Backup storage reclamation (D18 W01, server side) — the server now chooses and publishes the incremental-dedupe base pin under a fixed lease, retention writes a durable backup_snapshot_retirements tombstone instead of a bare delete, each job/snapshot is stamped with a storage_identity at dispatch, and parent/incremental/late-result lineage is tracked through both the live-result and reconcile-adoption paths; retention's retirements now commit per row instead of inside one ambient transaction. The GC sweep rewrite that actually shrinks bucket usage is a later wave (#5483).
  • Bare-metal rebuild — a whole-machine manifest keeps excluded mount points (/proc, /sys, /dev, /run, /tmp, /var/tmp, /mnt, /media) as empty directory entries so a rebuilt root has real mount points for systemd and update-initramfs (#5587); a resumed rebuild after a strict-mode failure now reuses restore progress instead of re-restoring every file, and validate skips files the restore already reported as failed under AllowPartialRestore (#5584); a whole-machine snapshot's manifest size/checksum now reflect the bytes actually uploaded — files that mutate mid-backup are re-measured once and marked volatile instead of failing restore's size check, and the run's own checkpoint journal is excluded (#5583).
  • Backup helper — MSSQL and Hyper-V on-demand backups no longer 500 on the queued ack; policy-managed devices no longer answer "backup not configured"; SQL Server 2022/2025 self-signed certificates are accepted; BACKUP TO DISK targets the instance's default path and skips compression on Express; BMR downloads follow presigned redirects without leaking the recovery token (#5461).
  • Retention GC — expired-snapshot cleanup no longer aborts on the first snapshot that was ever restored, verified or tokened; per-row isolation with a 1 h production floor on BACKUP_GC_GRACE_MS (#5418). System-state and layout objects are marked live so the new layouts survive the 48 h grace (#5445, #5523).
  • AI approvals — approval headlines name the device (display name, then hostname) on every intent path, not just chats opened from a device page (#5374); the service name in an execute_command headline matches what the dispatcher actually targets (#5464); the MCP-bootstrap funnel tool is renamed get_invite_funnel so the model stops answering "fleet status" with invite counts, with a dispatch-only alias for one release (#5373).
  • Auth — org-scoped sessions are now evaluated against the partner IP allowlist; MFA/passkey settings calls retry a 428 auth_binding_rotation_required once transparently; CF Access and SSO logins 302 back to /login on a binding failure instead of returning raw JSON (#5475).
  • Enrollment — key creation, reads, counts and lifecycle operations enforce site scope (#5446).
  • Docs — every one of the 170 docs pages was read against source: ~230 findings fixed in two phases, including the missing BREEZE_BOOTSTRAP_ADMIN_EMAIL/_PASSWORD prerequisite, features documented as working that were not implemented, and ~25 wrong UI paths (#5422, #5437); actual Linux support stated (X11-only capture, apt/dnf patching, dpkg/rpm inventory) (#5476); manifest signing-key rotation documented as a fleet re-enrollment (#5488).
  • CI — every code-signing job now depends on release-lineage validation (#5463); the standing hosted-images publish workflow is removed (#5462); the unused Chrome apt source no longer breaks Rust and Guided Setup jobs (#5383); @tiptap/core, nodemailer and sharp bumped past three HIGH advisories (#5434).

Fixed

  • Windows MSI refused every fresh install (#5525). The Windows MSI refused every fresh install on Windows 10/11 from v0.110.0 through v0.111.1 with "Breeze Agent requires Windows 10 or Windows Server 2016 or later". Windows Installer reports VersionNT=603 on all Windows 10+ by design (Microsoft KB 3202260); the floor now reads the registry (CurrentMajorVersionNumber). Existing agents were unaffected (self-update never runs msiexec); only new MSI enrollments were blocked. Repair, upgrade and uninstall can never be blocked by the new condition, and legacy Windows still gets the same message.
  • Lenovo warranty never worked — the provider issued a GET to an endpoint that only accepts POST, so every Lenovo device landed in unknown; rewritten with two providers and live-verified against real serials (#5477).
  • Bare-metal recovery — public recovery-token routes queried on the bare pool, so forced RLS rejected every valid token as "Invalid recovery token" (broken since April) (#5418); system-state artifacts were published where the consumer never looked, so recovery downloaded them and applied nothing while reporting completed (#5445, #5458); the Linux restorer looked for flat filenames the collector never wrote (#5444); bmr-recover hit the shared per-IP download limiter after ~134 files and its completion payload overflowed the body cap (#5418); BMR downloads 400'd when the API and S3 share a host (#5461).
  • Backup data integrityreport and report.gz collapsed to one object key (silent loss); incremental dedupe could reference another device's base snapshot; Windows VSS-backed restore re-rooted under HarddiskVolumeShadowCopyN; API browse/restore indexed by VSS sourcePath so Windows selective restore and browse were unusable; POST /backup/bmr/media failed ENOSPC under the container's 64 MB /tmp (#5418).
  • Retention — expired-snapshot cleanup aborted with 23503 on the first restored/verified/tokened snapshot, stopping retention and the object sweep for the whole deployment (#5418, migration 140004).
  • Remote desktop — End on a multi-replica deployment marked the row disconnected but never reached the agent, so the operator kept screen and input after clicking End; a concurrent terminal write could be clobbered into a clean disconnected; End had no site-scope check (#5518). Helper-hosted sessions (Windows service / macOS daemon installs) would have died 150 s after start under the new lease without the IPC bridge that ships with it (#5481).
  • PAM mobile push fan-out silently dropped partner-scope approvers whenever it ran under an org-scoped context; the mobile decide path for elevation-linked approvals had no live permission check (#5480).
  • Portal — an Entra-provisioned portal user could authenticate against a stale local password hash or be driven through the local reset flow; the generic profile PATCH could write passwordHash (#5471).
  • Invitations — an invitation that omitted a site scope widened to the whole organization; a concurrent change to the delegator's own scope could be raced into a wider invitation (#5486).
  • Site-restricted operators could mutate org-wide governance objects (webhooks, notification channels, software and peripheral policies, configuration policies, PAM config, backup destinations and profiles) through the API and the AI-tool surface, and could decide quarantines, read automation history and legacy alert definitions outside their site (#5482, #5376).
  • Approval headlines showed on device 6eae0f70… instead of the hostname on general chats, tier-2 per-step approvals, MCP and unscoped agent intents (#5374).
  • Mobile — Home strip said "no issues" while Systems showed 3, and painted offline devices red on one surface and amber on the other (#5371); "Show fleet status" chip narrated an empty fleet under a strip reading 36 online (#5373); toasts covered the composer, the Ask Breeze input or the row they announced (#5378); tapping a finding did nothing (#5375); stopped timers landed as No description (#5372).
  • Xcode Cloud — Maven now redirects Hermes tarballs to a host that 404s, so the podspec silently built Hermes from source and failed on a missing cmake; the post-clone hook fetches and sha1-verifies the prebuilt tarball (#5379).
  • Sensitive-data recurring scans ran under the approving user's authority indefinitely after that user lost it (#5359); the same for recurring network-baseline scans (#5521).

Security

Self-hosters are strongly encouraged to upgrade. This release lands the bulk of the 2026-09-05 security review remediations. No public advisories are pending for this range.

Security — action required

  • Agent: private script/restore staging (SEC-121, High) — the privileged agent staged scripts in a shared, predictable /tmp/breeze-scripts tree and used predictable restore/resume paths, so a less-privileged local user could plant a symlink and make root/SYSTEM write outside the intended tree (proven on Linux). Every script now gets an OS-created private directory and exclusive file, restore publication goes through descriptor-relative atomic renameat, and downloaded bytes are verified before publication, on Linux, macOS and Windows. Promote the 0.112.0 agent to any host with untrusted local accounts (#5519)
  • PAM auto-approve rules are suspended (#5480). Migration 150200 rewrites every pam_rules row with verdict = 'auto_approve' to require_approval, regardless of enabled, keeping the original in suspended_verdict. Every matching elevation now waits for a human until an admin holding pam:manage_policy re-approves it: PATCH /pam/rules/:id { "reapprove": true } (MFA required), or the Re-approve action on the PAM rules page. Approving, denying or revoking an elevation now needs pam:approve; a technician who could approve via devices:execute gets 403 pam_approve_required until granted.
  • Recurring sensitive-data scans are deactivated (#5359). Migrations 140001140003 deactivate every legacy recurring sensitive-data policy and fail queued or running scheduled scans. Each policy shows as inactive; a user with devices:write + devices:execute re-activates it (MFA step-up) before it recurs again. There is no separate re-approve button. Manual scans are unaffected.
  • Recurring network-baseline scans are paused (#5521). Migration 150600 marks every enabled recurring baseline schedule_blocked_reason = 'reapproval_required'; the page shows "Recurring scan paused: re-approval required" and saving the schedule arms it under the saving user. "Scan Now" is unaffected. Auto-created baselines now start with recurrence disabled. Hosted regions had zero scheduled baselines at upgrade.
  • New permissions replace implicit access (#5480, #5517, #5472). pam:approve, pam:manage_policy, accounting:read, accounting:manage, workspace:read|write|credentials|execute and connected_apps:read|manage are granted only to Partner Admin (via *:*) and the built-in Org Admin (every per-partner clone, via migrations 150030/150031/150032/150200/150500). Custom roles and every other built-in role (Partner Technician, Partner Billing, Org Technician, …) lose access until you grant the new keys in Settings → Roles. Concretely: every interactive QuickBooks route now needs accounting:*, so a Partner Technician or Partner Billing user who could browse or sync QuickBooks gets 403 until granted; Workspace sources and partner connected apps (OAuth/MCP) need workspace:* / connected_apps:*.
  • Office add-in technician bindings are revoked (#5475). Migration 150051 revokes every pre-existing binding (none carry trustworthy MFA-generation provenance) and the bind route now requires the technician to be on TOTP. Technicians re-run the Entra bind ceremony. Hosted regions had zero bindings.
  • Outstanding installer bootstrap tokens are invalidated (#5446). Migration 141001 deletes unused bootstrap-derived enrollment keys and bumps the generation on their parents. Regenerate any installer links or copies you handed out before the upgrade. Stop old API replicas before applying; an older API must not resume serving after this cutover.
  • Remote desktop needs an updated agent and is capped at 12 hours (#5481). Only an agent reporting revocationLeaseProtocolVersion: 1 can start a desktop session; older agents get 503 agent_upgrade_required ("Remote desktop needs an agent update on this device"). Expect a gap of up to one heartbeat interval (60 s) per device while agents auto-update. If you pin an agent version, desktop sessions are unavailable until you update the pin. maxSessionDurationHours is clamped to 1–12: 0 no longer means unlimited, values above 12 read as 12, and saving a new out-of-range value is refused. Removing a membership, changing a role, narrowing site scope or flipping force_mfa now ends the live session within ~25 s. A Redis outage longer than 90 s ends live desktop sessions. Terminal, Files and VNC are unaffected.
  • Session issuance has no opt-out any more (#5475). AUTH_BROWSER_TRANSITIONS_ENFORCED is removed; every issuance path (password, CF Access, TOTP, SMS, recovery code, passkey, refresh, registration, invite) goes through the guarded issuer. Roll all API replicas in one pass; the safety net is an image rollback, not a flag. Mobile app builds older than v0.109.0 cannot sign in until updated. Existing browser sessions self-heal on their next refresh.
  • Legacy policy execution routes are gone (#5467). POST /policies/:id/evaluate, /remediate and /activate are retired (they could dispatch a partner-wide automation run on devices:read). Use POST /automations/:id/trigger. /deactivate is unchanged. No first-party consumer used them.
  • Site-restricted technicians lose PSA and DNS-security connection management (#5475) and can no longer create or edit org-wide governance objects (#5482) — they can still assign an existing policy to their own site.
  • Client-IP attribution changes when proxy trust is off (#5475). With no TRUSTED_PROXY_CIDRS, the raw socket peer address is used instead of the unknown sentinel. Verify TRUSTED_PROXY_CIDRS is set on every deployment behind a real proxy.
  • New MFA gates on four more write surfaces, if ENABLE_2FA=true (SEC-070, SEC-071, SEC-097, SEC-107; #5541, #5543). User-risk training/feedback writes and the org-wide risk-scoring policy, platform-admin extension enable/disable, and configuration-policy "effective" mutations now require a live MFA claim on the session, the same way PAM approvals already do. With MFA disabled instance-wide, behaviour is unchanged. With MFA enabled, a session that authenticated without completing MFA now gets 403/428 on these routes where it previously succeeded.
  • Manual Windows patch installs now require the device to have reported the update as pending (SEC-115, #5561). POST /devices/:id/patches/install used to accept any catalog patch id; it now inner-joins device_patches and requires an exact match on the target device plus a pending observation. A patch the device hasn't (yet) reported now 404s instead of dispatching — expect this if you push installs immediately after adding a device to a catalog before its next patch-inventory heartbeat.
  • Two new invoice-lifecycle error codes (SEC-105, #5550). Deleting a time_entry or ticket_part that an issued invoice line already references now returns 409 ENTRY_BILLED / 409 PART_BILLED instead of silently deleting the operational source row out from under a live invoice. Invoice void is the only way to release a billed row. A same-named data-repair migration (160150) resets any pre-existing orphaned billed rows back to not_billed on upgrade — check Postgres logs for its RAISE WARNING row counts; a non-zero count means those rows were reachable through the bug this release closes.

Security — hardening

  • Remote access — session credentials and consent verdicts are bound to the live agent credential generation (migration 150040); authorization is revalidated before parsing live tunnel/remote traffic; denials are latched; dispatch is fenced to the deciding org; device-reset notification is ordered after the transaction commits (#5474). End goes through the durable relay with a status guard and site check (#5518).
  • Auth — mobile refresh-token families are bound to the issuing device (migration 150050); MFA factor transitions commit atomically; alert-channel and webhook secret mutations are bound to the credential that created them (#5475).
  • Portal — password and Entra identity paths are separated; sessions, tokens and invitations carry a per-user auth_epoch (migration 150010) so any epoch bump invalidates them immediately; secret-safe audit events on every portal auth endpoint (#5471).
  • Invitations — the delegating membership row is locked and the requested site scope validated in the same transaction (#5486).
  • Installer trust chain — bootstrap token redemption and enrollment refuse cross-host and HTTPS→HTTP redirects; first-install staging verifies the full signed release tuple against the embedded Ed25519 manifest key before any privileged fallback binary runs; the macOS Swift installer, watchdog and desktop-helper bootstrap adopt the same checks; API-side installer and binary services verify release lineage (#5469). Enrollment keys bind bootstrap credentials to the parent key generation (#5446).
  • Backup helper admission — the session broker admits breeze-backup only via a one-time, agent-spawned PID reservation; an independently launched copy of the genuine binary can no longer claim the backup IPC slot (#5466). The agent never deletes another snapshot's remote objects, and manifest publish is fenced by a server-granted lease (#5456).
  • Governance objects — an approval_generation counter on webhooks, software_policies and backup_configs (migration 150100) is snapshotted at enqueue and compared at execution, so an already-queued job never acts on a since-edited or disabled config; a mechanical coverage guard walks every write path (#5482).
  • Software deployment — dependency identity and version pinned at approval (migration 150020); org download policies restricted to the caller's site authority (#5473).
  • Sensitive-data scans — database guards keep recurring scans within the approving user's current execution and site permissions through scheduling, retries and command creation (#5359). Network baselines — creator-bound versioned authority re-resolved live at dispatch under FOR UPDATE; system-scope arming refused; AI network tools honour site scope; the baseline API response is an explicit allowlist (#5521).
  • Quarantine decisions require MFA; legacy alert-definition reads require alerts:read (#5376).
  • AI toolsrevoke_elevation requires pam:approve (#5480); every AI-tool twin of a governance write path carries the site-ceiling gate (#5482).
  • Supply chain — signing jobs gated on lineage validation (#5463); no standing workflow can publish server images (#5462); dependency advisories cleared (#5434).
  • Site-scope reads closed across six more surfaces — site is an app-layer axis only (Postgres RLS enforces org, not site), so each of these needed an application-level fix: disaster-recovery plan/execution reads, including the shared AI/MCP DR tools (SEC-078, #5539); fleet log search, aggregation, trends and async pattern-correlation results (SEC-079, SEC-080, #5542); software-deployment parent reads/cancel, billing settings, principal disable, API-key mutation, login branding, network-change links and MCP bootstrap (SEC-019, SEC-031, SEC-033, SEC-046, SEC-066, SEC-114, SEC-138, #5544); AI-agent run history, SentinelOne status (web and the shared AI tool), performance insights, playbook history and reliability reads (SEC-052, SEC-064, SEC-113, SEC-147, SEC-148, #5546); ticket assignee eligibility and inbound-reply/triage-aggregate binding (SEC-022, SEC-104, SEC-153, #5547); backup verification listings, vault mutations and local-vault tenant binding (SEC-021, SEC-024, SEC-026, #5554); patch compliance-report generation and download (SEC-095, #5563).
  • Billing / Stripe — direct-account refund and dispute events are now polled per connection and persisted to a new stripe_financial_events table before the reversal cursor advances; a durable state machine applies refunds monotonically and disputes on a provider-timestamp high-water mark, with invoice-first lock ordering shared with manual writers and QuickBooks deletion published only after commit (SEC-151, SEC-152, #5503).
  • AI budget enforcement is now atomic (SEC-111, SEC-142, SEC-143, #5555). Budget admission across every technician-facing LLM entry point (including Office draft accounting) reserves spend durably before dispatch instead of check-then-spend, closing a window where concurrent turns for the same org could all pass the same stale read and overspend the daily/monthly cap by an unbounded multiple. Reservations are time-bounded and swept by a new background job; session and aggregate cost columns move from real/float4 to numeric(20,6) so high totals no longer lose whole cents.
  • OAuth grant revocation is now durable everywhere it's checked (SEC-139, SEC-140, #5553). Applies only to MCP_OAUTH_ENABLED deployments (default false). Revoking a public OAuth client used to leave a live authorization code/PKCE verifier exchangeable until an eager Redis marker expired, after which the full consented credential family could be restored under an already-revoked grant; the durable oauth_grants row is now the continuing authority everywhere.
  • Remote desktop: the unaudited file-drop data channel is removed (SEC-127, #5559). The agent no longer offers a filedrop WebRTC data channel — it had no server-resolved authorization, no central audit trail, and no first-party consumer (the viewer never used it).
  • Refresh-token family revocation is reported truthfully (SEC-004, #5549). The /refresh reuse-detection audit row used to claim "entire family revoked" unconditionally; it now reports each store's actual outcome (confirmed / unavailable / failed for Redis and Postgres independently), so an operator reading the audit trail can tell contained families from uncontained ones.
  • Agent-log retention and default ordering anchor to server receipt time, not the agent-supplied timestamp (SEC-117, #5548). An agent that stamps rows in the far future could previously keep them past retention indefinitely and pin itself above real evidence in the default view; retention now prunes on created_at, and diagnostic/watchdog log views and the search_agent_logs AI tool order by receipt time first. Explicit since/until filters still match on agent event time, so investigation semantics don't change.

Self-Hosting / Upgrade Notes

No breaking API changes beyond the three retired legacy policy routes (#5467), plus two new 409 error codes on time-entry/ticket-part deletion (ENTRY_BILLED / PART_BILLED, additive). No new required environment variables — verified against apps/api/src/config/validate.ts for the whole v0.111.1..v0.112.0 range. One env var is removed. Several defaults and permissions change on upgrade — read "Security — action required" above and "Behaviour changes" below before you pull.

Upgrade command — bump BREEZE_VERSION in /opt/breeze/.env, then:

docker compose pull api web portal && docker compose up -d binaries-init api web portal

(portal is a separate container — include it. If you have opted into the worker split, add worker. Building from source: pnpm install first.) Roll api, web and portal together and in one pass — #5475 has no flag-level rollback and #5481 expects every replica on the new lease protocol. Assert version parity across every ghcr.io/lanternops/breeze/* container afterwards rather than trusting the service list.

Sequencing for #5446: stop old API replicas, let the migration apply, restart only fixed replicas. An older API must not resume serving after the cutover.

Rollback order (#5481): agents before the API. A new agent talking to a rolled-back API gets no lease answer and ends its desktop session at ~150 s. The two added columns are additive and need no schema rollback. Rolling back #5521 resumes dispatching every quarantined network baseline under no authority; clear scan_schedule.enabled on the affected rows first. Prefer roll-forward for everything in this release.

Database — 34 idempotent migrations. Take a snapshot first. All auto-apply on API boot via autoMigrate (unless AUTO_MIGRATE=false). None abort on pre-existing data. Operator-visible effects, verified by reading the SQL:

  1. 2026-10-15-150200-pam-dedicated-permissions.sql (#5480) — rewrites every pam_rules row with verdict auto_approve to require_approval (original kept in suspended_verdict); seeds pam:approve / pam:manage_policy and grants them to every built-in Org Admin. Only PATCH /pam/rules/:id {"reapprove": true} with pam:manage_policy + MFA restores auto-approve.
  2. 2026-10-15-150051-office-addin-binding-mfa-epoch.sql (#5475) — revokes every pre-existing Office add-in user binding; technicians re-run the add-in binding.
  3. 2026-10-15-140001 / 140002 / 140003-sensitive-data-*.sql (#5359) — deactivate all legacy recurring sensitive-data policies and fail queued/running scheduled scans until each policy is re-activated (MFA).
  4. 2026-10-15-141001-installer-bootstrap-credential-generation.sql (#5446) — deletes unused bootstrap-derived enrollment keys and bumps generation on parents, invalidating outstanding installer bootstrap tokens.
  5. 2026-10-15-150030 / 150031 / 150032 (#5472) and 150500-accounting-dedicated-permissions.sql (#5517) — seed workspace:*, connected_apps:*, accounting:read, accounting:manage and grant them only to the built-in Org Admin (Partner Admin holds *:*). Custom roles and technicians lose implicit access; QuickBooks routes now need accounting:*.
  6. 2026-10-15-140004-backup-snapshot-lineage-fk-set-null.sql (#5418) — restore_jobs.snapshot_id and recovery_tokens.snapshot_id become nullable; five FKs onto backup_snapshots move to ON DELETE SET NULL so retention can delete a snapshot that was ever restored, verified or tokened.
  7. 2026-10-15-150600-network-baseline-recurring-authority.sql (#5521) — quarantines every enabled recurring network-baseline scan with schedule_blocked_reason = 'reapproval_required' until re-saved by an authorized user; adds the authority envelope columns.
  8. 2026-10-15-150001-bound-webhook-retries.sql (#5468) — normalizes legacy notification_channels webhook retryCount values into 0–2 (count reported via RAISE WARNING).
  9. 2026-10-15-150050-mobile-refresh-family-binding.sql (#5475) — adds a partial index on refresh_token_families in-transaction (not CONCURRENTLY). Instant at hosted scale (hundreds to low thousands of rows); run off-peak if yours is very large.
  10. 2026-10-15-160150-repair-orphan-billed-sources.sql (#5550) — resets any pre-existing time_entries / ticket_parts row marked billed without a matching issued invoice line back to not_billed. Deliberately reports counts and affected ids via RAISE WARNING even when zero rows match, so the rollout leaves an auditable record — check Postgres logs after upgrade; a non-zero count means those rows were reachable through the SEC-105 bug this release closes.
  11. 2026-10-15-160101-ai-cost-numeric.sql (#5555) — widens ai_sessions.total_cost_cents and ai_cost_usage.total_cost_cents from real/float4 to numeric(20,6). Existing values are cast, not recomputed; no data loss, slightly wider storage.
  12. 2026-10-15-160100 / 160102-ai-budget-reservations*.sql (#5555) — new ai_budget_reservations table (durable pre-dispatch AI budget holds, TTL-bounded, swept by a new background job). No existing data touched.
  13. 2026-10-15-160200-bare-metal-recoveries.sql (#5569), 160201-backup-jobs-base-pin-and-storage-identity.sql / 160202-backup-snapshot-retirements.sql (#5483) — new tables/columns for boot-media recovery state and backup storage reclamation bookkeeping. DDL only, no existing rows rewritten.
  14. 2026-10-15-170000-service-deliverables.sql, 170100-tickets-work-kind.sql, 170200-organization-key-dates.sql (#5580) — new tables for the service-deliverables feature; tickets gains work_kind (defaults every existing ticket to support, so nothing currently in your data changes category). DDL only.
  15. 2026-10-15-150400-stripe-financial-reconciliation.sql (#5503) — new stripe_financial_events table plus six new columns on invoice_stripe_payments and cursor columns on stripe_connect_accounts. Only exercised if you run direct-account Stripe Connect; no action needed otherwise.
  16. 2026-10-15-160110-local-vault-device-org-fk.sql (#5554), 160120-patch-compliance-report-site-scope.sql (#5563), 160130-agent-log-receipt-time-indexes.sql (#5548) — supporting constraints/indexes for the site-scope and log-ordering fixes above. No data rewritten.
  17. Additive, no data change: 150010 (portal_users.auth_epoch, backfilled to 1), 150020 (pinned deployment dependencies), 150040 (remote consent generation), 150100 (approval_generation on webhooks / software_policies / backup_configs), 150300 (devices.revocation_lease_protocol_version, remote_sessions.permissions_epoch_snapshot), 160010 (backup_snapshots.layout_manifest / bare_metal_restorable / bare_metal_reasons).

Environment variables

  • Removed: AUTH_BROWSER_TRANSITIONS_ENFORCED (#5475). Delete it from .env; it is ignored. There is no toggle back to the unguarded issuance path.
  • New, optional: LENOVO_WARRANTY_ENABLED=true (credential-free pcsupport lookup) and/or LENOVO_API_KEY (official Lenovo Warranty API); DELL_CLIENT_ID / DELL_CLIENT_SECRET are now mapped through compose rather than needing hand-added lines (#5477). RECOVERY_MEDIA_WORK_DIR (default /data/recovery-work, moves bare-metal media builds off the 64 MB /tmp tmpfs) and BACKUP_GC_GRACE_MS (lab-only; production floor 1 h, default 48 h unchanged) (#5418). Map any you set explicitly in the api service environment: block.
  • Check: TRUSTED_PROXY_CIDRS — with proxy trust unset, client-IP attribution now shows the socket peer instead of unknown (#5475).

Fresh installs and image pinning (#5469). .env.example and deploy/docker-compose.prod.yml now expect signed, digest-pinned image references (ghcr.io/lanternops/breeze/api@sha256:…) resolved from release-artifact-manifest.json via scripts/release/verify-release-images.sh; the guided setup requires that verification, fail-closed, for 0.112.0 and later (SIGNED_IMAGE_INVENTORY_MIN_VERSION) and falls back to tag-tracking below it. Existing installs on the root docker-compose.yml with BREEZE_VERSION-driven refs keep working with the upgrade line above. Rotating RELEASE_ARTIFACT_MANIFEST_PUBLIC_KEYS should be treated as a fleet re-enrollment — agents pin the keys they enrolled under and there is no remote revocation yet (#5488).

Behaviour changes / flags

  • Desktop sessions: 12 h hard cap, fail-closed revocation lease, agent update required (#5481) — see "action required".
  • Webhooks: one attempt per invocation, BullMQ owns retries, retryCount capped at 2, permanent failures dead-letter immediately; /test and /retry on a paused webhook return 409 (#5468, #5482).
  • Backup exclude patterns with a leading / are root-anchored (#5523). File backups of trees containing symlinks, special directories or non-default owners produce formatVersion: 3 manifests; older agents cannot read those entries (#5520).
  • Retention GC no longer stops on a restored/verified/tokened snapshot (#5418). Bucket usage still does not shrink — the GC sweep rewrite that reclaims space from retired snapshots is a later D18 wave; this release lands the base-pin, lease and retirements bookkeeping it depends on (#5483, agent half #5456).
  • The agent never deletes another snapshot's remote objects; legacy servers (no baseSnapshotId in the payload) keep the bucket-listing dedupe path (#5456).
  • Auto-created network baselines start with recurrence disabled (#5521).
  • MCP tool get_fleet_status is renamed get_invite_funnel; the old name is a dispatch-only alias for one release and no longer appears in tools/list (#5373).
  • Org-scoped sessions are now checked against the partner IP allowlist (#5475).
  • hpProvider is de-registered from warranty lookup; HP has no working lookup (#5477).
  • Emergency hosted image builds are done by hand (docker buildx build … --push); the hosted-images.yml dispatch workflow is gone (#5462).
  • If ENABLE_2FA=true: user-risk training/feedback/policy writes, platform-admin extension enable/disable and configuration-policy "effective" mutations now require a live MFA claim on the session (SEC-070, SEC-071, SEC-097, SEC-107). No effect if MFA is disabled instance-wide.
  • Manual Windows patch installs (POST /devices/:id/patches/install) now 404 if the target device hasn't reported the patch as pending; wait for the next patch-inventory heartbeat after adding a device to a catalog (SEC-115).
  • Agent diagnostic/watchdog log default views and search_agent_logs now order by server receipt time first, not the agent-supplied event timestamp; explicit since/until filters are unaffected (SEC-117).
  • Deleting an invoiced time_entry or ticket_part now 409s (ENTRY_BILLED / PART_BILLED) instead of silently succeeding; void the invoice first (SEC-105).
  • The remote-desktop agent no longer offers a filedrop WebRTC data channel; it had no consumer in any first-party client (SEC-127).
  • If MCP_OAUTH_ENABLED=true: OAuth grant revocation is now enforced durably against the oauth_grants row on every bearer path, closing a window where a revoked grant's still-live authorization code could restore the credential family after the Redis revocation marker expired (SEC-139, SEC-140). Default is false; no effect otherwise.

Feature flags: none added or removed beyond the AUTH_BROWSER_TRANSITIONS_ENFORCED removal.

Full Changelog: v0.111.1...v0.112.0

What's Changed

  • fix(scans): preserve current authority for recurring scans by @ToddHebebrand in #5359
  • docs: v0.111.1 hotfix note in upgrades by @ToddHebebrand in #5358
  • fix(api): enforce site-scoped operator controls by @ToddHebebrand in #5376
  • docs(mobile): App Store listing copy for the shipped 1.0 feature set; iPhone-only + tab-name fixes by @ToddHebebrand in #5369
  • fix(api): resolve device hostname for approval headlines on every intent path (#5363) by @ToddHebebrand in #5374
  • fix(mobile): single top-anchored toast host; retire per-screen toasts (#5368) by @ToddHebebrand in #5378
  • fix(mobile): fetch prebuilt Hermes in the Xcode Cloud post-clone hook; cmake fallback by @ToddHebebrand in #5379
  • feat(tickets,mobile): set a requester contact on new tickets; accept requesterContactId on the staff ticket API (#5367) by @ToddHebebrand in #5377
  • fix(mobile): count findings in the Home fleet strip and share the bar severity mapping (#5364) by @ToddHebebrand in #5371
  • fix(ai,mobile): rename get_fleet_status to get_invite_funnel; reword Home chip (#5362) by @ToddHebebrand in #5373
  • feat(mobile): Stop timer opens the ticket with the internal-note composer focused (#5366) by @ToddHebebrand in #5372
  • docs: full-corpus review, phase 1 — correct wrong, stale and misnavigated content by @ToddHebebrand in #5422
  • chore(deps): bump @tiptap/core, nodemailer, sharp for Trivy HIGH advisories (#5432) by @ToddHebebrand in #5434
  • feat(mobile): findings list + detail with acknowledge/dismiss/reopen from the Systems tab (#5365) by @ToddHebebrand in #5375
  • docs: full-corpus review, phase 2 — add missing coverage by @ToddHebebrand in #5437
  • Backup assurance campaign: 18 defects fixed, harness, docs (Linux bare-metal recovery and retention GC proven end to end) by @ToddHebebrand in #5418
  • fix(ci): isolate unused Chrome apt source during dependency setup by @ToddHebebrand in #5383
  • fix(agent/bmr): Linux system-state restorer matches collector layout, /etc exclude policy (D15 W03) by @ToddHebebrand in #5444
  • docs(backup): D18 storage-reclamation design spec + W01–W04 plans (#5429, feature #5449) by @ToddHebebrand in #5454
  • docs/test(backup): D15 W04 — system-state BMR integration cell, Windows offline-hive decision, plan tracking by @ToddHebebrand in #5455
  • feat(backup): publish system-state under its own snapshot prefix + GC awareness (D15 W01) by @ToddHebebrand in #5445
  • feat(backup): BMR consumer verifies system-state checksums, enforces required steps, fixes status derivation (D15 W02) by @ToddHebebrand in #5458
  • fix(enrollment): enforce site scope and revoke rotated installer credentials by @ToddHebebrand in #5446
  • chore(ci): remove manual hosted-images publish workflow by @ToddHebebrand in #5462
  • fix(ci): signing jobs depend on release lineage validation by @ToddHebebrand in #5463
  • fix(ai): serviceName selector only falls back on an explicit undefined name by @ToddHebebrand in #5464
  • fix(policies): retire legacy evaluate/remediate/activate routes by @ToddHebebrand in #5467
  • feat(agent/backup): server-owned dedupe base, publish lease fence, never delete remote objects (D18 W03) by @ToddHebebrand in #5456
  • fix(webhooks): bound automatic delivery retries per queued cycle by @ToddHebebrand in #5468
  • fix(agent/backup): broker admits backup helper only via a one-time agent-spawned PID reservation by @ToddHebebrand in #5466
  • fix(portal): separate password and Entra identity paths; epoch-check invitations and live sessions by @ToddHebebrand in #5471
  • fix(remote): bind sessions to live credential generation; latch denials; fence dispatch to the deciding org by @ToddHebebrand in #5474
  • docs(agent): state actual Linux support — X11-only capture, apt/dnf patching, dpkg/rpm inventory by @ToddHebebrand in #5476
  • fix(workspace): require explicit permissions for workspace and connected-app actions by @ToddHebebrand in #5472
  • fix(api): make the Lenovo warranty provider actually work (POST pcsupport + official supportapi) by @ToddHebebrand in #5477
  • Backup assurance phase 2: Windows bare-metal recovery, Windows 10, Hyper-V and SQL Server proven on real hosts; 8 more defects fixed by @ToddHebebrand in #5461
  • fix(software): pin deployment dependency package identity and version at approval by @ToddHebebrand in #5473
  • fix(installer): verify release asset identity and signature before privileged fallback; protect first-install staging by @ToddHebebrand in #5469
  • fix(remote): fail-closed revocation lease and 12h hard cap for desktop sessions by @ToddHebebrand in #5481
  • fix(authz): site-restricted users cannot mutate organization-wide governance objects by @ToddHebebrand in #5482
  • fix(auth): enforce session transition epochs on every issuance path; bind mobile refresh families; atomic factor finalization by @ToddHebebrand in #5475
  • fix(pam): dedicated pam:approve and pam:manage_policy permissions; suspend legacy auto-approve rules pending re-approval by @ToddHebebrand in #5480
  • docs(self-host): treat manifest signing-key rotation as agent re-enrollment by @ToddHebebrand in #5488
  • fix(tenancy): enforce invitation scope ceilings with row-locked membership validation by @ToddHebebrand in #5486
  • chore(ops): backup --data, HIGH+CRITICAL audit gate, restore-test readiness race, backup alerts, teardown docs by @ToddHebebrand in #5490
  • docs(backup): bare-metal recovery from Breeze boot media — spec + W01/W02 plans (#5493) by @ToddHebebrand in #5502
  • docs(backup): W03 rebuild engine (Linux) plan for bare-metal recovery (#5493) by @ToddHebebrand in #5504
  • fix(accounting): dedicated accounting:read / accounting:manage permissions; full-partner authority on every QuickBooks route (SEC-057) by @ToddHebebrand in #5517
  • fix(discovery): creator-bound versioned authority for recurring network-baseline scans; AI network tool site scope (SEC-146) by @ToddHebebrand in #5521
  • feat(backup): file-backup fidelity — symlinks, directories, ownership, full mode bits (W02) by @ToddHebebrand in #5520
  • fix(remote): revalidate full authority on every desktop viewer transition; End dispatches via relay with a status guard (SEC-038 wave 0) by @ToddHebebrand in #5518
  • feat(backup): bare-metal recovery W01 — layout manifest, whole-machine preset, restorability guard by @ToddHebebrand in #5523
  • feat(web): devices list refresh button + soft org context switch (no full reload) by @ToddHebebrand in #5526
  • fix(agent): descriptor-based private staging for scripts and restore on Linux, macOS and Windows (SEC-121) by @ToddHebebrand in #5519
  • release: v0.112.0 What's New entry; clear shipped draft notes by @ToddHebebrand in #5528
  • feat(rebuild): bare-metal recovery Linux rebuild engine (W03) by @ToddHebebrand in #5529
  • chore(skills): security-review skill delegates methodology to the private breeze-security workspace by @ToddHebebrand in #5491
  • fix(installer): derive the Windows 10 / Server 2016 floor from the registry, not VersionNT by @ToddHebebrand in #5525
  • docs(security): SEC-038 remote desktop start fence plan (feature #5531) by @ToddHebebrand in #5538
  • fix(billing): reconcile Stripe reversals durably — direct-account refund/dispute observation + monotonic reversal state (SEC-151, SEC-152) by @ToddHebebrand in #5503
  • fix(authz): require MFA for extension runtime toggles and configuration-policy effective mutations (SEC-097, SEC-107) by @ToddHebebrand in #5543
  • fix(user-risk): require MFA for writes and scope reads to visible sites (SEC-070, SEC-071) by @ToddHebebrand in #5541
  • fix(logs): scope fleet log reads and correlation rules to current site authority (SEC-079, SEC-080) by @ToddHebebrand in #5542
  • fix(auth): report acknowledged refresh-token family revocation truthfully (SEC-004) by @ToddHebebrand in #5549
  • fix(ai): scope agent run history, SentinelOne status, performance insights, playbook history and reliability reads to visible sites (SEC-052, SEC-064, SEC-113, SEC-147, SEC-148) by @ToddHebebrand in #5546
  • fix(authz): enforce site and partner ceilings on billing settings, principal disable, API-key mutation, login branding, network-change links and MCP bootstrap (SEC-019, SEC-031, SEC-033, SEC-066, SEC-114, SEC-138) by @ToddHebebrand in #5544
  • fix(logs): anchor agent-log retention and read windows to server receipt time (SEC-117) by @ToddHebebrand in #5548
  • fix(agent): remove the unaudited desktop file-drop data channel (SEC-127) by @ToddHebebrand in #5559
  • fix(oauth): durable grant revocation on every bearer path and atomic token-artifact consumption (SEC-139, SEC-140) by @ToddHebebrand in #5553
  • fix(backup): minimize verification listings, bind vault mutations to current site, bind local vaults to the device tenant (SEC-021, SEC-024, SEC-026) by @ToddHebebrand in #5554
  • docs: sync technical docs for v0.112.0 by @ToddHebebrand in #5564
  • fix(dr): enforce site scope on disaster-recovery reads and AI DR tools (SEC-078) by @ToddHebebrand in #5539
  • fix(ticketing): enforce assignee eligibility, bind inbound replies to the requester, scope triage aggregates by site (SEC-022, SEC-104, SEC-153) by @ToddHebebrand in #5547
  • fix(billing): preserve invoiced source records and reserve billed state for invoice issue (SEC-105, SEC-154) by @ToddHebebrand in #5550
  • fix(patching): bind Windows update installs to device-observed updates on both agent and API (SEC-115) by @ToddHebebrand in #5561
  • fix(patching): bind compliance report generation and download to the requester's site ceiling (SEC-095) by @ToddHebebrand in #5563
  • fix(ai): atomic budget reservations across every technician-facing LLM entry point; Office draft accounting (SEC-111, SEC-142, SEC-143) by @ToddHebebrand in #5555
  • feat: bare-metal recovery codes, state machine, heartbeat check-in (W04a) by @ToddHebebrand in #5569
  • feat(backup): whole-machine profile = one system_image snapshot with files, layout and state (#5493) by @ToddHebebrand in #5572
  • feat(deliverables): W01 schema, core services, contract and org record surfaces (#5573) by @ToddHebebrand in #5580
  • D18 W01: server-chosen base pin, publish lease, retirements table, storage identity, lineage by @ToddHebebrand in #5483
  • fix(rebuild): resumed rebuilds reuse restore progress; validate skips known partial failures (#5493) by @ToddHebebrand in #5584
  • fix(backup): manifest size/checksum reflect the uploaded bytes; volatile files are advisory on restore; exclude the run's own journal (#5581) by @ToddHebebrand in #5583
  • docs(backup-assurance): W03 bare-metal rebuild → boot proof on KIT (#5493) by @ToddHebebrand in #5586
  • fix(backup): excluded directories stay in the manifest so mount points survive a rebuild (#5493) by @ToddHebebrand in #5587
  • docs(backup-assurance): W03 clean boot proof rerun on fixed main (#5493) by @ToddHebebrand in #5589
  • fix(installer): OS-floor search property must be public (WIX0012) by @ToddHebebrand in #5571

Full Changelog: v0.111.1...v0.112.0

Don't miss a new breeze release

NewReleases is sending notifications on new releases.