github LanternOps/breeze v0.87.0

latest releases: v0.119.0, v0.118.2, v0.118.1...
3 months ago

Breeze RMM 0.87.0 — UniFi network integration, downloadable + branded reports, M365 email-to-ticket, distributor catalog import, and the BE-16 vulnerability correlation job — plus invoice/contract polish and a batch of correctness + hardening fixes.

A feature-heavy release (286 files) that's still a light upgrade for operators: 10 idempotent migrations (all new-table creates + cheap metadata-only column changes — no large-table rewrites or backfills), no new required environment variables, and no breaking changes.

Summary

  • UniFi network integration — connect a partner's UniFi Site Manager cloud account to map UniFi sites → Breeze sites and reconcile UniFi devices as discovered assets, with an optional agent-side read-only deep-telemetry collector. Opt-in per partner; off until you connect an account (#2040).
  • Reports — persisted, downloadable report runs and a new client-facing Security & Compliance Posture report with a partner-branded PDF (logo, scorecard, per-device tables) for insurance/vetting (#2058).
  • Email-to-ticket (M365) — connect a Microsoft 365 shared mailbox so customer email becomes tickets and replies send back from that mailbox, with no MX/forwarding changes (#2045). Inbound alias is now editable independently of the partner slug (#2060).
  • Billing / distributors — import hardware & subscriptions from TD SYNNEX and Pax8 into the catalog, then quote/contract them with internal unit cost, markup, SKU/part-number, and margin tracking (kept off customer-facing documents); plus a QuickBooks customer import that creates Organizations + default Sites (#2025, #2048, #2044).
  • Vulnerability management — the BE-16 correlation step is now wired into a daily job, gated per-org behind a new vulnerability config-policy feature (default OFF) (#2050).
  • Fixes & hardening — self-hosted email-to-ticket / Stripe webhooks reaching their handlers again, MSI bootstrap enrollment, one-click alert suppress, MCP partner-role resolution, and a UniFi GDPR-cascade gap (#2054, #2049, #2020, #2019, #2052/#2055).

Added

  • UniFi network integration — connect a partner's UniFi Site Manager cloud account (encrypted API key), map UniFi sites to Breeze sites, and reconcile UniFi devices into discovered assets. An optional agent-side, read-only deep-telemetry collector polls the local UniFi Network controller for richer device/client data. A BullMQ sync worker runs on a 30-minute scheduler. Opt-in: nothing happens until a partner connects an account. (#2040)
  • Downloadable report runs + Security & Compliance Posture report — report runs are now persisted and downloadable, and there's a new client-facing Security & Compliance Posture report (for insurance/vetting) rendered as a partner-branded PDF (logo, scorecard, per-device tables). CSV/Excel exports retain full per-row data; the PDF is the polished client deliverable. (#2058)
  • M365 shared-mailbox email-to-ticket — connect a Microsoft 365 shared mailbox (admin-consent flow) so customer email becomes tickets and replies are sent back from that mailbox via Microsoft Graph — no MX or forwarding changes. On first connect only new mail is ticketed (no historical backfill). Optional; see upgrade notes for the Azure app + env vars. (#2045)
  • QuickBooks customer import — browse, select, and import QuickBooks customers as Breeze Organizations, each with a default Site carrying contact/address. MFA-gated and idempotent on re-import. (#2048)
  • Distributor catalog import + line cost & margin — import hardware and subscriptions from TD SYNNEX and Pax8 into the catalog, then quote and contract them with internal unit cost, markup, SKU/part-number, and margin tracking. Cost and SKU are snapshotted on the line and kept off customer-facing documents. (#2025)

Improved

  • Editable inbound email alias — partner admins can set a custom inbound email local-part, decoupled from the partner slug, so an awkward auto-generated tickets@… address can be changed without DB access. Ships dark (defaults to the slug); the original slug address keeps working forever. (#2060)
  • Editable organization type — Org Type (Customer vs Internal) is now editable on Settings → Organization → General, not just at create time. (#2061)
  • Vulnerability correlation daily job — the previously-uncalled BE-16 correlation step now runs daily (13:00 UTC), gated behind a new vulnerability config-policy feature ({ enabled }, default OFF). Enable Vulnerability Scanning on a policy and assign it to an org to populate findings. (#2050)
  • Invoice polish + internal margin panel — payments are gated to issued (non-draft) invoices, the document header dedupes when no seller is named, operator hints surface (taxable-but-no-tax-rate, no billing contact), and a new internal "Margin" summary panel appears on the invoice editor/detail rails (gated on invoices:read, never shown on customer documents). (#2044)
  • Contracts list/detail UX — the bulk bar keeps selected rows visible, "Delete drafts" is only offered when a draft is selected, empty states split into first-run vs filtered-empty, the redundant Organization column is dropped in org-locked embeds, and date inputs render correctly in dark mode. (#2024)
  • Device list & sidebar quality-of-life — the device list Columns dropdown gains a "Reset to defaults" button (restores column visibility + order), and the sidebar gains a "Collapse all" button that collapses every section except the one holding the active page. (#2015)

Fixed

  • Self-hosted email-to-ticket & Stripe webhooks reaching their handlers — a wildcard authMiddleware on the webhook-CRUD router blanketed the entire /webhooks/* subtree, 401'ing the public signature-gated inbound-email, Stripe, and Stripe Connect webhooks before their HMAC handlers ran (broke self-hosted email-to-ticket, #2053). Auth is now applied per-route. (#2054)
  • MSI bootstrap CA enrollment token — MSI installs succeeded but agents never enrolled ("no bootstrap token present") because a deferred EXE custom action couldn't read [CustomActionData]. Fixed by referencing [OriginalDatabase] directly. Takes effect in this release's signed MSI; the immediate workaround remains a property-based silent install (SERVER_URL=… ENROLLMENT_KEY=…). (#2049)
  • One-click alert suppress — single-alert Suppress sent no request body, so the API's required { until } always returned 400 and suppress never worked. Adds a duration picker (default 24h) and surfaces the server's specific error. (#2020)
  • Confirm contract cancel — the terminal Cancel action on a contract (irreversible; stops all future invoicing) previously fired on a single unconfirmed click and now routes through a confirmation dialog. Pause/resume stay immediate. (#2024)
  • MCP owning-partner resolution + SSE public URL — org-scoped (manual) API keys reached MCP tools/call with no partner, causing "Insufficient permissions: no role assigned" for partner-admin users; the key's owning partner is now resolved for role resolution only (RLS visibility unchanged). The SSE endpoint event also now advertises the configured public base URL instead of a bare http:// URL behind an HTTPS proxy. (#2019)
  • UniFi org data now included in GDPR erasure — the five new org_id UniFi tables weren't registered in the org-deletion cascade, which would have orphaned UniFi data on org delete; both are now enrolled in ORG_CASCADE_DELETE_ORDER. (#2052, #2055)

Security

  • Per-route auth on /webhooks (defense-in-depth) — replacing the /webhooks/* wildcard authMiddleware with per-route auth ensures the public, HMAC-verified webhook endpoints (inbound email, Stripe, Stripe Connect) are reached and validated by their own signature checks instead of being blanket-401'd. Requires a new build to take effect. (#2054)

Self-Hosting / Upgrade Notes

No breaking changes. Existing orgs are unaffected by the new feature gates below — UniFi, M365 email-to-ticket, and vulnerability scanning are all opt-in and stay off until you turn them on.

  1. Upgrade command. Bump BREEZE_VERSION=0.87.0 in .env, then docker compose pull api web && docker compose up -d. If you build from source, run pnpm install after pulling. Node pin is unchanged (v22.20.0).
  2. Database — 10 idempotent migrations, auto-applied on boot via autoMigrate (unless AUTO_MIGRATE=false). They are all new-table creates (UniFi ×2, ticket mailbox connections, report runs, report type) with RLS enabled inline, plus cheap metadata-only column changes (quote_lines/invoice_lines description DROP NOT NULL, new accounting/cost/SKU/inbound-local-part columns) and indexes on the new empty tables. No large-table rewrites or backfills — boot is fast.
  3. No new required environment variables. The M365 email-to-ticket feature adds three optional vars — TICKET_MAILBOX_M365_CLIENT_ID, TICKET_MAILBOX_M365_CLIENT_SECRET (API) and PUBLIC_TICKET_MAILBOX_APP_ID (web). Unset = the feature is disabled and nothing breaks at boot. To enable it, register a separate multi-tenant Azure app with Graph application permissions Mail.ReadWrite + Mail.Send, redirect URI <PUBLIC_URL>/api/v1/tickets/mailbox/callback, set those three vars, and map them in the api/web environment: blocks of your compose (a value in .env alone isn't enough). If you reverse-proxy over HTTPS, make sure PUBLIC_API_URL is set to your external URL so the MCP SSE endpoint advertises the right scheme (#2019).
  4. Behavior changes & feature flags.
    • vulnerability config-policy feature — default OFF. A new per-org policy gate; the daily BE-16 correlation job (13:00 UTC) only populates findings for orgs whose assigned policy enables scanning. No existing org is opted in. (#2050)
    • UniFi integration — opt-in. No sync runs until a partner connects a UniFi Site Manager account; the agent telemetry collector is off until enabled per site. (#2040)
    • Inbound email alias ships dark — partners.inbound_local_part defaults to null (= use the partner slug), so existing inbound addresses are unchanged. (#2060)

Full Changelog: v0.86.0...v0.87.0


What's Changed

  • feat(web): reset-columns button on device list + collapse-all in sidebar by @ToddHebebrand in #2015
  • Confirm contract cancel + polish contracts list/detail UX by @ToddHebebrand in #2024
  • Catalog distributor import + quote/contract line cost & margin by @ToddHebebrand in #2025
  • Polish invoices + internal margin summary panel by @ToddHebebrand in #2044
  • feat(unifi): UniFi network integration — cloud Site Manager (P1) + agent deep telemetry (P2a) by @ToddHebebrand in #2040
  • test(web): de-flake PamRuleModal preview zod-error test by @ToddHebebrand in #2047
  • Import QuickBooks customers as Orgs + Sites by @ToddHebebrand in #2048
  • fix(installer): MSI bootstrap CA never delivered the enrollment token by @ToddHebebrand in #2049
  • feat(vuln): wire BE-16 correlation into a daily job, gated per-org by config policy by @ToddHebebrand in #2050
  • fix(tenancy): register UniFi org-scoped tables in the GDPR cascade by @ToddHebebrand in #2052
  • fix(api): per-route auth on /webhooks so inbound-email + Stripe webhooks reach their handlers by @ToddHebebrand in #2054
  • fix(unifi): enroll UniFi org-scoped tables in ORG_CASCADE_DELETE_ORDER (#2051) by @ToddHebebrand in #2055
  • M365 Exchange shared-mailbox email-to-ticket (inbound + outbound) by @ToddHebebrand in #2045
  • docs: reliability macOS/Linux de-junk note for 0.86.0 by @ToddHebebrand in #2016
  • fix(mcp): resolve owning partner for org-scoped keys + honor public base URL in SSE endpoint by @ToddHebebrand in #2019
  • fix(web): make one-click alert Suppress work (send a duration) by @ToddHebebrand in #2020
  • chore(deps): bump node from fb71d01 to a0b9bf0 in /docker by @dependabot[bot] in #2043
  • chore(deps): bump node from fb71d01 to a0b9bf0 in /apps/api by @dependabot[bot] in #2042
  • chore(deps): bump node from fb71d01 to a0b9bf0 in /apps/web by @dependabot[bot] in #2041
  • chore(deps): bump bullmq from 5.79.1 to 5.79.2 by @dependabot[bot] in #2037
  • chore(deps): bump eslint from 10.5.0 to 10.6.0 in the linting group by @dependabot[bot] in #2036
  • chore(deps): bump hono from 4.12.26 to 4.12.27 in the hono group by @dependabot[bot] in #2032
  • chore(deps): bump the tailwind group with 2 updates by @dependabot[bot] in #2030
  • chore(deps): bump @tanstack/react-query from 5.101.0 to 5.101.2 in the tanstack group by @dependabot[bot] in #2029
  • chore(deps): bump @types/office-js from 1.0.592 to 1.0.593 in the typescript-tooling group by @dependabot[bot] in #2027
  • chore(deps): bump @playwright/test from 1.61.0 to 1.61.1 in /e2e-tests by @dependabot[bot] in #2035
  • chore(deps): bump playwright from 1.61.0 to 1.61.1 in /e2e-tests by @dependabot[bot] in #2034
  • chore(deps): bump @anthropic-ai/sdk from 0.105.0 to 0.106.0 in /e2e-tests by @dependabot[bot] in #2033
  • chore(deps): bump the react-native-animation group with 2 updates by @dependabot[bot] in #2026
  • chore(deps): bump @astrojs/starlight from 0.40.0 to 0.41.1 in the astro group by @dependabot[bot] in #2028
  • fix(deps): repair duplicate-key corruption in pnpm-lock.yaml by @ToddHebebrand in #2056
  • chore(deps): bump @aws-sdk/client-s3 from 3.1065.0 to 3.1076.0 by @dependabot[bot] in #2038
  • chore(deps): bump @types/node from 26.0.0 to 26.0.1 in /e2e-tests by @dependabot[bot] in #2031
  • feat(reports): downloadable report runs + security posture report + impeccable branded PDF redesign by @ToddHebebrand in #2058
  • fix(ci): allowlist placeholder test email domains in customer-PII guard by @ToddHebebrand in #2059
  • feat(tickets): editable inbound email alias (decouple from partner slug) by @ToddHebebrand in #2060
  • feat(web): make organization type editable on the General settings tab by @ToddHebebrand in #2061
  • fix(ci): allowlist tickets.localhost + theirdomain.com in customer-PII guard by @ToddHebebrand in #2062

Full Changelog: v0.86.0...v0.87.0

Don't miss a new breeze release

NewReleases is sending notifications on new releases.