From 15df9e400514cf6877ffe3c65a9356e923ad9f9c Mon Sep 17 00:00:00 2001 From: John Gruber Date: Wed, 19 Aug 2026 22:29:39 -0500 Subject: [PATCH 01/12] docs: fix registry/cosign/upgrade metadata and the USER-gate authoring guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #177 review (bonnyr-f5) — Majors and nits. - Registry: ghcr.io/jlcode-tech -> ghcr.io/f5devcentral in docs/DOCKER.md, user-pack/install-guide.html, and the publish-signed-images.sh example. CI publishes to ghcr.io/f5devcentral, so the documented pull/verify targets were a third-party namespace. - cosign verify: keyless CI signing puts the Actions issuer + workflow ref in the cert, so --certificate-oidc-issuer https://github.com/login/oauth would never verify a CI-published image. Corrected to https://token.actions.githubusercontent.com and a --certificate-identity-regexp matching the release workflow, in DOCKER.md and the publish script. - Authoring guide: it taught `USER ` / a named user, which the 4.0.0 non-root gate now refuses. Rewritten to require a bare numeric uid, with the `USER nonroot` -> `USER 65532` (or 1000) migration spelled out. - MIN_UPGRADE_FROM: was v3.1.6, the newest final tag -- exactly what ci.yml says it must not be. Set to v3.0.1 (one release back). (Older 2.x sources would need 2.x final tags, which this repo doesn't carry; noted for the maintainers.) - CHANGELOG: bridged the 3.x gap (v3.1.6, v3.0.1 entries) so the auto-inserted 4.0.0 entry won't sit directly above v2.10.74, with the USER-gate upgrade note; header no longer says "v2" only. - container_runner.is_root_user docstring: dropped the stale "KNOWN GAP" self- reference; it now fails closed on any non-numeric USER, subsuming named aliases. - Nits: .env.example developer home path -> generic; .trivyignore CVE-2026-7598 revisit date (expired 2026-08-12) pushed with a note; #408 roadmap items marked Shipped (4.0.0) in ROADMAP.md and roadmap.yaml. Left as-is: the historical 2026-06 roadmap sync-note prose that mentions the old jlcode-tech namespace -- those are dated records of past state, not instructions. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- .env.example | 2 +- .trivyignore | 2 +- CHANGELOG.md | 20 ++++++++++++++++++- MIN_UPGRADE_FROM | 2 +- .../services/execution/container_runner.py | 4 +++- docs/DOCKER.md | 18 ++++++++--------- ...er modules and blueprints for BNK Forge.md | 10 +++++++--- docs/ROADMAP.md | 2 +- docs/roadmap.yaml | 2 +- scripts/publish-signed-images.sh | 8 ++++---- user-pack/install-guide.html | 8 ++++---- 11 files changed, 51 insertions(+), 27 deletions(-) diff --git a/.env.example b/.env.example index 1c024c7..fa1a481 100644 --- a/.env.example +++ b/.env.example @@ -58,7 +58,7 @@ # Set HOST_REPO_PATH in docker-compose.yml to enable the GUI upgrade button. # Without this, use SSH + ./upgrade.sh for server upgrades. # -# HOST_REPO_PATH=/home/jarrodl/bnk-forge-v2 +# HOST_REPO_PATH=/path/to/bnk-forge # ============================================================================ # ENVIRONMENT (development/staging/production) diff --git a/.trivyignore b/.trivyignore index 46fa12f..dd1b9cb 100644 --- a/.trivyignore +++ b/.trivyignore @@ -33,7 +33,7 @@ CVE-2026-33186 # malicious remote SSH server, which our HTTP backend never does. # Upstream: no Debian backport yet (Trivy reports empty fix column). # REVISIT: monthly via https://security-tracker.debian.org/tracker/CVE-2026-7598 -# escalate to pin-from-sid if no fix by 2026-08-12 +# escalate to pin-from-sid if no fix by 2026-10-31 (prior 2026-08-12 deadline passed; still no upstream fix) # Tracked: memory/followup_trivyignore_cve_2026_7598_revisit.md # Added: 2026-05-12 CVE-2026-7598 diff --git a/CHANGELOG.md b/CHANGELOG.md index 294cdf6..e7d4c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,27 @@ # Changelog -All notable changes to BNK-Forge v2. +All notable changes to BNK-Forge. --- +## v3.1.6 (2026-08-10) — 3.1.x line + +Milestone tag `v3.1.6` — the last release before 4.0.0, and the initial public +release on `f5devcentral`. This entry bridges a gap where the 3.x line was not +recorded here; for the per-change detail see the `v3.0.1..v3.1.6` git range and +the PRs merged into it. Notable across 3.1.x: the container-runner hardening +series (#408) and the ADR-424 bare-metal/DPU work. + +> **Heads-up for the 4.0.0 upgrade:** the container runner's non-root gate now +> refuses *named* users — an image using the distroless-standard `USER nonroot` +> is rejected. Switch it to a numeric uid (`USER 65532`, or `USER 1000`). See +> the 4.0.0 entry for the full note. + +## v3.0.1 (2026-04-09) — 3.0.x line + +Milestone tag `v3.0.1`, the first 3.x release after the 2.x line below. Bridged +entry; see the `v2.10.74..v3.0.1` git range for the per-change detail. + ## v2.10.74 (2026-03-04) — TMM Debug Panel Enhancements: F5 Docs Commands, Netkvest, Bug Fix ### Bug Fixes diff --git a/MIN_UPGRADE_FROM b/MIN_UPGRADE_FROM index d80dc33..b105cea 100644 --- a/MIN_UPGRADE_FROM +++ b/MIN_UPGRADE_FROM @@ -1 +1 @@ -v3.1.6 +v3.0.1 diff --git a/backend/services/execution/container_runner.py b/backend/services/execution/container_runner.py index 79c2f61..38b2438 100644 --- a/backend/services/execution/container_runner.py +++ b/backend/services/execution/container_runner.py @@ -550,7 +550,9 @@ def is_root_user(image_user: str | None) -> bool: An image that never declares USER reports an empty string and runs as root — that is the common case and must be caught. - Closes the numeric bypass only — see the KNOWN GAP note in the body. + Fails closed on anything that is not a bare non-zero decimal uid, + which also subsumes the named-alias case (see the body) — there is no + remaining KNOWN GAP. Only the uid half decides this. Docker's USER is ``[:]``, so an image declaring ``USER 0:100`` or ``USER root:wheel`` runs as uid 0 diff --git a/docs/DOCKER.md b/docs/DOCKER.md index f25eaeb..ae8e6da 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -89,24 +89,24 @@ Rekor transparency log entry), and `` with the image digest. ```bash # Verify the signature cosign verify \ - ghcr.io/jlcode-tech/bnk-forge-api@ \ - --certificate-identity \ - --certificate-oidc-issuer https://github.com/login/oauth + ghcr.io/f5devcentral/bnk-forge-api@ \ + --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/.*' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com # Verify + extract the SBOM attestation cosign verify-attestation \ --type cyclonedx \ - --certificate-identity \ - --certificate-oidc-issuer https://github.com/login/oauth \ - ghcr.io/jlcode-tech/bnk-forge-api@ \ + --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/.*' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com \ + ghcr.io/f5devcentral/bnk-forge-api@ \ | jq -r '.payload' | base64 -d | jq . # Verify + extract the SLSA provenance attestation cosign verify-attestation \ --type slsaprovenance \ - --certificate-identity \ - --certificate-oidc-issuer https://github.com/login/oauth \ - ghcr.io/jlcode-tech/bnk-forge-api@ \ + --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/.*' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com \ + ghcr.io/f5devcentral/bnk-forge-api@ \ | jq -r '.payload' | base64 -d | jq . ``` diff --git a/docs/How to write CI container runner modules and blueprints for BNK Forge.md b/docs/How to write CI container runner modules and blueprints for BNK Forge.md index 4357830..5351f98 100644 --- a/docs/How to write CI container runner modules and blueprints for BNK Forge.md +++ b/docs/How to write CI container runner modules and blueprints for BNK Forge.md @@ -654,9 +654,13 @@ The container engine is deliberately constrained: which is the default for most base images*). The workspace is mounted from the host, so a root container would be a host-root write primitive. Forge does **not** silently remap you to another uid with `--user`: that would override your image's `USER` and break your - own state writes. So: put `USER ` in your Dockerfile. uid **1000** matches the - workspace owner and is the safe choice. This mirrors Kubernetes `runAsNonRoot`, which the - Kubernetes runner applies to the same artifacts. + own state writes. So: put a **numeric** `USER` in your Dockerfile. The gate requires a bare + decimal uid — uid **1000** matches the workspace owner and is the safe choice. A **named** + user such as `USER nonroot` (the distroless default) is now **refused**: a name can't be + resolved to a uid without the image's own `/etc/passwd`, so it can't be proven non-root. + If you were on `USER nonroot`, switch to its numeric equivalent `USER 65532` (or `USER 1000`). + Forge does **not** silently remap you with `--user`. This mirrors Kubernetes `runAsNonRoot`, + which the Kubernetes runner applies to the same artifacts. - **A dedicated network** — steps attach to the `bnk-forge-artifacts` bridge network rather than the daemon's default bridge, so artifact containers don't sit alongside unrelated containers. Egress still works (you can reach cloud control planes); you just don't share diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 12a346f..8aec597 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -48,7 +48,7 @@ Source sweep: memories + ADRs (D-001…D-028) + GitHub issues + open PRs, 2026-0 | **awsbnkctl review follow-up** | 💤 Deferred | [#155](https://github.com/f5devcentral/bnk-forge/issues/155) | SimpleNamespace shim hardening + configured-shape contract test. (awsbnkctl side: SSO refresh-token bootstrap — sibling repo.) | | **Review follow-ups (deferred polish)** | 💤 Deferred | [#153](https://github.com/f5devcentral/bnk-forge/issues/153) · [#154](https://github.com/f5devcentral/bnk-forge/issues/154) · [#156](https://github.com/f5devcentral/bnk-forge/issues/156) · [#157](https://github.com/f5devcentral/bnk-forge/issues/157) | #153 (#144 TTFT/ports) · #154 (#146 _kind_to_snake) · #156 (#151 MCP envelope sweep) · #157 (#148 BNK substring/prefix). Non-blocking; created 2026-05-27. | | **Module catalog auto-sync on boot + wire blueprint wizard 'Sync' CTA** | ⚪ Planned | [#419](https://github.com/f5devcentral/bnk-forge/issues/419) | Fresh install / volume wipe leaves the git module catalog (bnk/app/infra packs) un-synced, so BNK/app blueprints are DOA until an operator runs Catalog→Advanced→Modules→'Sync all'. The wizard's 'requires sync' prompt is wired to no endpoint. Fix: (1) boot-time auto-sync step (non-fatal, ref-aware) after builtin seeders; (2) wire wizard CTA to POST /api/module-library/sync. Separate from the d019/adr-204 execution_engine seeder-guard fix. | -| **CI container runner engine — security hardening follow-ups** | ⚪ Planned | [#408](https://github.com/f5devcentral/bnk-forge/issues/408) · [PR #340](https://github.com/f5devcentral/bnk-forge/issues/340) | Non-blocking follow-ups from the #340 review (all prior blockers verified fixed pre-merge). Priority: non-root Docker gate bypass via `USER 0:` (host-root primitive), `state.outputs_file` path traversal (arbitrary worker-file read), registry `/test` cross-operator credential exfil + SSRF, install-script PAT/password xtrace leak, K8s deny-all egress netpol breaks the artifact. Plus same-project cluster-name clobber + nits. | +| **CI container runner engine — security hardening follow-ups** | ✅ Shipped (4.0.0) | [#408](https://github.com/f5devcentral/bnk-forge/issues/408) · [PR #340](https://github.com/f5devcentral/bnk-forge/issues/340) | Non-blocking follow-ups from the #340 review (all prior blockers verified fixed pre-merge). Priority: non-root Docker gate bypass via `USER 0:` (host-root primitive), `state.outputs_file` path traversal (arbitrary worker-file read), registry `/test` cross-operator credential exfil + SSRF, install-script PAT/password xtrace leak, K8s deny-all egress netpol breaks the artifact. Plus same-project cluster-name clobber + nits. | | **Multi-version module catalog — immutable module versions, exact pin resolution (ADR D-033)** | 🟡 In progress | [#433](https://github.com/f5devcentral/bnk-forge/issues/433) · [PR #436](https://github.com/f5devcentral/bnk-forge/issues/436) | Module identity becomes (source, path, version); hashed rows immutable; blueprints resolve pins exactly (BLUEPRINT_MODULE_VERSION_MISSING); ProjectModule FK becomes a true pin with explicit change-version action + UI/MCP. Combined PR #436 (supersedes stacked #434/#435). ADR: docs/adr/D-033-multi-version-module-catalog.md (PR #432). | | **Module test actions — vendor-CLI e2e/scenario/bench tests via pipeline (ADR D-034)** | ⚪ Planned | [#454](https://github.com/f5devcentral/bnk-forge/issues/454) · [PR #453](https://github.com/f5devcentral/bnk-forge/issues/453) | Container-artifact manifests gain a declarative actions block; container engine gains one generic action dispatcher; UI offers actions on post-apply modules (per-scenario + run-all-green, amber behind warning). v1 results = logs + pass/fail. Scaling = edit vars + re-apply, not an action. Tool-embedded tests → pipeline; external load (aiperf agents) stays in Benchmarks. Slices: PR-1 backend, PR-2 UI, PR-3 packs/docs. ADR: docs/adr/D-034-module-test-actions.md (PR #453). Sibling: #452 cluster auto-registration. | | **Container-runner contract hardening — min_forge_version + declared capabilities** | ⚪ Planned | [#465](https://github.com/f5devcentral/bnk-forge/issues/465) | Phase 3 of the ctl-runner review. min_forge_version + capability requirements (e.g. wide docker-socket proxy) become machine-checkable artifact-manifest fields enforced at sync/import; Forge injects the proxy endpoint instead of external manifests hardcoding DOCKER_HOST; schema_version evolution policy lands in EXT-003. | diff --git a/docs/roadmap.yaml b/docs/roadmap.yaml index fee9719..9373301 100644 --- a/docs/roadmap.yaml +++ b/docs/roadmap.yaml @@ -353,7 +353,7 @@ sections: - '#419' note: 'Fresh install / volume wipe leaves the git module catalog (bnk/app/infra packs) un-synced, so BNK/app blueprints are DOA until an operator runs Catalog→Advanced→Modules→''Sync all''. The wizard''s ''requires sync'' prompt is wired to no endpoint. Fix: (1) boot-time auto-sync step (non-fatal, ref-aware) after builtin seeders; (2) wire wizard CTA to POST /api/module-library/sync. Separate from the d019/adr-204 execution_engine seeder-guard fix.' - title: CI container runner engine — security hardening follow-ups - status: planned + status: shipped refs: - '#408' - 'PR #340' diff --git a/scripts/publish-signed-images.sh b/scripts/publish-signed-images.sh index c3c0a02..0e582bf 100755 --- a/scripts/publish-signed-images.sh +++ b/scripts/publish-signed-images.sh @@ -20,7 +20,7 @@ # BNK_FORGE_REGISTRY=ghcr.io/your-org BNK_FORGE_VERSION=3.1.6 ./scripts/publish-signed-images.sh --execute # # Environment variables: -# BNK_FORGE_REGISTRY — required; e.g. ghcr.io/jlcode-tech +# BNK_FORGE_REGISTRY — required; e.g. ghcr.io/f5devcentral # BNK_FORGE_VERSION — optional; defaults to contents of ./VERSION # DRY_RUN — set to 0 to execute (equivalent to --execute) # @@ -33,7 +33,7 @@ # Consumer verification (see docs/DOCKER.md for full details): # cosign verify @ \ # --certificate-identity \ -# --certificate-oidc-issuer https://github.com/login/oauth +# --certificate-oidc-issuer https://token.actions.githubusercontent.com set -euo pipefail @@ -295,13 +295,13 @@ else echo " cosign verify \\" echo " ${REGISTRY}/bnk-forge-api@ \\" echo " --certificate-identity \\" - echo " --certificate-oidc-issuer https://github.com/login/oauth" + echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com" echo "" echo " Verify the SBOM attestation:" echo " cosign verify-attestation \\" echo " --type cyclonedx \\" echo " --certificate-identity \\" - echo " --certificate-oidc-issuer https://github.com/login/oauth \\" + echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com \\" echo " ${REGISTRY}/bnk-forge-api@" fi echo "========================================================" diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 1c2109c..21c2851 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -113,7 +113,7 @@ F5

BNK Forge — Install Guide

-
Private registry edition  |  customer-build distribution  |  registry: ghcr.io/jlcode-tech
+
Private registry edition  |  customer-build distribution  |  registry: ghcr.io/f5devcentral
@@ -145,7 +145,7 @@

Step 1 — Authenticate to the registry

Credentials — handle with care
Your read-access token is provided separately (out-of-band). It is a GitHub PAT scoped to - read:packages for the ghcr.io/jlcode-tech registry. + read:packages for the ghcr.io/f5devcentral registry. Do not share it, commit it to version control, or embed it in scripts. If you believe the token has been exposed, contact the person who gave it to you immediately.
@@ -195,7 +195,7 @@

Step 3 — Configure

BNK_FORGE_REGISTRY - ghcr.io/jlcode-tech + ghcr.io/f5devcentral Points to the private registry. @@ -359,7 +359,7 @@

Troubleshooting

BNK Forge install guide — generated for customer-build distribution  ·  - Registry: ghcr.io/jlcode-tech  ·  Tag: customer-build + Registry: ghcr.io/f5devcentral  ·  Tag: customer-build
From b30f09f1abe9bc8c29be859d626887152cc68263 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Wed, 19 Aug 2026 23:09:49 -0500 Subject: [PATCH 02/12] review fix: MIN_UPGRADE_FROM must name a real tag; cosign identity is not an email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwiget's re-review of #183. BLOCKER 1 — MIN_UPGRADE_FROM: v3.0.1 does not exist in this repo. f5devcentral/ bnk-forge is a squashed public mirror carrying exactly one final tag, v3.1.6, so `git rev-parse --verify v3.0.1^{commit}` fails and the mandatory migration-upgrade-from gate goes red. Reverted to v3.1.6 (the only resolvable final tag). It is the degenerate one-release-wide window the ci.yml check warns against, but that can't be fixed by naming a tag that doesn't exist -- it widens only as more finals are cut here. Rewrote the misleading ci.yml comment (it cited "v3.0.1 pins cryptography 44", from the upstream history this mirror doesn't share) to state that reality. BLOCKER 2 — publish-signed-images.sh still told operators to verify by email. The issuer was corrected but `--certificate-identity ` / `` (lines 35, 297, 303) remained, which can never verify against the Actions OIDC issuer -- the identity is the workflow-ref URI. Changed all three (and the DOCKER.md examples) to `--certificate-identity-regexp` matching `.../.github/workflows/release.yml@*`, the workflow that actually signs. Non-blocking, also addressed: - Identity regexp narrowed from "any workflow" to release.yml specifically. - ROADMAP #408 was marked "Shipped (4.0.0)", but no v4.0.0 tag exists (the release aborted at the protected-branch push) -> "Merged (unreleased)" / status: merged (only the #408 item; the 33 pre-existing shipped items intact). - CHANGELOG 3.x bridge reworded so the dates don't read as if the tags live in this squashed mirror. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- .github/workflows/ci.yml | 17 +++++++++++------ CHANGELOG.md | 11 ++++++----- MIN_UPGRADE_FROM | 2 +- docs/DOCKER.md | 6 +++--- docs/ROADMAP.md | 2 +- docs/roadmap.yaml | 2 +- scripts/publish-signed-images.sh | 6 +++--- 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d8f62e..b2da845 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -668,12 +668,17 @@ jobs: # dependencies. No `|| true` here: if this cannot run, the job has # nothing to say and must fail loudly rather than silently continue. # - # Known fragility, accepted deliberately: the floor's models are - # imported under CURRENT pins, so the gap widens every time a - # dependency moves. v3.0.1 pins cryptography 44 and staging is on 50 — - # six majors — and it holds only because the floor tree touches just - # Fernet, hazmat.primitives.serialization and Ed25519PrivateKey, all - # unchanged across that range. When it does bite, it bites as a + # Known limitation on this repo: f5devcentral/bnk-forge is a squashed + # public mirror and carries exactly ONE final tag, v3.1.6, so the floor + # is currently that tag and the upgrade window is one release wide — + # the degenerate case this check otherwise warns against. It can't be + # widened by naming an older tag (v3.0.1 etc. from the upstream history + # aren't reachable here); it widens only as more finals are cut on this + # repo. Accepted deliberately. + # + # The floor's models are imported under CURRENT pins, so a dependency + # gap can still bite once the window does widen. When it does, it bites + # as a # MANDATORY gate failing hard on a commit that changed nothing # relevant. The fix then is to raise MIN_UPGRADE_FROM to a release # whose models import cleanly, not to add `|| true` here: a floor that diff --git a/CHANGELOG.md b/CHANGELOG.md index e7d4c26..55b9f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,8 @@ All notable changes to BNK-Forge. ## v3.1.6 (2026-08-10) — 3.1.x line -Milestone tag `v3.1.6` — the last release before 4.0.0, and the initial public -release on `f5devcentral`. This entry bridges a gap where the 3.x line was not +Milestone `v3.1.6` — the last release before 4.0.0, and the initial public +release tag on `f5devcentral` (the one final tag this squashed mirror carries). This entry bridges a gap where the 3.x line was not recorded here; for the per-change detail see the `v3.0.1..v3.1.6` git range and the PRs merged into it. Notable across 3.1.x: the container-runner hardening series (#408) and the ADR-424 bare-metal/DPU work. @@ -17,10 +17,11 @@ series (#408) and the ADR-424 bare-metal/DPU work. > is rejected. Switch it to a numeric uid (`USER 65532`, or `USER 1000`). See > the 4.0.0 entry for the full note. -## v3.0.1 (2026-04-09) — 3.0.x line +## v3.0.1 — 3.0.x line -Milestone tag `v3.0.1`, the first 3.x release after the 2.x line below. Bridged -entry; see the `v2.10.74..v3.0.1` git range for the per-change detail. +The first 3.x release after the 2.x line below (upstream tag dated 2026-04-09). +Bridged entry; this repo is a squashed public mirror, so the `v3.0.1` tag and its +per-change history live upstream, not here. ## v2.10.74 (2026-03-04) — TMM Debug Panel Enhancements: F5 Docs Commands, Netkvest, Bug Fix diff --git a/MIN_UPGRADE_FROM b/MIN_UPGRADE_FROM index b105cea..d80dc33 100644 --- a/MIN_UPGRADE_FROM +++ b/MIN_UPGRADE_FROM @@ -1 +1 @@ -v3.0.1 +v3.1.6 diff --git a/docs/DOCKER.md b/docs/DOCKER.md index ae8e6da..b18be6a 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -90,13 +90,13 @@ Rekor transparency log entry), and `` with the image digest. # Verify the signature cosign verify \ ghcr.io/f5devcentral/bnk-forge-api@ \ - --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/.*' \ + --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/release\.yml@.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com # Verify + extract the SBOM attestation cosign verify-attestation \ --type cyclonedx \ - --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/.*' \ + --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/release\.yml@.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ghcr.io/f5devcentral/bnk-forge-api@ \ | jq -r '.payload' | base64 -d | jq . @@ -104,7 +104,7 @@ cosign verify-attestation \ # Verify + extract the SLSA provenance attestation cosign verify-attestation \ --type slsaprovenance \ - --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/.*' \ + --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/release\.yml@.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ghcr.io/f5devcentral/bnk-forge-api@ \ | jq -r '.payload' | base64 -d | jq . diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 8aec597..8a1104b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -48,7 +48,7 @@ Source sweep: memories + ADRs (D-001…D-028) + GitHub issues + open PRs, 2026-0 | **awsbnkctl review follow-up** | 💤 Deferred | [#155](https://github.com/f5devcentral/bnk-forge/issues/155) | SimpleNamespace shim hardening + configured-shape contract test. (awsbnkctl side: SSO refresh-token bootstrap — sibling repo.) | | **Review follow-ups (deferred polish)** | 💤 Deferred | [#153](https://github.com/f5devcentral/bnk-forge/issues/153) · [#154](https://github.com/f5devcentral/bnk-forge/issues/154) · [#156](https://github.com/f5devcentral/bnk-forge/issues/156) · [#157](https://github.com/f5devcentral/bnk-forge/issues/157) | #153 (#144 TTFT/ports) · #154 (#146 _kind_to_snake) · #156 (#151 MCP envelope sweep) · #157 (#148 BNK substring/prefix). Non-blocking; created 2026-05-27. | | **Module catalog auto-sync on boot + wire blueprint wizard 'Sync' CTA** | ⚪ Planned | [#419](https://github.com/f5devcentral/bnk-forge/issues/419) | Fresh install / volume wipe leaves the git module catalog (bnk/app/infra packs) un-synced, so BNK/app blueprints are DOA until an operator runs Catalog→Advanced→Modules→'Sync all'. The wizard's 'requires sync' prompt is wired to no endpoint. Fix: (1) boot-time auto-sync step (non-fatal, ref-aware) after builtin seeders; (2) wire wizard CTA to POST /api/module-library/sync. Separate from the d019/adr-204 execution_engine seeder-guard fix. | -| **CI container runner engine — security hardening follow-ups** | ✅ Shipped (4.0.0) | [#408](https://github.com/f5devcentral/bnk-forge/issues/408) · [PR #340](https://github.com/f5devcentral/bnk-forge/issues/340) | Non-blocking follow-ups from the #340 review (all prior blockers verified fixed pre-merge). Priority: non-root Docker gate bypass via `USER 0:` (host-root primitive), `state.outputs_file` path traversal (arbitrary worker-file read), registry `/test` cross-operator credential exfil + SSRF, install-script PAT/password xtrace leak, K8s deny-all egress netpol breaks the artifact. Plus same-project cluster-name clobber + nits. | +| **CI container runner engine — security hardening follow-ups** | 🟢 Merged (unreleased) | [#408](https://github.com/f5devcentral/bnk-forge/issues/408) · [PR #340](https://github.com/f5devcentral/bnk-forge/issues/340) | Non-blocking follow-ups from the #340 review (all prior blockers verified fixed pre-merge). Priority: non-root Docker gate bypass via `USER 0:` (host-root primitive), `state.outputs_file` path traversal (arbitrary worker-file read), registry `/test` cross-operator credential exfil + SSRF, install-script PAT/password xtrace leak, K8s deny-all egress netpol breaks the artifact. Plus same-project cluster-name clobber + nits. | | **Multi-version module catalog — immutable module versions, exact pin resolution (ADR D-033)** | 🟡 In progress | [#433](https://github.com/f5devcentral/bnk-forge/issues/433) · [PR #436](https://github.com/f5devcentral/bnk-forge/issues/436) | Module identity becomes (source, path, version); hashed rows immutable; blueprints resolve pins exactly (BLUEPRINT_MODULE_VERSION_MISSING); ProjectModule FK becomes a true pin with explicit change-version action + UI/MCP. Combined PR #436 (supersedes stacked #434/#435). ADR: docs/adr/D-033-multi-version-module-catalog.md (PR #432). | | **Module test actions — vendor-CLI e2e/scenario/bench tests via pipeline (ADR D-034)** | ⚪ Planned | [#454](https://github.com/f5devcentral/bnk-forge/issues/454) · [PR #453](https://github.com/f5devcentral/bnk-forge/issues/453) | Container-artifact manifests gain a declarative actions block; container engine gains one generic action dispatcher; UI offers actions on post-apply modules (per-scenario + run-all-green, amber behind warning). v1 results = logs + pass/fail. Scaling = edit vars + re-apply, not an action. Tool-embedded tests → pipeline; external load (aiperf agents) stays in Benchmarks. Slices: PR-1 backend, PR-2 UI, PR-3 packs/docs. ADR: docs/adr/D-034-module-test-actions.md (PR #453). Sibling: #452 cluster auto-registration. | | **Container-runner contract hardening — min_forge_version + declared capabilities** | ⚪ Planned | [#465](https://github.com/f5devcentral/bnk-forge/issues/465) | Phase 3 of the ctl-runner review. min_forge_version + capability requirements (e.g. wide docker-socket proxy) become machine-checkable artifact-manifest fields enforced at sync/import; Forge injects the proxy endpoint instead of external manifests hardcoding DOCKER_HOST; schema_version evolution policy lands in EXT-003. | diff --git a/docs/roadmap.yaml b/docs/roadmap.yaml index 9373301..da6917c 100644 --- a/docs/roadmap.yaml +++ b/docs/roadmap.yaml @@ -353,7 +353,7 @@ sections: - '#419' note: 'Fresh install / volume wipe leaves the git module catalog (bnk/app/infra packs) un-synced, so BNK/app blueprints are DOA until an operator runs Catalog→Advanced→Modules→''Sync all''. The wizard''s ''requires sync'' prompt is wired to no endpoint. Fix: (1) boot-time auto-sync step (non-fatal, ref-aware) after builtin seeders; (2) wire wizard CTA to POST /api/module-library/sync. Separate from the d019/adr-204 execution_engine seeder-guard fix.' - title: CI container runner engine — security hardening follow-ups - status: shipped + status: merged refs: - '#408' - 'PR #340' diff --git a/scripts/publish-signed-images.sh b/scripts/publish-signed-images.sh index 0e582bf..c689f41 100755 --- a/scripts/publish-signed-images.sh +++ b/scripts/publish-signed-images.sh @@ -32,7 +32,7 @@ # # Consumer verification (see docs/DOCKER.md for full details): # cosign verify @ \ -# --certificate-identity \ +# --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/release\.yml@.*' \ # --certificate-oidc-issuer https://token.actions.githubusercontent.com set -euo pipefail @@ -294,13 +294,13 @@ else echo " Verify a signed image:" echo " cosign verify \\" echo " ${REGISTRY}/bnk-forge-api@ \\" - echo " --certificate-identity \\" + echo " --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/.github/workflows/release.yml@.*' \\" echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com" echo "" echo " Verify the SBOM attestation:" echo " cosign verify-attestation \\" echo " --type cyclonedx \\" - echo " --certificate-identity \\" + echo " --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/.github/workflows/release.yml@.*' \\" echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com \\" echo " ${REGISTRY}/bnk-forge-api@" fi From db83511af8ad458a770f16ce85c78547d4c30b2d Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 00:39:59 -0500 Subject: [PATCH 03/12] review fix: make 'merged' a real roadmap status key; regenerate; fix regexp escapes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwiget's re-review of #183. docs/ROADMAP.md and docs/roadmap.html are generated from docs/roadmap.yaml by bin/roadmap-gen.py, and my "Merged (unreleased)" wording was hand-edited into the generated file with a status: merged that the schema didn't know -- so regeneration reverted the label to a bare "merged" and the HTML still bucketed the #408 item under Planned with the planned dot. - Added `merged` to status_legend (emoji 🟢, dot d-ship -- green, "done but unreleased", reusing the shipped dot rather than new CSS), and extended the schema-comment key list. - Ran bin/roadmap-gen.py: the #408 row now derives as "🟢 Merged (unreleased)", planned count drops 35 -> 34, and roadmap.html (which was stale) is current. ROADMAP.md is unchanged because the generated output now matches. Nit: the echoed cosign identity regexps at publish-signed-images.sh:297/303 now carry the same `\.` escapes as the header comment at line 35 (bare dots were a functional superset, just inconsistent). Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- docs/roadmap.html | 4 ++-- docs/roadmap.yaml | 6 +++++- scripts/publish-signed-images.sh | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/roadmap.html b/docs/roadmap.html index eb4ba5c..126ff53 100644 --- a/docs/roadmap.html +++ b/docs/roadmap.html @@ -68,7 +68,7 @@
23
In progress (tracked)
-
35
Planned (tracked)
+
34
Planned (tracked)
50+
PRs merged to staging
34
Issues closed
@@ -129,7 +129,7 @@

Active / next — issue-tracked, actionable now

Review follow-ups

    -
  • CI container runner engine — security hardening follow-ups#408 · PR #340
  • +
  • CI container runner engine — security hardening follow-ups#408 · PR #340
diff --git a/docs/roadmap.yaml b/docs/roadmap.yaml index da6917c..73252f0 100644 --- a/docs/roadmap.yaml +++ b/docs/roadmap.yaml @@ -17,7 +17,7 @@ # "AUTO:planned" are computed from item counts; any other # value (e.g. "25+") is a static meta number. # status_legend: key -> { emoji, dot, label } used for BOTH md + html. -# keys: shipped | in_progress | blocked | deferred | planned +# keys: shipped | merged | in_progress | blocked | deferred | planned # sections: ordered list of # - id: stable slug (used by roadmap-add.py --section) # number: section number for the md heading "## N. ..." @@ -77,6 +77,10 @@ status_legend: emoji: ✅ dot: d-ship label: Shipped + merged: + emoji: 🟢 + dot: d-ship + label: Merged (unreleased) in_progress: emoji: 🟡 dot: d-prog diff --git a/scripts/publish-signed-images.sh b/scripts/publish-signed-images.sh index c689f41..79ed237 100755 --- a/scripts/publish-signed-images.sh +++ b/scripts/publish-signed-images.sh @@ -294,13 +294,13 @@ else echo " Verify a signed image:" echo " cosign verify \\" echo " ${REGISTRY}/bnk-forge-api@ \\" - echo " --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/.github/workflows/release.yml@.*' \\" + echo " --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/release\.yml@.*' \\" echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com" echo "" echo " Verify the SBOM attestation:" echo " cosign verify-attestation \\" echo " --type cyclonedx \\" - echo " --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/.github/workflows/release.yml@.*' \\" + echo " --certificate-identity-regexp 'https://github.com/f5devcentral/bnk-forge/\.github/workflows/release\.yml@.*' \\" echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com \\" echo " ${REGISTRY}/bnk-forge-api@" fi From 00249f88779a613e54dd6fdeaf03a1d084f1601c Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 02:03:25 -0500 Subject: [PATCH 04/12] docs: correct the v3.1.6 changelog, the cosign verify prose, and the trivy note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bonnyrf5 aggregate review, #183. CHANGELOG v3.1.6: the entry claimed the container-runner hardening series (#408) and ADR-424 were "notable across 3.1.x", but the v3.1.6 tag is a single squashed "initial public release" commit and that work lives after it (staging: #2/#123/ #161 hardening, → 4.0.0). Corrected the attribution to 4.0.0, fixed the PR numbers, and dropped the misleading "see the v3.0.1..v3.1.6 range" pointer (a squashed tag has no per-change history to read). DOCKER.md verify: the prose still told readers to replace , but the commands were already switched to --certificate-identity-regexp for the release workflow, leaving orphaned. Rewrote the prose to match, and added the missing caveat: an image signed locally via the manual SIGN_EXECUTE path is bound to the maintainer's own identity, not the workflow's, so it verifies with --certificate-identity , not this regexp. .trivyignore CVE-2026-7598: the extension was justified by asserting "still no upstream fix" without a check. Replaced with an instruction to confirm via a trivy re-run / tracker before extending, and a near next-check date. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- .trivyignore | 9 +++++++-- CHANGELOG.md | 10 ++++++---- docs/DOCKER.md | 11 +++++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.trivyignore b/.trivyignore index dd1b9cb..c14b2bf 100644 --- a/.trivyignore +++ b/.trivyignore @@ -32,8 +32,13 @@ CVE-2026-33186 # The vulnerable code path requires libssh2 to negotiate auth with a # malicious remote SSH server, which our HTTP backend never does. # Upstream: no Debian backport yet (Trivy reports empty fix column). -# REVISIT: monthly via https://security-tracker.debian.org/tracker/CVE-2026-7598 -# escalate to pin-from-sid if no fix by 2026-10-31 (prior 2026-08-12 deadline passed; still no upstream fix) +# REVISIT: monthly. Do not extend this entry on the assertion that no fix exists — +# confirm it: re-run `trivy image` (or check the tracker) and only keep +# the ignore while the fix column is still empty for our base image's +# libssh2. https://security-tracker.debian.org/tracker/CVE-2026-7598 +# The 2026-08-12 deadline lapsed without that re-check; next check by +# 2026-09-12, and escalate to pin-from-sid if a fixed version is then +# available and we're still ignoring it. # Tracked: memory/followup_trivyignore_cve_2026_7598_revisit.md # Added: 2026-05-12 CVE-2026-7598 diff --git a/CHANGELOG.md b/CHANGELOG.md index 55b9f3c..b2945e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,12 @@ All notable changes to BNK-Forge. ## v3.1.6 (2026-08-10) — 3.1.x line Milestone `v3.1.6` — the last release before 4.0.0, and the initial public -release tag on `f5devcentral` (the one final tag this squashed mirror carries). This entry bridges a gap where the 3.x line was not -recorded here; for the per-change detail see the `v3.0.1..v3.1.6` git range and -the PRs merged into it. Notable across 3.1.x: the container-runner hardening -series (#408) and the ADR-424 bare-metal/DPU work. +release tag on `f5devcentral`. This mirror is squashed: the `v3.1.6` tag is a +single `feat: initial public release` commit, so there is no per-change history +behind it to link here. Work that came *after* this tag — including the +container-runner hardening series (#2, #123, #161) and the ADR-424 bare-metal/DPU +work — is part of 4.0.0, not v3.1.6, and is recorded under the 4.0.0 entry when +that release is cut. > **Heads-up for the 4.0.0 upgrade:** the container runner's non-root gate now > refuses *named* users — an image using the distroless-standard `USER nonroot` diff --git a/docs/DOCKER.md b/docs/DOCKER.md index b18be6a..a84a0ee 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -83,8 +83,15 @@ The script signs each image by digest (not tag) and attaches two attestations: ### Verifying signatures (consumers) -Replace `` with the email of the person who signed the images (visible in the -Rekor transparency log entry), and `` with the image digest. +Replace `` with the image digest you're verifying. You do **not** fill in a +signer — official images are signed by the release workflow (`release.yml`), and the +commands below already pin that identity with `--certificate-identity-regexp … release.yml@…`. + +> **Note:** this verifies images published by CI. If a maintainer signed an image +> locally via the manual path above (`SIGN_EXECUTE=1`), it is bound to *that +> person's* OIDC identity, not the workflow's, so it will not match the regexp +> here — verify it with `--certificate-identity ` instead. Official +> releases always go through `release.yml`. ```bash # Verify the signature From b9a6f24302f89313d3dc4a983746832e3df0ccc5 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 06:45:09 -0500 Subject: [PATCH 05/12] docs(install-guide): public registry (no login) + settable admin password MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two corrections to the customer install guide. Public registry (bonnyrf5 #183 finding — the stale jlcode-forgebot login): now that the images are public on ghcr.io/f5devcentral, no docker login is needed to pull. Removed the entire "Step 1 — Authenticate" flow, the read-access-token prerequisite, the credentials callout, and the auth troubleshooting entry; renumbered the remaining steps 1-4; and reworded the intro/sub-header/registry note from "private" to "public". Admin password (new settable-default scheme, #184/#186): replaced the admin/changeme login with DEFAULT_ADMIN_PASSWORD. Added it to the .env table, and rewrote First login: log in with the password you set, or — if unset — retrieve the random one generated on first launch from /app/keys/initial_admin_password (docker compose exec ... cat) or the startup logs. The account requires a password change on first login either way. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- user-pack/install-guide.html | 84 +++++++++++++++--------------------- 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 21c2851..7f5af8a 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -113,7 +113,7 @@ F5

BNK Forge — Install Guide

-
Private registry edition  |  customer-build distribution  |  registry: ghcr.io/f5devcentral
+
Public registry edition  |  customer-build distribution  |  registry: ghcr.io/f5devcentral
@@ -122,9 +122,9 @@

BNK Forge — Install Guide

- This guide walks you through installing BNK Forge from the private GitHub Container Registry. - You will authenticate to the registry using the read-only bot credential you were provided, - download the install package, make a small configuration change, and run a single script. + This guide walks you through installing BNK Forge from the public GitHub Container Registry. + You download the install package, make a small configuration change, and run a single + script. The images are public, so no registry login is required. The entire process takes under ten minutes on a fast connection; the first image pull may take a few minutes depending on bandwidth.
@@ -134,40 +134,12 @@

Prerequisites

  • Docker Engine 24+ — on macOS or Windows, Docker Desktop satisfies both this and the Compose requirement.
  • Docker Compose v2 (≥ 2.24) — verify with docker compose version.
  • -
  • Read-access token — the <READ_TOKEN> provided to you separately (see Step 1).
  • ~5–10 GB free disk space — for images and persistent data volumes.
  • Network access to ghcr.io — outbound HTTPS (port 443) must be allowed.
-

Step 1 — Authenticate to the registry

- -
-
Credentials — handle with care
- Your read-access token is provided separately (out-of-band). It is a GitHub PAT scoped to - read:packages for the ghcr.io/f5devcentral registry. - Do not share it, commit it to version control, or embed it in scripts. - If you believe the token has been exposed, contact the person who gave it to you immediately. -
- -

Run the following command, replacing <READ_TOKEN> with the token you received. - The bot username is fixed — use it exactly as shown:

- -
$ echo '<READ_TOKEN>' | docker login ghcr.io -u jlcode-forgebot --password-stdin
- -

Expected output:

-
Login Succeeded
- -
-
Token stays cached
- Docker stores the credential in your OS keychain (or ~/.docker/config.json). - You only need to log in once per machine. If you later see a denied or - unauthorized error during a pull, re-run the command above — the token may have been - rotated. See the Troubleshooting section for details. -
- - -

Step 2 — Download & extract the package

+

Step 1 — Download & extract the package

You should have received a bnk-forge-<version>.tar.gz archive alongside this guide. Save it to a convenient location, then extract it:

@@ -177,8 +149,8 @@

Step 2 — Download & extract the package

All subsequent commands are run from inside this directory.

- -

Step 3 — Configure

+ +

Step 2 — Configure

Copy the example environment file and open it in your editor:

@@ -196,7 +168,7 @@

Step 3 — Configure

BNK_FORGE_REGISTRY ghcr.io/f5devcentral - Points to the private registry. + Points to the public registry. BNK_FORGE_VERSION @@ -218,6 +190,12 @@

Step 3 — Configure

your choice Change from the default. Used by the MCP integration layer. + + DEFAULT_ADMIN_PASSWORD + your choice (recommended) + Initial admin login password. If you leave it unset, a random one is + generated on first launch (see Step 4). Either way you must change it on first login. +
@@ -227,8 +205,8 @@

Step 3 — Configure

before running the installer — they cannot be changed easily after the stack first starts.
- -

Step 4 — Install

+ +

Step 3 — Install

Run the installer for your platform. It will pull the images and bring the full stack up. The first run may take a few minutes while images download.

@@ -251,8 +229,8 @@

Step 4 — Install

When the installer finishes you will see:

✅ Installation complete!
- -

Step 5 — First login

+ +

Step 4 — First login

  1. @@ -267,12 +245,20 @@

    Step 5 — First login

    (Firefox). This is expected.

  2. -

    Log in with the default credentials:
    - Username: admin  /  Password: changeme

    +

    Log in as the admin user:
    + Username: admin  /  Password: the value you set for + DEFAULT_ADMIN_PASSWORD in Step 2.

    +
    +
    Didn't set DEFAULT_ADMIN_PASSWORD?
    + If you left it unset, BNK Forge generated a random initial password on first launch and + wrote it to /app/keys/initial_admin_password inside the backend container; + the startup logs also point to it. Retrieve it with: +
    $ docker compose exec backend cat /app/keys/initial_admin_password
    +
    -
    Change the admin password immediately
    - The default password is well-known. Go to User menu → Change Password as your - very first action after login. +
    You must change the password on first login
    + The admin account is flagged to require a password change — set a strong one when prompted + (or via User menu → Change Password) before doing anything else.
  3. @@ -335,10 +321,10 @@

    Troubleshooting

    • - denied: denied or unauthorized: unauthenticated during pull - Your docker login session has lapsed, or the token does not have the - read:packages scope. Re-run Step 1 with your current token. - If the problem persists, contact the person who issued the token. + manifest unknown, or a pull that hangs or times out + The images are public, so no login is required. Check that BNK_FORGE_REGISTRY + is ghcr.io/f5devcentral and that the version tag exists, and that outbound + HTTPS to ghcr.io (port 443) is allowed through any proxy or firewall.
    • Backend service not healthy From 97208784920aad08efb6696f5c1f234fbbb394d1 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 07:40:23 -0500 Subject: [PATCH 06/12] docs(install-guide): correct the MCP_PASSWORD guidance for the no-default scheme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwiget non-blocking note (via #188): after #187/#188 the MCP password has no default to 'change from' — it must be set, empty disables MCP rather than leaving a weak credential, and it is reconciled on every boot so it CAN be rotated later (unlike postgres/redis, which are baked in at first init). Updated the .env table row and split the 'cannot be changed easily' callout so that claim applies only to postgres/redis, with MCP called out as set-required-but-rotatable. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- user-pack/install-guide.html | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 7f5af8a..06c3820 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -187,8 +187,10 @@

      Step 2 — Configure

      MCP_PASSWORD - your choice - Change from the default. Used by the MCP integration layer. + required + Set a value — no default ships. Shared by the backend and the MCP server; + leaving it empty disables MCP (the backend logs why). Unlike the two above it is + re-applied on every boot, so you can rotate it later by changing it and restarting. DEFAULT_ADMIN_PASSWORD @@ -199,10 +201,13 @@

      Step 2 — Configure

      -
      Change all three passwords
      - The defaults in .env.example are well-known placeholders. - Replace POSTGRES_PASSWORD, REDIS_PASSWORD, and MCP_PASSWORD - before running the installer — they cannot be changed easily after the stack first starts. +
      Set strong passwords before first start
      + POSTGRES_PASSWORD and REDIS_PASSWORD replace well-known + placeholder defaults, and they are baked in when the database and cache first + initialize — get them right before running the installer, as they cannot be changed + easily afterward. MCP_PASSWORD has no default and must be set (empty + disables MCP); unlike the other two it is reconciled on every boot, so it can be + rotated later by editing .env and restarting.
      From abbc777d5019e7517dc790af5b873a5ef895b42e Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 19:13:22 -0500 Subject: [PATCH 07/12] fix: install guide points at real tags; qualify the cosign claim; trivy exp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bonnyr-f5 BLOCK review of #183. All findings reproduced and confirmed. BLOCKER — the install guide's happy path 404'd: the registry was corrected to ghcr.io/f5devcentral but BNK_FORGE_VERSION stayed 'customer-build', a jlcode-tech- era tag no workflow builds. Every pull returned manifest unknown. Switched all five sites to the tags the release actually publishes (rolling 'latest'; pin example now a real version '3.1.6' instead of the dead '3.0.1-cb.'). MAJOR — DOCKER.md's blanket "images are signed" is false for 100% of currently- published images: release-publish is gated on a successful final/manual release, and both main-branch runs failed before it. Qualified: signed "from v4.0.0 onward (the first release cut through the signing pipeline)". MINOR — roadmap.yaml:219 was a LIVE in-progress row still naming ghcr.io/jlcode- tech (not the dated historical prose the PR body claimed); pointed it at f5devcentral and regenerated ROADMAP.md/roadmap.html. Added trivy 'exp:2026-09-12' to CVE-2026-7598 so the revisit is machine-enforced, not a prose deadline. Acknowledged (documented on the PR): the roadmap issue links >#191 are upstream numbers absent from this squashed mirror; the PR-body claim about MIN_UPGRADE_FROM is stale (the file correctly reads v3.1.6). Merge #183 LAST (after #186 and #188). Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- .trivyignore | 2 +- docs/DOCKER.md | 2 +- docs/ROADMAP.md | 2 +- docs/roadmap.yaml | 2 +- user-pack/install-guide.html | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.trivyignore b/.trivyignore index c14b2bf..e095ee3 100644 --- a/.trivyignore +++ b/.trivyignore @@ -41,7 +41,7 @@ CVE-2026-33186 # available and we're still ignoring it. # Tracked: memory/followup_trivyignore_cve_2026_7598_revisit.md # Added: 2026-05-12 -CVE-2026-7598 +CVE-2026-7598 exp:2026-09-12 # CVE-2026-42010: GnuTLS Authentication Bypass via NUL Character in DN parsing # Affects: libgnutls30t64 in our Debian Trixie base image (3.8.9-3+deb13u2) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index a84a0ee..537592e 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -58,7 +58,7 @@ docker build --target worker --build-arg INSTALL_INFRACOST=true -t bnk-forge-wor ## Keyless Image Signing, SBOM, and Provenance -BNK Forge images published to the registry are signed with **keyless cosign** (Sigstore Fulcio + +BNK Forge images published **from v4.0.0 onward** (the first release cut through the signing pipeline) are signed with **keyless cosign** (Sigstore Fulcio + Rekor transparency log). No long-lived signing key is stored — the signature is bound to the OIDC identity of whoever ran the publish script at the time of signing. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 8a1104b..6997546 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -28,7 +28,7 @@ Source sweep: memories + ADRs (D-001…D-028) + GitHub issues + open PRs, 2026-0 | **Ops: MCP service account + celery-beat healthcheck** | ✅ Shipped | [PR #214](https://github.com/f5devcentral/bnk-forge/issues/214) | Shipped. PR #214 merged. Dedicated non-human `mcp` account (admin-rotation no longer breaks MCP auth) + mtime-freshness beat healthcheck. | | **Benchmark experience / security hardening** | 🟡 In progress | [PR #211](https://github.com/f5devcentral/bnk-forge/issues/211) · [PR #251](https://github.com/f5devcentral/bnk-forge/issues/251) · [PR #282](https://github.com/f5devcentral/bnk-forge/issues/282) · [PR #286](https://github.com/f5devcentral/bnk-forge/issues/286) · [#294](https://github.com/f5devcentral/bnk-forge/issues/294) | PR #282 (scenario override guard security fix) + PR #286 (remote agent-host provisioning + built-in agent + Slice-4 auth + ported tests) in review. PR #251 (authz/WS-auth/atomic-claim/TLS+SSRF) folds into #211; gated on a maintainer driving #211→staging. #294 (benchmarks page IA + New Run wizard port) deferred pending user decision. | | **GHCR customer-build publish target + postgres-backup compose drift fix** | ✅ Shipped | [PR #242](https://github.com/f5devcentral/bnk-forge/issues/242) · [PR #243](https://github.com/f5devcentral/bnk-forge/issues/243) | Shipped. PR #242 (postgres-backup compose drift fix) + PR #243 (GHCR customer-build publish target) merged. | -| **Multi-arch image publish + dist uninstall-purge fix + install messaging** | 🟡 In progress | [PR #285](https://github.com/f5devcentral/bnk-forge/issues/285) · [PR #288](https://github.com/f5devcentral/bnk-forge/issues/288) | PR #285 (push-customer-build-multiarch target: amd64+arm64 to ghcr.io/jlcode-tech) + PR #288 (uninstall --purge deletes wrong compose volumes fix + stale install messaging) in review. | +| **Multi-arch image publish + dist uninstall-purge fix + install messaging** | 🟡 In progress | [PR #285](https://github.com/f5devcentral/bnk-forge/issues/285) · [PR #288](https://github.com/f5devcentral/bnk-forge/issues/288) | PR #285 (push-customer-build-multiarch target: amd64+arm64 to ghcr.io/f5devcentral) + PR #288 (uninstall --purge deletes wrong compose volumes fix + stale install messaging) in review. | | **Alembic migration deduplication (v2_131)** | ✅ Shipped | [PR #283](https://github.com/f5devcentral/bnk-forge/issues/283) | Shipped. PR #283 merged. Deduped v2_130 collision (benchmark_run_groups renumbered → v2_131); broken staging migration head fixed. | | **Release automation — RC-on-staging / final-on-main (conventional-commit bumps)** | 🟡 In progress | [PR #297](https://github.com/f5devcentral/bnk-forge/issues/297) | PR #297 open; team decision pending — may be closed in favor of manual release flow. | | **D-021 — existing-proxy discovery & migration to BNK (P1+P2+P3)** | ✅ Shipped | [#233](https://github.com/f5devcentral/bnk-forge/issues/233) · [PR #276](https://github.com/f5devcentral/bnk-forge/issues/276) · ADR D-021 | Shipped. PR #276 consolidated (closes epic #233). Proxy discovery, migration path to BNK, P1/P2/P3 complete. | diff --git a/docs/roadmap.yaml b/docs/roadmap.yaml index 73252f0..de9f9ec 100644 --- a/docs/roadmap.yaml +++ b/docs/roadmap.yaml @@ -216,7 +216,7 @@ sections: refs: - 'PR #285' - 'PR #288' - note: 'PR #285 (push-customer-build-multiarch target: amd64+arm64 to ghcr.io/jlcode-tech) + PR #288 (uninstall --purge deletes wrong compose volumes fix + stale install messaging) in review.' + note: 'PR #285 (push-customer-build-multiarch target: amd64+arm64 to ghcr.io/f5devcentral) + PR #288 (uninstall --purge deletes wrong compose volumes fix + stale install messaging) in review.' group: Top of queue — PRs open (CI-green) - title: Alembic migration deduplication (v2_131) status: shipped diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 06c3820..2d86d88 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -113,7 +113,7 @@ F5

      BNK Forge — Install Guide

      -
      Public registry edition  |  customer-build distribution  |  registry: ghcr.io/f5devcentral
      +
      Public registry edition  |  current-release distribution  |  registry: ghcr.io/f5devcentral
      @@ -172,8 +172,8 @@

      Step 2 — Configure

      BNK_FORGE_VERSION - customer-build - Rolling latest build. To pin a specific build, use a tag like 3.0.1-cb.<sha>. + latest + Rolling latest release. To pin a specific version, use its tag, e.g. 3.1.6. POSTGRES_PASSWORD @@ -300,7 +300,7 @@

      Verify the stack is healthy

      Updating to a newer build

      -

      Because BNK_FORGE_VERSION=customer-build is a rolling tag, updating is simple. +

      Because BNK_FORGE_VERSION=latest is a rolling tag, updating is simple. From the install directory:

      # Recommended — uses the installer for any migration steps:
      @@ -349,8 +349,8 @@ 

      Troubleshooting

      - BNK Forge install guide — generated for customer-build distribution  ·  - Registry: ghcr.io/f5devcentral  ·  Tag: customer-build + BNK Forge install guide — generated for the current release  ·  + Registry: ghcr.io/f5devcentral  ·  Tag: latest
      From 49e0d3ad7879144a81835d7bd03546a61487ae00 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 20:47:56 -0500 Subject: [PATCH 08/12] docs: CHANGELOG upgrade note for MCP_SERVICE_PASSWORD; fix dist 404 defaults; USER 1000 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bonnyr-f5 round-2 REVISE of #183. MAJOR — the fail-closed MCP_SERVICE_PASSWORD change (#188) makes every existing staging/production install SystemExit on upgrade (they all ship changeme/ mcp-service-changeme), and no PR documented it. #183 owns CHANGELOG.md, so the 4.0.0 heads-up now carries that upgrade note alongside the container-runner one, and no longer dangles a "see the 4.0.0 entry" reference to a section that doesn't exist yet. MINOR — USER 65532 in the heads-up can't write a 1000:1000 workspace; recommend USER 1000. dist/.env.example shipped ghcr.io/your-org + BNK_FORGE_VERSION=3.0.1 (a verified 404); fixed to ghcr.io/f5devcentral + latest. Acknowledged (follow-up): trivy exp: on all entries + drop .trivyignore from paths-ignore; the DOCKER.md local-signing verify cert-identity/issuer pairing; the cosign identity regexp anchoring; the roadmap 'merged' chip. Merge #183 LAST. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- CHANGELOG.md | 17 +++++++++++++---- dist/.env.example | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2945e3..e5562f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,19 @@ container-runner hardening series (#2, #123, #161) and the ADR-424 bare-metal/DP work — is part of 4.0.0, not v3.1.6, and is recorded under the 4.0.0 entry when that release is cut. -> **Heads-up for the 4.0.0 upgrade:** the container runner's non-root gate now -> refuses *named* users — an image using the distroless-standard `USER nonroot` -> is rejected. Switch it to a numeric uid (`USER 65532`, or `USER 1000`). See -> the 4.0.0 entry for the full note. +> **Heads-up for the 4.0.0 upgrade — two breaking changes:** +> +> 1. **Container runner non-root gate:** it now refuses *named* users — an image +> using the distroless-standard `USER nonroot` is rejected. Switch it to a +> numeric uid. Use **`USER 1000`** (it can write the workspace, which is +> chowned `1000:1000`); `USER 65532` clears the gate but cannot write the +> workspace without a relaxed mount mode. +> 2. **`MCP_SERVICE_PASSWORD` is now required (bonnyr-f5 #188):** in +> staging/production the backend refuses to boot if it is unset or still the +> shipped default (`changeme` / `mcp-service-changeme`). Every existing +> install has one of those values, so **set `MCP_SERVICE_PASSWORD` to a real +> secret (the same value the MCP server gets as `BNK_FORGE_PASSWORD`) before +> upgrading**, or the stack will `SystemExit` at startup. ## v3.0.1 — 3.0.x line diff --git a/dist/.env.example b/dist/.env.example index 0eaa12b..6584d7a 100644 --- a/dist/.env.example +++ b/dist/.env.example @@ -15,8 +15,8 @@ COMPOSE_PROJECT_NAME=bnk-forge # ── Container Registry ────────────────────────────────────────────────────── # Where to pull BNK Forge images from (no trailing slash) -BNK_FORGE_REGISTRY=ghcr.io/your-org -BNK_FORGE_VERSION=3.0.1 +BNK_FORGE_REGISTRY=ghcr.io/f5devcentral +BNK_FORGE_VERSION=latest # ── Database ──────────────────────────────────────────────────────────────── POSTGRES_PASSWORD=bnkforge_dev_password From ad2a790d54bf4555cd21e776b1e9872357feb15d Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 23:24:32 -0500 Subject: [PATCH 09/12] docs(install-guide): correct First-login and MCP creds to this tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-3 BLOCK: the guide instructed credential routes that fail on the tree it ships in. BLOCKER 1 (First login): both documented routes were #186's, absent here. DEFAULT_ADMIN_PASSWORD reaches no process (config.py declares no env_file and no compose/helm file wires it), so the seed is always changeme; and /app/keys/initial_admin_password exists nowhere but the guide sentence. The diff had also deleted the only true instruction. Restore the accurate admin/changeme login plus the "change immediately" callout, matching what dist/install.sh and dist/README.md already print in the same tarball. BLOCKER 2 (MCP_PASSWORD): "no default ships / leaving it empty disables MCP" is false — dist/.env.example ships MCP_PASSWORD=changeme and empty resolves to admin/changeme via ${MCP_PASSWORD:-changeme}. Reword to the real scheme: a well-known default to change, re-read each boot, that must match a real BNK Forge user. Drop the inert DEFAULT_ADMIN_PASSWORD row. Every credential the guide now names resolves to code in this tree. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- user-pack/install-guide.html | 39 ++++++++++++------------------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 2d86d88..7b75c06 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -187,16 +187,11 @@

      Step 2 — Configure

      MCP_PASSWORD - required - Set a value — no default ships. Shared by the backend and the MCP server; - leaving it empty disables MCP (the backend logs why). Unlike the two above it is - re-applied on every boot, so you can rotate it later by changing it and restarting. - - - DEFAULT_ADMIN_PASSWORD - your choice (recommended) - Initial admin login password. If you leave it unset, a random one is - generated on first launch (see Step 4). Either way you must change it on first login. + your choice + Change from the well-known default. Credential the bundled MCP server uses to + authenticate to BNK Forge; it must match a real BNK Forge user (the default + MCP_USERNAME is admin). It is re-read from .env + on every boot, so if you change that user's password, update this to match and restart. @@ -205,9 +200,9 @@

      Step 2 — Configure

      POSTGRES_PASSWORD and REDIS_PASSWORD replace well-known placeholder defaults, and they are baked in when the database and cache first initialize — get them right before running the installer, as they cannot be changed - easily afterward. MCP_PASSWORD has no default and must be set (empty - disables MCP); unlike the other two it is reconciled on every boot, so it can be - rotated later by editing .env and restarting. + easily afterward. MCP_PASSWORD also ships as a well-known default and should + be changed; unlike the other two it is re-read on every boot, so it can be changed later + by editing .env and restarting. @@ -250,20 +245,12 @@

      Step 4 — First login

      (Firefox). This is expected.

    • -

      Log in as the admin user:
      - Username: admin  /  Password: the value you set for - DEFAULT_ADMIN_PASSWORD in Step 2.

      -
      -
      Didn't set DEFAULT_ADMIN_PASSWORD?
      - If you left it unset, BNK Forge generated a random initial password on first launch and - wrote it to /app/keys/initial_admin_password inside the backend container; - the startup logs also point to it. Retrieve it with: -
      $ docker compose exec backend cat /app/keys/initial_admin_password
      -
      +

      Log in with the default credentials:
      + Username: admin  /  Password: changeme

      -
      You must change the password on first login
      - The admin account is flagged to require a password change — set a strong one when prompted - (or via User menu → Change Password) before doing anything else. +
      Change the admin password immediately
      + The default password is well-known — change it as your very first action after login, + via User menu → Change Password, before doing anything else.
    • From a84ead1aa302cf6e901f9c9d4c80632ee8bb6ad4 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Thu, 20 Aug 2026 23:39:07 -0500 Subject: [PATCH 10/12] docs+dist: align USER guidance, fix dist 404 class, upgrade prereqs, trivy expiries Round-3 follow-ups from bonnyr-f5's review. USER 65532 -> USER 1000 (three sites now agree): the workspace is chowned 1000:1000, so only uid 1000 both clears the non-root gate and can write it; 65532 clears the gate but cannot write. Corrected the CHANGELOG note (dropping the non-existent "relaxed mount mode" escape), the runtime refusal message in container_runner.py, and the authoring guide. The refusal string is not asserted by any test ("runs as root" is preserved), and 65532 remains a valid gate-acceptance fixture in the unit tests. dist 404 class fix: replaced the stale private-org registry in the shipped artifacts (dist/docker-compose.yml, dist/README.md) and the pull-defaults in scripts/ibm_cloud_bnk_forge.sh with ghcr.io/f5devcentral, bumped stale 3.0.1 examples to 3.1.6, dropped the obsolete "Authenticate to registry" section (pulls are anonymous), and replaced ghcr.io/jlcode-tech with f5devcentral in the roadmap. Source-side push/placeholder your-org values are intentionally left (they are user-substituted, not the 404 bug) -- see PR comment. Upgrade prerequisites: the install guide's "Updating" walkthrough now surfaces the 4.0.0 breaking changes it previously omitted -- registry/version reconciliation, the numeric-USER artifact requirement, and the MCP_SERVICE_PASSWORD boot requirement -- framed as forward-looking 4.0.0 notes, consistent with the CHANGELOG heads-up and the merge-ordering commitment. .trivyignore: added exp:2026-11-30 review-by dates to the ten entries that lacked one, and documented that exp: dates force a re-check. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- .trivyignore | 22 ++++----- CHANGELOG.md | 7 +-- .../services/execution/container_runner.py | 3 +- dist/README.md | 45 +++++++------------ dist/docker-compose.yml | 20 ++++----- ...er modules and blueprints for BNK Forge.md | 4 +- docs/ROADMAP.md | 2 +- docs/roadmap.yaml | 2 +- scripts/ibm_cloud_bnk_forge.sh | 14 +++--- user-pack/install-guide.html | 19 ++++++++ 10 files changed, 75 insertions(+), 63 deletions(-) diff --git a/.trivyignore b/.trivyignore index e095ee3..829c581 100644 --- a/.trivyignore +++ b/.trivyignore @@ -4,26 +4,28 @@ # projects rebuild with a patched Go version. # # Review this file periodically and remove entries when upstream fixes are available. +# Each entry carries an `exp:` review-by date — Trivy drops the suppression after it, +# forcing a re-check. Extend an entry only after re-confirming no upstream fix exists. # CVE-2025-68121: Go stdlib crypto/tls - Unexpected session resumption # Fixed in Go >= 1.24.13 / 1.25.7 / 1.26.0-rc.3 # Affects: helm (Go 1.25.0), kubectl, tofu (Go 1.25.6), infracost (Go 1.25.4) # All current latest releases use Go < 1.25.7 — no upstream fix available yet # Added: 2026-02-23 -CVE-2025-68121 +CVE-2025-68121 exp:2026-11-30 # CVE-2024-45337: golang.org/x/crypto/ssh - Misuse of ServerConfig.PublicKeyCallback # Present in infracost binary's bundled dependencies # Not exploitable in our context (we don't run an SSH server via infracost) # Added: 2026-02-23 -CVE-2024-45337 +CVE-2024-45337 exp:2026-11-30 # CVE-2026-33186: gRPC authorization bypass (google.golang.org/grpc < 1.79.3) # Affects: helm and tofu binaries in Docker image (grpc v1.76.0) # Status: Waiting for upstream helm/tofu releases with fixed grpc # Tracked: GitHub issue #50 # Added: 2026-04-15 -CVE-2026-33186 +CVE-2026-33186 exp:2026-11-30 # CVE-2026-7598: libssh2 — integer overflow via large username/password # Affects: libssh2-1t64 1.11.1-1 in Debian trixie base image @@ -54,7 +56,7 @@ CVE-2026-7598 exp:2026-09-12 # REVISIT: monthly via https://security-tracker.debian.org/tracker/CVE-2026-42010 # Pattern mirror of CVE-2026-33845 / CVE-2026-7598 suppressions. # Added: 2026-05-14 -CVE-2026-42010 +CVE-2026-42010 exp:2026-11-30 # CVE-2026-42496: perl — Archive::Tar < 3.08 extracts symlinks unsafely # CVE-2026-8376: perl — heap buffer overflow in the interpreter (<= 5.43.10) @@ -74,8 +76,8 @@ CVE-2026-42010 # https://security-tracker.debian.org/tracker/CVE-2026-8376 # Drop once Debian ships a trixie point-release with patched perl. # Added: 2026-06-02 -CVE-2026-42496 -CVE-2026-8376 +CVE-2026-42496 exp:2026-11-30 +CVE-2026-8376 exp:2026-11-30 # CVE-2026-13221: libperl5.40 — silently incorrect results in Perl <= 5.43.9 # Affects: libperl5.40 5.40.1-6 in the python:3.11-slim (Debian trixie) base image @@ -85,7 +87,7 @@ CVE-2026-8376 # REVISIT: monthly via https://security-tracker.debian.org/tracker/CVE-2026-13221 # drop once Debian ships a trixie update with a patched libperl5.40. # Added: 2026-07-15 -CVE-2026-13221 +CVE-2026-13221 exp:2026-11-30 # CVE-2026-60002: openssh-client — memory corruption in SSH client # Affects: openssh-client in the python:3.11-slim (Debian trixie) base image @@ -96,7 +98,7 @@ CVE-2026-13221 # REVISIT: monthly via https://security-tracker.debian.org/tracker/CVE-2026-60002 # drop once Debian ships a trixie update with a patched openssh-client. # Added: 2026-07-15 -CVE-2026-60002 +CVE-2026-60002 exp:2026-11-30 # CVE-2026-33845: GnuTLS DTLS — reachable-assert / auth bypass in DN parsing # Affects: libgnutls30t64 in our Debian Trixie base image (3.8.9-3+deb13u2) @@ -110,7 +112,7 @@ CVE-2026-60002 # Check: https://security-tracker.debian.org/tracker/CVE-2026-33845 # Tracked: GitHub issue #103 # Added: 2026-05-06 -CVE-2026-33845 +CVE-2026-33845 exp:2026-11-30 # CVE-2026-57433: perl Storable signed-integer flaw (Storable < 3.41) # Affects: libperl5.40, perl-base (5.40.1-6) in our Debian Trixie base image. @@ -122,4 +124,4 @@ CVE-2026-33845 # trixie-security. Check: https://security-tracker.debian.org/tracker/CVE-2026-57433 # Tracked: GitHub issue #492 # Added: 2026-07-22 -CVE-2026-57433 +CVE-2026-57433 exp:2026-11-30 diff --git a/CHANGELOG.md b/CHANGELOG.md index e5562f8..6af3b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,10 @@ that release is cut. > > 1. **Container runner non-root gate:** it now refuses *named* users — an image > using the distroless-standard `USER nonroot` is rejected. Switch it to a -> numeric uid. Use **`USER 1000`** (it can write the workspace, which is -> chowned `1000:1000`); `USER 65532` clears the gate but cannot write the -> workspace without a relaxed mount mode. +> numeric uid. Use **`USER 1000`**: the workspace is mounted from the host +> and chowned `1000:1000`, so uid 1000 is the only value that clears the gate +> *and* can write it. A higher uid such as `65532` passes the non-root gate but +> cannot write the workspace, so the step fails on its first write. > 2. **`MCP_SERVICE_PASSWORD` is now required (bonnyr-f5 #188):** in > staging/production the backend refuses to boot if it is unset or still the > shipped default (`changeme` / `mcp-service-changeme`). Every existing diff --git a/backend/services/execution/container_runner.py b/backend/services/execution/container_runner.py index 38b2438..ef1434e 100644 --- a/backend/services/execution/container_runner.py +++ b/backend/services/execution/container_runner.py @@ -649,7 +649,8 @@ def _fail(message: str, stdout: str = "") -> StepResult: f"Artifact image {spec.image_digest} runs as root " f"(USER={image_user or ''}). Refusing to start it: the workspace is " f"mounted from the host, so a root container is a host-root write primitive. " - f"Rebuild the image with a NUMERIC non-root USER (e.g. `USER 65532`). " + f"Rebuild the image with a NUMERIC non-root USER — `USER 1000` matches " + f"the workspace owner (chowned 1000:1000), so the step can write it. " f"A named user is refused because it cannot be resolved to a uid " f"without the image's own /etc/passwd — `USER toor` may well be uid 0. " f"The Kubernetes substrate already enforces this: runAsNonRoot is " diff --git a/dist/README.md b/dist/README.md index 7c0c011..3dafbbe 100644 --- a/dist/README.md +++ b/dist/README.md @@ -3,7 +3,7 @@ ## Prerequisites - **Docker Engine 24+** with **Docker Compose v2.24+** -- Access to the BNK Forge container registry (if private) +- Network access to `ghcr.io` (images are public — no registry login required) - 4 GB RAM minimum (8 GB recommended) - 10 GB disk space @@ -12,8 +12,8 @@ ### 1. Download and extract ```bash -tar xzf bnk-forge-3.0.1.tar.gz -cd bnk-forge-3.0.1 +tar xzf bnk-forge-3.1.6.tar.gz +cd bnk-forge-3.1.6 ``` ### 2. Configure @@ -27,25 +27,12 @@ nano .env # Set BNK_FORGE_REGISTRY and passwords | Variable | Description | Example | |---|---|---| -| `BNK_FORGE_REGISTRY` | Container registry URL (no trailing slash) | `ghcr.io/your-org` | -| `BNK_FORGE_VERSION` | Image version tag | `3.0.1` | +| `BNK_FORGE_REGISTRY` | Container registry URL (no trailing slash) | `ghcr.io/f5devcentral` (public) | +| `BNK_FORGE_VERSION` | Image version tag | `3.1.6` | | `POSTGRES_PASSWORD` | PostgreSQL password | *(change for production)* | | `REDIS_PASSWORD` | Redis password | *(change for production)* | -### 3. Authenticate to registry (if private) - -```bash -# GitHub Container Registry -echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin - -# Docker Hub -docker login - -# AWS ECR -aws ecr get-login-password | docker login --username AWS --password-stdin ACCOUNT.dkr.ecr.REGION.amazonaws.com -``` - -### 4. Install +### 3. Install **Linux server** (host networking — production): ```bash @@ -59,7 +46,7 @@ chmod +x install.sh ./install.sh --local ``` -### 5. Access +### 4. Access - **Mac/Windows (`--local`):** open **https://localhost** - **Linux server:** open **https://\** — the installer prints the exact URL at the end @@ -183,7 +170,7 @@ gunzip -c backup_20260417.sql.gz | docker exec -i bnk-forge-postgres psql -U bnk ## File Structure ``` -bnk-forge-3.0.1/ +bnk-forge-3.1.6/ ├── docker-compose.yml # Main compose (Linux server — host networking) ├── docker-compose.local.yml # Overlay for macOS/Windows (bridge networking) ├── .env.example # Configuration template @@ -235,17 +222,17 @@ This creates `dist/bnk-forge-VERSION.tar.gz` containing all files needed for ins echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin # Build + push all images for amd64 + arm64 (default) -make push-images BNK_FORGE_REGISTRY=ghcr.io/your-org +make push-images BNK_FORGE_REGISTRY=ghcr.io/f5devcentral # Or push only amd64 (faster, if you don't need ARM) -make push-images BNK_FORGE_REGISTRY=ghcr.io/your-org PLATFORMS=linux/amd64 +make push-images BNK_FORGE_REGISTRY=ghcr.io/f5devcentral PLATFORMS=linux/amd64 ``` -This uses `docker buildx build --push` to build all 6 images (api, worker, beat, frontend, proxy, mcp) for both architectures and push **multi-arch manifest lists** to the registry. Each tag (e.g., `bnk-forge-api:3.0.1`) is a manifest that Docker automatically resolves to the correct platform on `docker pull`. +This uses `docker buildx build --push` to build all 6 images (api, worker, beat, frontend, proxy, mcp) for both architectures and push **multi-arch manifest lists** to the registry. Each tag (e.g., `bnk-forge-api:3.1.6`) is a manifest that Docker automatically resolves to the correct platform on `docker pull`. **Verify the manifest:** ```bash -docker manifest inspect ghcr.io/your-org/bnk-forge-api:3.0.1 +docker manifest inspect ghcr.io/f5devcentral/bnk-forge-api:3.1.6 ``` You should see entries for both `linux/amd64` and `linux/arm64`. @@ -266,8 +253,8 @@ gh release create v${VERSION} dist/bnk-forge-${VERSION}.tar.gz \ ### What `gh release create` does -1. Creates a Git tag (`v3.0.1`) on the current commit -2. Creates a GitHub Release page at `https://github.com/your-org/bnk-forge/releases/tag/v3.0.1` +1. Creates a Git tag (`v3.1.6`) on the current commit +2. Creates a GitHub Release page at `https://github.com/f5devcentral/bnk-forge/releases/tag/v3.1.6` 3. Uploads the tarball as a downloadable release asset ### End-user download URL @@ -276,8 +263,8 @@ After publishing, users can download and install with: ```bash # Download from GitHub Releases -curl -L https://github.com/your-org/bnk-forge/releases/download/v3.0.1/bnk-forge-3.0.1.tar.gz | tar xz -cd bnk-forge-3.0.1 +curl -L https://github.com/f5devcentral/bnk-forge/releases/download/v3.1.6/bnk-forge-3.1.6.tar.gz | tar xz +cd bnk-forge-3.1.6 ./install.sh ``` diff --git a/dist/docker-compose.yml b/dist/docker-compose.yml index 286b7de..36456d0 100644 --- a/dist/docker-compose.yml +++ b/dist/docker-compose.yml @@ -11,7 +11,7 @@ # # Prerequisites: # - Docker Engine 24+ with Compose v2.24+ -# - Authenticated to the container registry (if private) +# - Network access to ghcr.io (images are public — no registry login required) # # Configuration: # Copy .env.example to .env and set your passwords before first start. @@ -19,8 +19,8 @@ # ── Registry configuration ────────────────────────────────────────────────── # Set BNK_FORGE_REGISTRY and BNK_FORGE_VERSION in .env or environment: -# BNK_FORGE_REGISTRY=ghcr.io/your-org (no trailing slash) -# BNK_FORGE_VERSION=3.0.1 (or "latest") +# BNK_FORGE_REGISTRY=ghcr.io/f5devcentral (no trailing slash) +# BNK_FORGE_VERSION=3.1.6 (or "latest") x-backend-env: &backend-env DATABASE_URL: postgresql://bnkforge:${POSTGRES_PASSWORD:-bnkforge_dev_password}@localhost:5432/bnkforge @@ -160,7 +160,7 @@ services: memory: 32M backend: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-api:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-api:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-backend network_mode: host logging: *default-logging @@ -201,7 +201,7 @@ services: memory: 256M celery-worker: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-celery-worker network_mode: host logging: *default-logging @@ -233,7 +233,7 @@ services: memory: 512M celery-worker-2: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-celery-worker-2 network_mode: host logging: *default-logging @@ -265,7 +265,7 @@ services: memory: 512M celery-beat: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-beat:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-beat:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-celery-beat network_mode: host logging: *default-logging @@ -295,7 +295,7 @@ services: memory: 64M frontend: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-frontend:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-frontend:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-frontend network_mode: host logging: *default-logging @@ -321,7 +321,7 @@ services: memory: 32M proxy: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-proxy:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-proxy:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-proxy network_mode: host logging: *default-logging @@ -347,7 +347,7 @@ services: memory: 32M mcp: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-mcp:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-mcp:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-mcp network_mode: host logging: *default-logging diff --git a/docs/How to write CI container runner modules and blueprints for BNK Forge.md b/docs/How to write CI container runner modules and blueprints for BNK Forge.md index 5351f98..2caf399 100644 --- a/docs/How to write CI container runner modules and blueprints for BNK Forge.md +++ b/docs/How to write CI container runner modules and blueprints for BNK Forge.md @@ -658,7 +658,9 @@ The container engine is deliberately constrained: decimal uid — uid **1000** matches the workspace owner and is the safe choice. A **named** user such as `USER nonroot` (the distroless default) is now **refused**: a name can't be resolved to a uid without the image's own `/etc/passwd`, so it can't be proven non-root. - If you were on `USER nonroot`, switch to its numeric equivalent `USER 65532` (or `USER 1000`). + If you were on `USER nonroot`, switch to `USER 1000` — it matches the workspace owner (chowned + `1000:1000`), so your state writes under `mount_path` succeed. A higher uid such as `65532` clears + the non-root gate but cannot write the host-mounted workspace. Forge does **not** silently remap you with `--user`. This mirrors Kubernetes `runAsNonRoot`, which the Kubernetes runner applies to the same artifacts. - **A dedicated network** — steps attach to the `bnk-forge-artifacts` bridge network rather diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 6997546..555a229 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -7,7 +7,7 @@ Source sweep: memories + ADRs (D-001…D-028) + GitHub issues + open PRs, 2026-06-12. **Deep doc sweep 2026-06-03:** swept `docs/specs/`, sprint plans, and strategic docs; statuses **code-verified** before assignment (many specs that read as "proposed" are in fact already built — see §10). New gap issues from the sweep: #216/#217/#218. **2026-06-09 sync:** D-021/D-022 fleet epics shipped (PRs #276/#277); D-027 zero-toast shipped (PRs #260/#261/#278); D-028 unified blueprint catalog shipped (PR #274); D-001 Phase 3 / D-019 E1/E3/E6 / Ops MCP+celery / AWS cred-expiry UX all shipped. D-023 (classic BIG-IP) + D-020 (F5 design-system) + benchmark experience remain in-flight. -**2026-06-12 sync:** cb-rebuild → staging de-stacking COMPLETE; new `customer-build` integration line live on localhost (staging + all open PRs + customer deltas); multi-arch images published `ghcr.io/jlcode-tech 3.1.6-cb.72b29dbb` + rolling `customer-build`. D-023 P1-P3 shipped (PR #280); alembic dedupe shipped (PR #283). 13 PRs in review: #282 (scenario override guard), #284 (BNK registry-driven GA/ReleaseRegistry), #285 (multiarch publish), #286 (benchmark remote agent-host + Slice-4 auth), #287 (AI-Analyzer rename + Migration tab fix), #288 (dist uninstall-purge fix), #290 (D-023 P4 CIS coverage), #291 (Dashboard Command Center fleet section), #292 (bfb-cache atime/LRU fix), #293 (runtime brand flag #289), #295 (CIS IngressClass kind-aware classify), #296 (top-level Infrastructure section), #297 (release automation — team decision pending). D-020 reskin branch fully rebuilt 2026-06-10/11 (complete reskin + anvil ForgeLogo general rebrand). +**2026-06-12 sync:** cb-rebuild → staging de-stacking COMPLETE; new `customer-build` integration line live on localhost (staging + all open PRs + customer deltas); multi-arch images published `ghcr.io/f5devcentral 3.1.6-cb.72b29dbb` + rolling `customer-build`. D-023 P1-P3 shipped (PR #280); alembic dedupe shipped (PR #283). 13 PRs in review: #282 (scenario override guard), #284 (BNK registry-driven GA/ReleaseRegistry), #285 (multiarch publish), #286 (benchmark remote agent-host + Slice-4 auth), #287 (AI-Analyzer rename + Migration tab fix), #288 (dist uninstall-purge fix), #290 (D-023 P4 CIS coverage), #291 (Dashboard Command Center fleet section), #292 (bfb-cache atime/LRU fix), #293 (runtime brand flag #289), #295 (CIS IngressClass kind-aware classify), #296 (top-level Infrastructure section), #297 (release automation — team decision pending). D-020 reskin branch fully rebuilt 2026-06-10/11 (complete reskin + anvil ForgeLogo general rebrand). **Human view (clickable):** `docs/roadmap.html` · **Contribution flow:** `docs/ROADMAP_PROCESS.md` · (local per-clone agent queue: `.agent/backlog/BACKLOG.md`). --- diff --git a/docs/roadmap.yaml b/docs/roadmap.yaml index de9f9ec..cab7ad4 100644 --- a/docs/roadmap.yaml +++ b/docs/roadmap.yaml @@ -55,7 +55,7 @@ meta: **2026-06-09 sync:** D-021/D-022 fleet epics shipped (PRs #276/#277); D-027 zero-toast shipped (PRs #260/#261/#278); D-028 unified blueprint catalog shipped (PR #274); D-001 Phase 3 / D-019 E1/E3/E6 / Ops MCP+celery / AWS cred-expiry UX all shipped. D-023 (classic BIG-IP) + D-020 (F5 design-system) + benchmark experience remain in-flight. - **2026-06-12 sync:** cb-rebuild → staging de-stacking COMPLETE; new `customer-build` integration line live on localhost (staging + all open PRs + customer deltas); multi-arch images published `ghcr.io/jlcode-tech 3.1.6-cb.72b29dbb` + rolling `customer-build`. D-023 P1-P3 shipped (PR #280); alembic dedupe shipped (PR #283). 13 PRs in review: #282 (scenario override guard), #284 (BNK registry-driven GA/ReleaseRegistry), #285 (multiarch publish), #286 (benchmark remote agent-host + Slice-4 auth), #287 (AI-Analyzer rename + Migration tab fix), #288 (dist uninstall-purge fix), #290 (D-023 P4 CIS coverage), #291 (Dashboard Command Center fleet section), #292 (bfb-cache atime/LRU fix), #293 (runtime brand flag #289), #295 (CIS IngressClass kind-aware classify), #296 (top-level Infrastructure section), #297 (release automation — team decision pending). D-020 reskin branch fully rebuilt 2026-06-10/11 (complete reskin + anvil ForgeLogo general rebrand). + **2026-06-12 sync:** cb-rebuild → staging de-stacking COMPLETE; new `customer-build` integration line live on localhost (staging + all open PRs + customer deltas); multi-arch images published `ghcr.io/f5devcentral 3.1.6-cb.72b29dbb` + rolling `customer-build`. D-023 P1-P3 shipped (PR #280); alembic dedupe shipped (PR #283). 13 PRs in review: #282 (scenario override guard), #284 (BNK registry-driven GA/ReleaseRegistry), #285 (multiarch publish), #286 (benchmark remote agent-host + Slice-4 auth), #287 (AI-Analyzer rename + Migration tab fix), #288 (dist uninstall-purge fix), #290 (D-023 P4 CIS coverage), #291 (Dashboard Command Center fleet section), #292 (bfb-cache atime/LRU fix), #293 (runtime brand flag #289), #295 (CIS IngressClass kind-aware classify), #296 (top-level Infrastructure section), #297 (release automation — team decision pending). D-020 reskin branch fully rebuilt 2026-06-10/11 (complete reskin + anvil ForgeLogo general rebrand). **Human view (clickable):** `docs/roadmap.html` · **Contribution flow:** `docs/ROADMAP_PROCESS.md` · (local per-clone agent queue: `.agent/backlog/BACKLOG.md`). diff --git a/scripts/ibm_cloud_bnk_forge.sh b/scripts/ibm_cloud_bnk_forge.sh index 39d4ff5..34f562e 100644 --- a/scripts/ibm_cloud_bnk_forge.sh +++ b/scripts/ibm_cloud_bnk_forge.sh @@ -462,7 +462,7 @@ services: restart: unless-stopped backend: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-api:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-api:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-backend network_mode: host logging: *default-logging @@ -495,7 +495,7 @@ services: start_period: 30s celery-worker: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-celery-worker network_mode: host logging: *default-logging @@ -513,7 +513,7 @@ services: restart: unless-stopped celery-worker-2: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-worker:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-celery-worker-2 network_mode: host logging: *default-logging @@ -531,7 +531,7 @@ services: restart: unless-stopped celery-beat: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-beat:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-beat:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-celery-beat network_mode: host logging: *default-logging @@ -547,7 +547,7 @@ services: restart: unless-stopped frontend: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-frontend:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-frontend:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-frontend network_mode: host logging: *default-logging @@ -563,7 +563,7 @@ services: start_period: 10s proxy: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-proxy:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-proxy:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-proxy network_mode: host logging: *default-logging @@ -575,7 +575,7 @@ services: restart: unless-stopped mcp: - image: ${BNK_FORGE_REGISTRY:-ghcr.io/your-org}/bnk-forge-mcp:${BNK_FORGE_VERSION:-latest} + image: ${BNK_FORGE_REGISTRY:-ghcr.io/f5devcentral}/bnk-forge-mcp:${BNK_FORGE_VERSION:-latest} container_name: bnk-forge-mcp network_mode: host logging: *default-logging diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 7b75c06..8406200 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -300,6 +300,25 @@

      Updating to a newer build

      If you pinned a specific build tag in .env, update BNK_FORGE_VERSION to the new tag before running the command above.

      +
      +
      Before upgrading an older install to 4.0.0
      + If your .env predates this release, reconcile it first, or the upgrade will fail + to pull or refuse to boot: +
        +
      • Registry & version. Set BNK_FORGE_REGISTRY=ghcr.io/f5devcentral + (older packages pointed at a private org that no longer resolves), and replace any pinned + BNK_FORGE_VERSION such as 3.0.1 with latest or a + current tag like 3.1.6 — a stale pin pulls a tag that no longer exists.
      • +
      • Non-root artifact images. The container runner now refuses any image whose + USER is root or a named user (e.g. USER nonroot). Rebuild + your own runner images with a numeric USER 1000 before upgrading.
      • +
      • MCP service credential. In staging/production the backend now refuses to boot if + MCP_SERVICE_PASSWORD is unset or still the shipped default. Set it to a real + secret (the same value the MCP server receives as BNK_FORGE_PASSWORD) before + upgrading, or the stack will exit at startup.
      • +
      +
      +

      Uninstall

      From a93d51f8ef654209bf9647b51dde80a2322391b1 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Fri, 21 Aug 2026 02:10:44 -0500 Subject: [PATCH 11/12] docs: make MCP boot-refusal a forward-looking 4.0.0/#188 note; fix MCP-cred 401 trap, operator image, dupes Round-4 review fixes (bonnyr-f5, comment 5366217504): BLOCKER (INV-23/INV-17): CHANGELOG.md and install-guide.html asserted, in the present tense, that the backend "refuses to boot / SystemExit"s on an unset or default MCP_SERVICE_PASSWORD. Verified against the tree: validate_production() (backend/core/config.py:181) checks only JWT/ENCRYPTION keys and ALLOWED_ORIGINS, never MCP_SERVICE_PASSWORD, and the default "mcp-service-changeme" still ships (config.py:103). That boot-time check lives in #188, not here. Reframed both docs as explicitly forward-looking ("from 4.0.0", "via #188", "once that check is in the tree") so #183's docs are true for the tree #183 ships, without dropping the #188 heads-up. Also: - install-guide Step 2: setting MCP_PASSWORD to an independent secret before first login 401s the MCP server against the still-default admin credential (dist wires MCP as admin/changeme). Rewrote the guidance to keep MCP_PASSWORD equal to the admin account password and rotate them together. - restart -> "docker compose up -d" where .env changes must be re-read (a plain restart does not re-read .env). - Documented the 7th signed/published image bnk-forge-operator in docs/DOCKER.md and dist/README.md (release.yml + publish-signed-images.sh build/sign 7). - Removed the duplicated "--user" sentence in the CI-runner module guide. - Removed the orphaned .callout-cred CSS (no element uses it). install-guide.html verified tag-balanced. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- CHANGELOG.md | 15 ++++--- dist/README.md | 2 +- docs/DOCKER.md | 2 +- ...er modules and blueprints for BNK Forge.md | 3 +- user-pack/install-guide.html | 42 ++++++++++--------- 5 files changed, 35 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af3b45..20143e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,12 +22,15 @@ that release is cut. > and chowned `1000:1000`, so uid 1000 is the only value that clears the gate > *and* can write it. A higher uid such as `65532` passes the non-root gate but > cannot write the workspace, so the step fails on its first write. -> 2. **`MCP_SERVICE_PASSWORD` is now required (bonnyr-f5 #188):** in -> staging/production the backend refuses to boot if it is unset or still the -> shipped default (`changeme` / `mcp-service-changeme`). Every existing -> install has one of those values, so **set `MCP_SERVICE_PASSWORD` to a real -> secret (the same value the MCP server gets as `BNK_FORGE_PASSWORD`) before -> upgrading**, or the stack will `SystemExit` at startup. +> 2. **`MCP_SERVICE_PASSWORD` becomes required in 4.0.0 (via bonnyr-f5 #188):** +> starting with 4.0.0 the backend refuses to boot in staging/production if it +> is unset or still a shipped default (`changeme` / `mcp-service-changeme`). +> That boot-time check ships in #188 — it is *not* in the 3.1.x line and is +> called out here only so the upgrade step is ready before #188 lands. Every +> existing install still carries one of those defaults, so before upgrading to +> 4.0.0 **set `MCP_SERVICE_PASSWORD` to a real secret** (the same value the MCP +> server receives as `BNK_FORGE_PASSWORD`); once #188 is in the tree, leaving +> it at a default will `SystemExit` the stack at startup. ## v3.0.1 — 3.0.x line diff --git a/dist/README.md b/dist/README.md index 3dafbbe..0aa4fc3 100644 --- a/dist/README.md +++ b/dist/README.md @@ -228,7 +228,7 @@ make push-images BNK_FORGE_REGISTRY=ghcr.io/f5devcentral make push-images BNK_FORGE_REGISTRY=ghcr.io/f5devcentral PLATFORMS=linux/amd64 ``` -This uses `docker buildx build --push` to build all 6 images (api, worker, beat, frontend, proxy, mcp) for both architectures and push **multi-arch manifest lists** to the registry. Each tag (e.g., `bnk-forge-api:3.1.6`) is a manifest that Docker automatically resolves to the correct platform on `docker pull`. +This uses `docker buildx build --push` to build all 7 images (api, worker, beat, frontend, proxy, mcp, operator) for both architectures and push **multi-arch manifest lists** to the registry. Each tag (e.g., `bnk-forge-api:3.1.6`) is a manifest that Docker automatically resolves to the correct platform on `docker pull`. **Verify the manifest:** ```bash diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 537592e..65944d3 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -119,7 +119,7 @@ cosign verify-attestation \ Apply the same commands to the other image names: `bnk-forge-worker`, `bnk-forge-beat`, `bnk-forge-frontend`, `bnk-forge-proxy`, -`bnk-forge-mcp`. +`bnk-forge-mcp`, `bnk-forge-operator`. ### OCI Labels diff --git a/docs/How to write CI container runner modules and blueprints for BNK Forge.md b/docs/How to write CI container runner modules and blueprints for BNK Forge.md index 2caf399..48a4e79 100644 --- a/docs/How to write CI container runner modules and blueprints for BNK Forge.md +++ b/docs/How to write CI container runner modules and blueprints for BNK Forge.md @@ -661,8 +661,7 @@ The container engine is deliberately constrained: If you were on `USER nonroot`, switch to `USER 1000` — it matches the workspace owner (chowned `1000:1000`), so your state writes under `mount_path` succeed. A higher uid such as `65532` clears the non-root gate but cannot write the host-mounted workspace. - Forge does **not** silently remap you with `--user`. This mirrors Kubernetes `runAsNonRoot`, - which the Kubernetes runner applies to the same artifacts. + This mirrors Kubernetes `runAsNonRoot`, which the Kubernetes runner applies to the same artifacts. - **A dedicated network** — steps attach to the `bnk-forge-artifacts` bridge network rather than the daemon's default bridge, so artifact containers don't sit alongside unrelated containers. Egress still works (you can reach cloud control planes); you just don't share diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 8406200..83dfbbb 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -63,12 +63,6 @@ code{background:#eef0f2;border:1px solid var(--bd);border-radius:3px;padding:1px 5px; font-size:12.5px;color:var(--ink2);font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;} - /* ── Callout: credentials (red left border) ── */ - .callout-cred{background:#fff5f5;border-left:4px solid var(--f5red);border-radius:0 6px 6px 0; - padding:14px 18px;margin:14px 0;box-shadow:0 1px 2px rgba(20,30,40,.04);} - .callout-cred .callout-title{font-weight:700;color:var(--f5red);font-size:13px; - text-transform:uppercase;letter-spacing:.6px;margin-bottom:6px;} - /* ── Callout: warning (amber) ── */ .callout-warn{background:var(--warn-bg);border-left:4px solid var(--warn-bd);border-radius:0 6px 6px 0; padding:14px 18px;margin:14px 0;color:var(--warn);} @@ -100,7 +94,7 @@ @media print{ header{-webkit-print-color-adjust:exact;print-color-adjust:exact;} pre{white-space:pre-wrap;word-break:break-all;} - .callout-cred,.callout-warn,.callout-info{-webkit-print-color-adjust:exact;print-color-adjust:exact;} + .callout-warn,.callout-info{-webkit-print-color-adjust:exact;print-color-adjust:exact;} body{font-size:13px;} main{padding:10px 20px 30px;} } @@ -187,11 +181,17 @@

      Step 2 — Configure

      MCP_PASSWORD - your choice - Change from the well-known default. Credential the bundled MCP server uses to - authenticate to BNK Forge; it must match a real BNK Forge user (the default - MCP_USERNAME is admin). It is re-read from .env - on every boot, so if you change that user's password, update this to match and restart. + match the admin password + Must equal the password of the user in MCP_USERNAME. Credential the + bundled MCP server uses to authenticate to BNK Forge; it must match a real BNK Forge user + (the default MCP_USERNAME is admin, whose initial password is + changeme). Leave it at changeme for the first boot so it matches + the seeded admin account; it is re-read from .env on every boot, so after you + change the admin password (Step 4) set this to the same value and re-run + docker compose up -d (a plain docker compose restart does not + re-read .env). Setting it to an + independent secret before first login makes the MCP server 401 against the still-default + admin credential. @@ -200,9 +200,12 @@

      Step 2 — Configure

      POSTGRES_PASSWORD and REDIS_PASSWORD replace well-known placeholder defaults, and they are baked in when the database and cache first initialize — get them right before running the installer, as they cannot be changed - easily afterward. MCP_PASSWORD also ships as a well-known default and should - be changed; unlike the other two it is re-read on every boot, so it can be changed later - by editing .env and restarting. + easily afterward. MCP_PASSWORD is different: it must always equal the password + of the admin account it authenticates as, so leave it at the shipped changeme + for first boot and rotate it together with the admin password afterward — it is + re-read on every boot, so edit .env and re-run docker compose up -d + to apply the new value (a plain docker compose restart does not re-read + .env). @@ -312,10 +315,11 @@

      Updating to a newer build

    • Non-root artifact images. The container runner now refuses any image whose USER is root or a named user (e.g. USER nonroot). Rebuild your own runner images with a numeric USER 1000 before upgrading.
    • -
    • MCP service credential. In staging/production the backend now refuses to boot if - MCP_SERVICE_PASSWORD is unset or still the shipped default. Set it to a real - secret (the same value the MCP server receives as BNK_FORGE_PASSWORD) before - upgrading, or the stack will exit at startup.
    • +
    • MCP service credential (from 4.0.0). Starting with 4.0.0 the backend refuses to + boot in staging/production if MCP_SERVICE_PASSWORD is unset or still a shipped + default. Set it to a real secret (the same value the MCP server receives as + BNK_FORGE_PASSWORD) before upgrading, or the stack will exit at startup once + that check is in the tree.
    From ee20de0a7a558d125385d59d9082d4897ea939b7 Mon Sep 17 00:00:00 2001 From: John Gruber Date: Fri, 21 Aug 2026 07:32:45 -0500 Subject: [PATCH 12/12] docs: fix inert MCP_SERVICE_PASSWORD upgrade step, disclose seeded mcp admin, commit merge ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-5 review (bonnyr-f5) fixes, verified against this tree: F1 (BLOCKER): the 4.0.0 upgrade step told dist operators to set MCP_SERVICE_PASSWORD, which the dist backend never reads — the dist &backend-env anchor passes no such variable, and config.py declares no env_file (process env only). The dist MCP client authenticates with MCP_PASSWORD (dist/docker-compose.yml:357 -> BNK_FORGE_PASSWORD). Rewrote the step to name MCP_PASSWORD, state MCP_SERVICE_PASSWORD is inert here, and mark the #188 boot-check as forward-looking (from 4.0.0). F2 (Major): disclosed the seeded `mcp` admin-role account whose default password (mcp-service-changeme) is published in the public config.py and is reconciled back on every boot, so it cannot be rotated in this bundle; noted #186 removes it. No longer hidden. F3 (Major): documented that MCP borrows the human admin login only until #186 wires the dedicated `mcp` service account; do not prescribe the human credential as the permanent machine identity. F4 (Major, INV-4): committed the #183 x #186 merge ordering in CHANGELOG (with/after #186 + #188), not only in a PR comment, incl. conflict- resolution guidance for user-pack/install-guide.html. Minors: swept dist/install.sh registry line ("images are public") to match dist/docker-compose.yml; made the dist/README.md end-user download URL an explicit version placeholder (no v3.1.6 release/asset exists); added the valid `merged` status to ROADMAP_PROCESS.md, roadmap-add.py help, and the roadmap-gen stats tally. Roadmap regenerates byte-identical. Claude-Session: https://claude.ai/code/session_01UpRYiFserdBE5ESHn759N4 --- CHANGELOG.md | 11 +++++++++++ bin/roadmap-add.py | 2 +- bin/roadmap-gen.py | 3 ++- dist/README.md | 11 +++++++---- dist/install.sh | 2 +- docs/ROADMAP_PROCESS.md | 4 ++-- user-pack/install-guide.html | 27 ++++++++++++++++++++++----- 7 files changed, 46 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20143e2..03118a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,17 @@ that release is cut. > 4.0.0 **set `MCP_SERVICE_PASSWORD` to a real secret** (the same value the MCP > server receives as `BNK_FORGE_PASSWORD`); once #188 is in the tree, leaving > it at a default will `SystemExit` the stack at startup. +> +> **Merge ordering (integration dependency).** The dist-bundle wiring these two +> steps assume — the dedicated `mcp` service account for the bundled MCP server, +> and the `MCP_SERVICE_PASSWORD` boot check — arrives in **bonnyr-f5 #186** (service +> account + removal of the shipped `changeme` / `mcp-service-changeme` defaults) and +> **#188** (boot check). This release documents them forward-looking and is therefore +> sequenced to merge **with or after #186 + #188**. Merged ahead of them, the +> `MCP_SERVICE_PASSWORD` guidance is inert for the dist stack (the compose file does +> not pass that variable to the backend) and #186 will conflict in +> `user-pack/install-guide.html` — resolve by taking #186's credential model, not by +> re-adding the `changeme` default this guide describes as a stopgap. ## v3.0.1 — 3.0.x line diff --git a/bin/roadmap-add.py b/bin/roadmap-add.py index eb65507..c4ddc83 100755 --- a/bin/roadmap-add.py +++ b/bin/roadmap-add.py @@ -75,7 +75,7 @@ def main(): ap = argparse.ArgumentParser(description="Append an item to docs/roadmap.yaml") ap.add_argument("--section", help="section id (see --list-sections)") ap.add_argument("--title") - ap.add_argument("--status", help="status key (shipped/in_progress/blocked/deferred/planned)") + ap.add_argument("--status", help="status key (shipped/merged/in_progress/blocked/deferred/planned)") ap.add_argument("--refs", default="", help='comma-separated, e.g. "#216,PR #188"') ap.add_argument("--note", default="") ap.add_argument("--group", default="") diff --git a/bin/roadmap-gen.py b/bin/roadmap-gen.py index 5dd30b0..77a3aae 100755 --- a/bin/roadmap-gen.py +++ b/bin/roadmap-gen.py @@ -395,11 +395,12 @@ def main(): print("Wrote %s" % MD_PATH) print("Wrote %s" % HTML_PATH) print( - "Stats: in_progress=%d planned=%d shipped=%d blocked=%d deferred=%d" + "Stats: in_progress=%d planned=%d shipped=%d merged=%d blocked=%d deferred=%d" % ( count_status(data["sections"], "in_progress"), count_status(data["sections"], "planned"), count_status(data["sections"], "shipped"), + count_status(data["sections"], "merged"), count_status(data["sections"], "blocked"), count_status(data["sections"], "deferred"), ) diff --git a/dist/README.md b/dist/README.md index 0aa4fc3..a74fdce 100644 --- a/dist/README.md +++ b/dist/README.md @@ -259,12 +259,15 @@ gh release create v${VERSION} dist/bnk-forge-${VERSION}.tar.gz \ ### End-user download URL -After publishing, users can download and install with: +Once a full (non-prerelease) `vX.Y.Z` release with an attached tarball exists, users +download and install with the URL below — substitute the version you actually published +(the example `3.1.6` is illustrative; no release asset exists until you cut one): ```bash -# Download from GitHub Releases -curl -L https://github.com/f5devcentral/bnk-forge/releases/download/v3.1.6/bnk-forge-3.1.6.tar.gz | tar xz -cd bnk-forge-3.1.6 +# Download from GitHub Releases — replace 3.1.6 with your published version +VERSION=3.1.6 +curl -L https://github.com/f5devcentral/bnk-forge/releases/download/v${VERSION}/bnk-forge-${VERSION}.tar.gz | tar xz +cd bnk-forge-${VERSION} ./install.sh ``` diff --git a/dist/install.sh b/dist/install.sh index 2c8be22..a57cdb9 100644 --- a/dist/install.sh +++ b/dist/install.sh @@ -10,7 +10,7 @@ # # Prerequisites: # - Docker Engine 24+ with Compose v2.24+ -# - Authenticated to the container registry (if private) +# - Network access to ghcr.io (images are public — no registry login required) # set -euo pipefail diff --git a/docs/ROADMAP_PROCESS.md b/docs/ROADMAP_PROCESS.md index 8bdbe7a..7451532 100644 --- a/docs/ROADMAP_PROCESS.md +++ b/docs/ROADMAP_PROCESS.md @@ -34,7 +34,7 @@ backend/.venv/bin/python bin/roadmap-add.py \ ``` - `--list-sections` prints the available section ids + headings. -- `--status` must be one of: `shipped`, `in_progress`, `blocked`, `deferred`, `planned`. +- `--status` must be one of: `shipped`, `merged`, `in_progress`, `blocked`, `deferred`, `planned` (the keys in `status_legend`; `merged` = merged to staging but unreleased). - `--refs` is comma-separated; values like `#216` / `PR #188` become GitHub links. - `--group` (optional) buckets the item into a named card on the HTML view. - After adding, also update the **§12 issue index** (`render: raw`, edited by hand in the yaml) and re-run the generator so the index stays in sync. @@ -49,7 +49,7 @@ backend/.venv/bin/python bin/roadmap-add.py \ ## Status vocabulary -✅ shipped · 🟡 in-progress / partial · ⛔ blocked (state the blocker) · 💤 deferred (state the resume-trigger) · ⚪ not-started / proposed. +✅ shipped · 🟢 merged (merged to staging, unreleased) · 🟡 in-progress / partial · ⛔ blocked (state the blocker) · 💤 deferred (state the resume-trigger) · ⚪ not-started / proposed. ## Where things live diff --git a/user-pack/install-guide.html b/user-pack/install-guide.html index 83dfbbb..f2b1aca 100644 --- a/user-pack/install-guide.html +++ b/user-pack/install-guide.html @@ -206,6 +206,17 @@

    Step 2 — Configure

    re-read on every boot, so edit .env and re-run docker compose up -d to apply the new value (a plain docker compose restart does not re-read .env). +

    A second administrator ships seeded — mcp. Besides admin, + this build seeds an active admin-role account named mcp whose password is a shipped + default (mcp-service-changeme) published in the public repository. This compose file + wires no .env variable to the backend for it, and the backend reconciles its + password back to that default on every boot, so there is no supported way to rotate it in + this release — treat it as a live, publicly-known credential and keep the backend API off + untrusted networks until you upgrade. bonnyr-f5 #186 removes this shipped default and re-points + the bundled MCP server at this dedicated mcp service account, so that from 4.0.0 MCP + no longer borrows the human admin login; this release is sequenced to land with or + after #186. Until #186 is in the build you install, the bundled MCP server continues to + authenticate as admin via MCP_PASSWORD, as described above.

    @@ -315,11 +326,17 @@

    Updating to a newer build

  4. Non-root artifact images. The container runner now refuses any image whose USER is root or a named user (e.g. USER nonroot). Rebuild your own runner images with a numeric USER 1000 before upgrading.
  5. -
  6. MCP service credential (from 4.0.0). Starting with 4.0.0 the backend refuses to - boot in staging/production if MCP_SERVICE_PASSWORD is unset or still a shipped - default. Set it to a real secret (the same value the MCP server receives as - BNK_FORGE_PASSWORD) before upgrading, or the stack will exit at startup once - that check is in the tree.
  7. +
  8. MCP service credential (from 4.0.0). In this bundle the MCP server authenticates + with MCP_PASSWORD (delivered to the MCP container as + BNK_FORGE_PASSWORD), so keep MCP_PASSWORD matching your admin + password as described in the settings table above. Setting MCP_SERVICE_PASSWORD + in this .env has no effect: this compose file does not pass that + variable to the backend, so the backend never reads it here. From 4.0.0, once bonnyr-f5 + #186 wires the MCP server to a dedicated mcp service account and #188 adds a + boot check, the backend will refuse to start in staging/production while + MCP_SERVICE_PASSWORD is unset or a shipped default — that is the point at + which you set that variable to a real secret. Neither mechanism ships in this bundle yet, + so this release is sequenced to land with or after #186 + #188.