feat: add Dockerfile, docker-compose, and Helm chart

Multi-stage Docker build (node + golang + scratch), ~30MB image.
docker-compose.yml for local development with volume persistence.
Helm chart with configurable Deployment, Service, PVC, Ingress,
and Prometheus ServiceMonitor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Algis Dumbris
2026-03-13 20:17:28 +02:00
co-authored by Claude Opus 4.6
parent a187cb86d1
commit b8460dcfa4
11 changed files with 350 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
.git
bin/
data/
web/node_modules/
web/.svelte-kit/
*.test
*.out
.env
.env.*
.claude/
.specify/
__pycache__/
+26
View File
@@ -0,0 +1,26 @@
# Stage 1: Build web frontend
FROM node:20-alpine AS web-builder
WORKDIR /app/web
COPY web/package*.json ./
RUN npm ci
COPY web/ ./
RUN npm run build
# Stage 2: Build Go binary
FROM golang:1.23-alpine AS go-builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
COPY --from=web-builder /app/web/build internal/web/dist/
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /synapbus ./cmd/synapbus/
# Stage 3: Runtime
FROM scratch
COPY --from=go-builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=go-builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=go-builder /synapbus /synapbus
EXPOSE 8080
VOLUME ["/data"]
ENTRYPOINT ["/synapbus"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8080", "--data", "/data"]
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v2
name: synapbus
description: Agent-to-agent messaging for AI swarms
type: application
version: 0.1.0
appVersion: "0.1.0"
@@ -0,0 +1,51 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "synapbus.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "synapbus.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "synapbus.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "synapbus.labels" -}}
helm.sh/chart: {{ include "synapbus.chart" . }}
{{ include "synapbus.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "synapbus.selectorLabels" -}}
app.kubernetes.io/name: {{ include "synapbus.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
@@ -0,0 +1,78 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "synapbus.fullname" . }}
labels:
{{- include "synapbus.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "synapbus.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "synapbus.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- serve
- --host
- "0.0.0.0"
- --port
- "8080"
- --data
- /data
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: http
initialDelaySeconds: 3
periodSeconds: 5
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "synapbus.fullname" . }}
{{- else }}
emptyDir: {}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -0,0 +1,41 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "synapbus.fullname" . }}
labels:
{{- include "synapbus.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "synapbus.fullname" $ }}
port:
name: http
{{- end }}
{{- end }}
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if .Values.persistence.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "synapbus.fullname" . }}
labels:
{{- include "synapbus.labels" . | nindent 4 }}
spec:
accessModes:
{{- toYaml .Values.persistence.accessModes | nindent 4 }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- end }}
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "synapbus.fullname" . }}
labels:
{{- include "synapbus.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "synapbus.selectorLabels" . | nindent 4 }}
@@ -0,0 +1,20 @@
{{- if .Values.metrics.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "synapbus.fullname" . }}
labels:
{{- include "synapbus.labels" . | nindent 4 }}
{{- with .Values.metrics.serviceMonitor.additionalLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
selector:
matchLabels:
{{- include "synapbus.selectorLabels" . | nindent 6 }}
endpoints:
- port: http
path: /metrics
interval: {{ .Values.metrics.serviceMonitor.interval }}
scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }}
{{- end }}
+66
View File
@@ -0,0 +1,66 @@
replicaCount: 1
image:
repository: ghcr.io/smart-mcp-proxy/synapbus
pullPolicy: IfNotPresent
tag: "latest"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: false
name: ""
service:
type: ClusterIP
port: 8080
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: synapbus.local
paths:
- path: /
pathType: Prefix
tls: []
# - secretName: synapbus-tls
# hosts:
# - synapbus.local
persistence:
enabled: true
storageClass: ""
accessModes:
- ReadWriteOnce
size: 1Gi
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
env:
SYNAPBUS_LOG_LEVEL: info
SYNAPBUS_METRICS: "true"
metrics:
enabled: true
serviceMonitor:
interval: 30s
scrapeTimeout: 10s
additionalLabels: {}
nodeSelector: {}
tolerations: []
affinity: {}
+18
View File
@@ -0,0 +1,18 @@
services:
synapbus:
build: .
ports:
- "8080:8080"
volumes:
- synapbus-data:/data
environment:
- SYNAPBUS_LOG_LEVEL=info
- SYNAPBUS_METRICS=true
restart: unless-stopped
# Note: healthcheck is omitted because the runtime image uses scratch,
# which has no shell, wget, or curl. Use an external health checker
# (e.g., Docker healthcheck with a sidecar, or orchestrator-level probes)
# to poll GET /healthz and GET /readyz on port 8080.
volumes:
synapbus-data: