Go’s plugin system requires dynamic linking to load .so files at runtime. By default, Bifrost builds are statically linked for maximum portability across Linux distributions - they bundle all dependencies including the C standard library (libc). However, statically linked binaries cannot load Go plugins.To use custom plugins with Bifrost, you must build a dynamically linked binary that links against the system’s libc at runtime.
Dynamic plugins only work on Linux and macOS (Darwin). Windows is not supported by Go’s plugin system.
# Build for Linux AMD64 (uses Docker if cross-compiling)make build DYNAMIC=1 GOOS=linux GOARCH=amd64# Build for Linux ARM64make build DYNAMIC=1 GOOS=linux GOARCH=arm64
Use this for Alpine-based deployments or when you want minimal image size.
Complete Dockerfile for Alpine (musl libc)
Copy
Ask AI
# --- UI Build Stage: Build the Next.js frontend ---FROM node:24-alpine3.22 AS ui-builderWORKDIR /app# Copy UI package files and install dependenciesCOPY ui/package*.json ./RUN npm ci# Copy UI source codeCOPY ui/ ./# Build UI (skip the copy-build step)RUN npx next buildRUN node scripts/fix-paths.js# --- Go Build Stage: Compile the Go binary ---FROM golang:1.24.3-alpine3.22 AS builderWORKDIR /app# Install dependencies including gcc for CGO and sqliteRUN apk add --no-cache gcc musl-dev sqlite-dev# Set environment for CGO-enabled build (required for go-sqlite3 and plugins)ENV CGO_ENABLED=1 GOOS=linuxCOPY transports/go.mod transports/go.sum ./RUN go mod download# Copy source code and dependenciesCOPY transports/ ./COPY --from=ui-builder /app/out ./bifrost-http/ui# Build the binary with CGO enabled for DYNAMIC LINKINGENV GOWORK=offARG VERSION=unknownRUN go build \ -ldflags="-w -s -X main.Version=v${VERSION}" \ -a -trimpath \ -o /app/main \ ./bifrost-http# Verify build succeededRUN test -f /app/main || (echo "Build failed" && exit 1)# --- Runtime Stage: Minimal runtime image ---FROM alpine:3.22WORKDIR /app# Install runtime dependencies for CGO-enabled dynamic binary# musl: C standard library (required for CGO binaries)# libgcc: GCC runtime library# ca-certificates: For HTTPS connectionsRUN apk add --no-cache musl libgcc ca-certificates# Create data directory and set up userCOPY --from=builder /app/main .COPY --from=builder /app/docker-entrypoint.sh .# Getting argumentsARG ARG_APP_PORT=8080ARG ARG_APP_HOST=0.0.0.0ARG ARG_LOG_LEVEL=infoARG ARG_LOG_STYLE=jsonARG ARG_APP_DIR=/app/data# Environment variables with defaults (can be overridden at runtime)ENV APP_PORT=$ARG_APP_PORT \ APP_HOST=$ARG_APP_HOST \ LOG_LEVEL=$ARG_LOG_LEVEL \ LOG_STYLE=$ARG_LOG_STYLE \ APP_DIR=$ARG_APP_DIRRUN mkdir -p $APP_DIR/logs && \ adduser -D -s /bin/sh appuser && \ chown -R appuser:appuser /app && \ chmod +x /app/docker-entrypoint.shUSER appuser# Declare volume for data persistenceVOLUME ["/app/data"]EXPOSE $APP_PORT# Health check for container status monitoringHEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${APP_PORT}/metrics || exit 1# Use entrypoint script that handles volume permissions and argument processingENTRYPOINT ["/app/docker-entrypoint.sh"]CMD ["/app/main"]
Key changes from static build:
Line 40-44: Removed -extldflags '-static' and -tags "sqlite_static"
Removed UPX compression step (optional, but simpler)
Runtime uses musl libc from Alpine base image
Build and run:
Copy
Ask AI
# Build the imagedocker build -f transports/Dockerfile -t bifrost:dynamic-alpine .# Run the containerdocker run -p 8080:8080 -v ./plugins:/app/data/plugins bifrost:dynamic-alpine
Use this for Debian/Ubuntu-based deployments or when deploying to glibc-based systems.
Complete Dockerfile for Debian (glibc)
Copy
Ask AI
# --- UI Build Stage: Build the Next.js frontend ---FROM node:24-bookworm AS ui-builderWORKDIR /app# Copy UI package files and install dependenciesCOPY ui/package*.json ./RUN npm ci# Copy UI source codeCOPY ui/ ./# Build UIRUN npx next buildRUN node scripts/fix-paths.js# --- Go Build Stage: Compile the Go binary ---FROM golang:1.24.3-bookworm AS builderWORKDIR /app# Install dependencies including gcc for CGO and sqliteRUN apt-get update && apt-get install -y \ gcc \ libc6-dev \ libsqlite3-dev \ && rm -rf /var/lib/apt/lists/*# Set environment for CGO-enabled build (required for go-sqlite3 and plugins)ENV CGO_ENABLED=1 GOOS=linuxCOPY transports/go.mod transports/go.sum ./RUN go mod download# Copy source code and dependenciesCOPY transports/ ./COPY --from=ui-builder /app/out ./bifrost-http/ui# Build the binary with CGO enabled for DYNAMIC LINKINGENV GOWORK=offARG VERSION=unknownRUN go build \ -ldflags="-w -s -X main.Version=v${VERSION}" \ -a -trimpath \ -o /app/main \ ./bifrost-http# Verify build succeededRUN test -f /app/main || (echo "Build failed" && exit 1)# --- Runtime Stage: Minimal runtime image ---FROM debian:bookworm-slimWORKDIR /app# Install runtime dependencies for CGO-enabled dynamic binary# libc6: GNU C Library (required for glibc-linked binaries)# ca-certificates: For HTTPS connectionsRUN apt-get update && apt-get install -y \ libc6 \ ca-certificates \ wget \ && rm -rf /var/lib/apt/lists/*# Create data directory and set up userCOPY --from=builder /app/main .COPY --from=builder /app/docker-entrypoint.sh .# Getting argumentsARG ARG_APP_PORT=8080ARG ARG_APP_HOST=0.0.0.0ARG ARG_LOG_LEVEL=infoARG ARG_LOG_STYLE=jsonARG ARG_APP_DIR=/app/data# Environment variables with defaults (can be overridden at runtime)ENV APP_PORT=$ARG_APP_PORT \ APP_HOST=$ARG_APP_HOST \ LOG_LEVEL=$ARG_LOG_LEVEL \ LOG_STYLE=$ARG_LOG_STYLE \ APP_DIR=$ARG_APP_DIRRUN mkdir -p $APP_DIR/logs && \ useradd -m -s /bin/sh appuser && \ chown -R appuser:appuser /app && \ chmod +x /app/docker-entrypoint.shUSER appuser# Declare volume for data persistenceVOLUME ["/app/data"]EXPOSE $APP_PORT# Health check for container status monitoringHEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${APP_PORT}/metrics || exit 1# Use entrypoint script that handles volume permissions and argument processingENTRYPOINT ["/app/docker-entrypoint.sh"]CMD ["/app/main"]
Key differences from Alpine version:
Uses bookworm (Debian 12) base images instead of Alpine
Installs apt packages instead of apk
Runtime uses glibc (libc6) instead of musl
Uses useradd instead of adduser for user creation
Build and run:
Copy
Ask AI
# Build the imagedocker build -f transports/Dockerfile.debian -t bifrost:dynamic-debian .# Run the containerdocker run -p 8080:8080 -v ./plugins:/app/data/plugins bifrost:dynamic-debian
Plugins must be built with the exact same environment as your Bifrost binary:
Copy
Ask AI
# If Bifrost was built with Alpine/musldocker run --rm \ -v "$PWD:/work" \ -w /work \ golang:1.24.3-alpine3.22 \ sh -c "apk add --no-cache gcc musl-dev && \ go build -buildmode=plugin -o myplugin.so main.go"# If Bifrost was built with Debian/glibc docker run --rm \ -v "$PWD:/work" \ -w /work \ golang:1.24.3-bookworm \ sh -c "apt-get update && apt-get install -y gcc && \ go build -buildmode=plugin -o myplugin.so main.go"
# Start Bifrost with your plugin configured./tmp/bifrost-http -config config.json# Check logs for plugin initialization# Should see: "Plugin loaded successfully: your-plugin-name"
If your plugin imports any of these packages, use compatible versions to avoid runtime issues. Check transports/go.mod, core/go.mod, and framework/go.mod for complete dependency lists.
To see all dependencies used by Bifrost across its three main modules:
Copy
Ask AI
# View transport layer dependenciescat transports/go.mod# View core dependenciescat core/go.mod# View framework dependenciescat framework/go.mod# Or list all dependencies for a specific modulecd transports && go list -m allcd ../core && go list -m allcd ../framework && go list -m all
When creating a plugin, your go.mod should match Bifrost’s Go version:
Copy
Ask AI
module github.com/example/my-plugingo 1.24.3require ( github.com/maximhq/bifrost/core v1.2.26 // Optional: Add framework for advanced features // github.com/maximhq/bifrost/framework v1.1.33 // Add other dependencies as needed, matching versions from Bifrost's go.mod files // github.com/bytedance/sonic v1.14.1 // github.com/rs/zerolog v1.34.0)
Import only the Bifrost modules you need. Most plugins only require core. Use framework if you need access to config stores, vector stores, or other framework features.
cannot load plugin: plugin was built with a different version of package runtime/internal/sys
Cause: Plugin and Bifrost were built with different Go versions.Solution: Use the exact same Go version (Go 1.24.3) for both:
Copy
Ask AI
# Check Go version used for Bifrost./tmp/bifrost-http -version# Verify your Go version matchesgo version # Should output: go version go1.24.3# See full compatibility requirements
# Build both Bifrost and pluginmake build DYNAMIC=1cd examples/plugins/hello-world && make build# Test loading./tmp/bifrost-http -config examples/plugins/hello-world/config.json