Changelog
Version v1.2.0
What’s new
BridgePort v1.2.0 introduces scoped API tokens and service accounts for finer-grained automation access, ships a much-improved config file scanner (new detection, polished diff UX, fewer false positives), and rolls out a proper release pipeline with stable image channels. Database migrations apply automatically on container restart — no manual SQL.
Action required before upgrading
Skip this section if you don’t use these features.
1. External scripts that mint per-user API tokens
POST /api/auth/tokens has been removed. Existing tokens keep working (auto-migrated to all-environments scope with the owner’s current role), but any script that minted its own tokens needs updating:
- Use
POST /api/admin/tokens(admin-only), or - Mint tokens via the new
/admin/integrationsUI
Token issuance is now an admin operation so scoped tokens can be tracked centrally. (#96)
2. Pinning to :latest or :stable
These tags no longer track master. They now mean “last released version”. If you want to keep tracking master, switch to the new :edge channel:
# Before — :latest used to point at master HEADimage: ghcr.io/bridgeinpt/bridgeport:latest
# Afterimage: ghcr.io/bridgeinpt/bridgeport:edge # master HEADimage: ghcr.io/bridgeinpt/bridgeport:latest # latest release (this one: v1.2.0)image: ghcr.io/bridgeinpt/bridgeport:1.2 # minor-lockedimage: ghcr.io/bridgeinpt/bridgeport:1 # major-lockedFull channels table: docs/operations/upgrades.md. (#103)
Database migrations
One migration applies automatically on container restart:
20260520120000_add_api_token_scoping_and_service_accounts— adds scope columns toApiToken, creates theServiceAccounttable, and addsapiTokenId/serviceAccountIdcolumns toAuditLogfor forensics. (#96)
No manual SQL required (per the database-migration golden rule).
Features
Scoped API tokens & service accounts (#96)
Tokens can now be scoped to specific environments:
- Calls outside the token’s allowlist return
403;GET /api/environmentsis filtered automatically - Effective role is
min(token, owner)— demote a user and their tokens demote immediately - All ~120 audit-log call sites now record the token or service account that performed each action
A new /admin/integrations page provides a sectioned UI for:
- API Tokens — list / create with scope picker, copy-once display, revoke
- Service Accounts — full CRUD, individual disable
- Webhooks and OAuth Apps are placeholders for future work
Config file scanner
Missing reference detection (#102). A new missing_reference suggestion surfaces ${KEY} and ${KEY:-default} references in config files that have no matching secret or variable defined. These are sorted to the top of scan results so they’re easy to spot and fix.
Sharper preview & confirm UX (#102):
- Apply dialog now prefills the actual value — no more retyping
- Preview shows collapsible per-file diff hunks instead of full file dumps; auto-expanded for ≤3 files
- Disabled buttons render as visibly disabled across the UI
Fewer false positives (#104). Three classes of unhelpful suggestions are gone:
- YAML extraction is UPPER_SNAKE_CASE only —
restart: unless-stoppedno longer triggers a${RESTART}suggestion - Cross-key repetition within the same file is no longer a detection signal
- Variable substitution only rewrites full RHS values — no more accidental substring replacements (e.g.
ENVIRONMENT=stagingrewritingapp-staging.bridgein.cominside another variable)
Sentry admin test tab (#99)
A new tab under /admin/notifications → Sentry lets admins:
- See backend + frontend DSN status and resolved environment at a glance
- Send synthetic test errors with one click (backend captures + flushes; frontend throws on next tick to mimic real uncaught errors)
- View inline env-var setup instructions when no DSN is configured
Fixes
Sentry release tag (#99)
APP_VERSION was only reaching the UI build stage, so the backend fell back to package.json (1.0.0). Both /health and Sentry deploy correlation reported the wrong version. Now propagated to the production stage and used everywhere it’s needed.
Security
- CVE-2026-39406 — patched
@hono/node-server(middleware bypass inserveStaticvia repeated slashes). Pulled in transitively by@prisma/devwhich pins an exact version, so a parent bump can’t fix it. Patched via npmoverridesto^1.19.13. Dev-only scope, but clears the alert. (#100) - Command injection in agent deploy — SSH agent deployment now writes the systemd unit file via SFTP instead of a shell heredoc, closing CodeQL alerts #2 and #3. (
'EOF'heredoc quoting doesn’t protect against the delimiter word appearing inside the content.) (#71) fast-jwt6.2.2 → 6.2.4 and other backend-security-group bumps. (#84, #94)
Release pipeline (#103)
This release ships through the new pipeline introduced in this PR:
release.ymltriggers onv*.*.*tags, builds the image withdocker/metadata-actionsemver tags (:vX.Y.Z,:X.Y.Z,:X.Y,:X,:stable,:latest), and publishes the GitHub Release from the annotated tag message verbatim- Prereleases (e.g.
v1.2.0-rc.1) only publish exact-version tags — never:latest,:stable, major or minor build.ymlis now “Build Edge Image” — publishes:edge+ immutable:YYYYMMDDHH-sha, and skips builds for doc-only changes viapaths-ignore- A new
/releaseslash command drives the curated workflow used to produce these notes
Under the hood
Prisma 6 → 7 (#81)
Manual migration (replaces Dependabot PRs that couldn’t auto-merge due to the deployment-model change):
urlmoved fromschema.prismainto a newprisma.config.tsPrismaClientwired to@prisma/adapter-better-sqlite3driver adapterbinaryTargetsdropped from the generator block- Dockerfile rebuilds
better-sqlite3in the prod-deps layer for Alpine
Other notable bumps
- Node base image —
22-alpine→26-alpine(#86) - dockerode — 4 → 5 (#76)
- Backend / frontend / CLI dependency-group bumps: #72, #73, #74, #75, #80, #82, #83, #85, #92, #93, #95
Documentation
- New canonical Channels table in
docs/operations/upgrades.md(summary copied into the README) - Pinning recommendations added to
docker/docker-compose.ymlanddocs/installation.md - Refreshed guides: users, secrets, API reference, configuration (#96, #99, #102, #103)