Skip to content

Commit 4eba559

Browse files
committed
feat: create the claw machine, run openclaw (and friends) on kubernetes
0 parents  commit 4eba559

257 files changed

Lines changed: 33525 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.config/Tiltfile

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# === ClawMachine Development ===
2+
# Requirements: kind cluster (with kind-config.yaml for Cilium), docker, helm, kubectl, mise
3+
# Cluster setup: kind create cluster --name clawmachine --config kind-config.yaml
4+
#
5+
# Usage:
6+
# tilt up # interactive dev (ESO + Cilium)
7+
# tilt ci # CI mode (build + deploy + verify)
8+
# tilt up -- --no-cilium # skip Cilium
9+
# tilt up -- --1password-connect # include 1Password Connect (needs credentials)
10+
# tilt up -- --no-external-secrets # skip ESO
11+
12+
load('ext://namespace', 'namespace_create')
13+
load('ext://helm_remote', 'helm_remote')
14+
15+
# Parse args — all three infra deps enabled by default
16+
config.define_bool('cilium')
17+
config.define_bool('external-secrets')
18+
config.define_bool('1password-connect')
19+
cfg = config.parse()
20+
enable_cilium = cfg.get('cilium', True)
21+
enable_eso = cfg.get('external-secrets', True)
22+
enable_op_connect = cfg.get('1password-connect', False)
23+
24+
# --- Namespaces ---
25+
namespace_create('claw-machine')
26+
27+
# --- Build Steps ---
28+
29+
# Chart packaging — embeds bot charts into the Go binary
30+
local_resource(
31+
'chart-package',
32+
cmd='cd .. && mise run charts',
33+
deps=[
34+
'../control-plane/charts/picoclaw',
35+
'../control-plane/charts/ironclaw',
36+
'../control-plane/charts/clawmachine',
37+
'../control-plane/charts/openclaw',
38+
'../control-plane/charts/busybox',
39+
'../control-plane/charts/vendor',
40+
],
41+
labels=['build'],
42+
)
43+
44+
# --- Bot Images (deployed at runtime by the control plane, not by Tilt) ---
45+
# Use local_resource so they build, load into kind, and show up in the UI.
46+
47+
local_resource(
48+
'picoclaw-image',
49+
cmd='docker build -t ghcr.io/zackerydev/picoclaw:0.1.2 ../docker/picoclaw && kind load docker-image ghcr.io/zackerydev/picoclaw:0.1.2 --name clawmachine',
50+
deps=['../docker/picoclaw'],
51+
labels=['build'],
52+
)
53+
54+
local_resource(
55+
'ironclaw-image',
56+
cmd='docker build -t ghcr.io/zackerydev/ironclaw:0.11.1 ../docker/ironclaw && kind load docker-image ghcr.io/zackerydev/ironclaw:0.11.1 --name clawmachine',
57+
deps=['../docker/ironclaw'],
58+
labels=['build'],
59+
)
60+
61+
local_resource(
62+
'toolbox-image',
63+
cmd='docker build -t ghcr.io/zackerydev/clawmachine-toolbox:0.1.0 ../docker/toolbox && kind load docker-image ghcr.io/zackerydev/clawmachine-toolbox:0.1.0 --name clawmachine',
64+
deps=['../docker/toolbox'],
65+
labels=['build'],
66+
)
67+
68+
local_resource(
69+
'openclaw-image',
70+
cmd='docker build -t ghcr.io/zackerydev/openclaw:2026.2.21 ../docker/openclaw && kind load docker-image ghcr.io/zackerydev/openclaw:2026.2.21 --name clawmachine',
71+
deps=['../docker/openclaw'],
72+
labels=['build'],
73+
)
74+
75+
# --- Control Plane ---
76+
77+
docker_build(
78+
'clawmachine',
79+
'../control-plane',
80+
ignore=[
81+
'node_modules',
82+
'.gocache',
83+
'styles',
84+
'e2e',
85+
'.air.toml',
86+
'.prettierrc.yaml',
87+
'.env.example',
88+
'compose.yml',
89+
'package.yaml',
90+
'pnpm-lock.yaml',
91+
],
92+
live_update=[
93+
sync('../control-plane/templates', '/app/templates'),
94+
sync('../control-plane/static', '/app/static'),
95+
],
96+
)
97+
98+
# Backup/restore jobs in bot charts default to ghcr.io/zackerydev/clawmachine:0.1.0.
99+
# Mirror the local control-plane image to that ref inside kind to avoid ImagePullBackOff in dev.
100+
local_resource(
101+
'clawmachine-backup-image',
102+
cmd=' && '.join([
103+
'docker image inspect clawmachine:latest >/dev/null 2>&1 || docker build -t clawmachine:latest ../control-plane',
104+
'docker tag clawmachine:latest ghcr.io/zackerydev/clawmachine:0.1.0',
105+
'kind load docker-image ghcr.io/zackerydev/clawmachine:0.1.0 --name clawmachine',
106+
]),
107+
deps=['../control-plane'],
108+
labels=['build'],
109+
)
110+
111+
# --- Infrastructure ---
112+
113+
# External Secrets Operator (default: on)
114+
# Must wait for Cilium CNI — without networking, pods can't reach the API server
115+
if enable_eso:
116+
helm_remote(
117+
'external-secrets',
118+
repo_url='https://charts.external-secrets.io',
119+
release_name='external-secrets',
120+
namespace='external-secrets',
121+
create_namespace=True,
122+
)
123+
if enable_cilium:
124+
k8s_resource('external-secrets', resource_deps=['cilium'], labels=['infra'])
125+
k8s_resource('external-secrets-webhook', resource_deps=['cilium'], labels=['infra'])
126+
k8s_resource('external-secrets-cert-controller', resource_deps=['cilium'], labels=['infra'])
127+
128+
# Cilium CNI (default: on — Kind's kindnet doesn't enforce NetworkPolicies)
129+
# Cilium provides DNS-aware network policies via CiliumNetworkPolicy + toFQDNs,
130+
# plus Hubble for flow observability. Replaces Calico as of HL-241.
131+
if enable_cilium:
132+
local_resource(
133+
'cilium',
134+
cmd=' && '.join([
135+
'helm repo add cilium https://helm.cilium.io/ 2>/dev/null || true',
136+
'helm repo update cilium',
137+
'helm upgrade --install cilium cilium/cilium'
138+
+ ' --namespace kube-system'
139+
+ ' --set operator.replicas=1'
140+
+ ' --set hubble.relay.enabled=true'
141+
+ ' --set hubble.ui.enabled=true'
142+
+ ' --set kubeProxyReplacement=false'
143+
+ ' --set socketLB.hostNamespaceOnly=true'
144+
+ ' --wait --timeout 120s',
145+
'echo "Waiting for Cilium daemonset..."',
146+
'kubectl -n kube-system rollout status daemonset/cilium --timeout=120s',
147+
]),
148+
labels=['infra'],
149+
)
150+
local_resource(
151+
'hubble-ui-port-forward',
152+
cmd='kubectl -n kube-system get svc hubble-ui >/dev/null',
153+
serve_cmd='kubectl -n kube-system port-forward svc/hubble-ui 12000:80',
154+
resource_deps=['cilium'],
155+
labels=['infra'],
156+
)
157+
158+
# 1Password Connect Server (default: off)
159+
# Pod won't be ready without op-credentials secret — that's expected in dev.
160+
# Ignore readiness so tilt ci doesn't block on it.
161+
if enable_op_connect:
162+
helm_remote(
163+
'connect',
164+
repo_url='https://1password.github.io/connect-helm-charts',
165+
release_name='connect',
166+
namespace='1password',
167+
create_namespace=True,
168+
)
169+
k8s_resource(
170+
'onepassword-connect',
171+
pod_readiness='ignore',
172+
labels=['infra'],
173+
)
174+
175+
# --- LocalStack (S3-compatible endpoint for backup/restore testing) ---
176+
177+
k8s_yaml(blob("""
178+
apiVersion: apps/v1
179+
kind: Deployment
180+
metadata:
181+
name: localstack
182+
namespace: claw-machine
183+
labels:
184+
app: localstack
185+
spec:
186+
replicas: 1
187+
selector:
188+
matchLabels:
189+
app: localstack
190+
template:
191+
metadata:
192+
labels:
193+
app: localstack
194+
spec:
195+
containers:
196+
- name: localstack
197+
image: localstack/localstack:latest
198+
ports:
199+
- containerPort: 4566
200+
env:
201+
- name: SERVICES
202+
value: s3
203+
- name: DEFAULT_REGION
204+
value: us-east-1
205+
readinessProbe:
206+
httpGet:
207+
path: /_localstack/health
208+
port: 4566
209+
initialDelaySeconds: 5
210+
periodSeconds: 10
211+
---
212+
apiVersion: v1
213+
kind: Service
214+
metadata:
215+
name: localstack
216+
namespace: claw-machine
217+
labels:
218+
app: localstack
219+
spec:
220+
selector:
221+
app: localstack
222+
ports:
223+
- port: 4566
224+
targetPort: 4566
225+
"""))
226+
227+
k8s_resource(
228+
'localstack',
229+
port_forwards=['4566:4566'],
230+
labels=['infra'],
231+
)
232+
233+
# --- Deploy ClawMachine ---
234+
235+
k8s_yaml(helm(
236+
'../control-plane/charts/clawmachine',
237+
name='clawmachine',
238+
namespace='claw-machine',
239+
set=[
240+
'image.repository=clawmachine',
241+
'image.tag=latest',
242+
'image.pullPolicy=Never',
243+
'replicaCount=1',
244+
'devMode=true',
245+
# Allow Tilt live_update file sync to write to /app/templates and /app/static.
246+
# readOnlyRootFilesystem blocks kubectl exec writes used by live_update.
247+
'securityContext.readOnlyRootFilesystem=false',
248+
],
249+
))
250+
251+
k8s_resource(
252+
'clawmachine',
253+
port_forwards=['8080:8080'],
254+
resource_deps=['chart-package'],
255+
labels=['app'],
256+
)

.config/goreleaser.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
version: 2
2+
3+
project_name: clawmachine
4+
5+
before:
6+
hooks:
7+
- mise run charts
8+
9+
builds:
10+
- id: clawmachine
11+
dir: control-plane
12+
main: ./cmd/clawmachine/
13+
binary: clawmachine
14+
ldflags:
15+
- -s -w
16+
- -X main.version={{ .Version }}
17+
env:
18+
- CGO_ENABLED=0
19+
goos:
20+
- linux
21+
- darwin
22+
- windows
23+
goarch:
24+
- amd64
25+
- arm64
26+
ignore:
27+
- goos: windows
28+
goarch: arm64
29+
30+
archives:
31+
- id: default
32+
formats:
33+
- tar.gz
34+
format_overrides:
35+
- goos: windows
36+
formats:
37+
- zip
38+
name_template: >-
39+
{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}
40+
files:
41+
- README.md
42+
- LICENSE
43+
44+
checksum:
45+
name_template: checksums.txt
46+
47+
changelog:
48+
sort: asc
49+
filters:
50+
exclude:
51+
- "^docs:"
52+
- "^test:"
53+
- "^ci:"
54+
- "^chore:"
55+
56+
homebrew_casks:
57+
- repository:
58+
owner: zackerydev
59+
name: homebrew-tap
60+
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
61+
directory: Casks
62+
homepage: https://github.com/zackerydev/clawmachine
63+
description: Kubernetes-native dashboard for AI bots
64+
65+
dockers_v2:
66+
- dockerfile: control-plane/Dockerfile
67+
images:
68+
- ghcr.io/zackerydev/clawmachine
69+
tags:
70+
- "{{ .Version }}"
71+
- latest
72+
platforms:
73+
- linux/amd64
74+
- linux/arm64
75+
labels:
76+
org.opencontainers.image.title: "{{ .ProjectName }}"
77+
org.opencontainers.image.version: "{{ .Version }}"
78+
org.opencontainers.image.source: "{{ .GitURL }}"
79+
extra_files:
80+
- control-plane
81+
82+
release:
83+
github:
84+
owner: zackerydev
85+
name: clawmachine
86+
prerelease: auto
87+
name_template: "v{{ .Version }}"

.config/lefthook.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# https://lefthook.dev
2+
pre-commit:
3+
parallel: true
4+
commands:
5+
vet:
6+
glob: "*.go"
7+
run: mise run vet
8+
fix:
9+
glob: "*.go"
10+
run: mise run fix
11+
lint:
12+
glob: "*.go"
13+
run: mise run lint
14+
charts:
15+
glob: "charts/**/*"
16+
run: mise run charts
17+
actionlint:
18+
glob: ".github/workflows/*.{yml,yaml}"
19+
run: actionlint
20+
21+
# pre-push:
22+
# commands:
23+
# vet:
24+
# run: mise run vet
25+
# lint:
26+
# run: mise run lint
27+
# fix:
28+
# run: mise run fix
29+
# test:
30+
# run: mise run test
31+
# e2e:
32+
# run: mise run e2e

0 commit comments

Comments
 (0)