diff --git a/Makefile b/Makefile
index 493bbcf5a5..bf88ace2ba 100644
--- a/Makefile
+++ b/Makefile
@@ -265,15 +265,25 @@ _build-with-docker: # Internal target for Docker-based cross-compilation
exit 1; \
fi
-docker-image: build-ui ## Build Docker image
+docker-image: build-ui ## Build Docker image (LOCAL=1 to use Dockerfile.local)
@echo "$(GREEN)Building Docker image...$(NC)"
$(eval GIT_SHA=$(shell git rev-parse --short HEAD))
- @docker build -f transports/Dockerfile -t bifrost -t bifrost:$(GIT_SHA) -t bifrost:latest .
- @echo "$(GREEN)Docker image built: bifrost, bifrost:$(GIT_SHA), bifrost:latest$(NC)"
+ $(eval DOCKERFILE=$(if $(LOCAL),transports/Dockerfile.local,transports/Dockerfile))
+ @docker build -f $(DOCKERFILE) -t bifrost -t bifrost:$(GIT_SHA) -t bifrost:latest .
+ @echo "$(GREEN)Docker image built: bifrost, bifrost:$(GIT_SHA), bifrost:latest (using $(DOCKERFILE))$(NC)"
-docker-run: ## Run Docker container
+docker-run: ## Run Docker container (Usage: make docker-run [CONFIG=path/to/config.json or path/to/dir/])
@echo "$(GREEN)Running Docker container...$(NC)"
- @docker run -e APP_PORT=$(PORT) -e APP_HOST=0.0.0.0 -p $(PORT):$(PORT) -e LOG_LEVEL=$(LOG_LEVEL) -e LOG_STYLE=$(LOG_STYLE) -v $(shell pwd):/app/data bifrost
+ @CONFIG_PATH="$(abspath $(CONFIG))"; \
+ if [ -n "$(CONFIG)" ]; then \
+ if [ -d "$$CONFIG_PATH" ]; then \
+ CONFIG_PATH="$$CONFIG_PATH/config.json"; \
+ fi; \
+ CONFIG_MOUNT="-v $$CONFIG_PATH:/app/data/config.json"; \
+ else \
+ CONFIG_MOUNT=""; \
+ fi; \
+ docker run -e APP_PORT=$(PORT) -e APP_HOST=0.0.0.0 -p $(PORT):$(PORT) -e LOG_LEVEL=$(LOG_LEVEL) -e LOG_STYLE=$(LOG_STYLE) -v $(shell pwd):/app/data $$CONFIG_MOUNT bifrost
docs: ## Prepare local docs
@echo "$(GREEN)Preparing local docs...$(NC)"
diff --git a/config.json b/config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/docs.json b/docs/docs.json
index 88c589ac0a..c4fb9be8e6 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -451,6 +451,13 @@
}
]
},
+ {
+ "tab": "Security",
+ "icon": "shield",
+ "pages": [
+ "security"
+ ]
+ },
{
"tab": "Benchmarks",
"icon": "chart-line",
diff --git a/docs/media/security/codeowners.png b/docs/media/security/codeowners.png
new file mode 100644
index 0000000000..de6f881b80
Binary files /dev/null and b/docs/media/security/codeowners.png differ
diff --git a/docs/media/security/codeql.png b/docs/media/security/codeql.png
new file mode 100644
index 0000000000..ac02a8c694
Binary files /dev/null and b/docs/media/security/codeql.png differ
diff --git a/docs/media/security/dep-pinning.png b/docs/media/security/dep-pinning.png
new file mode 100644
index 0000000000..6369e279e0
Binary files /dev/null and b/docs/media/security/dep-pinning.png differ
diff --git a/docs/media/security/hardned-base-image.png b/docs/media/security/hardned-base-image.png
new file mode 100644
index 0000000000..777af9a5f4
Binary files /dev/null and b/docs/media/security/hardned-base-image.png differ
diff --git a/docs/media/security/scout-image-score.png b/docs/media/security/scout-image-score.png
new file mode 100644
index 0000000000..4e3355fc97
Binary files /dev/null and b/docs/media/security/scout-image-score.png differ
diff --git a/docs/media/security/step-security.png b/docs/media/security/step-security.png
new file mode 100644
index 0000000000..1d85b04dd3
Binary files /dev/null and b/docs/media/security/step-security.png differ
diff --git a/docs/security.mdx b/docs/security.mdx
new file mode 100644
index 0000000000..c04145de54
--- /dev/null
+++ b/docs/security.mdx
@@ -0,0 +1,289 @@
+---
+title: "Security at Bifrost"
+description: "Overview of security practices across Bifrost's CI/CD pipelines, container images, supply chain, and deployment infrastructure."
+icon: "shield"
+sidebarTitle: "Security"
+---
+
+Bifrost applies defense-in-depth across its open-source and enterprise repositories. Every pull request,
+dependency update, and container image goes through multiple layers of automated security checks before
+reaching production.
+
+| Domain | Tool / Practice | Coverage |
+| --- | --- | --- |
+| Dependency Scanning | Snyk Open Source | Go, Node, Python — all projects |
+| SAST | Snyk Code, CodeQL | Full codebase static analysis |
+| Container Scanning | Docker Scout | Docker Hub auto-scan on push |
+| Artifact Scanning | GCP Artifact Registry | Enterprise container images |
+| Dependency Updates | Dependabot | gomod, npm, Docker, GitHub Actions |
+| Supply Chain | SHA pinning, npm provenance | 100 % of GitHub Actions (OSS) |
+| Container Hardening | FIPS base image, non-root user | Production Dockerfile |
+| Security Hardening | StepSecurity, CODEOWNERS | Workflow hardening, code review gates |
+| Network Security | Tailscale VPN | Enterprise deployments |
+
+---
+
+## Vulnerability Scanning — Snyk
+
+Bifrost runs two Snyk scanning jobs on every push and pull request. Results are uploaded as
+[SARIF](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning)
+to the GitHub Security tab.
+
+
+
+ The **Snyk Open Source** job scans all Go, Node, and Python dependencies for known vulnerabilities.
+
+ ```yaml
+ snyk test --all-projects --detection-depth=4 --sarif-file-output=snyk.sarif
+ ```
+
+ - Scans every module across `core/`, `framework/`, `transports/`, `plugins/`, `ui/`, and `tests/`
+ - Detection depth of 4 catches transitive dependencies
+ - Snyk CLI pinned to `v1.1303.2`
+
+
+
+
+ The **Snyk Code** job performs Static Application Security Testing on the full codebase.
+ A complete build is performed first (Go + Node + Python) so Snyk can analyze compiled artifacts.
+
+ ```yaml
+ snyk code test --sarif-file-output=snyk-code.sarif
+ ```
+
+ - Detects injection flaws, hardcoded secrets, insecure crypto, and other code-level vulnerabilities
+ - Builds the full project before scanning for accurate analysis
+
+
+
+
+ Snyk checks can be skipped by including `--skip-pipeline` in the first line of a commit message. This is
+ intended for documentation-only or CI configuration changes.
+
+
+---
+
+## Container Image Security
+
+### Dockerfile Hardening
+
+Production containers follow a strict hardening checklist:
+
+
+
+ Three stages — UI builder (Node), Go builder, and minimal Alpine runtime — ensure no build tools or
+ source code leak into the final image.
+
+
+ Production images use a FIPS 140-2 validated Alpine base image with compliant OpenSSL.
+
+
+ The FIPS base image includes a dedicated `appuser`. The container runs as this unprivileged user —
+ never as root.
+
+
+ Go binaries are compiled with `-ldflags="-w -s"` to strip debug symbols and DWARF information,
+ reducing attack surface and image size.
+
+
+
+Additional hardening measures:
+
+- **Static builds** — Compiled with `-tags "sqlite_static"` and `-extldflags '-static'` for fully static linking
+- **Build verification** — `RUN test -f /app/main || exit 1` ensures the binary exists before proceeding
+- **CVE patching** — Enterprise Dockerfiles include targeted patches (e.g., `apk upgrade --no-cache openssl` for CVE-2026-22796)
+- **Minimal runtime dependencies** — The FIPS base image provides only essential libraries (`musl`, `libgcc`, `ca-certificates`)
+
+```dockerfile
+# Runtime stage excerpt (production)
+FROM
+WORKDIR /app
+
+COPY --from=builder /app/main .
+COPY --from=builder /app/docker-entrypoint.sh .
+
+RUN mkdir -p $APP_DIR/logs
+USER appuser
+
+ENTRYPOINT ["/app/docker-entrypoint.sh"]
+CMD ["/app/main"]
+```
+
+
+
+
+
+### Docker Scout
+
+[Docker Scout](https://docs.docker.com/scout/) is enabled at the Docker Hub repository level for the
+`maximhq/bifrost` image. Every image pushed to Docker Hub is automatically scanned for CVEs against
+continuously updated vulnerability databases.
+
+
+
+
+
+### GCP Artifact Registry Scanning
+
+Enterprise images are pushed to **GCP Artifact Registry** (and AWS ECR for select environments).
+GCP Artifact Registry provides [built-in vulnerability scanning](https://cloud.google.com/artifact-registry/docs/analysis)
+that automatically analyzes container images for OS and language package vulnerabilities.
+
+---
+
+## Supply Chain Security
+
+### GitHub Actions SHA Pinning
+
+All GitHub Actions in the open-source repository are pinned to exact commit SHAs — not mutable version
+tags. This prevents supply chain attacks where a compromised action maintainer could push malicious code
+to an existing tag.
+
+```yaml
+# Every action is pinned to a full SHA with a version comment
+- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
+- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
+```
+
+**Coverage:**
+
+| Repository | Pinning Strategy | Actions Pinned |
+| --- | --- | --- |
+| `bifrost` (OSS) | Full SHA with version comment | 103 / 103 (100 %) |
+| `bifrost-enterprise` | Version tags (`@v4`) | Upgrade planned |
+
+### NPM Provenance
+
+Published npm packages include [SLSA provenance attestations](https://docs.npmjs.com/generating-provenance-statements),
+providing a verifiable link between the published package and its source commit.
+
+```yaml
+permissions:
+ id-token: write # Required for npm provenance
+
+# ...
+npm publish --provenance --access public
+```
+
+### Dependency Pinning in CI
+
+All language runtimes are pinned to specific versions across CI workflows to ensure reproducible builds
+and prevent unexpected behavior from runtime updates.
+
+| Runtime | Pinned Version | Used For |
+| --- | --- | --- |
+| Go | `1.26.1` | Core build, tests |
+| Node | `25` | UI build, npm packages |
+| Python | `3.11` | Integration and governance tests |
+| uv | SHA-pinned via `astral-sh/setup-uv` | Python package management |
+
+Python test dependencies are locked via `uv.lock` files for deterministic, reproducible installs:
+
+```
+tests/integrations/uv.lock
+tests/governance/uv.lock
+```
+
+
+
+
+
+---
+
+## Dependency Management — Dependabot
+
+Dependabot monitors four ecosystems on a weekly schedule, automatically opening pull requests for
+outdated or vulnerable dependencies.
+
+
+
+ Covers `/core`, `/framework`, `/transports`, `/plugins/*`, and `/examples/**`
+
+
+ Covers `/ui`, `/npx`, and `/examples/**`
+
+
+ Monitors base images in `/transports`
+
+
+ Tracks action version updates across all workflows
+
+
+
+A separate **Dependabot Alerts** workflow runs daily and automatically
+creates GitHub issues for any open Dependabot security alerts, categorized by severity and ecosystem.
+
+---
+
+## Code Analysis — CodeQL
+
+GitHub's [CodeQL](https://codeql.github.com/) performs semantic code analysis on every push and pull request,
+complementing Snyk's SAST coverage with GitHub-native findings.
+
+- Analyzes Go and JavaScript/TypeScript codebases
+- Detects security vulnerabilities, bugs, and code quality issues using GitHub's query suites
+- Results appear directly in the GitHub Security tab alongside Snyk findings
+
+
+
+
+
+---
+
+## Workflow & Network Security
+
+### Principle of Least Privilege
+
+All GitHub Actions workflows follow the principle of least privilege. Permissions are set at the
+job level, not the workflow level, and are scoped to the minimum required.
+
+| Permission | Granted To | Reason |
+| --- | --- | --- |
+| `contents: read` | All jobs (default) | Read repository code |
+| `contents: write` | Tag creation, releases | Create git tags and releases |
+| `security-events: write` | Snyk jobs | Upload SARIF to Security tab |
+| `id-token: write` | Cloud auth, npm publish | OIDC federation, npm provenance |
+| `pull-requests: write` | PR test reporters | Post test results as PR comments |
+
+### Tailscale VPN
+
+Enterprise deployment workflows authenticate through [Tailscale](https://tailscale.com/) before
+accessing any infrastructure. This ensures that CI/CD runners can only reach deployment targets
+through an encrypted, identity-aware network — never over the public internet.
+
+```yaml
+- name: Authenticate Tailscale
+ uses: tailscale/github-action@v4
+ with:
+ oauth-client-id: '${{ secrets.TS_OAUTH_CLIENT_ID }}'
+ oauth-secret: '${{ secrets.TS_OAUTH_SECRET }}'
+ tags: 'tag:gha-ci'
+ version: 1.84.0
+```
+
+- **OAuth-based authentication** — No long-lived API keys; runners authenticate via OAuth client credentials
+- **Version pinned** — Tailscale `1.84.0` to prevent unexpected behavior from updates
+- **Tagged runners** — `gha-ci` tag enables Tailscale ACL policies scoped to CI/CD access
+
+### StepSecurity
+
+[StepSecurity](https://www.stepsecurity.io/) automatically hardens GitHub Actions workflows by applying
+security best practices across all CI/CD pipelines.
+
+- Adds `permissions` blocks to workflows that are missing them
+- Pins action versions to full SHAs where mutable tags were used
+- Detects insecure patterns like unquoted interpolations and artifact poisoning risks
+
+
+
+
+
+### CODEOWNERS
+
+Critical paths in the repository are protected by a `CODEOWNERS` file, ensuring that changes to
+security-sensitive areas require review from designated maintainers before merging.
+
+
+
+
diff --git a/transports/Dockerfile b/transports/Dockerfile
index a18bf4ff71..afc2357b7a 100644
--- a/transports/Dockerfile
+++ b/transports/Dockerfile
@@ -44,19 +44,14 @@ RUN go build \
-o /app/main \
./bifrost-http
-# Verify build succeeded
+# Verify build succeeded and prepare entrypoint
RUN test -f /app/main || (echo "Build failed" && exit 1)
+RUN chmod +x /build/transports/docker-entrypoint.sh
# --- Runtime Stage: Minimal runtime image ---
-FROM alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
+FROM bifrosthq/dhi-alpine-base:3.22-fips_bifrost-v27032026
WORKDIR /app
-# Install runtime dependencies for CGO-enabled binary
-# musl: C standard library (required for CGO binaries)
-# libgcc: GCC runtime library
-# ca-certificates: For HTTPS connections
-RUN apk add --no-cache musl libgcc ca-certificates wget
-
# Create data directory and set up user
COPY --from=builder /app/main .
COPY --from=builder /app/docker-entrypoint.sh .
@@ -86,10 +81,7 @@ ENV GOGC="" \
GOMEMLIMIT=""
-RUN mkdir -p $APP_DIR/logs && \
- adduser -D -s /bin/sh appuser && \
- chown -R appuser:appuser /app && \
- chmod +x /app/docker-entrypoint.sh
+RUN mkdir -p $APP_DIR/logs
USER appuser
@@ -97,10 +89,6 @@ USER appuser
VOLUME ["/app/data"]
EXPOSE $APP_PORT
-# Health check for container status monitoring
-HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
- CMD wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:${APP_PORT}/health || exit 1
-
# Use entrypoint script that handles volume permissions and argument processing
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["/app/main"]
diff --git a/transports/Dockerfile.local b/transports/Dockerfile.local
index 124e507d68..2a03e34e4d 100644
--- a/transports/Dockerfile.local
+++ b/transports/Dockerfile.local
@@ -64,19 +64,14 @@ RUN cd /build/transports && \
-o /app/main \
./bifrost-http
-# Verify build succeeded
+# Verify build succeeded and prepare entrypoint
RUN test -f /app/main || (echo "Build failed" && exit 1)
+RUN chmod +x /build/transports/docker-entrypoint.sh
# --- Runtime Stage: Minimal runtime image ---
-FROM alpine:3.23.3
+FROM bifrosthq/dhi-alpine-base:3.22-fips_bifrost-v27032026
WORKDIR /app
-# Install runtime dependencies for CGO-enabled binary
-# musl: C standard library (required for CGO binaries)
-# libgcc: GCC runtime library
-# ca-certificates: For HTTPS connections
-RUN apk add --no-cache musl libgcc ca-certificates wget
-
# Create data directory and set up user
COPY --from=builder /app/main .
COPY --from=builder /build/transports/docker-entrypoint.sh .
@@ -96,10 +91,7 @@ ENV APP_PORT=$ARG_APP_PORT \
APP_DIR=$ARG_APP_DIR
-RUN mkdir -p $APP_DIR/logs && \
- adduser -D -s /bin/sh appuser && \
- chown -R appuser:appuser /app && \
- chmod +x /app/docker-entrypoint.sh
+RUN mkdir -p $APP_DIR/logs
USER appuser
@@ -107,10 +99,6 @@ USER appuser
VOLUME ["/app/data"]
EXPOSE $APP_PORT
-# Health check for container status monitoring
-HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
- CMD wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:${APP_PORT}/health || exit 1
-
# Use entrypoint script that handles volume permissions and argument processing
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["/app/main"]