https://github.com/stepandel/clawup
Deploy fleets of specialized OpenClaw agents to your cloud. Define identities in YAML, provision with one command, track changes in git.
https://github.com/stepandel/clawup
aws clawup cli hetzner infrastructure npm openclaw pulumi tailscale typescript yaml
Last synced: 4 months ago
JSON representation
Deploy fleets of specialized OpenClaw agents to your cloud. Define identities in YAML, provision with one command, track changes in git.
- Host: GitHub
- URL: https://github.com/stepandel/clawup
- Owner: stepandel
- License: mit
- Created: 2026-02-07T19:06:57.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-01T03:14:56.000Z (4 months ago)
- Last Synced: 2026-03-01T06:06:31.427Z (4 months ago)
- Topics: aws, clawup, cli, hetzner, infrastructure, npm, openclaw, pulumi, tailscale, typescript, yaml
- Language: TypeScript
- Homepage: https://clawup.sh
- Size: 126 MB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Contributing: docs/contributing.mdx
- License: LICENSE
Awesome Lists containing this project
README
# Clawup
[](https://www.npmjs.com/package/clawup)
[](./LICENSE)
Deploy a fleet of specialized [OpenClaw](https://openclaw.bot/) AI agents on **AWS** or **Hetzner Cloud** — managed entirely from your terminal.
## What Is This?
Clawup provisions autonomous AI agents with persistent memory, role-specific behavior, and secure networking. Each agent runs in a Docker sandbox with its own identity — personality, skills, tools, and model preferences — connected over a Tailscale mesh VPN with no public port exposure.
You define _what_ your agents do. Clawup handles _where_ and _how_ they run.
```
┌─────────────────────────────────────────────────────────────────────┐
│ AWS VPC / Hetzner Cloud │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Agent A │ │ Agent B │ │ Agent C │ │
│ │ │ │ │ │ │ │
│ │ • OpenClaw │ │ • OpenClaw │ │ • OpenClaw │ │
│ │ • Docker │ │ • Docker │ │ • Docker │ │
│ │ • Tailscale │ │ • Tailscale │ │ • Tailscale │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ └──────────────────┼──────────────────┘ │
└────────────────────────────┼────────────────────────────────────────┘
│
┌────────────▼────────────┐
│ Tailscale Mesh VPN │
│ (Encrypted P2P) │
└────────────┬────────────┘
│
┌────────────▼────────────┐
│ Your Machine │
└─────────────────────────┘
```
## Identity System
Agents are defined by **identities** — composable, self-contained packages that include everything an agent needs to operate: personality, skills, model preferences, plugin configuration, and dependencies.
### How Identities Work
An identity is a directory with an `identity.yaml` manifest and workspace files:
```
my-identity/
├── identity.yaml # Manifest: model, plugins, deps, skills, template vars
├── SOUL.md # Personality, role, approach, communication style
├── IDENTITY.md # Name, role, emoji
├── HEARTBEAT.md # Periodic tasks and state machine logic
├── TOOLS.md # Tool reference (integrations, local env)
├── AGENTS.md # Multi-agent coordination instructions
├── BOOTSTRAP.md # First-run setup instructions
├── USER.md # Owner-specific info (templated)
└── skills/ # Bundled skills
└── my-skill/
└── SKILL.md
```
Identities can live in a **Git repo**, a **monorepo subdirectory**, or a **local path**. Point any agent at any identity:
```yaml
# clawup.yaml
agents:
- name: agent-researcher
displayName: Atlas
role: researcher
identity: "https://github.com/your-org/your-identities#researcher"
identityVersion: "v1.0.0" # optional: pin to a tag or commit
volumeSize: 20
```
### `identity.yaml`
The identity manifest declares the agent's defaults:
```yaml
name: researcher
displayName: Atlas
role: researcher
emoji: telescope
description: Deep research, source analysis, report generation
volumeSize: 20
model: anthropic/claude-sonnet-4-5
codingAgent: claude-code
deps:
- brave-search
plugins:
- slack
pluginDefaults:
slack:
mode: socket
dm:
enabled: true
policy: open
skills:
- research-report
templateVars:
- OWNER_NAME
- TIMEZONE
- WORKING_HOURS
- USER_NOTES
# Additional secrets not covered by plugins/deps
requiredSecrets:
- notionApiKey # → _NOTION_API_KEY in .env
```
### Built-in Identities
Clawup ships with three built-in identities to get you started:
| Alias | Role | What It Does |
|-------|------|-------------|
| **Juno** | PM | Breaks down tickets, researches requirements, plans & sequences work, tracks progress |
| **Titus** | Engineer | Picks up tickets, writes code via Claude Code, builds/tests, creates PRs |
| **Scout** | Tester | Reviews PRs, tests happy/sad/edge cases, files bugs, verifies fixes |
These are standard identities hosted in a Git repo — the same format as any custom identity you'd create.
### Creating Your Own Identity
See the [`examples/identity/`](./examples/identity/) directory for a complete, minimal example (a "researcher" agent), and the [Creating Identities](./docs/guides/creating-identities.mdx) guide for the full authoring reference covering:
- Identity structure and required files
- `identity.yaml` field reference
- Workspace file conventions
- Skill authoring (private and public)
- Template variable substitution
- Available registries (deps, plugins, coding agents)
## Quick Start
### 1. Install
```bash
npm install -g clawup
```
### 2. Generate Config
```bash
clawup init
```
Scaffolds a `clawup.yaml` manifest and `.env.example` with sensible defaults (AWS, us-east-1, all built-in agents). Edit `clawup.yaml` by hand to customize your provider, region, instance type, owner info, and agents.
### 3. Fill in Secrets
```bash
cp .env.example .env
# Edit .env and fill in your API keys
```
### 4. Validate & Configure
```bash
clawup setup
```
Validates all secrets from `.env`, fetches Linear user UUIDs, and configures Pulumi. If any secrets are missing, it prints exactly what's needed.
### 5. Deploy
```bash
clawup deploy
```
### 6. Validate
Wait 3-5 minutes for cloud-init to complete, then:
```bash
clawup validate
```
### 7. Access Your Agents
```bash
clawup ssh # SSH by name, role, or alias
```
## CLI Reference
Run `clawup --help` for the full list.
| Command | Description |
|---------|-------------|
| `clawup init` | Generate clawup.yaml scaffold (or refresh from identity changes) |
| `clawup setup` | Validate secrets from `.env` and configure Pulumi |
| `clawup deploy` | Deploy agents (`pulumi up` under the hood) |
| `clawup deploy --local` | Deploy to local Docker containers |
| `clawup status` | Show agent statuses and outputs |
| `clawup status --local` | Show local Docker container status |
| `clawup ssh ` | SSH to an agent by name, role, or alias |
| `clawup ssh --local` | Shell into a local Docker container |
| `clawup validate` | Health check all agents via Tailscale |
| `clawup validate --local` | Health check local Docker containers |
| `clawup redeploy` | Update agents in-place (`pulumi up --refresh`) |
| `clawup redeploy --local` | Redeploy local Docker containers |
| `clawup destroy` | Tear down all resources (with confirmation) |
| `clawup destroy --local` | Destroy local Docker containers only |
| `clawup list` | Show project config |
| `clawup config show` | Display current config |
| `clawup config show --json` | Config in JSON format |
| `clawup config set ` | Update a config value |
| `clawup config set -a ` | Update a per-agent config value |
| `clawup secrets set ` | Set a Pulumi secret (e.g. API keys) |
| `clawup secrets list` | Show which secrets are configured (redacted) |
| `clawup push` | Push workspace files, skills, and config to running agents |
| `clawup webhooks setup` | Configure Linear webhooks for deployed agents |
| `clawup update` | Update clawup CLI to the latest version |
Agent resolution is flexible — all of these target the same agent:
```bash
clawup ssh juno # by alias
clawup ssh pm # by role
clawup ssh agent-pm # by resource name
```
## Cloud Providers
### Provider Comparison
| Feature | AWS | Hetzner Cloud |
|---------|-----|---------------|
| **3x Agents (monthly)** | ~$110-120 | ~$18-22 |
| **Instance Type** | t3.medium (2 vCPU, 4GB) | CX22 (2 vCPU, 4GB) |
| **Storage** | ~$2.40/month per 30GB | Included |
| **Data Transfer** | ~$5-10/month | 20TB included |
| **Regions** | Global (25+) | EU & US (5 locations) |
| **Setup Complexity** | Moderate (VPC, IAM) | Simple (API token) |
Use Hetzner for development and cost savings (~80% cheaper). Use AWS for production or global reach.
### What Gets Provisioned
Each agent gets:
- Cloud instance (EC2 or Hetzner server) with Ubuntu 24.04 LTS
- Docker (for OpenClaw sandbox)
- Node.js v22, OpenClaw CLI, coding agent CLI (from registry), GitHub CLI
- Tailscale VPN (encrypted mesh, no public ports)
- Workspace files and skills injected from its identity
- AI model configured per-identity (with fallback support)
- Plugins and deps installed per-identity
All agents share a single VPC/network for cost optimization.
## Dependencies
You need the following installed on your **local machine** before running `clawup init`.
### Required (all providers)
| Dependency | Why | Install |
|------------|-----|---------|
| **Node.js 18+** | Runtime for CLI and Pulumi program | [nodejs.org](https://nodejs.org/) |
| **Pulumi CLI** | Infrastructure provisioning | [pulumi.com/docs/iac/download-install](https://www.pulumi.com/docs/iac/download-install/) |
| **Pulumi Account** | State management and encrypted secrets | [app.pulumi.com/signup](https://app.pulumi.com/signup) |
| **Tailscale** | Secure mesh VPN to reach your agents | [tailscale.com/download](https://tailscale.com/download) |
### Required (provider-specific)
Pick one depending on where you want to deploy:
| Provider | Dependency | Install |
|----------|-----------|---------|
| **AWS** | AWS CLI (configured with credentials) | [aws.amazon.com/cli](https://aws.amazon.com/cli/) — then run `aws configure` |
| **Hetzner** | API token with Read & Write permissions | [console.hetzner.cloud](https://console.hetzner.cloud/) → Project → Security → API Tokens |
### Tailscale Setup
Tailscale requires a few one-time setup steps:
1. [Create an account](https://login.tailscale.com/start)
2. [Enable HTTPS certificates](https://tailscale.com/kb/1153/enabling-https) (required for OpenClaw web UI)
3. [Generate a reusable auth key](https://login.tailscale.com/admin/settings/keys) with tags
4. Note your tailnet DNS name (e.g., `tail12345.ts.net`)
### Installed on agents automatically
These are provisioned on the cloud instances via cloud-init — you do **not** need them locally:
- Docker, Node.js v22, OpenClaw CLI, coding agent CLI (e.g., Claude Code)
- Tailscale (agent-side)
- GitHub CLI, Brave Search, and other deps (per-identity)
- OpenClaw plugins: Linear, Slack (per-identity)
## Required API Keys
| Key | Required | Where to Get |
|-----|----------|--------------|
| **Anthropic Credentials** | Yes | [API Key](https://console.anthropic.com/) or OAuth token (`claude setup-token`) |
| **Tailscale Auth Key** | Yes | [Tailscale Admin](https://login.tailscale.com/admin/settings/keys) (reusable, with tags) |
| **Slack Bot Token** | No | [Slack API](https://api.slack.com/apps) — per agent |
| **Linear API Token** | No | [Linear Settings](https://linear.app/settings/api) — per agent |
| **GitHub Token** | No | [GitHub Settings](https://github.com/settings/tokens) — per agent |
### Claude Code Authentication
Two authentication methods are supported:
| Method | Token Format | Best For |
|--------|-------------|----------|
| **API Key** | `sk-ant-api03-...` | Pay-as-you-go API usage |
| **OAuth Token** | `sk-ant-oat01-...` | Pro/Max subscription (flat rate) |
The system auto-detects which type you provide and sets the correct environment variable.
## Updating & Redeploying
For in-place updates that preserve Tailscale devices and existing infrastructure:
```bash
clawup redeploy
```
This runs `pulumi up --refresh` to sync cloud state and apply changes. If the stack doesn't exist yet, it falls back to a fresh deploy automatically.
For local Docker containers:
```bash
clawup redeploy --local
```
For a clean rebuild (when in-place update can't recover):
```bash
clawup destroy -y && clawup deploy -y
```
## Configuration
### Viewing & Modifying Config
View your current configuration without opening the manifest file:
```bash
clawup config show # Human-readable summary
clawup config show --json # Full JSON output
```
Modify config values with validation (no need to re-run `init`):
```bash
clawup config set region us-west-2
clawup config set instanceType t3.large
clawup config set instanceType cx32 -a atlas # Per-agent override
clawup config set volumeSize 50 -a atlas # Per-agent volume
```
Run `clawup redeploy` after changing config to apply.
### `clawup.yaml`
Generated by `clawup init`. This manifest drives the entire deployment:
```yaml
stackName: dev
provider: aws
region: us-east-1
instanceType: t3.medium
ownerName: Your Name
timezone: America/New_York
workingHours: 9am-6pm
secrets:
anthropicApiKey: "${env:ANTHROPIC_API_KEY}"
tailscaleAuthKey: "${env:TAILSCALE_AUTH_KEY}"
tailnetDnsName: "${env:TAILNET_DNS_NAME}"
tailscaleApiKey: "${env:TAILSCALE_API_KEY}"
agents:
- name: agent-pm
displayName: Juno
role: pm
identity: "https://github.com/your-org/army-identities#pm"
volumeSize: 30
secrets:
slackBotToken: "${env:PM_SLACK_BOT_TOKEN}"
slackAppToken: "${env:PM_SLACK_APP_TOKEN}"
plugins:
- openclaw-linear
- slack
deps:
- gh
- brave-search
- name: agent-researcher
displayName: Atlas
role: researcher
identity: "./my-identities/researcher"
volumeSize: 20
```
The `secrets` section uses `${env:VAR}` references — actual values are loaded from a `.env` file at init time. A `.env.example` is generated alongside the manifest. Model, backup model, and coding agent are configured in the identity (not the manifest). The manifest defines _which_ agents to deploy and _where_.
### Pulumi Config
Secrets are stored encrypted in Pulumi config, set automatically by `clawup setup`. You can also manage them directly:
```bash
pulumi config set --secret anthropicApiKey sk-ant-xxxxx
pulumi config set --secret tailscaleAuthKey tskey-auth-xxxxx
pulumi config set tailnetDnsName tail12345.ts.net
```
### Pulumi ESC
For more advanced secret management, use [Pulumi ESC](https://www.pulumi.com/docs/esc/). See `esc/clawup-secrets.yaml.example` for the full template.
## Project Structure
```
clawup/
├── packages/
│ ├── core/ # @clawup/core — shared types, constants, registries
│ │ └── src/
│ │ ├── schemas/ # Zod schemas (source of truth for types)
│ │ ├── constants.ts
│ │ ├── identity.ts # Identity loader (Git repos, local paths)
│ │ ├── plugin-registry.ts
│ │ ├── coding-agent-registry.ts
│ │ ├── dep-registry.ts
│ │ └── skills.ts
│ ├── cli/ # clawup CLI (published npm package)
│ │ ├── bin.ts # Entry point (Commander.js)
│ │ ├── commands/ # init, deploy, redeploy, status, ssh, validate, destroy, config, list, push, secrets, webhooks, update
│ │ ├── tools/ # Tool implementations (adapter-based)
│ │ ├── lib/ # CLI-only: config, pulumi, ui, tailscale, exec
│ │ └── adapters/ # Runtime adapters (CLI vs API)
│ ├── pulumi/ # @clawup/pulumi — infrastructure as code
│ │ └── src/
│ │ ├── components/
│ │ │ ├── openclaw-agent.ts # AWS EC2 agent component
│ │ │ ├── hetzner-agent.ts # Hetzner Cloud agent component
│ │ │ ├── cloud-init.ts # Cloud-init script generation
│ │ │ └── config-generator.ts # OpenClaw config builder
│ │ ├── shared-vpc.ts
│ │ └── index.ts # Main Pulumi stack program
│ └── web/ # Next.js dashboard (clawup-web)
├── identities/ # Built-in identity stubs (point to external repos)
├── examples/ # Example identities for reference
│ └── identity/ # Complete "researcher" identity example
├── docs/ # Documentation (Mintlify site + guides)
├── esc/ # Pulumi ESC secret templates
├── scripts/ # Shell script helpers
├── Pulumi.yaml # Pulumi project config (points to packages/pulumi/)
└── pnpm-workspace.yaml # Monorepo workspace config
```
## Security
- All agent ports bind to `127.0.0.1` — access is via **Tailscale only**
- No public port exposure; Tailscale Serve proxies traffic
- Token-based gateway authentication
- Secrets encrypted via Pulumi config
- Cloud-init scripts use environment variable interpolation
- SSH available as fallback for debugging
## Troubleshooting
### Agents not appearing in Tailscale
1. Wait 3-5 minutes for cloud-init to complete
2. Check logs: `clawup ssh 'sudo cat /var/log/cloud-init-output.log | tail -100'`
3. Verify your Tailscale auth key is valid and reusable
### OpenClaw gateway not running
```bash
clawup ssh 'openclaw gateway status'
clawup ssh 'journalctl -u openclaw -n 50'
clawup ssh 'openclaw gateway restart'
```
### SSH connection refused
1. Check Tailscale is running locally: `tailscale status`
2. Verify the agent appears in your tailnet
3. Ensure you're using the correct tailnet DNS name
### Pulumi state issues
```bash
pulumi refresh # Refresh state from actual infrastructure
pulumi cancel # Force unlock if locked
```
## Development
For contributing to Clawup itself:
```bash
git clone https://github.com/stepandel/clawup.git
cd clawup
pnpm install
pnpm build # Build all packages
pnpm test # Run all tests
# Individual packages
pnpm --filter @clawup/core build # Build core
pnpm --filter clawup build # Build CLI
pnpm --filter @clawup/pulumi build # Build Pulumi
pnpm --filter clawup-web dev # Web dev server
```
## License
MIT
## Related
- [OpenClaw Documentation](https://docs.openclaw.ai/)
- [Pulumi AWS Provider](https://www.pulumi.com/registry/packages/aws/)
- [Pulumi Hetzner Provider](https://www.pulumi.com/registry/packages/hcloud/)
- [Pulumi ESC](https://www.pulumi.com/docs/esc/)
- [Tailscale Documentation](https://tailscale.com/kb/)