# Single-replica shared proxy with its state (SQLite) on one volume. # For two or more replicas, use kubernetes-ha.yaml (middleware store in Postgres). # Apply into whatever namespace you like: kubectl apply -n caveman -f kubernetes.yaml apiVersion: v1 kind: Secret metadata: name: caveman-proxy type: Opaque stringData: ANTHROPIC_API_KEY: REPLACE_WITH_YOUR_PROVIDER_KEY # CAVEMAN_AUTH_TOKEN and CAVEMAN_METRICS_TOKEN are deliberately absent: any # placeholder written here passes validation and ships as a real, guessable # token. The auth token is this manifest's only inbound credential (clients send # it as x-cave-api-key or Bearer), so without it the pod exits at startup on its # default 0.0.0.0 listen. The metrics token gates /metrics, which is otherwise # open. Generate both and put them in this same Secret BEFORE applying this file: # kubectl create secret generic caveman-proxy \ # --from-literal=CAVEMAN_AUTH_TOKEN="$(openssl rand -hex 32)" \ # --from-literal=CAVEMAN_METRICS_TOKEN="$(openssl rand -hex 32)" # A later `kubectl apply -f kubernetes.yaml` adds the provider key beside them # and leaves the tokens untouched. --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: caveman-proxy-data spec: accessModes: ["ReadWriteOnce"] resources: requests: # Two stores are capped: the middleware store (576 MiB by default, # CAVEMAN_MIDDLEWARE_MAX_BYTES) and the proxy's recovery store (512 MiB, # CAVEMAN_CCR_MAX_BYTES). On top come SQLite indexes, free pages and the # WAL, the spend database (one metadata row per proxied request, never # capped), and 32 MiB of rotated proxy.log. 5 Gi leaves room for all of # it; raise it with the caps. docs/technical/deploy.md#sizing storage: 5Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: caveman-proxy spec: # One replica, deliberately, and Recreate: state is SQLite on a # ReadWriteOnce volume, which takes exactly one writer. A RollingUpdate would # start the new pod beside the old one on the same database. Need more # replicas or zero-downtime rollouts? Use kubernetes-ha.yaml. replicas: 1 strategy: type: Recreate selector: matchLabels: app: caveman-proxy template: metadata: labels: app: caveman-proxy spec: securityContext: runAsNonRoot: true runAsUser: 65532 # Makes the mounted PVC writable by uid 65532. fsGroup: 65532 seccompProfile: type: RuntimeDefault containers: - name: caveman-proxy # Pinned by digest to the signed bin-v2.1.0 image. image: ghcr.io/juliusbrussee/caveman-proxy:bin-v2.1.0@sha256:da293df3c6929c7b31eba580f7af74da0448e83a55a973cca86e81041877d0bb imagePullPolicy: IfNotPresent ports: - containerPort: 8787 envFrom: - secretRef: name: caveman-proxy env: # Bedrock users: set the region and give the pod's ServiceAccount # an IRSA role with bedrock:InvokeModel*. # - name: AWS_REGION # value: us-east-1 # The proxy routes record (byte-safe pass-through); the framework # middleware compresses for clients that ask it to. - name: CAVEMAN_MODE value: record - name: CAVEMAN_MIDDLEWARE_MODE value: compress volumeMounts: - name: data mountPath: /data # The readiness body reports "middleware":"degraded" when its store # cannot take a write (a full disk or a read-only volume); the check # allows 2 s. The pod stays ready: the provider routes still serve. readinessProbe: httpGet: path: /health/ready port: 8787 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 livenessProbe: httpGet: path: /health/live port: 8787 initialDelaySeconds: 10 securityContext: # The binary writes only under CAVEMAN_HOME=/data, which is the PVC. readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: ["ALL"] resources: requests: cpu: 100m memory: 256Mi limits: cpu: "1" memory: 1Gi volumes: - name: data persistentVolumeClaim: claimName: caveman-proxy-data --- apiVersion: v1 kind: Service metadata: name: caveman-proxy spec: type: ClusterIP selector: app: caveman-proxy ports: - port: 8787 targetPort: 8787 --- # Only pods labelled caveman-client: "true" reach the proxy (label your # Prometheus too, or add a rule for it); the proxy reaches DNS and provider APIs # over HTTPS. EKS Pod Identity also needs egress to 169.254.170.23/32 on port 80. # Kubelet probes are not affected. apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: caveman-proxy spec: podSelector: matchLabels: app: caveman-proxy policyTypes: ["Ingress", "Egress"] ingress: - from: - podSelector: matchLabels: caveman-client: "true" ports: - port: 8787 protocol: TCP egress: - ports: - port: 53 protocol: UDP - port: 53 protocol: TCP - ports: - port: 443 protocol: TCP