Skip to content

Commit 3901dac

Browse files
pluginmdclaude
andcommitted
feat(platform): add multi-tenant orchestrator (zcplatform)
Introduces zcplatform — a Rust/Axum backend + React/TypeScript SPA that provisions, isolates, and manages multiple ZeroClaw agent instances as Docker containers with per-tenant config, channels, and monitoring. Backend (Rust/Axum): - OTP + JWT auth with RBAC (Viewer/Contributor/Manager/Owner + super-admin) - XChaCha20-Poly1305 vault encryption for secrets at rest - Docker container lifecycle with security hardening (cap-drop ALL, read-only rootfs, resource limits) - Caddy reverse proxy integration (wildcard subdomains, auto-HTTPS) - Background tasks: health checker (30s), usage collector (5min), resource collector (60s) - SQLite with WAL mode, single writer + N readers pool - Tenant provisioning pipeline: slug/port/UID allocation → filesystem → config render → container → health check - Full audit logging Frontend (React/TypeScript/Vite): - Dark-themed admin SPA with Tailwind CSS v4 - Setup wizard: 4-step tenant creation (name → provider → channels → deploy) - Tenant detail: Overview, Config, Channels, Usage, Members tabs - Schema-driven forms for 13 channel types and 14 AI providers - React Query for server state management Documentation (12 files): - Vision, architecture, user flows, backend/frontend internals - Security model, deployment guide, configuration reference - Scaling & operations, service provider guide, API reference Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent 1342956 commit 3901dac

114 files changed

Lines changed: 21690 additions & 1 deletion

File tree

Some content is hidden

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = [".", "crates/robot-kit"]
2+
members = [".", "crates/robot-kit", "multi_tenants"]
33
resolver = "2"
44

55
[package]

multi_tenants/.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Local config (may contain secrets)
2+
platform.toml
3+
4+
# Runtime data
5+
data/
6+
7+
# Build artifacts (regenerated by `npm run build`)
8+
static/
9+
10+
# Planning docs (internal)
11+
docs/PLAN.md
12+
docs/assets/
13+
14+
# Rust build artifacts
15+
target/

multi_tenants/Cargo.toml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
[package]
2+
name = "zcplatform"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "ZeroClaw Multi-Tenant Platform"
6+
7+
[[bin]]
8+
name = "zcplatform"
9+
path = "src/main.rs"
10+
11+
[dependencies]
12+
# Web framework
13+
axum = { version = "0.8", features = ["macros"] }
14+
tower = "0.5"
15+
tower-http = { version = "0.6", features = ["cors", "trace", "fs"] }
16+
17+
# Async runtime
18+
tokio = { version = "1.42", features = ["rt-multi-thread", "macros", "signal", "time", "process", "fs", "sync"] }
19+
20+
# CLI
21+
clap = { version = "4.5", features = ["derive"] }
22+
23+
# Database (must match workspace root rusqlite version to avoid sqlite3 link conflict)
24+
rusqlite = { version = "0.37", features = ["bundled", "backup"] }
25+
26+
# Serialization
27+
serde = { version = "1.0", features = ["derive"] }
28+
serde_json = "1.0"
29+
toml = "1.0"
30+
31+
# Encryption
32+
chacha20poly1305 = "0.10"
33+
rand = "0.10"
34+
35+
# Auth
36+
jsonwebtoken = "9"
37+
sha2 = "0.10"
38+
subtle = "2.6"
39+
hex = "0.4"
40+
41+
# Template engine (config rendering)
42+
tera = "1.20"
43+
44+
# Error handling
45+
anyhow = "1.0"
46+
thiserror = "2.0"
47+
48+
# UUID
49+
uuid = { version = "1.11", features = ["v4"] }
50+
51+
# Time
52+
chrono = { version = "0.4", features = ["serde"] }
53+
54+
# Logging
55+
tracing = "0.1"
56+
tracing-subscriber = { version = "0.3", features = ["fmt", "ansi", "env-filter"] }
57+
58+
# HTTP client (for health checks, Caddy API)
59+
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
60+
61+
# Email (SMTP)
62+
lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1-rustls-tls", "builder", "hostname"] }

multi_tenants/deploy/caddy.env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Caddy environment variables
2+
# Place at /etc/caddy/caddy.env and restart Caddy
3+
4+
# Required for wildcard TLS via Cloudflare DNS challenge
5+
CLOUDFLARE_API_TOKEN=your-cloudflare-api-token-here
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ZeroClaw Multi-Tenant Platform Configuration
2+
# Copy to /opt/zcplatform/platform.toml and customize.
3+
4+
# Platform API server
5+
host = "127.0.0.1"
6+
port = 8080
7+
8+
# Database (SQLite, WAL mode)
9+
database_path = "/opt/zcplatform/data/platform.db"
10+
11+
# Encryption key file
12+
master_key_path = "/opt/zcplatform/data/master.key"
13+
14+
# Tenant data directory
15+
data_dir = "/opt/zcplatform/data/tenants"
16+
17+
# Docker
18+
docker_image = "zeroclaw-tenant:latest"
19+
docker_network = "zcplatform-internal"
20+
port_range = [10001, 10999]
21+
uid_range = [10001, 10999]
22+
23+
# Domain (enables Caddy subdomain routing)
24+
# domain = "example.com"
25+
26+
# Caddy admin API
27+
caddy_api_url = "http://localhost:2019"
28+
29+
# JWT secret (auto-generated during bootstrap if omitted)
30+
# jwt_secret = "..."
31+
32+
# Plan definitions
33+
[plans.free]
34+
max_messages_per_day = 100
35+
max_channels = 2
36+
max_members = 3
37+
memory_mb = 256
38+
cpu_limit = 0.5
39+
40+
[plans.starter]
41+
max_messages_per_day = 1000
42+
max_channels = 5
43+
max_members = 10
44+
memory_mb = 384
45+
cpu_limit = 0.75
46+
47+
[plans.pro]
48+
max_messages_per_day = 10000
49+
max_channels = 10
50+
max_members = 20
51+
memory_mb = 512
52+
cpu_limit = 1.0
53+
54+
[plans.enterprise]
55+
max_messages_per_day = 100000
56+
max_channels = 50
57+
max_members = 100
58+
memory_mb = 1024
59+
cpu_limit = 2.0
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[Unit]
2+
Description=ZeroClaw Multi-Tenant Platform
3+
Documentation=https://github.com/zeroclaw-labs/zeroclaw
4+
After=network.target docker.service
5+
Requires=docker.service
6+
7+
[Service]
8+
Type=simple
9+
User=root
10+
WorkingDirectory=/opt/zcplatform
11+
ExecStart=/opt/zcplatform/zcplatform-bin serve --config /opt/zcplatform/platform.toml
12+
ExecReload=/bin/kill -HUP $MAINPID
13+
Restart=on-failure
14+
RestartSec=5
15+
16+
# Security hardening
17+
NoNewPrivileges=true
18+
ProtectSystem=strict
19+
ReadWritePaths=/opt/zcplatform/data
20+
ProtectHome=true
21+
PrivateTmp=true
22+
23+
# Logging
24+
StandardOutput=journal
25+
StandardError=journal
26+
SyslogIdentifier=zcplatform
27+
28+
# Environment
29+
Environment=RUST_LOG=zcplatform=info
30+
31+
[Install]
32+
WantedBy=multi-user.target
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM alpine:3.21
2+
3+
RUN apk add --no-cache tinyproxy
4+
5+
COPY tinyproxy.conf /etc/tinyproxy/tinyproxy.conf
6+
COPY egress-entrypoint.sh /entrypoint.sh
7+
RUN chmod +x /entrypoint.sh
8+
9+
USER nobody
10+
EXPOSE 8888
11+
12+
ENTRYPOINT ["/entrypoint.sh"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Minimal tenant container based on ZeroClaw release image.
2+
# The ZeroClaw binary must be copied into the build context before building.
3+
4+
FROM gcr.io/distroless/cc-debian13:nonroot
5+
6+
COPY zeroclaw /usr/local/bin/zeroclaw
7+
8+
ENV ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace
9+
ENV HOME=/zeroclaw-data
10+
ENV ZEROCLAW_ALLOW_PUBLIC_BIND=true
11+
12+
WORKDIR /zeroclaw-data
13+
USER 65534:65534
14+
15+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
16+
CMD ["/usr/local/bin/zeroclaw", "status"]
17+
18+
ENTRYPOINT ["zeroclaw"]
19+
CMD ["gateway"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/sh
2+
set -e
3+
4+
CONFIG="/etc/tinyproxy/tinyproxy.conf"
5+
RUNTIME_CONFIG="/tmp/tinyproxy.conf"
6+
7+
# Copy base config
8+
cp "$CONFIG" "$RUNTIME_CONFIG"
9+
10+
# Add allowed CONNECT domains from env
11+
if [ -n "$ALLOWED_CONNECT" ]; then
12+
echo "$ALLOWED_CONNECT" | tr ',' '\n' | while read -r line; do
13+
port=$(echo "$line" | cut -d: -f2)
14+
echo "ConnectPort $port" >> "$RUNTIME_CONFIG"
15+
done
16+
fi
17+
18+
exec tinyproxy -d -c "$RUNTIME_CONFIG"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Port 8888
2+
Listen 0.0.0.0
3+
Timeout 30
4+
MaxClients 100
5+
Allow 172.30.0.0/16
6+
# ConnectPort lines added dynamically by entrypoint

0 commit comments

Comments
 (0)