Skip to content

v2.3.0

Choose a tag to compare

@github-actions github-actions released this 12 Jun 10:51
· 39 commits to master since this release
Immutable release. Only release title and notes can be modified.
v2.3.0
ecce556

What's new

BridgePort 2.3.0 adds outbound webhook subscriptions with HMAC-signed, retried delivery and Idempotency-Key support for safe POST retries, ships a typed OpenAPI spec (/openapi.json) generated from the real request schemas, and surfaces service-type/language badges and deployment servers in the UI. One database migration applies automatically on container start — no manual steps.

One behavior change to know about if you use multi-channel Slack routing and per-environment channel overrides together — see below.


Behavior changes

Per-environment Slack overrides now demultiplex fan-outs

Skip this if you don't set per-environment Slack channel overrides.

Previously, a per-environment Slack channel override only acted as a fallback for unrouted notification types. A type routed to multiple channels globally fired into all of them from every environment, so you couldn't mute one environment's noise without losing another's.

Now an override collapses a fan-out to that environment's own channel:

  • Exactly one matched route → sent there; the override is not consulted (env-agnostic single routes still reach every environment).
  • More than one match (fan-out) → if the originating environment has a usable override, send only to that environment's channel; otherwise the full fan-out is preserved.
  • No match → environment override as fallback, else the global default (unchanged).

Impact: any environment that has both a multi-channel routing and a per-environment override set will now receive only its override channel instead of the full fan-out. Single-routed types and unrouted-fallback behavior are unchanged. To split a shared alert per environment: route the type to >1 channel globally, then set each environment's override to its own channel. (#221)


Database migrations

One migration, applied automatically on container start (BridgePort's golden rule — zero human intervention).

  • 20260611215341_webhooks_idempotency — adds three tables: WebhookSubscription (env-scoped endpoints, encrypted signing secret), WebhookDelivery (per-attempt delivery records with retry state), and IdempotencyKey (24h dedupe store for mutating POSTs). Additive only — no changes to existing tables. (#234)

Features

Webhook subscriptions (#234)

Environment-scoped outbound webhooks let external systems react to BridgePort events in near-real-time, as an alternative to the existing admin-scoped notification fan-out.

  • Manage subscriptions under /api/environments/:envId/webhooks (create / list / get / delete), with delivery history at GET …/:id/deliveries.
  • Deliveries are HMAC-signed (X-BridgePort-Signature: sha256=…), sent in the background, and retried with exponential backoff (5 attempts).
  • Terminal events fire within seconds: deployment.completed/failed, plan.completed/failed, backup.completed/failed, sync.completed.
  • Signing secrets are encrypted at rest (AES-256-GCM) and never returned by the API (hasSecret boolean only).
  • This is a separate system from the admin-scoped WebhookConfig notifications (X-Webhook-Signature) — different contracts, kept side by side rather than retrofitted.

Idempotency-Key for safe POST retries (#234)

Mutating POSTs now honor a client-supplied Idempotency-Key header. A global hook dedupes against the key for a 24-hour window and replays the original response, so a client can safely retry a POST whose outcome is unknown.

  • Same key, mismatched body → 409 IDEMPOTENCY_KEY_REUSED.
  • Same key, request still in progress → 409.

Typed OpenAPI spec (#227)

/openapi.json and Swagger UI now carry real request contracts, generated from the existing Zod validation schemas — one source of truth, no doc/validation drift.

  • A checked-in openapi.json snapshot plus a CI drift check that fails if routes change without regenerating the spec.
  • Documentation-only (uses Zod 4's native z.toJSONSchema(), no new runtime deps); runtime validation and the custom error envelope are unchanged.

Service-type & language badges, deployment servers (#223)

  • Service-type badge on each service card (promoted from inline text; keeps the Generic fallback).
  • Config-file language badge (e.g. yaml, nginx) on text config files, alongside the binary badge.
  • Servers on service detail — a linked list of deployment servers above Deployment History, plus a new linked Server column in the history table ( for legacy rows). No schema change.

API changes

Added

  • POST /api/environments/:envId/webhooks — create a webhook subscription. (#234)
  • GET /api/environments/:envId/webhooks — list subscriptions. (#234)
  • GET /api/environments/:envId/webhooks/:id — get a subscription. (#234)
  • DELETE /api/environments/:envId/webhooks/:id — delete a subscription. (#234)
  • GET /api/environments/:envId/webhooks/:id/deliveries — delivery history. (#234)
  • Idempotency-Key request header honored on mutating POSTs. (#234)
  • serviceDeployment.server field on GET /api/services/:id/deployments-history responses (null for legacy rows). (#223)
  • Typed request/error contracts now present throughout /openapi.json. (#227)

Deprecated

  • The sync result envelope's legacy success alias is flagged deprecated: true in the OpenAPI spec — prefer the canonical field. (#227)

Removed

  • None.

Security

  • GHSA-w5hq-g745-h8pq (uuid) — missing buffer bounds check in v3/v5/v6. Pinned to ^11.1.1 via pnpm override. Transitive and dev/tooling-only (hyperidautocannon, stress tests); does not reach the production runtime or Docker image, and the vulnerable code path was never called. (#231)
  • GHSA-92pp-h63x-v22m (@hono/node-server)serveStatic middleware bypass via repeated slashes. Pinned to ^1.19.13. Transitive and dev-only (@prisma/devprisma); not in the production runtime. (#231)

Under the hood

Toolchain migrated from npm to pnpm (#226)

The entire JS/TS toolchain now runs on a single pnpm 11 workspace (root backend + ui/), resolved by one root pnpm-lock.yaml. Supply-chain controls move to pnpm's native allowBuilds allowlist and minimumReleaseAge cooldown; CI, the multi-stage Docker build, and Dependabot were all converted. Dev keeps full strict phantom-dependency protection. Contributors: use pnpm install (see updated docs/development/).

UI dependency upgrades

  • React 19 + recharts 3react/react-dom 18.3 → 19.2.7 and recharts 2.15 → 3.8.1 (client-side only; backend/agent/CLI untouched). (#220)
  • Tailwind CSS 4 — 3.4 → 4.3 (Oxide engine, CSS-first @theme config, Vite plugin replaces the PostCSS pipeline). Like-for-like, no design changes. (#233)

Other notable bumps

  • golang.org/x/term (CLI) — 0.43.0 → 0.44.0 (#228)
  • @types/testing-library__jest-dom (dev) — 5.14.9 → 6.0.0 (#229)
  • /build skill fix — runs the two scoped vitest configs instead of bare npm test (which produced ~759 false failures in this repo). (#224)
    -----BEGIN SSH SIGNATURE-----
    U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEyiv4hf6iBgr34ICjN6HnEP/vs
    Yr31eNU5HhdkQaYd4AAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
    AAAAQDDO04K4v3ckb7ElqF+Sfamr5V97DJ1h3Y37atrVlIRmgcKAmANljMESORjqwD+/f9
    fINOxMjM6nyBRviMjfeg0=
    -----END SSH SIGNATURE-----