skills-ws 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +69 -8
- package/package.json +2 -2
- package/skills/aleph-vm-replication/SKILL.md +677 -0
- package/SECURITY.md +0 -45
package/bin/cli.mjs
CHANGED
|
@@ -260,7 +260,10 @@ async function detectTarget() {
|
|
|
260
260
|
for (const c of candidates) {
|
|
261
261
|
try { await stat(c); return c; } catch {}
|
|
262
262
|
}
|
|
263
|
-
|
|
263
|
+
// Default: create ~/.claude/skills if nothing found
|
|
264
|
+
const defaultTarget = join(process.env.HOME || "~", ".claude", "skills");
|
|
265
|
+
await mkdir(defaultTarget, { recursive: true });
|
|
266
|
+
return defaultTarget;
|
|
264
267
|
}
|
|
265
268
|
|
|
266
269
|
// ── Main ─────────────────────────────────────────────────────
|
|
@@ -336,13 +339,71 @@ async function main() {
|
|
|
336
339
|
return;
|
|
337
340
|
}
|
|
338
341
|
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
// No args = interactive install (same as `install`)
|
|
343
|
+
if (!args[0] || args[0] === "help" || args[0] === "-h" || args[0] === "--help") {
|
|
344
|
+
if (args[0] === "help" || args[0] === "-h" || args[0] === "--help") {
|
|
345
|
+
process.stdout.write(` ${B}Usage:${R}\n\n`);
|
|
346
|
+
process.stdout.write(` ${CYAN}npx skills-ws${R} Interactive picker\n`);
|
|
347
|
+
process.stdout.write(` ${CYAN}npx skills-ws list${R} List all skills\n`);
|
|
348
|
+
process.stdout.write(` ${CYAN}npx skills-ws install <name>${R} Install specific skill(s)\n`);
|
|
349
|
+
process.stdout.write(` ${CYAN}npx skills-ws install all${R} Install everything\n`);
|
|
350
|
+
process.stdout.write(`\n ${DIM}${skills.length} skills | skills.ws${R}\n\n`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Interactive picker
|
|
355
|
+
for (let i = 0; i < skills.length; i++) {
|
|
356
|
+
process.stdout.write(
|
|
357
|
+
` ${GRAY}${String(i + 1).padStart(2)}${R} ${GREEN}${skills[i].name.padEnd(24)}${R}${DIM}${skills[i].desc.slice(0, 50)}${R}\n`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
process.stdout.write(`\n ${YELLOW}Enter numbers or names (comma-separated), or 'all':${R}\n`);
|
|
361
|
+
|
|
362
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
363
|
+
const answer = await new Promise((resolve) => rl.question(` ${CYAN}> ${R}`, resolve));
|
|
364
|
+
rl.close();
|
|
365
|
+
|
|
366
|
+
const names = [];
|
|
367
|
+
if (answer.trim().toLowerCase() === "all") {
|
|
368
|
+
names.push(...skills.map((s) => s.name));
|
|
369
|
+
} else {
|
|
370
|
+
for (const part of answer.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
371
|
+
const num = parseInt(part);
|
|
372
|
+
if (!isNaN(num) && num >= 1 && num <= skills.length) names.push(skills[num - 1].name);
|
|
373
|
+
else names.push(part);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (names.length === 0) {
|
|
378
|
+
process.stdout.write(` ${DIM}Nothing selected.${R}\n`);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const target = await detectTarget();
|
|
383
|
+
process.stdout.write(`\n ${DIM}${target}${R}\n\n`);
|
|
384
|
+
|
|
385
|
+
let installed = 0;
|
|
386
|
+
for (const name of names) {
|
|
387
|
+
const skill = skills.find((s) => s.name === name);
|
|
388
|
+
if (!skill) {
|
|
389
|
+
process.stdout.write(` ${YELLOW}skip${R} ${name} ${DIM}(not found)${R}\n`);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
await copyDir(skill.dir, join(target, name));
|
|
393
|
+
installed++;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
await playInstallProgress(installed);
|
|
397
|
+
process.stdout.write("\n");
|
|
398
|
+
|
|
399
|
+
for (const name of names) {
|
|
400
|
+
if (skills.find((s) => s.name === name)) {
|
|
401
|
+
process.stdout.write(` ${GREEN}+${R} ${name}\n`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
process.stdout.write(`\n ${DIM}skills.ws${R}\n\n`);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
346
407
|
}
|
|
347
408
|
|
|
348
409
|
process.on("SIGINT", () => { showCursor(); process.exit(0); });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skills-ws",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.2",
|
|
4
|
+
"description": "70 agent skills for AI coding assistants \u2014 marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skills-ws": "./bin/cli.mjs"
|
|
7
7
|
},
|
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
# Aleph Cloud VM Self-Replication
|
|
2
|
+
|
|
3
|
+
> Create Aleph Cloud VM instances and deploy autonomous AI agents that can replicate themselves endlessly across the decentralized cloud.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This skill enables an AI agent to:
|
|
8
|
+
1. Set up an Aleph Cloud account with credits or $ALEPH tokens
|
|
9
|
+
2. Create VM instances (compute nodes) on the Aleph network
|
|
10
|
+
3. Install OpenClaw (or any AI agent framework) on the VM
|
|
11
|
+
4. Configure the agent with credentials and identity
|
|
12
|
+
5. Enable the new agent to repeat this process — self-replication
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- `aleph-client` CLI installed (`pip install aleph-client`)
|
|
17
|
+
- A funded Aleph Cloud account (credits on BASE/ETH/AVAX/SOL, or $ALEPH tokens for holding)
|
|
18
|
+
- An SSH keypair for VM access
|
|
19
|
+
- AI provider API key (Anthropic, OpenAI, etc.) to share with spawned agents
|
|
20
|
+
|
|
21
|
+
### Install aleph-client
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install aleph-client --break-system-packages
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> On Ubuntu 24+ you need `--break-system-packages` or use a venv.
|
|
28
|
+
|
|
29
|
+
### Check Version
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
aleph --version
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Step 1: Account Setup
|
|
36
|
+
|
|
37
|
+
### Import or Create a Private Key
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Import existing private key
|
|
41
|
+
aleph account create --private-key <YOUR_HEX_PRIVATE_KEY> --chain BASE --active
|
|
42
|
+
|
|
43
|
+
# Or generate a new one
|
|
44
|
+
aleph account create --chain BASE --active
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Keys are stored at `~/.aleph-im/private-keys/`. Always `chmod 600` key files.
|
|
48
|
+
|
|
49
|
+
### Check Balance
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
aleph account balance
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
You need credits or $ALEPH tokens. Credits are the simplest (pay-as-you-go). Buy credits at https://account.aleph.im/ or hold $ALEPH tokens.
|
|
56
|
+
|
|
57
|
+
### Pricing Reference
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
aleph pricing instance
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Tier | Compute Units | vCPUs | RAM | Default Disk | Credits/Hour | Credits/Day |
|
|
64
|
+
|------|---------------|-------|-----|-------------|-------------|-------------|
|
|
65
|
+
| 1 | 1 | 1 | 2 GiB | 20 GiB | 14,250 | 342,000 |
|
|
66
|
+
| 2 | 2 | 2 | 4 GiB | 40 GiB | 28,500 | 684,000 |
|
|
67
|
+
| 3 | 4 | 4 | 8 GiB | 80 GiB | 57,000 | 1,368,000 |
|
|
68
|
+
| 4 | 6 | 6 | 12 GiB | 120 GiB | 85,500 | 2,052,000 |
|
|
69
|
+
|
|
70
|
+
> Disk size can be overridden with `--rootfs-size <MiB>`. The tier sets the minimum; you can allocate more (e.g., 40GB on a Tier 1). Extra disk costs ~4,416 credits/GiB/day.
|
|
71
|
+
|
|
72
|
+
## Step 2: Generate SSH Keypair
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
ssh-keygen -t ed25519 -f ~/.ssh/aleph_agent -N "" -C "agent@aleph"
|
|
76
|
+
chmod 600 ~/.ssh/aleph_agent
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Step 3: Find a Compute Resource Node (CRN)
|
|
80
|
+
|
|
81
|
+
### List Active CRNs (Top 10 by Score)
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
aleph node compute --active --json 2>/dev/null | python3 -c "
|
|
85
|
+
import sys, json
|
|
86
|
+
nodes = json.load(sys.stdin)
|
|
87
|
+
scored = []
|
|
88
|
+
for n in nodes:
|
|
89
|
+
score = n.get('score', 0) or 0
|
|
90
|
+
name = n.get('name', 'unknown')
|
|
91
|
+
node_hash = n.get('hash', '')
|
|
92
|
+
url = n.get('address', '')
|
|
93
|
+
if score > 0.5: # Score is 0.0–1.0 (not percentage)
|
|
94
|
+
scored.append((score, name, node_hash, url))
|
|
95
|
+
scored.sort(reverse=True)
|
|
96
|
+
for s, name, h, url in scored[:10]:
|
|
97
|
+
print(f'{s*100:.1f}% | {name} | {h} | {url}')
|
|
98
|
+
"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Get Best CRN Hash (for scripts)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
CRN_HASH=$(aleph node compute --active --json 2>/dev/null | python3 -c "
|
|
105
|
+
import sys, json
|
|
106
|
+
nodes = json.load(sys.stdin)
|
|
107
|
+
best = max(
|
|
108
|
+
(n for n in nodes if (n.get('score', 0) or 0) > 0.5),
|
|
109
|
+
key=lambda n: n.get('score', 0),
|
|
110
|
+
default=None
|
|
111
|
+
)
|
|
112
|
+
print(best['hash'] if best else '')
|
|
113
|
+
")
|
|
114
|
+
echo "Best CRN: $CRN_HASH"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Step 4: Create the VM Instance
|
|
118
|
+
|
|
119
|
+
### Known Rootfs Hashes
|
|
120
|
+
|
|
121
|
+
| OS | Rootfs Hash |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Ubuntu 24 | `5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e` |
|
|
124
|
+
| Ubuntu 22 | (default if `--rootfs` omitted) |
|
|
125
|
+
|
|
126
|
+
### Create Instance (Non-Interactive)
|
|
127
|
+
|
|
128
|
+
The `aleph instance create` command has an interactive TUI for CRN selection. To bypass it completely, use `--crn-hash` + `--skip-volume`:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
aleph instance create \
|
|
132
|
+
--name "agent-v1" \
|
|
133
|
+
--compute-units 1 \
|
|
134
|
+
--rootfs 5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e \
|
|
135
|
+
--rootfs-size 40960 \
|
|
136
|
+
--payment-type credit \
|
|
137
|
+
--payment-chain BASE \
|
|
138
|
+
--ssh-pubkey-file ~/.ssh/aleph_agent.pub \
|
|
139
|
+
--crn-hash "$CRN_HASH" \
|
|
140
|
+
--crn-auto-tac \
|
|
141
|
+
--skip-volume \
|
|
142
|
+
--verbose
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Critical flags:**
|
|
146
|
+
- `--crn-hash <HASH>`: Bypasses the interactive CRN picker TUI entirely
|
|
147
|
+
- `--crn-auto-tac`: Auto-accepts CRN Terms & Conditions
|
|
148
|
+
- `--skip-volume`: Skips the "add extra volumes?" interactive prompt
|
|
149
|
+
- `--rootfs`: OS image hash (omit for Ubuntu 22 default)
|
|
150
|
+
- `--rootfs-size`: Disk size in MiB (40960 = 40GB; overrides tier default)
|
|
151
|
+
|
|
152
|
+
### If Interactive TUI Still Appears
|
|
153
|
+
|
|
154
|
+
Some versions of aleph-client may still show prompts. Use pexpect:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
#!/usr/bin/env python3
|
|
158
|
+
"""Automate aleph instance create when CLI flags don't fully bypass TUI."""
|
|
159
|
+
import pexpect
|
|
160
|
+
import sys
|
|
161
|
+
import re
|
|
162
|
+
|
|
163
|
+
CRN_HASH = sys.argv[1] if len(sys.argv) > 1 else ""
|
|
164
|
+
NAME = sys.argv[2] if len(sys.argv) > 2 else f"agent-{int(__import__('time').time())}"
|
|
165
|
+
|
|
166
|
+
cmd = (
|
|
167
|
+
f'aleph instance create '
|
|
168
|
+
f'--name "{NAME}" '
|
|
169
|
+
f'--compute-units 1 '
|
|
170
|
+
f'--rootfs 5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e '
|
|
171
|
+
f'--rootfs-size 40960 '
|
|
172
|
+
f'--payment-type credit '
|
|
173
|
+
f'--payment-chain BASE '
|
|
174
|
+
f'--ssh-pubkey-file ~/.ssh/aleph_agent.pub '
|
|
175
|
+
f'--crn-auto-tac '
|
|
176
|
+
f'--skip-volume '
|
|
177
|
+
f'--verbose'
|
|
178
|
+
)
|
|
179
|
+
if CRN_HASH:
|
|
180
|
+
cmd += f' --crn-hash {CRN_HASH}'
|
|
181
|
+
|
|
182
|
+
child = pexpect.spawn('/bin/bash', ['-c', cmd], timeout=180, encoding='utf-8')
|
|
183
|
+
child.logfile_read = sys.stdout
|
|
184
|
+
|
|
185
|
+
while True:
|
|
186
|
+
try:
|
|
187
|
+
idx = child.expect([
|
|
188
|
+
r'(?i)rootfs.*type', # Rootfs type prompt
|
|
189
|
+
r'(?i)disk.*size', # Disk size prompt
|
|
190
|
+
r'(?i)add.*volume', # Add volume prompt
|
|
191
|
+
r'(?i)confirm|deploy|proceed', # Confirmation prompt
|
|
192
|
+
r'(?i)select.*crn|choose.*node', # CRN selection
|
|
193
|
+
pexpect.EOF,
|
|
194
|
+
pexpect.TIMEOUT,
|
|
195
|
+
], timeout=120)
|
|
196
|
+
|
|
197
|
+
if idx == 0:
|
|
198
|
+
child.sendline('ubuntu24')
|
|
199
|
+
elif idx == 1:
|
|
200
|
+
child.sendline('40960')
|
|
201
|
+
elif idx == 2:
|
|
202
|
+
child.sendline('n')
|
|
203
|
+
elif idx == 3:
|
|
204
|
+
child.sendline('y')
|
|
205
|
+
elif idx == 4:
|
|
206
|
+
child.sendline('') # Press Enter for top/default choice
|
|
207
|
+
elif idx == 5:
|
|
208
|
+
break # EOF — done
|
|
209
|
+
elif idx == 6:
|
|
210
|
+
break # Timeout — done or stuck
|
|
211
|
+
except Exception as e:
|
|
212
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
213
|
+
break
|
|
214
|
+
|
|
215
|
+
print("\n" + child.before if child.before else "")
|
|
216
|
+
child.close()
|
|
217
|
+
sys.exit(child.exitstatus or 0)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Install pexpect: `pip install pexpect --break-system-packages`
|
|
221
|
+
|
|
222
|
+
Usage: `python3 create_instance.py <CRN_HASH> <INSTANCE_NAME>`
|
|
223
|
+
|
|
224
|
+
### Parse Instance Hash After Creation
|
|
225
|
+
|
|
226
|
+
The CLI outputs the instance details. Extract the hash:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# List instances and get the newest one
|
|
230
|
+
aleph instance list 2>&1
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The output shows a table with instance hash, SSH connection details, and mapped ports. Look for:
|
|
234
|
+
```
|
|
235
|
+
ssh root@<HOST_IP> -p <PORT> -i <ssh-private-key-file>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Programmatic SSH Details
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# Parse connection info for the most recent instance
|
|
242
|
+
aleph instance list 2>&1 | grep -oP 'ssh root@\S+ -p \d+'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Wait and Connect
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Wait for VM to boot (30-60 seconds)
|
|
249
|
+
sleep 45
|
|
250
|
+
|
|
251
|
+
# Connect
|
|
252
|
+
ssh -o StrictHostKeyChecking=no root@<HOST_IP> -p <MAPPED_PORT> -i ~/.ssh/aleph_agent
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Step 5: Install OpenClaw on the New VM
|
|
256
|
+
|
|
257
|
+
SSH into the VM, then:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Install Node.js 22
|
|
261
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
|
262
|
+
DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs git
|
|
263
|
+
|
|
264
|
+
# Install OpenClaw
|
|
265
|
+
npm install -g openclaw
|
|
266
|
+
|
|
267
|
+
# Create workspace
|
|
268
|
+
mkdir -p /root/openclaw/memory /root/openclaw/skills
|
|
269
|
+
|
|
270
|
+
# Verify
|
|
271
|
+
node --version # Should show v22.x
|
|
272
|
+
openclaw --version
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
> Use `DEBIAN_FRONTEND=noninteractive` to prevent dpkg prompts in non-TTY sessions.
|
|
276
|
+
|
|
277
|
+
## Step 6: Configure Agent Auth & Gateway
|
|
278
|
+
|
|
279
|
+
### Set Up Anthropic API Key
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
mkdir -p /root/.openclaw/agents/main/agent /root/.openclaw/agents/main/sessions
|
|
283
|
+
|
|
284
|
+
cat > /root/.openclaw/agents/main/agent/auth-profiles.json << 'EOF'
|
|
285
|
+
{
|
|
286
|
+
"version": 1,
|
|
287
|
+
"profiles": {
|
|
288
|
+
"anthropic:default": {
|
|
289
|
+
"type": "token",
|
|
290
|
+
"provider": "anthropic",
|
|
291
|
+
"token": "<YOUR_ANTHROPIC_API_KEY>"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"lastGood": {
|
|
295
|
+
"anthropic": "anthropic:default"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
EOF
|
|
299
|
+
chmod 600 /root/.openclaw/agents/main/agent/auth-profiles.json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
> **Critical:** The file MUST be named `auth-profiles.json` (not `auth.json`). The gateway reads `auth-profiles.json` — using the wrong filename silently fails with "No API key found for provider".
|
|
303
|
+
|
|
304
|
+
### Configure OpenClaw
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
GATEWAY_TOKEN=$(openssl rand -hex 24)
|
|
308
|
+
echo "Gateway token: $GATEWAY_TOKEN" # Save this!
|
|
309
|
+
|
|
310
|
+
cat > /root/.openclaw/openclaw.json << EOF
|
|
311
|
+
{
|
|
312
|
+
"auth": {
|
|
313
|
+
"profiles": {
|
|
314
|
+
"anthropic:default": {
|
|
315
|
+
"provider": "anthropic",
|
|
316
|
+
"mode": "token"
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"agents": {
|
|
321
|
+
"defaults": {
|
|
322
|
+
"model": {
|
|
323
|
+
"primary": "anthropic/claude-sonnet-4-20250514"
|
|
324
|
+
},
|
|
325
|
+
"workspace": "/root/openclaw"
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"gateway": {
|
|
329
|
+
"port": 18789,
|
|
330
|
+
"mode": "local",
|
|
331
|
+
"bind": "loopback",
|
|
332
|
+
"auth": {
|
|
333
|
+
"mode": "token",
|
|
334
|
+
"token": "$GATEWAY_TOKEN"
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
EOF
|
|
339
|
+
|
|
340
|
+
# Harden permissions
|
|
341
|
+
chmod 700 /root/.openclaw
|
|
342
|
+
chmod 600 /root/.openclaw/openclaw.json
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Start Gateway
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
openclaw gateway install --force
|
|
349
|
+
openclaw gateway start
|
|
350
|
+
|
|
351
|
+
# Wait for startup
|
|
352
|
+
sleep 3
|
|
353
|
+
|
|
354
|
+
# Verify — MUST pass the token
|
|
355
|
+
openclaw gateway status --token "$GATEWAY_TOKEN"
|
|
356
|
+
# Should show: Runtime: running ... RPC probe: ok
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
> If `RPC probe: failed` after `gateway start`, run `openclaw gateway install --force` then `openclaw gateway restart`. The systemd service file must have the token synced.
|
|
360
|
+
|
|
361
|
+
### Test Agent
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
OPENCLAW_GATEWAY_TOKEN="$GATEWAY_TOKEN" openclaw agent --agent main -m "Hello! Who are you? Reply in one sentence."
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
If the gateway route fails, it falls back to embedded mode (still works, just slower on first call).
|
|
368
|
+
|
|
369
|
+
## Step 7: Copy Identity & Skills
|
|
370
|
+
|
|
371
|
+
From the parent machine, copy workspace files to the new VM:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
HOST="<HOST_IP>"
|
|
375
|
+
PORT="<MAPPED_PORT>"
|
|
376
|
+
KEY="$HOME/.ssh/aleph_agent"
|
|
377
|
+
SCP="scp -i $KEY -P $PORT -o StrictHostKeyChecking=no"
|
|
378
|
+
|
|
379
|
+
# Core identity files
|
|
380
|
+
for f in SOUL.md AGENTS.md IDENTITY.md USER.md TOOLS.md HEARTBEAT.md; do
|
|
381
|
+
[ -f "/root/openclaw/$f" ] && $SCP /root/openclaw/$f root@$HOST:/root/openclaw/$f
|
|
382
|
+
done
|
|
383
|
+
|
|
384
|
+
# Memory (optional — fresh start or shared context)
|
|
385
|
+
[ -f "/root/openclaw/MEMORY.md" ] && $SCP /root/openclaw/MEMORY.md root@$HOST:/root/openclaw/MEMORY.md
|
|
386
|
+
|
|
387
|
+
# Skills directory (recursive)
|
|
388
|
+
scp -r -i $KEY -P $PORT -o StrictHostKeyChecking=no /root/openclaw/skills/ root@$HOST:/root/openclaw/skills/
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Step 8: Enable Self-Replication
|
|
392
|
+
|
|
393
|
+
For the new agent to replicate itself, it needs:
|
|
394
|
+
|
|
395
|
+
1. **aleph-client CLI** on its VM
|
|
396
|
+
2. **Aleph private key** (or delegated key) to create instances
|
|
397
|
+
3. **This skill** in its skills directory
|
|
398
|
+
4. **Sufficient credits/tokens** in the account
|
|
399
|
+
|
|
400
|
+
### Install aleph-client on the Child VM
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
ssh -i $KEY -p $PORT root@$HOST << 'REMOTE'
|
|
404
|
+
apt-get install -y python3-pip
|
|
405
|
+
pip install aleph-client --break-system-packages
|
|
406
|
+
|
|
407
|
+
# Verify
|
|
408
|
+
aleph --version
|
|
409
|
+
REMOTE
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Transfer Aleph Private Key
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
# SENSITIVE — never log this key
|
|
416
|
+
$SCP /root/.aleph-im/private-keys/your-key.key root@$HOST:/root/.aleph-im/private-keys/your-key.key
|
|
417
|
+
ssh -i $KEY -p $PORT root@$HOST "chmod 700 /root/.aleph-im/private-keys && chmod 600 /root/.aleph-im/private-keys/*.key"
|
|
418
|
+
|
|
419
|
+
# Activate on the child
|
|
420
|
+
ssh -i $KEY -p $PORT root@$HOST "aleph account create --private-key \$(cat /root/.aleph-im/private-keys/your-key.key) --chain BASE --active"
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Verify Self-Replication Capability
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
ssh -i $KEY -p $PORT root@$HOST << 'REMOTE'
|
|
427
|
+
echo "=== Checking replication readiness ==="
|
|
428
|
+
echo -n "aleph-client: "; aleph --version
|
|
429
|
+
echo -n "Balance: "; aleph account balance 2>&1 | head -3
|
|
430
|
+
echo -n "SSH key: "; ls ~/.ssh/aleph_agent.pub 2>/dev/null && echo "OK" || echo "MISSING — generate with: ssh-keygen -t ed25519 -f ~/.ssh/aleph_agent -N ''"
|
|
431
|
+
echo -n "This skill: "; ls ~/openclaw/skills/aleph-vm-replication/SKILL.md 2>/dev/null && echo "OK" || echo "MISSING"
|
|
432
|
+
echo -n "OpenClaw: "; openclaw --version
|
|
433
|
+
echo "=== Ready to replicate ==="
|
|
434
|
+
REMOTE
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Now the child agent can read this SKILL.md and create its own children — recursively.
|
|
438
|
+
|
|
439
|
+
## Step 9: Manage Instances
|
|
440
|
+
|
|
441
|
+
### List All Instances
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
aleph instance list
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Delete an Instance
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
aleph instance delete <INSTANCE_HASH>
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Check Account Balance (Monitor Burn Rate)
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
aleph account balance
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Replication Script (All-in-One)
|
|
460
|
+
|
|
461
|
+
This script automates the full cycle: find CRN → create VM → wait → install OpenClaw → configure → verify.
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
#!/bin/bash
|
|
465
|
+
# replicate.sh — Create a new Aleph Cloud agent clone
|
|
466
|
+
# Usage: ./replicate.sh [name] [compute-units] [anthropic-key]
|
|
467
|
+
set -euo pipefail
|
|
468
|
+
|
|
469
|
+
AGENT_NAME="${1:-agent-$(date +%s)}"
|
|
470
|
+
COMPUTE_UNITS="${2:-1}"
|
|
471
|
+
ANTHROPIC_KEY="${3:-$ANTHROPIC_API_KEY}"
|
|
472
|
+
SSH_KEY="$HOME/.ssh/aleph_agent"
|
|
473
|
+
ROOTFS="5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e"
|
|
474
|
+
ROOTFS_SIZE=40960
|
|
475
|
+
|
|
476
|
+
if [ -z "$ANTHROPIC_KEY" ]; then
|
|
477
|
+
echo "ERROR: Set ANTHROPIC_API_KEY env var or pass as 3rd arg"
|
|
478
|
+
exit 1
|
|
479
|
+
fi
|
|
480
|
+
|
|
481
|
+
# Generate SSH key if needed
|
|
482
|
+
if [ ! -f "$SSH_KEY" ]; then
|
|
483
|
+
echo "=== Generating SSH keypair ==="
|
|
484
|
+
ssh-keygen -t ed25519 -f "$SSH_KEY" -N "" -C "$AGENT_NAME@aleph"
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
# Find best CRN
|
|
488
|
+
echo "=== Finding best CRN ==="
|
|
489
|
+
CRN_HASH=$(aleph node compute --active --json 2>/dev/null | python3 -c "
|
|
490
|
+
import sys, json
|
|
491
|
+
nodes = json.load(sys.stdin)
|
|
492
|
+
best = max(
|
|
493
|
+
(n for n in nodes if (n.get('score', 0) or 0) > 0.5),
|
|
494
|
+
key=lambda n: n.get('score', 0),
|
|
495
|
+
default=None
|
|
496
|
+
)
|
|
497
|
+
print(best['hash'] if best else '')
|
|
498
|
+
")
|
|
499
|
+
|
|
500
|
+
if [ -z "$CRN_HASH" ]; then
|
|
501
|
+
echo "ERROR: No suitable CRN found"
|
|
502
|
+
exit 1
|
|
503
|
+
fi
|
|
504
|
+
echo "Selected CRN: $CRN_HASH"
|
|
505
|
+
|
|
506
|
+
# Create instance
|
|
507
|
+
echo "=== Creating instance: $AGENT_NAME ==="
|
|
508
|
+
aleph instance create \
|
|
509
|
+
--name "$AGENT_NAME" \
|
|
510
|
+
--compute-units "$COMPUTE_UNITS" \
|
|
511
|
+
--rootfs "$ROOTFS" \
|
|
512
|
+
--rootfs-size "$ROOTFS_SIZE" \
|
|
513
|
+
--payment-type credit \
|
|
514
|
+
--payment-chain BASE \
|
|
515
|
+
--ssh-pubkey-file "${SSH_KEY}.pub" \
|
|
516
|
+
--crn-hash "$CRN_HASH" \
|
|
517
|
+
--crn-auto-tac \
|
|
518
|
+
--skip-volume \
|
|
519
|
+
--verbose
|
|
520
|
+
|
|
521
|
+
# Wait for boot
|
|
522
|
+
echo "=== Waiting 45s for VM to boot ==="
|
|
523
|
+
sleep 45
|
|
524
|
+
|
|
525
|
+
# Get connection details
|
|
526
|
+
echo "=== Connection details ==="
|
|
527
|
+
aleph instance list 2>&1 | tail -30
|
|
528
|
+
|
|
529
|
+
# Parse SSH connection (user must extract HOST and PORT from output above)
|
|
530
|
+
echo ""
|
|
531
|
+
echo "=== $AGENT_NAME created ==="
|
|
532
|
+
echo ""
|
|
533
|
+
echo "Next steps:"
|
|
534
|
+
echo " 1. Find HOST and PORT from the instance list above"
|
|
535
|
+
echo " 2. SSH in: ssh root@<HOST> -p <PORT> -i $SSH_KEY"
|
|
536
|
+
echo " 3. Run the setup script:"
|
|
537
|
+
echo " ssh root@<HOST> -p <PORT> -i $SSH_KEY 'bash -s' < setup-agent.sh"
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Post-Creation Setup Script
|
|
541
|
+
|
|
542
|
+
Save this as `setup-agent.sh` and pipe it via SSH:
|
|
543
|
+
|
|
544
|
+
```bash
|
|
545
|
+
#!/bin/bash
|
|
546
|
+
# setup-agent.sh — Run on a fresh Aleph VM after creation
|
|
547
|
+
# Usage: ssh root@HOST -p PORT -i KEY 'bash -s' < setup-agent.sh
|
|
548
|
+
set -e
|
|
549
|
+
|
|
550
|
+
ANTHROPIC_KEY="${1:-}"
|
|
551
|
+
|
|
552
|
+
echo "=== Installing Node.js 22 ==="
|
|
553
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
|
554
|
+
DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs git python3-pip
|
|
555
|
+
|
|
556
|
+
echo "=== Installing OpenClaw ==="
|
|
557
|
+
npm install -g openclaw
|
|
558
|
+
|
|
559
|
+
echo "=== Installing aleph-client ==="
|
|
560
|
+
pip install aleph-client --break-system-packages
|
|
561
|
+
|
|
562
|
+
echo "=== Creating workspace ==="
|
|
563
|
+
mkdir -p /root/openclaw/{memory,skills}
|
|
564
|
+
mkdir -p /root/.openclaw/agents/main/{agent,sessions}
|
|
565
|
+
|
|
566
|
+
echo "=== Configuring gateway ==="
|
|
567
|
+
GATEWAY_TOKEN=$(openssl rand -hex 24)
|
|
568
|
+
|
|
569
|
+
cat > /root/.openclaw/openclaw.json << CONF
|
|
570
|
+
{
|
|
571
|
+
"auth": {
|
|
572
|
+
"profiles": {
|
|
573
|
+
"anthropic:default": {
|
|
574
|
+
"provider": "anthropic",
|
|
575
|
+
"mode": "token"
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
"agents": {
|
|
580
|
+
"defaults": {
|
|
581
|
+
"model": {
|
|
582
|
+
"primary": "anthropic/claude-sonnet-4-20250514"
|
|
583
|
+
},
|
|
584
|
+
"workspace": "/root/openclaw"
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
"gateway": {
|
|
588
|
+
"port": 18789,
|
|
589
|
+
"mode": "local",
|
|
590
|
+
"bind": "loopback",
|
|
591
|
+
"auth": {
|
|
592
|
+
"mode": "token",
|
|
593
|
+
"token": "$GATEWAY_TOKEN"
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
CONF
|
|
598
|
+
|
|
599
|
+
chmod 700 /root/.openclaw
|
|
600
|
+
chmod 600 /root/.openclaw/openclaw.json
|
|
601
|
+
|
|
602
|
+
echo "=== Starting gateway ==="
|
|
603
|
+
openclaw gateway install --force
|
|
604
|
+
openclaw gateway start
|
|
605
|
+
sleep 3
|
|
606
|
+
|
|
607
|
+
echo ""
|
|
608
|
+
echo "=== SETUP COMPLETE ==="
|
|
609
|
+
echo "Gateway token: $GATEWAY_TOKEN"
|
|
610
|
+
echo "OpenClaw: $(openclaw --version)"
|
|
611
|
+
echo "Node: $(node --version)"
|
|
612
|
+
echo ""
|
|
613
|
+
echo "Remaining: copy auth-profiles.json, identity files, and aleph key"
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
## Security Considerations
|
|
617
|
+
|
|
618
|
+
- **Private keys**: Always `chmod 600`. Never commit to git. Never log or echo.
|
|
619
|
+
- **API keys**: Store in `auth-profiles.json` with `chmod 600`, not in env vars or config files.
|
|
620
|
+
- **Gateway tokens**: Generate unique per VM (`openssl rand -hex 24`). Sync with `openclaw gateway install --force`.
|
|
621
|
+
- **SSH**: Use ed25519 keys only. Disable password auth on production VMs.
|
|
622
|
+
- **Replication limits**: Monitor credit balance. Set alerts. Unbounded replication drains funds fast.
|
|
623
|
+
- **Network isolation**: Gateway binds to loopback by default. Only expose via Tailscale, never raw public IP.
|
|
624
|
+
- **Aleph key sharing**: All child VMs share the same Aleph account. One compromised VM = all VMs at risk. Consider delegated keys for production.
|
|
625
|
+
|
|
626
|
+
## Cost Planning
|
|
627
|
+
|
|
628
|
+
| Tier | vCPU / RAM | Credits/Day | Credits/Month | Approx $/Month |
|
|
629
|
+
|------|-----------|-------------|---------------|-----------------|
|
|
630
|
+
| 1 | 1 / 2 GiB | 342,000 | ~10.3M | ~$3-5 |
|
|
631
|
+
| 2 | 2 / 4 GiB | 684,000 | ~20.5M | ~$6-10 |
|
|
632
|
+
| 3 | 4 / 8 GiB | 1,368,000 | ~41M | ~$12-20 |
|
|
633
|
+
|
|
634
|
+
Extra disk beyond tier default: ~4,416 credits/GiB/day.
|
|
635
|
+
|
|
636
|
+
Check current pricing: `aleph pricing instance`
|
|
637
|
+
|
|
638
|
+
## Troubleshooting
|
|
639
|
+
|
|
640
|
+
### "No API key found for provider" Error
|
|
641
|
+
The gateway reads `auth-profiles.json`, **NOT** `auth.json`. Ensure:
|
|
642
|
+
- File is at `~/.openclaw/agents/main/agent/auth-profiles.json`
|
|
643
|
+
- Format matches the template in Step 6 (with `"version": 1` and `"profiles"` key)
|
|
644
|
+
- Run `openclaw gateway restart` after any auth changes
|
|
645
|
+
|
|
646
|
+
### RPC Probe Failed
|
|
647
|
+
```bash
|
|
648
|
+
openclaw gateway install --force
|
|
649
|
+
openclaw gateway restart
|
|
650
|
+
sleep 3
|
|
651
|
+
openclaw gateway status --token "$GATEWAY_TOKEN"
|
|
652
|
+
```
|
|
653
|
+
The systemd service file caches the token — `install --force` re-syncs it.
|
|
654
|
+
|
|
655
|
+
### Interactive TUI Blocks Automation
|
|
656
|
+
Use `--crn-hash <HASH>` + `--skip-volume` + `--crn-auto-tac`. If still interactive, use the pexpect script from Step 4.
|
|
657
|
+
|
|
658
|
+
### VM Not Reachable After Creation
|
|
659
|
+
- Wait 45-60 seconds (boot takes time on decentralized infra)
|
|
660
|
+
- Check `aleph instance list` for the mapped SSH port
|
|
661
|
+
- Verify the CRN is healthy: `aleph node compute --crn-hash <HASH> --json`
|
|
662
|
+
- Try IPv6 if IPv4 mapping isn't working
|
|
663
|
+
|
|
664
|
+
### Credit Balance Dropping Too Fast
|
|
665
|
+
- `aleph account balance` — check remaining credits
|
|
666
|
+
- `aleph instance list` — count running instances
|
|
667
|
+
- `aleph instance delete <HASH>` — kill unused instances
|
|
668
|
+
- Each running instance burns credits continuously, even idle
|
|
669
|
+
|
|
670
|
+
### Config Validation Errors ("Unrecognized key")
|
|
671
|
+
Only use config keys that OpenClaw recognizes. Known valid top-level keys:
|
|
672
|
+
`auth`, `agents`, `gateway`, `models`, `tools`, `bindings`, `messages`, `commands`, `hooks`, `channels`, `skills`, `plugins`, `wizard`, `meta`
|
|
673
|
+
|
|
674
|
+
Run `openclaw doctor --fix` to auto-remove invalid keys.
|
|
675
|
+
|
|
676
|
+
### dpkg Prompts Block SSH Scripts
|
|
677
|
+
Use `DEBIAN_FRONTEND=noninteractive` before apt commands in non-TTY sessions.
|
package/SECURITY.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Supported Versions
|
|
4
|
-
|
|
5
|
-
| Version | Supported |
|
|
6
|
-
|---------|-----------|
|
|
7
|
-
| 1.x | ✅ Current |
|
|
8
|
-
| < 1.0 | ❌ No longer supported |
|
|
9
|
-
|
|
10
|
-
## Reporting a Vulnerability
|
|
11
|
-
|
|
12
|
-
If you discover a security vulnerability in skills-ws, please report it responsibly:
|
|
13
|
-
|
|
14
|
-
**Email:** bob@openletz.com
|
|
15
|
-
**Subject:** `[SECURITY] skills-ws: <brief description>`
|
|
16
|
-
|
|
17
|
-
Please include:
|
|
18
|
-
- Description of the vulnerability
|
|
19
|
-
- Steps to reproduce
|
|
20
|
-
- Potential impact
|
|
21
|
-
- Suggested fix (if any)
|
|
22
|
-
|
|
23
|
-
We will acknowledge receipt within 48 hours and provide a timeline for a fix.
|
|
24
|
-
|
|
25
|
-
**Do NOT open a public GitHub issue for security vulnerabilities.**
|
|
26
|
-
|
|
27
|
-
## Security Model
|
|
28
|
-
|
|
29
|
-
- **Zero runtime dependencies** — no supply chain risk from third-party packages
|
|
30
|
-
- **No code execution** — skills are markdown files (SKILL.md), not executable code
|
|
31
|
-
- **No eval/exec patterns** — the CLI copies files only, never evaluates content
|
|
32
|
-
- **Environment-only credentials** — skills that reference API keys use environment variables exclusively
|
|
33
|
-
- **VirusTotal scanned** — all skill files are periodically scanned
|
|
34
|
-
- **Build provenance** — npm packages are published with Sigstore provenance attestation
|
|
35
|
-
|
|
36
|
-
## Scope
|
|
37
|
-
|
|
38
|
-
This policy covers:
|
|
39
|
-
- The `skills-ws` npm package
|
|
40
|
-
- The CLI tool (`npx skills-ws`)
|
|
41
|
-
- Skill content in `skills/` directory
|
|
42
|
-
|
|
43
|
-
This policy does NOT cover:
|
|
44
|
-
- Third-party tools referenced in skill documentation (e.g., Google Analytics, VirusTotal)
|
|
45
|
-
- User-modified skill files after installation
|