Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
f9e4389
chore: integration branch snapshot (internal origin/staging)
bonnyr-f5 Aug 10, 2026
57b0814
fix: module lifecycle control — destroy blast radius, disabled gating…
jgruber Aug 8, 2026
2ac3ec3
review fixes: close three more enabled bypasses, fix the cancel guard…
jgruber Aug 8, 2026
7005653
fix(mirror): restore files the public export silently dropped, and fi…
jgruber Aug 10, 2026
e6fa758
Merge pull request #121 from f5devcentral/fix/restore-mirror-dropped-…
jgruberf5 Aug 10, 2026
0480b94
Merge remote-tracking branch 'f5dc/staging' into ci-fix/pr1
jgruber Aug 10, 2026
f190593
fix(migrations): stop v2_152 dropping the unique index on container_r…
jgruber Aug 10, 2026
d5dfef3
Merge pull request #122 from f5devcentral/fix/v2152-unique-index-guard
jgruberf5 Aug 10, 2026
fbd47f3
Merge remote-tracking branch 'f5dc/staging' into ci-fix/pr1
jgruber Aug 10, 2026
36c1652
fix(container-runner): registry credential exfil, deny-all egress, no…
jgruber Aug 10, 2026
fe0e9a7
style: split multi-statement lines flagged by ruff (E702)
jgruber Aug 10, 2026
26a78b1
review fix: clear EVERY credential family on a registry host change, …
jgruber Aug 10, 2026
07c3606
test: pin the invariant that a project destroy still tears down DISAB…
jgruber Aug 10, 2026
0787f0b
review fixes: unconditional credential clearing, scoped egress, preci…
jgruber Aug 13, 2026
cad3bed
review fixes: kill on the worker, scope from the executing run, fail …
jgruber Aug 13, 2026
562afd9
style: fix import ordering in the new hardening tests (ruff I001)
jgruber Aug 13, 2026
4841a6e
test: pin the v2_152 unique-index invariant, and correct its downgrad…
jgruber Aug 14, 2026
242fefd
safety: refuse to run the migration regression tests against a non-di…
jgruber Aug 14, 2026
3708cc3
review fixes: give the migration tests their own database, and stop t…
jgruber Aug 17, 2026
60ee2a5
review fixes: one canonical step resolver, canonical host comparison,…
jgruber Aug 17, 2026
5b6c356
fix(ci): scope the migration-test guard to its own job, and exclude t…
jgruber Aug 17, 2026
2cad492
review fixes: adopt in-flight destroys into the run, resolve the subs…
jgruber Aug 17, 2026
7fb2088
self-review fix: a scratch-database failure must not skip past the re…
jgruber Aug 17, 2026
5b07cbc
self-review fix: the derived-host patterns refused real provider endp…
jgruber Aug 17, 2026
8c25d18
docs: record the residual adoption race rather than shipping an unver…
jgruber Aug 17, 2026
25ff794
Merge pull request #1 from f5devcentral/fix/module-lifecycle-control
jgruberf5 Aug 17, 2026
954cc29
Merge pull request #123 from f5devcentral/fix/container-runner-harden…
jgruberf5 Aug 17, 2026
e942085
Merge pull request #126 from f5devcentral/fix/v2152-regression-test
jgruberf5 Aug 17, 2026
7ece9b0
Container runner hardening: non-root gate bypass, outputs_file traver…
jgruberf5 Aug 17, 2026
79e4d73
Run the 746 integration tests CI never executed (#131)
jgruberf5 Aug 18, 2026
481efc8
Narrow the dist/nginx un-ignore rule to the two tracked confs (#132)
jgruberf5 Aug 18, 2026
1b48a7b
Refuse project deletion while modules still own cloud resources (#129)
jgruberf5 Aug 18, 2026
10fa9fb
Package the dist tarball from the tracked set, not the working tree (…
jgruberf5 Aug 18, 2026
0143456
Pair the dist proxy conf with an unprivileged container port mapping …
jgruberf5 Aug 18, 2026
db2fd0a
Destroy cli-bnkctl modules from the applied config, not the current f…
jgruberf5 Aug 18, 2026
5da7504
Enforce risk_class at runtime for destructive MCP tools (#141)
jgruberf5 Aug 18, 2026
1bad3e8
Contain a failed initial blueprint sync in a SAVEPOINT (#143)
jgruberf5 Aug 18, 2026
c7aeae8
Release the helm advisory lock even from an aborted transaction (#144)
jgruberf5 Aug 18, 2026
c8e9934
Reset modules stuck in a transient state when their worker dies (#145)
jgruberf5 Aug 18, 2026
0169f92
Resolve path-only module references through one canonical ordering (#…
jgruberf5 Aug 18, 2026
8a94c0d
Report the flashed DPU's allocated tmfifo address, not a constant (#147)
jgruberf5 Aug 18, 2026
7af0199
Bind the benchmark agent WS token to the agent identity (#41 F5) (#142)
jgruberf5 Aug 18, 2026
a019901
Require an agent-class token on the benchmark ingest/register endpoin…
jgruberf5 Aug 18, 2026
b5fb17b
Verify TLS on SSH-tunnelled clusters via tls-server-name (#150)
jgruberf5 Aug 18, 2026
29975f3
Never delete an interrupted-apply module without attempting a destroy…
jgruberf5 Aug 18, 2026
60353ab
Stop SSOAuthDialog updating state after unmount; mock the real endpoi…
jgruberf5 Aug 19, 2026
436d07d
Derive container time limits from the canonical step-set (#155)
jgruberf5 Aug 19, 2026
256a7e6
Restore @pytest.mark.unit on TestRootUserGate; guard this file agains…
jgruberf5 Aug 19, 2026
7a3ae11
Set secret-file mode via fchmod on the open descriptor, not chmod by …
jgruberf5 Aug 19, 2026
55503b4
RunModuleActionDialog: submit re-guard, reports invalidation on compl…
jgruberf5 Aug 19, 2026
1c4bcc7
Make container (and every engine's) step output discoverable from the…
jgruberf5 Aug 19, 2026
ea3f5dc
Container-runner hardening remainder: cluster ownership, mount_path, …
jgruberf5 Aug 19, 2026
8f9bdde
Resolve the registry-host allowlist fail-closed at runtime, same as i…
jgruberf5 Aug 19, 2026
5755bdf
Make the staging auto-close parser accept the PR template's own form …
jgruberf5 Aug 19, 2026
ec1e4c0
fix: record the failure cause as the state-transition reason (#164)
jgruberf5 Aug 19, 2026
5ceba92
fix: skip blueprint auto-sync when the linked blueprint source is ina…
jgruberf5 Aug 19, 2026
9832a13
Scope KubernetesCluster name uniqueness to (project_id, name) (#167)
jgruberf5 Aug 19, 2026
2c62fed
test: reset the reachability circuit-breaker registry between tests (…
jgruberf5 Aug 19, 2026
e436e35
Mount DriftDetailPanel in the drift-check dialog so Reconcile is reac…
jgruberf5 Aug 19, 2026
967f2e9
fix: warn (per-project) when sync deactivates a still-pinned module v…
jgruberf5 Aug 19, 2026
5cfd3e4
f5spkegress create template emits the current snatType/pseudoCNIConfi…
jgruberf5 Aug 19, 2026
e33c7e8
Surface backend error code/details as JSON fields, not a Python repr …
jgruberf5 Aug 19, 2026
40f9892
fix: give every MCP tool result one universal outcome key (ok) (#66) …
jgruberf5 Aug 19, 2026
a601048
test: hold the Login loading-state request open without a live timer …
jgruberf5 Aug 19, 2026
009840a
Redact bnk_config from the instance-wide global cluster list (#174)
jgruberf5 Aug 19, 2026
4a52ed4
Version derivation and release notes must read commit bodies (→ 4.0.0…
jgruberf5 Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 26 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# Root .dockerignore — reduces build context for frontend Dockerfile
# (which uses context: . to access VERSION file from repo root)
# Root .dockerignore — governs every image built with context: . , which is now
# the frontend AND the backend targets (api/worker/beat). Both need the repo-root
# VERSION file, which cannot be reached from a context rooted inside their own
# directory.
#
# The backend/ and bnk-operator/ have their own .dockerignore files.
# backend/.dockerignore no longer takes effect for those builds — Docker only
# reads the ignore file at the context root — so the backend-specific exclusions
# that still matter are reproduced below with a backend/ prefix.
#
# bnk-operator/ still builds with its own context and its own .dockerignore.

# Version control
.git/
Expand Down Expand Up @@ -31,16 +37,33 @@ htmlcov/
.ruff_cache/
.mypy_cache/
backend/.venv/
backend/venv/
backend/env/
backend/tests/
bnk-operator/tests/

# Local runtime data under backend/ — mounted as volumes in production, never
# part of an image. Previously excluded by backend/.dockerignore.
backend/projects/
backend/keys/
backend/state/
backend/workspaces/
backend/helm_charts/

# Node artifacts (frontend has its own context via COPY)
frontend-v2/node_modules/
frontend-v2/dist/

# Environment files (secrets!)
# OPS-004: exclude ALL .env files so they never enter a build context. These
# patterns have no path prefix, so they match <root>/.env only — they do NOT
# descend into backend/. The backend/ forms below carry over the rules from the
# deleted backend/.dockerignore, which is the one set that did not come across
# with the venvs and runtime dirs.
.env
.env.*
backend/.env
backend/.env.*
secrets/

# Test artifacts
Expand Down
20 changes: 20 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,26 @@
# MCP_SERVICE_PASSWORD=mcp-service-changeme
# DEFAULT_ADMIN_PASSWORD=changeme

# ============================================================================
# BENCHMARK AGENT AUTHENTICATION
# ============================================================================
#
# The agent-facing endpoints (POST /api/benchmarks/results, /results/aiperf,
# /agents) require an agent-class bearer token by default. Accepted roles:
# agent (provisioned agents + the built-in agent's bootstrap token), operator,
# admin. A viewer token authenticates but may not write here.
#
# The built-in forge-agent needs no setup: the backend mints a bootstrap token
# into a dedicated volume (bnk-forge-agent-token) at startup and
# docker-compose.yml mounts that volume, read-only, into the agent. External agents get a token from the provisioning flow
# (or set FORGE_AGENT_TOKEN to hand one to the built-in agent explicitly).
#
# Set to false ONLY on a trusted network where you want the open curl flow;
# it re-opens unauthenticated writes to those three endpoints.
#
# BENCHMARK_AGENT_AUTH_REQUIRED=true
# FORGE_AGENT_TOKEN=

# ============================================================================
# BRANDING (Frontend logo: F5 ball or neutral Forge anvil)
# ============================================================================
Expand Down
2 changes: 1 addition & 1 deletion .github/BRANCH_PROTECTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Release: Version bump + tag + changelog [manual, requires CI Gate]
| `P2 · Proxy Config` | No (aggregated) | Nginx config via `make test-proxy` |
| `P2 · DB Migrations` | No (aggregated) | Migration tests via `make test-db` |
| `P2 · Build · Frontend` | No (aggregated) | Build check via `make build-frontend-check` |
| `P3 · Integration Tests` | No (aggregated) | Integration tests via `make test-integration-full` |
| `P3 · Integration Tests` | No (aggregated) | Integration tests via `make test-integration` + `make test-integration-full` (complementary marker sets — both are needed to cover `tests/integration/`) |
| `P4 · Security Audit` | No (aggregated) | pip-audit + npm audit via `make security-audit` |
| `P4 · Docker Build + Scan` | No (aggregated) | Docker build + Trivy scan |
| `P5 · E2E Tests` | ❌ No | Manual/nightly only |
Expand Down
5 changes: 5 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ A clear and concise description of the changes in this Pull Request.

Fixes / Implements: #[Issue Number]

<!-- Use "Fixes #N" (or Closes/Resolves) when this PR should CLOSE the issue on
merge to staging -- the auto-close Action matches those keywords, including
in the "Fixes / Implements: #N" form above. Use "Refs #N" for a partial fix
that must leave the issue open. "Implements: #N" alone does not close. -->

---

## Architectural Decision Record (ADR)
Expand Down
33 changes: 32 additions & 1 deletion .github/workflows/auto-close-issues-on-staging-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,44 @@ jobs:
import os, re
body = os.environ.get("PR_BODY") or ""
repo = os.environ["REPO"]
# Neutralise fenced blocks and inline code spans BEFORE scanning. A PR
# that documents closing keywords (a parser change, a template change,
# a how-to) otherwise closes the issues it is merely talking about --
# #158's own description parsed as issues=94 128 7, two of them open
# with their real fixes still unmerged. Fences first (they may contain
# backticks), then spans. Both fence styles -- ``` and ~~~ -- since ~~~
# is what an author reaches for when the example itself contains
# backticks, i.e. exactly the case this exists for. Like-to-like: a
# ~~~ block must close with ~~~ (backreference), so a stray ~~~ cannot
# "close" a ``` fence and over-strip.
#
# Replace with a NON-WHITESPACE sentinel, never ''. Deleting the code
# outright glues a keyword onto a following reference -- "Fixes `x`
# #94" becomes "Fixes #94" and the [:\s]* separator swallows the
# gap -- a NEW false-close the pre-stripping parser never produced.
# The sentinel cannot be eaten by the separator or the "/ Implements"
# skip, so the barrier survives; a real "Fixes #94" in prose is
# unaffected because it contains no code to replace.
#
# Indented (4-space) code blocks are NOT neutralised: they need a
# line-based pass and nobody in this repo writes them. Known
# safe-direction gap: an unbalanced backtick in prose can swallow a
# real closing line -> the issue simply stays open (the pre-Action
# status quo).
CODE = "\u2400" # U+2400 SYMBOL FOR NULL: non-whitespace, never in a ref
body = re.sub(r'(```|~~~).*?\1', CODE, body, flags=re.S)
body = re.sub(r'`[^`]*`', CODE, body)
kw_pat = re.compile(r'(?i)\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\b')
ref_pat = re.compile(r'([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)?#(\d+)')
sep_pat = re.compile(r'[ \t,]+')
out=[]; seen=set()
for m in kw_pat.finditer(body):
i = m.end()
i += re.match(r'[:\s]*', body[i:]).end()
# Accept the PR template's own line, "Fixes / Implements: #N":
# allow an optional "/ Implements" (or "/ Closes" etc.) between the
# keyword and the reference. Before this, the template form matched
# NOTHING and every issue had to be closed by hand.
i += re.match(r'(?i)[:\s]*(?:/\s*(?:implements|closes|fixes|resolves)\s*)?[:\s]*', body[i:]).end()
while True:
r = ref_pat.match(body, i)
if not r:
Expand Down
Loading
Loading