Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions deploy-k8s/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Ignore real manifests (contain user-specific values and secrets)
*.yaml
!*-sample.yaml
91 changes: 91 additions & 0 deletions deploy-k8s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# ZeroClaw OpenShift deployment

Deploy a minimal ZeroClaw agent on OpenShift with an external LLM
provider (Anthropic, OpenAI, or any OpenAI-compatible API).

## Prerequisites

- `oc` CLI authenticated to your OpenShift cluster
- Container image pushed to an accessible registry
- API key for your LLM provider

## Quick start

1. Copy the sample manifests to create your real ones:

```bash
for f in deploy-k8s/*-sample.yaml; do cp "$f" "${f/-sample/}"; done
```

1. Edit `secret.yaml` and replace `REPLACE_WITH_YOUR_API_KEY` with
your actual API key
1. Update the `image` field in `deployment.yaml` to point to your
registry (e.g., `ghcr.io/youruser/zeroclaw:latest`)
1. Update the `namespace` in all files if you want a different name
1. Optionally edit `configmap.yaml` to change the provider or model
1. Apply all manifests:

```bash
oc apply -f deploy-k8s/
```

The real `.yaml` files are gitignored so your secrets and
customizations stay local.

## Verification

Check that the pod is running and the route is accessible:

```bash
oc -n zeroclaw get pods
oc -n zeroclaw get route zeroclaw
```

Test the health endpoint:

```bash
ROUTE=$(oc -n zeroclaw get route zeroclaw -o jsonpath='{.spec.host}')
curl -sf "https://${ROUTE}/health"
```

Send a test message:

```bash
curl -X POST "https://${ROUTE}/webhook" \
-H "Content-Type: application/json" \
-d '{"message": "hello, what model are you?"}'
```

## Configuration

Edit `configmap.yaml` to change runtime settings:

| Setting | Field | Default |
| ------- | ----- | ------- |
| LLM provider | `default_provider` | `anthropic` |
| Model | `default_model` | `claude-sonnet-4-20250514` |
| Temperature | `default_temperature` | `0.7` |
| Autonomy level | `autonomy.level` | `supervised` |

After editing, re-apply and restart the pod:

```bash
oc apply -f deploy-k8s/configmap.yaml
oc -n zeroclaw rollout restart deployment zeroclaw
```

## Notes

- **State is ephemeral.** Both `state` and `workspace` volumes use
`emptyDir` — agent memory and session history do not persist across
pod restarts. For production, replace these with
PersistentVolumeClaims.
- **Vanilla Kubernetes.** The `Route` object is OpenShift-specific. On
vanilla Kubernetes, replace `route-sample.yaml` with a Kubernetes
Ingress targeting port 42617.

## Cleanup

```bash
oc delete namespace zeroclaw
```
26 changes: 26 additions & 0 deletions deploy-k8s/configmap-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: zeroclaw-config
namespace: zeroclaw
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
data:
config.toml: |
workspace_dir = "/zeroclaw-data/workspace"
config_path = "/zeroclaw-data/.zeroclaw/config.toml"
default_provider = "anthropic"
default_model = "claude-sonnet-4-20250514"
default_temperature = 0.7

[gateway]
port = 42617
host = "[::]"
allow_public_bind = true
require_pairing = true
web_dist_dir = "/zeroclaw-data/web/dist"

[autonomy]
level = "supervised"
auto_approve = ["file_read", "file_write", "file_edit", "memory_recall", "memory_store", "web_search_tool", "web_fetch", "calculator", "glob_search", "content_search", "image_info", "weather", "git_operations"]
89 changes: 89 additions & 0 deletions deploy-k8s/deployment-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zeroclaw
namespace: zeroclaw
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: zeroclaw
template:
metadata:
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
spec:
containers:
- name: zeroclaw
# For production, pin to a version tag (e.g. ghcr.io/zeroclaw-labs/zeroclaw:v0.7.1)
image: ghcr.io/zeroclaw-labs/zeroclaw:latest
args: ["daemon"]
ports:
- name: gateway
containerPort: 42617
protocol: TCP
env:
- name: API_KEY
valueFrom:
secretKeyRef:
name: zeroclaw-api-key
key: api-key
- name: LANG
value: "C.UTF-8"
- name: HOME
value: "/zeroclaw-data"
- name: ZEROCLAW_WORKSPACE
value: "/zeroclaw-data/workspace"
- name: ZEROCLAW_GATEWAY_PORT
value: "42617"
volumeMounts:
- name: state
mountPath: /zeroclaw-data/.zeroclaw
- name: config
mountPath: /zeroclaw-data/.zeroclaw/config.toml
subPath: config.toml
readOnly: true
- name: workspace
mountPath: /zeroclaw-data/workspace
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "2"
livenessProbe:
httpGet:
path: /health
port: gateway
initialDelaySeconds: 10
periodSeconds: 60
timeoutSeconds: 10
readinessProbe:
httpGet:
path: /health
port: gateway
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumes:
- name: config
configMap:
name: zeroclaw-config
- name: state
emptyDir: {}
- name: workspace
emptyDir: {}
7 changes: 7 additions & 0 deletions deploy-k8s/namespace-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: zeroclaw
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
18 changes: 18 additions & 0 deletions deploy-k8s/route-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: zeroclaw
namespace: zeroclaw
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
spec:
to:
kind: Service
name: zeroclaw
weight: 100
port:
targetPort: gateway
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
11 changes: 11 additions & 0 deletions deploy-k8s/secret-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: zeroclaw-api-key
namespace: zeroclaw
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
type: Opaque
stringData:
api-key: "REPLACE_WITH_YOUR_API_KEY"
17 changes: 17 additions & 0 deletions deploy-k8s/service-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: zeroclaw
namespace: zeroclaw
labels:
app.kubernetes.io/name: zeroclaw
app.kubernetes.io/part-of: zeroclaw
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: zeroclaw
ports:
- name: gateway
port: 42617
targetPort: gateway
protocol: TCP
Loading