Skip to content

v2.0.0

Choose a tag to compare

@github-actions github-actions released this 29 May 09:42
· 78 commits to master since this release
Immutable release. Only release title and notes can be modified.
e02ff75

What's new

BridgePort 2.0 is a major release built around a redesigned service model, six new operator features, and a large performance wave.

  • Service is now a template — one Service (image, base compose, base env, deploy strategy) fans out to per-server ServiceDeployments, each with its own env/file overrides, deployed sequentially or in parallel.
  • One-click server bootstrap — install Docker + Compose, deploy the agent, tune sysctl, add swap.
  • Reusable config fragments, atomic batched syncs with rollback, and dry-run previews.
  • Self-describing HTTP API — OpenAPI spec, Swagger UI, and a standardized error envelope.
  • Richer topology diagram — external entities, server clustering, resizable boxes, stable anchors.
  • Performance — worst production transactions cut 5–12x; agent metrics-ingest throughput up ~5x.

Schema changes apply automatically on container start — existing 1:1 Service→Server records are backfilled into ServiceDeployments with no data loss and no manual steps. Programmatic API and CLI users should read Action required below before upgrading.


Performance

A sustained optimization wave, anchored by an 18-scenario stress suite (target: RPS ≥ 500, p99 ≤ 50 ms per endpoint) and validated against the worst production transactions in Sentry.

Hotpath before → after

Path Before After PR
Agent metrics ingest (25 conns) 115 RPS · p50 208 ms · p99 635 ms 639 RPS · p50 38 ms · p99 47 ms #183
metrics-summary (worst prod txn) p99 8.2 s in production p99 17–46 ms #138, #181
health-status (was N+1) p99 224 ms · 184 RPS p99 42 ms · 789 RPS #138, #147
server-metrics-history p99 395 ms · 1012 RPS p99 12 ms · 4200 RPS (+315%) #163, #181
service-metrics-history p99 57 ms · 622 RPS p99 12 ms · 3585 RPS (+476%) #181
container-images-list p50/p99 56/81 ms · 529 RPS p50/p99 11/21 ms · 669 RPS #163
Dashboard first paint ~6 s <500 ms #174

What changed

  • Metrics ingest batched into one transaction (#183) — each agent push was a serial chain of ~10–26 individual SQLite writes, each grabbing the single writer lock. Now:
    • one prisma.$transaction per push, createMany for service metrics, merged per-deployment updates, O(1) container→deployment map, throttled heartbeat.
  • Single-flight metrics cache (#181) — bursts of identical dashboard polls collapse to one compute:
    • server-metrics-history +315% RPS, service-metrics-history +476%, metrics-summary +423%.
    • cache sits behind existing auth/scope enforcement and is keyed by environment.
  • Monitoring pages — per-card loading (#178) — dropped the top-level render-gate; header/sidebar/lists paint immediately while each chart owns its own state. History endpoints gained ?maxPoints (LTTB downsample) and ?since= delta mode.
  • N+1 and hydration fixes (#138, #147, #145, #146, #148, #149, #150):
    • rewrote getEnvironmentMetricsSummary (Sentry's worst transaction, p99 8.2 s in prod).
    • health-status reads denormalized cache columns instead of scanning HealthCheckLog.
    • moved sendSystemNotification fan-out to an in-process queue so request handlers no longer block on per-user writes.
    • added SecretUsage/VarUsage join tables, columnar metrics-history responses, compound indexes.
  • Dashboard (#174) — ungated per-section rendering; replaced the per-DB backup-info fan-out with a single batched databases/backup-summary endpoint.

Action required before upgrading

Skip any item whose feature you don't use through the API or CLI. The web UI handles all of these transparently.

1. Service is now a template with per-server deployments (#137)

  • Runtime fields (containerName, status, health/check results, discovery) and per-server envOverrides moved to a new ServiceDeployment entity.
  • Image-update fields (autoUpdate, latestAvailableTag, lastUpdateCheckAt) moved to ContainerImage.
  • Existing data auto-migrates — each legacy 1:1 Service→Server record becomes one ServiceDeployment. No manual steps.
  • CLI users: update your binary — the Service struct changed shape; older builds will misread responses.
  • API clients reading service.server: a flattened services array and a service.server accessor are kept for back-compat this release, but new code should read service.serviceDeployments[].

2. PATCH of read-only fields is now rejected (#159)

  • A PATCH whose body contains a derived/read-only field (status, exposedPorts, lastCheckedAt, …) fails atomically with 422 + code: READONLY_FIELD, naming the field.
  • Affects services, servers, config-files, container-images, databases, registries, secrets.
  • Fix: send only the fields you intend to change — don't echo back computed fields.

3. Standardized error envelope (#155)

  • Every non-2xx response now returns:
{ "code": "VALIDATION_ERROR", "message": "...", "field": "...", "hint": "...", "requestId": "..." }
  • code is a documented enum (VALIDATION_ERROR, READONLY_FIELD, UNAUTHORIZED, FORBIDDEN_SCOPE, NOT_FOUND, CONFLICT, IDEMPOTENCY_KEY_REUSED, RATE_LIMITED, INTERNAL).
  • 5xx messages are redacted from clients but still logged + sent to Sentry.
  • Clients that parse error must read message/code instead.

4. Sync responses return an envelope (#159)

  • /config-files/:id/sync-all, /services/:id/sync-files, /servers/:serverId/sync-all-files now return { status, targetsAttempted, targetsSucceeded, targetsFailed, results }.
  • status is 'ok' | 'no_targets' | 'partial' | 'failed'; zero-target syncs now return 200 + 'no_targets' (was 400).
  • The old success: boolean field is a deprecated alias for this release only — migrate off it.

Features

Service templates + per-server deployments (#137)

  • New ServiceDeployment entity owns per-server runtime, status, agent check results, and envOverrides.
  • Deploy strategy chosen at deploy time:
    • sequential — halts on first failure with rollback context.
    • parallelPromise.allSettled, aggregated statuses.
  • ServiceDetail gains Overview / Deployments / Config tabs; per-deployment env overrides editable inline.

Server bootstrap (#156)

  • One Bootstrap action installs Docker + Compose plugin, deploys the agent, applies container-tuned sysctl defaults, and optionally creates a swap file.
  • Ubuntu/Debian only; idempotent (skip-if-present, no version upgrades); requires passwordless sudo / root SSH (preflighted).
  • Live progress streams over SSE into a BootstrapModal; separate Add swap recovery action logs before/after free -m.
  • fstab backed up once before any change; idempotent exact-line append (never sed -i).

Reusable config fragments (#162)

  • Env-scoped, named text blocks that multiple config files include via an ordered list.
  • Rendered as concat(fragment1, …, file.content) with placeholder substitution over the result (dotenv / compose env_file last-wins semantics).
  • Purely additive — files with no fragments attached render byte-for-byte unchanged.
  • Editing a fragment auto-resyncs every file that includes it; deleting an in-use fragment returns 409 listing dependents.
  • New Fragments CRUD page, ordered selector with reorder + preview pane, and POST /api/config-files/:id/preview.

Atomic multi-resource sync (#161)

  • POST /api/sync/batch runs config-file syncs as an all-or-nothing batch with optional rollback.
  • Idempotency-Key replays an identical body and returns 409 on body conflicts.
  • Per-op snapshots enable reverse-order restore + re-sync; GET /api/sync/batch/:batchId inspects past batches; audit rows link the batch ID.
  • v1 scope: config-file syncs only, single environment per batch (service-deploy deferred).

Dry-run previews (#160)

  • Preview a mutating call without performing it via ?dryRun=true or X-Dry-Run: true.
  • Supported on deploy, deployment-plan execute, config-file sync-all, and per-service sync-files.
  • Stops before any host write / container cycle / history row; image digests resolved from the registry (no docker pull); secret values redacted to *** (references preserved).
  • Sync dry-runs return a per-target unified diff vs the current host file.

Topology diagram upgrades (#154)

  • External entities — model off-platform sources/sinks as pill nodes with handles on all four sides; included in Mermaid export.
  • Resizable server boxes and server clustering (collapsible dashed containers that aggregate child edges).
  • Stable connection anchorsleft/right/top/bottom handle IDs preserved verbatim on connect.

Self-describing HTTP API (#155)

  • OpenAPI 3 spec at GET /openapi.json and Swagger UI at /api/docs (generated from registered routes).
  • GET /api/auth/me now returns top-level role, environments[], and scopes[] so clients can check authorization before making a call.
  • (Error-envelope change is under Action required.)

No-silent-success guarantees (#159)

  • Read-only-field PATCHes are rejected; zero-target syncs are surfaced explicitly rather than masquerading as success.
  • Client-visible details under Action required.

Server iteration in config templates (#134)

  • Go-style {{range servers ...}}{{end}} with tag / name (glob) / environment filters.
  • Per-server fields in the body: .name, .hostname, .publicIp, .privateIp, .id, .tags.
  • Two-stage render (iteration first, then ${KEY}), alphabetical by name for deterministic checksums; no new dependency.

Auto-resync config files on value change (#133)

  • PATCH /api/secrets/:id and PATCH /api/vars/:id re-render every config file in that env that references ${KEY}.
  • Fires only when the value actually changes; runs fire-and-forget, never blocks the PATCH.
  • New ConfigFile.autoResync flag lets operators pin a file's content; triggered syncs are tagged in the audit log.

Free-form service type tagging (#153)

  • Group services by workload type (django, postgres, redis, …) via a free-form typeTag.
  • Configure-modal input with autocomplete from existing tags; filter chip row on the Services list persisted via ?type=.

2.0 quality-of-life bundle (#136)

  • Restart bug fix — services with a compose path now run compose down + compose up --force-recreate so config changes are picked up (falls back to docker restart only when no compose path is set).
  • Per-env Slack channel override — env Settings → Notifications tab; explicit routing rules still win.
  • Config file syntax highlighting — CodeMirror editor, language auto-detected from filename.
  • Config-file "Attached to services" entries now link to the service detail page.

Docker logs visibility (#135)

  • Per-container docker logs captured during each deploy step and shown in the deployment plan view (no SSH).
  • Service logs modal gains a Load older button with scroll preservation; defaultLogLines now drives both capture and the viewer.

Fixes

  • Service→ServiceDeployment split fallout (#163, #152) — fixed crashes from the stale service.server shape on the Config Files page (#152), the Registries linked-services list, and the Add Dependency picker (all now read serviceDeployments[]).
  • Compose host port dropped (#132) — auto-managed compose no longer drops the host port when exposedPorts has no explicit host mapping.
  • Fragments sidebar icon (#173) — the Fragments page got its own icon instead of sharing one.

Security

  • CodeQL #34 (js/clear-text-logging, high) (#182)getRegistryCredentials() wraps decryption in try/catch and re-throws a sanitized, secret-free error, severing the path by which a decryption failure could log encrypted material / nonce. The scheduler now skips a single unreadable credential instead of aborting the whole update run.
  • Supply-chain hardening (#176):
    • root + ui/ .npmrc set min-release-age=1 and ignore-scripts=true to match the Dockerfile.
    • Dependabot publish cooldown per ecosystem (npm 5-day, others 3-day).
    • cleared the autocannon → hyperid → uuid@8.3.2 advisory chain via a uuid@^11 override (npm audit: 0 vulns).
  • Heredoc → writeFile (#163) — config-file sync paths no longer write rendered content through a shell heredoc, so content can't terminate it early or be reparsed.

Under the hood

  • Stress-test framework + CI (#138) — 18-scenario suite, soft-mode GitHub Actions workflow, sticky PR comments, thresholds checked in.
  • /build skill (#131) — drives a GitHub issue end-to-end to a review-clean PR.
  • Dependency bumps@fastify/cors → 11, pino-pretty → 13 (#177); TypeScript → ^6.0.3 across both workspaces (#175, #167); grouped Dependabot bumps: #120, #121, #122, #165.

Documentation

  • Rewritten services.md for the template/deployment split.
  • New server-bootstrap.md and supply-chain.md guides.
  • Expanded config-files.md (fragments, server iteration, syntax highlighting).
  • Refreshed architecture, API, monitoring, and environment-settings references.