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/.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/.trivyignore b/.trivyignore
index 46fa12f..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
@@ -32,11 +34,16 @@ 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-08-12
+# 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
+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)
@@ -49,7 +56,7 @@ CVE-2026-7598
# 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)
@@ -69,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
@@ -80,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
@@ -91,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)
@@ -105,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.
@@ -117,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 294cdf6..03118a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,54 @@
# 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 `v3.1.6` — the last release before 4.0.0, and the initial public
+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 — 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`**: 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` 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.
+>
+> **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
+
+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
### Bug Fixes
diff --git a/backend/services/execution/container_runner.py b/backend/services/execution/container_runner.py
index 79c2f61..ef1434e 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
@@ -647,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/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/.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
diff --git a/dist/README.md b/dist/README.md
index 7c0c011..a74fdce 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 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
-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,18 +253,21 @@ 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
-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/your-org/bnk-forge/releases/download/v3.0.1/bnk-forge-3.0.1.tar.gz | tar xz
-cd bnk-forge-3.0.1
+# 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/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/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/DOCKER.md b/docs/DOCKER.md
index f25eaeb..65944d3 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.
@@ -83,36 +83,43 @@ 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
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/release\.yml@.*' \
+ --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/release\.yml@.*' \
+ --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/release\.yml@.*' \
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
+ ghcr.io/f5devcentral/bnk-forge-api@ \
| jq -r '.payload' | base64 -d | jq .
```
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 4357830..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
@@ -654,9 +654,14 @@ 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 `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.
+ 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..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`).
---
@@ -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. |
@@ -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** | 🟢 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_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/docs/roadmap.html b/docs/roadmap.html
index eb4ba5c..126ff53 100644
--- a/docs/roadmap.html
+++ b/docs/roadmap.html
@@ -68,7 +68,7 @@
Private registry edition | customer-build distribution | registry: ghcr.io/jlcode-tech
+
Public registry edition | current-release distribution | registry: ghcr.io/f5devcentral
@@ -122,9 +116,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 +128,12 @@
Prerequisites
Docker Engine 24+ — on macOS or Windows, Docker Desktop satisfies both this and the Compose requirement.
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/jlcode-tech 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:
- 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 +143,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:
@@ -195,13 +161,13 @@
Step 3 — Configure
BNK_FORGE_REGISTRY
-
ghcr.io/jlcode-tech
-
Points to the private registry.
+
ghcr.io/f5devcentral
+
Points to the public registry.
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
@@ -215,20 +181,46 @@
Step 3 — Configure
MCP_PASSWORD
-
your choice
-
Change from the default. Used by the MCP integration layer.
+
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.
-
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 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).
+
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.
-
-
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 +243,8 @@
Step 4 — Install
When the installer finishes you will see:
✅ Installation complete!
-
-
Step 5 — First login
+
+
Step 4 — First login
@@ -271,8 +263,8 @@
Step 5 — First login
Username: admin / Password: changeme
Change the admin password immediately
- The default password is well-known. Go to User menu → Change Password as your
- very first action after login.
+ The default password is well-known — change it as your very first action after login,
+ via User menu → Change Password, before doing anything else.
@@ -309,7 +301,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:
@@ -322,6 +314,32 @@
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 (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.
+
+
+
Uninstall
@@ -335,10 +353,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.