-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add Docker image for capiscio/guard sidecar #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| name: Docker | ||
|
|
||
| on: | ||
| push: | ||
| tags: | ||
| - 'v*' | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: 'Tag to build (e.g., v2.3.0)' | ||
| required: true | ||
| type: string | ||
|
|
||
| env: | ||
| REGISTRY: docker.io | ||
| IMAGE_NAME: capiscio/guard | ||
|
|
||
| jobs: | ||
| build-and-push: | ||
| name: Build and Push Docker Image | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@v3 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Extract version | ||
| id: version | ||
| run: | | ||
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| VERSION="${{ inputs.tag }}" | ||
| else | ||
| VERSION="${GITHUB_REF#refs/tags/}" | ||
| fi | ||
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | ||
| echo "version_short=${VERSION#v}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Docker meta | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: | | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| tags: | | ||
| type=semver,pattern={{version}},value=${{ steps.version.outputs.version }} | ||
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.version.outputs.version }} | ||
| type=semver,pattern={{major}},value=${{ steps.version.outputs.version }} | ||
| type=raw,value=latest,enable=${{ github.event_name == 'push' }} | ||
|
|
||
| - name: Login to Docker Hub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
|
|
||
| - name: Build and push | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| platforms: linux/amd64,linux/arm64 | ||
| push: true | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: | | ||
| VERSION=${{ steps.version.outputs.version }} | ||
| COMMIT=${{ github.sha }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
|
|
||
| - name: Update Docker Hub description | ||
| uses: peter-evans/dockerhub-description@v4 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
| repository: ${{ env.IMAGE_NAME }} | ||
| short-description: "Security Sidecar for AI Agents - Universal Authority Layer" | ||
| readme-filepath: ./README.md | ||
|
|
||
| # Verify the image works | ||
| verify: | ||
| name: Verify Image | ||
| needs: build-and-push | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Pull and test image | ||
| run: | | ||
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build-and-push.outputs.version || 'latest' }} | ||
| docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build-and-push.outputs.version || 'latest' }} --version | ||
|
Comment on lines
+88
to
+97
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # syntax=docker/dockerfile:1 | ||
|
|
||
| # ============================================================================ | ||
| # CapiscIO Guard - Security Sidecar for AI Agents | ||
| # ============================================================================ | ||
| # Multi-stage build for a minimal, secure container image. | ||
| # | ||
| # Usage: | ||
| # docker pull capiscio/guard | ||
| # docker run -p 8080:8080 capiscio/guard \ | ||
| # gateway start --port 8080 --target http://your-agent:3000 --registry-url https://registry.capisc.io | ||
| # | ||
| # Build locally: | ||
| # docker build -t capiscio/guard . | ||
| # docker build --build-arg VERSION=v2.3.0 -t capiscio/guard:v2.3.0 . | ||
| # ============================================================================ | ||
|
|
||
| # ----------------------------------------------------------------------------- | ||
| # Stage 1: Build | ||
| # ----------------------------------------------------------------------------- | ||
| FROM golang:1.24-alpine AS builder | ||
|
|
||
| # Build arguments | ||
| ARG VERSION=dev | ||
| ARG COMMIT=unknown | ||
|
|
||
| # Install build dependencies | ||
| RUN apk add --no-cache git ca-certificates | ||
|
|
||
| WORKDIR /src | ||
|
|
||
| # Cache dependencies | ||
| COPY go.mod go.sum ./ | ||
| RUN go mod download | ||
|
|
||
| # Copy source | ||
| COPY . . | ||
|
|
||
| # Build static binary with version info | ||
| RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ | ||
| -ldflags="-w -s -X main.version=${VERSION} -X main.commit=${COMMIT}" \ | ||
|
Comment on lines
+40
to
+41
|
||
| -o /capiscio \ | ||
|
Comment on lines
+41
to
+42
|
||
| ./cmd/capiscio | ||
|
|
||
| # ----------------------------------------------------------------------------- | ||
| # Stage 2: Runtime (distroless for security) | ||
| # ----------------------------------------------------------------------------- | ||
| FROM gcr.io/distroless/static-debian12:nonroot | ||
|
|
||
| # Labels for container registry | ||
| LABEL org.opencontainers.image.title="CapiscIO Guard" | ||
| LABEL org.opencontainers.image.description="Security Sidecar for AI Agents - Universal Authority Layer" | ||
| LABEL org.opencontainers.image.url="https://capisc.io" | ||
| LABEL org.opencontainers.image.source="https://github.com/capiscio/capiscio-core" | ||
| LABEL org.opencontainers.image.vendor="CapiscIO" | ||
| LABEL org.opencontainers.image.licenses="Apache-2.0" | ||
|
|
||
| # Copy binary from builder | ||
| COPY --from=builder /capiscio /capiscio | ||
|
|
||
| # Expose default gateway port | ||
| EXPOSE 8080 | ||
|
|
||
| # Health check - verify binary is runnable | ||
| HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ | ||
| CMD ["/capiscio", "--version"] | ||
|
|
||
| # Run as non-root user (distroless nonroot = uid 65532) | ||
| USER nonroot:nonroot | ||
|
|
||
| # Default entrypoint | ||
| ENTRYPOINT ["/capiscio"] | ||
|
|
||
| # Default command (show help) | ||
| CMD ["--help"] | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,38 @@ | ||||||
| .PHONY: all build-cli build-python test clean proto docs | ||||||
| .PHONY: all build-cli build-python test clean proto docs docker docker-build docker-run | ||||||
|
||||||
| .PHONY: all build-cli build-python test clean proto docs docker docker-build docker-run | |
| .PHONY: all build-cli build-python test clean proto docs docker docker-build docker-run docker-push |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docker-run passes --target $(TARGET) but TARGET has no default, so running make docker-run without setting TARGET will pass an empty value and likely fail. Consider setting a sensible default (e.g., TARGET ?= http://host.docker.internal:3000) or erroring out when TARGET is unset.
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make docker-run prints an example using TARGET=http://localhost:3000, but from inside the container localhost refers to the container itself, not the host. This will typically fail unless the user runs with host networking. Consider defaulting TARGET to http://host.docker.internal:3000 (with a Linux note or --add-host=host.docker.internal:host-gateway) or erroring out when TARGET is empty/localhost to avoid a confusing failure.
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -121,6 +121,69 @@ curl -H "X-Capiscio-Badge: $(cat badge.jwt)" http://localhost:8080/api/v1/agent | |||||||||||||
| curl http://localhost:8080/api/v1/agent | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## 🐳 Docker | ||||||||||||||
|
|
||||||||||||||
| The **CapiscIO Guard** is available as a Docker image for easy deployment: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| docker pull capiscio/guard | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ### Quick Start with Docker | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| # Run the gateway in front of your agent | ||||||||||||||
| docker run -p 8080:8080 capiscio/guard \ | ||||||||||||||
| gateway start \ | ||||||||||||||
| --port 8080 \ | ||||||||||||||
| --target http://host.docker.internal:3000 \ | ||||||||||||||
| --registry-url https://registry.capisc.io | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
|
||||||||||||||
| > **Note for Linux users:** `host.docker.internal` is not available by default on many Linux Docker setups. | |
| > You can either: | |
| > - add `--add-host=host.docker.internal:host-gateway` to the `docker run` command so the container can reach your host at `http://host.docker.internal:3000`, or | |
| > - run your agent in another container on a user-defined Docker network and use the container name as the `--target` (as shown in the Docker Compose example below). |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The local test command suggests make docker-run TARGET=http://localhost:3000, but localhost inside the Guard container points back to the container, not your host agent, so this example will usually fail. Consider changing the example to TARGET=http://host.docker.internal:3000 (and add a note for Linux users, e.g. --add-host=host.docker.internal:host-gateway), or recommend using the Docker Compose example for local testing.
| # Test locally | |
| make docker-run TARGET=http://localhost:3000 | |
| # Test locally (container -> host agent) | |
| # macOS/Windows: host.docker.internal resolves to the host | |
| # Linux: ensure your docker run includes --add-host=host.docker.internal:host-gateway | |
| make docker-run TARGET=http://host.docker.internal:3000 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,7 @@ import ( | |
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| var version = "2.3.1" | ||
| var version = "2.4.0" | ||
|
||
|
|
||
| var rootCmd = &cobra.Command{ | ||
| Use: "capiscio", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,14 +41,16 @@ service BadgeService { | |
| rpc StartKeeper(StartKeeperRequest) returns (stream KeeperEvent); | ||
| } | ||
|
|
||
| // Trust level for badges (RFC-002 v1.1) | ||
| // Trust level for badges (RFC-002 §5) | ||
| // NOTE: Proto enum ordinals (1-5) map to RFC-002 level strings ("0"-"4") | ||
| // The badge JWT `vc.credentialSubject.level` uses the RFC string values | ||
| enum TrustLevel { | ||
| TRUST_LEVEL_UNSPECIFIED = 0; | ||
| TRUST_LEVEL_SELF_SIGNED = 1; // Self-signed (Level 0, did:key) | ||
| TRUST_LEVEL_DV = 2; // Domain Validated (Level 1) | ||
| TRUST_LEVEL_OV = 3; // Organization Validated (Level 2) | ||
| TRUST_LEVEL_EV = 4; // Extended Validated (Level 3) | ||
| TRUST_LEVEL_CV = 5; // Community Vouched (Level 4) | ||
| TRUST_LEVEL_SELF_SIGNED = 1; // RFC-002 Level "0": Self-Signed (SS) - did:key, iss == sub | ||
| TRUST_LEVEL_DV = 2; // RFC-002 Level "1": Registered (REG) - account registration | ||
| TRUST_LEVEL_OV = 3; // RFC-002 Level "2": Domain Validated (DV) - DNS/HTTP proof | ||
| TRUST_LEVEL_EV = 4; // RFC-002 Level "3": Organization Validated (OV) - legal entity | ||
| TRUST_LEVEL_CV = 5; // RFC-002 Level "4": Extended Validated (EV) - security audit | ||
|
Comment on lines
+44
to
+53
|
||
| } | ||
|
|
||
| // Badge claims structure | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For
workflow_dispatch, the workflow accepts an inputtagbutactions/checkoutdoesn’t check out that tag/commit (noref:set). This will build whatever is on the default branch while tagging/pushing it as the requested release tag. Setwith: ref: ${{ inputs.tag }}(or resolve the tag to a SHA) whengithub.event_name == 'workflow_dispatch'.