github LanternOps/breeze v0.108.0

latest releases: v0.121.1-hotfix.2, v0.121.1-hotfix.1, v0.121.0...
one month ago

Breeze RMM v0.108.0 — AI agents, per-partner BYO LLM keys, multi-currency, and quote revisions all land together.

This is a large release: 1,307 files and 26 migrations since v0.107.0. Most of it is four multi-wave programs finishing at once.

Summary

  • AI agents (waves 1–3d) — agents, a runs ledger, an ai_agent principal, agent-originated action intents, a headless runner in shadow mode, and an ai_triage automation action. Off by default behind BREEZE_AI_AGENTS_ENABLED.
  • Per-partner LLM BYOK (waves 1–4) — bring your own model provider key per partner, threaded through every AI call site, with a billing-source split so BYO usage doesn't draw platform credits. New AI Provider settings tab.
  • Multi-currency (waves 3–8) — catalog price books and costs, ticketing currency snapshots, org-level currency selection, reporting-only FX with exchange rates, and Stripe/QBO seams.
  • Quote revisions (W01–W05) — revision lineage, supersede-at-send, a Revise action, lineage banners, and portal replaced-quote views.
  • The Guest Ledger — customer portal redesign.
  • Tenant secret variables — tenantSecret script parameters are now live.

Added

  • AI agents — agent records, runs ledger, ai_agent principal and event types (#3838); permission-gated on ai_agents:read/write (#3840); in-app approval notifications and an /approvals inbox (#3877); agent-originated action intents — propose, decide, release (#3893, #3931); headless runner in shadow mode (#3953); ai_triage automation action with event-target binding (#3976).
  • Per-partner LLM BYOK — partner_llm_configs with a fail-loud resolver and /ai/provider routes (#3889), partner key threaded through every AI call site (#3904), billing_source split with credit-deduction suppression (#3907), AI Provider settings tab and usage indicator (#3914).
  • Multi-currency — catalog price books and costs (#3811), ticketing currency snapshots (#3810), formatting and checkout readiness (#3809), org currency selection with an owner-approved active-contract escape hatch (#3874), reporting-only FX and exchange rates (#3883), Stripe freeze and typed QBO accounting seam (#3882).
  • Quote revisions — revision lineage schema and superseded status (#3806), reviseQuote service and POST /quotes/:id/revise (#3829), supersede-at-send with concurrency hardening (#3873), retired public links now die at every token route (#3875), Revise action with lineage banners and portal replaced views (#3878).
  • Portal — The Guest Ledger customer portal redesign (#3814).
  • Scripts — secret tenant variables via tenantSecret parameters (#3820).
  • Mobile — swipe and multi-select to acknowledge alerts (#3727).
  • Devices — durable agent uninstall on device Remove, behind fail-closed provenance (#4001). Behaviour-neutral in this release: the flag defaults off and no caller sets it yet.

Fixed

  • Auth — passwordless SSO accounts can now enrol a first MFA factor via IdP re-authentication; previously such accounts could never satisfy requireMfa() (#4018).
  • Auth — a throttled /auth/refresh is treated as a wait rather than a logout (#3696).
  • Tenancy — the offboarding repair now rechecks its precondition under its own row lock, closing a race where an aborted offboarding could still queue agent uninstalls (#4022).
  • Notifications — channel credentials are redacted before the test-failure reason is composed, not after (#3992).
  • Web — late-arriving JWT scope no longer breaks ticketing tabs, patch approval, or the #rings deep link (#4013, #4010).
  • Agent (macOS) — capture errors no longer conflate "no display attached" with "Screen Recording denied", and a capture timeout no longer asserts a permission cause it hasn't established (#4042).
  • Plus ~40 further fixes across billing, patching, portal and CI.

Security

  • SSO accounts could not satisfy the MFA gate at all — passwordless SSO users had no path to enrol a first factor, so MFA-gated actions were unreachable for them. Fixed via a bound, single-use IdP re-authentication grant scoped to first-factor enrolment only (#4018).
  • Offboarding race — an operator aborting an offboarding could still have fresh self_uninstall commands queued against the rescued tenant, collected by the fleet as ordinary commands (#4022).
  • Credential exposure in notification-channel test errors — redaction now happens before the message is composed; normalisation is not substring-preserving, so scrubbing afterwards could publish a secret (#3992).
  • Partner-wide write guard extended to services/**, with distributor credential routes gated (#3928).

Two previously-fixed High-severity issues from v0.107.0 have now been publicly disclosed: GHSA-22hw-qc22-8g35 (shared patch-catalog metadata writable by any enrolled agent) and GHSA-2h5h-36rx-9r6r (restricted partner users could issue partner-wide credentials and rotate other admins' API keys). Both are fixed in 0.107.0 and later — if you are still on < 0.107.0, upgrade.

Self-Hosting / Upgrade Notes

No breaking changes.

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.)

Database — 26 migrations, and they matter. All are idempotent and auto-apply on API boot via autoMigrate (unless AUTO_MIGRATE=false). They create the AI-agent, LLM-config, exchange-rate, quote-revision and currency tables and add columns to existing ones. No large-table rewrites or backfills — the one backfill (managed-triage-automation-backfill) touches automation rows only. Take a database snapshot before upgrading anyway: this is the largest migration set in a single Breeze release so far.

Privileged statements were checked against a non-superuser role before tagging: the range contains no CREATE ROLE, no ALTER … OWNER TO, and no CREATE EXTENSION — the three constructs that broke a managed-Postgres deploy in v0.95.0. The GRANTs present are all on tables created in the same migration.

No new required environment variables. One new optional flag:

  • BREEZE_AI_AGENTS_ENABLED — platform kill switch for AI agents. Defaults to false. Leave it unset and nothing in the AI-agent feature set runs. If you set it, remember it must also be mapped in the api service's environment: block of your compose file — a value in .env alone is a silent no-op.

Behaviour changes

  • AI agents are off by default and additionally gated on the ai_agents:read/write permission, so no existing role gains access implicitly.
  • Multi-currency is opt-in per organization. Existing orgs keep their current currency until an owner selects otherwise; there is an owner-approved escape hatch for orgs with active contracts.
  • Device Remove is unchanged in this release. The durable-uninstall path (#4001) ships disabled — its flag defaults off and no caller sets it. The UI that opts in comes later.

Full Changelog: v0.107.0...v0.108.0


What's Changed

  • feat: multi-currency wave 5 — formatting & checkout readiness (#3777) by @ToddHebebrand in #3809
  • feat: multi-currency wave 3 — catalog price books & costs (#3775) by @ToddHebebrand in #3811
  • fix(mobile): throttle truncation reports to a state change (#3783) by @bdunncompany in #3795
  • feat(portal): The Guest Ledger — customer portal redesign by @ToddHebebrand in #3814
  • feat(quotes): W01 — revision lineage schema + superseded status by @ToddHebebrand in #3806
  • refactor(portal): MarkTone union, shared reply cap, authorType on comments by @ToddHebebrand in #3815
  • feat: multi-currency wave 4 — ticketing currency snapshots, match-or-skip rates, assembly blocked groups (#3776) by @ToddHebebrand in #3810
  • feat(quotes): W02 — reviseQuote service + POST /quotes/:id/revise by @ToddHebebrand in #3829
  • feat(api,web): activate secret tenant variables in scripts via tenantSecret parameters (#3409 PR4c-2) by @ToddHebebrand in #3820
  • fix: multi-currency wave 5 review follow-ups — legacy document_locale snapshot, portal pay-link transactions, Stripe cache failure modes, PDF safety (#3777) by @ToddHebebrand in #3833
  • fix: multi-currency wave 3 review follow-ups — contract catalog lines, feed-currency costs, resolver representability, bundle currency math (#3775) by @ToddHebebrand in #3832
  • feat(ai): AI agents wave 1 — agents, runs ledger, ai_agent principal, event types by @ToddHebebrand in #3838
  • docs: v0.107.0 release sweep (v0.105.1..v0.107.0) by @ToddHebebrand in #3835
  • docs(release-skill): fork-era abuse-asset gate + hosted signing lane step by @ToddHebebrand in #3830
  • feat(api,web): gate AI agent policies on ai_agents:read/write by @ToddHebebrand in #3840
  • feat: multi-currency wave 6 — org currency selection, release gate, active-contract escape hatch (#3778) by @ToddHebebrand in #3874
  • feat: multi-currency wave 8 — Stripe §10 freeze + typed QBO accounting seam (#3780) by @ToddHebebrand in #3882
  • feat: multi-currency wave 7 — reporting-only FX, exchange rates and dashboard segmentation (#3779) by @ToddHebebrand in #3883
  • docs(specs): per-partner LLM API config (hosted Anthropic BYOK) design by @ToddHebebrand in #3888
  • fix(api): hosted agent update 409 — canonical asset names, key-ID-bound verification, fail-closed local registration (#3836) by @ToddHebebrand in #3872
  • feat(api): per-partner LLM BYOK wave 1 — partner_llm_configs, fail-loud resolver, /ai/provider routes by @ToddHebebrand in #3889
  • chore(deps): bump the github-actions group across 1 directory with 5 updates by @dependabot[bot] in #3819
  • fix(api): lock the devices row before the cascade deletes its children by @bdunncompany in #3760
  • feat(quotes): W03 — supersede-at-send + concurrency hardening by @ToddHebebrand in #3873
  • feat(api): per-partner LLM BYOK wave 2 — partner key threaded through every AI call site by @ToddHebebrand in #3904
  • feat(quotes): W04 — retired public link dies at every token route by @ToddHebebrand in #3875
  • feat(quotes): W05 — Revise action, lineage banners, portal replaced views by @ToddHebebrand in #3878
  • test(quotes): W06 — revision race matrix + constraint proofs (integration) by @ToddHebebrand in #3879
  • feat(api): agent-originated action intent schema (AI agents wave 3a) by @ToddHebebrand in #3893
  • feat(api): per-partner LLM BYOK wave 3 — billing_source split, credit-deduction suppression by @ToddHebebrand in #3907
  • feat(api,web): in-app approval notifications + /approvals inbox (AI agents wave 2) by @ToddHebebrand in #3877
  • feat(web,docs): per-partner LLM BYOK wave 4 — AI Provider settings tab, usage indicator, BYO-LLM docs by @ToddHebebrand in #3914
  • fix(api): align stripeConnect route gating with the partner-wide access pattern by @ToddHebebrand in #3916
  • test(api): extend the partner-wide write guard to services/**; gate distributor credential routes by @ToddHebebrand in #3928
  • fix(api): make every captureMessage event groupable in Sentry (BREEZE-18) by @ToddHebebrand in #3913
  • fix(api): deadlock on vuln correlation vs agent software ingest (40P01) by @ToddHebebrand in #3911
  • fix(mobile): make a release build without a Sentry DSN impossible by @ToddHebebrand in #3910
  • fix(api): stagger epoch-aligned repeatable jobs off the 00:00 UTC pile by @ToddHebebrand in #3915
  • fix(api): stop reporting expected BullMQ retries as error-level Sentry events (BREEZE-1A, BREEZE-1J) by @ToddHebebrand in #3912
  • feat(api): agent-originated intents live — propose, decide, release (AI agents wave 3b) by @ToddHebebrand in #3931
  • chore(deps): bump the react-native-animation group with 2 updates by @dependabot[bot] in #3934
  • chore(deps): bump @types/pg from 8.21.0 to 8.23.1 in /e2e-tests by @dependabot[bot] in #3935
  • chore(deps): bump vitest from 4.1.10 to 4.1.11 in /e2e-tests by @dependabot[bot] in #3936
  • chore(deps): bump @types/office-js from 1.0.605 to 1.0.606 in the typescript-tooling group by @dependabot[bot] in #3938
  • chore(deps): bump the astro group with 3 updates by @dependabot[bot] in #3939
  • chore(deps): bump @fastify/busboy from 3.2.0 to 3.2.2 by @dependabot[bot] in #3950
  • fix(api): serialise bundle composition on a globally ordered item lock (#3816) by @bdunncompany in #3831
  • feat(api): headless AI-agent runner in shadow mode (AI agents wave 3c) by @ToddHebebrand in #3953
  • chore(deps): bump node-stream-zip from 1.15.0 to 1.16.0 by @dependabot[bot] in #3949
  • perf(api): stop the hourly risk-score refresh pinning a DB connection for 2 minutes by @ToddHebebrand in #3793
  • chore(deps): bump hono from 4.13.2 to 4.13.3 in the hono group across 1 directory by @dependabot[bot] in #3942
  • chore(deps): bump the testing group across 1 directory with 3 updates by @dependabot[bot] in #3947
  • chore(deps): bump eslint from 10.8.1 to 10.9.0 in the linting group across 1 directory by @dependabot[bot] in #3948
  • chore(deps): bump the mobile group across 1 directory with 5 updates by @dependabot[bot] in #3937
  • fix(deps): repair broken pnpm-lock.yaml (duplicated hono key reddens every CI job) by @ToddHebebrand in #3957
  • test(web): give the InvoiceWorkspace waitFor budget room under the test timeout (#3277) by @ToddHebebrand in #3956
  • docs(mobile): implementation plan for mobile time entry (#3206) by @ToddHebebrand in #3903
  • fix(api): unwrap DrizzleQueryError when reading SQLSTATE in softwareInstallMethods (#3881) by @ToddHebebrand in #3921
  • fix(web,api): alert rule "Test" reports the real evaluation verdict (#3752) by @ToddHebebrand in #3923
  • fix(api,shared): source audit actor_type/result enums from shared constants (#3908) by @ToddHebebrand in #3924
  • fix(web,api): persist and show WHY a notification channel test failed (#3697) by @ToddHebebrand in #3926
  • fix(web): stop a double-click on a confirm dialog hit-testing through to the list (#3705) by @ToddHebebrand in #3929
  • fix(agent): force a desktop repaint before the remote-desktop startup probe (#3951) by @ToddHebebrand in #3960
  • fix(agent): volume-aware recursive-delete boundary so Windows nested paths delete (#3932) by @ToddHebebrand in #3961
  • fix(api): count partner-level ring assignments via organizations.partner_id (#3954) by @ToddHebebrand in #3962
  • feat(api,web): ai_triage automation action with event-target binding (AI agents wave 3d) by @ToddHebebrand in #3976
  • fix(web): render angle brackets and HTML entities in locale strings (#3964) by @ToddHebebrand in #3971
  • fix(web): restore the dropped space between i18n labels and their values (#3965) by @ToddHebebrand in #3972
  • fix(alerts): resolve acknowledged/resolved-by user ids to display names (#3966) by @ToddHebebrand in #3973
  • fix(api): enforce organizations.slug uniqueness per partner in Postgres (#3967) by @ToddHebebrand in #3974
  • fix(auth): treat a throttled /auth/refresh as a wait, not a logout (#3696) by @ToddHebebrand in #3975
  • fix(web): keep the #rings deep link alive until the JWT scope is known (#4010) by @ToddHebebrand in #4012
  • fix(web): confirm-gate decommission from the device list/grid kebab (#4009) by @ToddHebebrand in #4011
  • fix(web): rename device Decommission to Remove, and stop every surface offering the action the API rejects by @ToddHebebrand in #3994
  • fix(web): put the alert-rule Test verdict on the live editor (#3988) by @ToddHebebrand in #4016
  • chore(deps): bump the aws-sdk group in /agent with 5 updates by @dependabot[bot] in #4005
  • chore(deps): bump cloud.google.com/go/storage from 1.64.0 to 1.65.0 in /agent by @dependabot[bot] in #4006
  • fix(api): unwrap the SQLSTATE in tenantCascade's isUndefinedTable (#3927) by @bdunncompany in #3998
  • fix(web): un-nest the AutomationTab delete button from the row expander (#3990) by @bdunncompany in #3999
  • fix(web): surface the API's own error on the Organizations page mutations (#3989) by @bdunncompany in #4003
  • feat(mobile): swipe and multi-select to acknowledge alerts without waiting by @bdunncompany in #3727
  • fix(web): update #4009's confirm-dialog assertions to #3994's renamed copy (main is red) by @ToddHebebrand in #4023
  • fix(web): stop the grid DeviceCard offering agent actions for network assets (#4014) by @ToddHebebrand in #4031
  • fix(web): react to late-arriving JWT scope in ticketing tabs + patch approval (#4013) by @ToddHebebrand in #4030
  • feat(api): durable agent uninstall on device Remove, behind fail-closed provenance by @ToddHebebrand in #4001
  • fix(web): distinguish a zero-data list from a search that matched nothing (#3992) by @bdunncompany in #4032
  • fix(api,web): stop losing agent error messages behind Cloudflare's 502/504 page by @ToddHebebrand in #4029
  • fix(api): redact channel credentials before composing the test-failure reason (#3992) by @ToddHebebrand in #4015
  • fix(api): recheck the tenant precondition under the repair's own lock (#4022) by @bdunncompany in #4034
  • fix(ci): reconcile the drift detector with side-branch hotfix releases (#4037) by @ToddHebebrand in #4043
  • fix(auth): let passwordless SSO accounts enroll a first MFA factor via IdP re-authentication (#4018) by @ToddHebebrand in #4041
  • docs(claude-md): fix broken pnpm vitest filter guidance (#4047) by @ToddHebebrand in #4058
  • fix(api,ci): pin a non-UTC TZ for auth/SSO test runs (#4046) by @ToddHebebrand in #4056
  • fix(config,api): pass DB password vars through turbo + fail fast on skipped breeze_app bootstrap (#4048) by @ToddHebebrand in #4057
  • fix(agent): stop macOS capture errors from guessing at their own cause (#4042) by @ToddHebebrand in #4061
  • fix(ci,docs): enforce that a new migration sorts after every committed one by @ToddHebebrand in #4063
  • test(api): pin the SSO reauth binding re-check against real Postgres (#4049) by @ToddHebebrand in #4065

Full Changelog: v0.107.0...v0.108.0

Don't miss a new breeze release

NewReleases is sending notifications on new releases.