somratpro commited on
Commit
d41fe21
Β·
verified Β·
1 Parent(s): 496ab5f

Upload 12 files

Browse files
Files changed (12) hide show
  1. CHANGELOG.md +27 -0
  2. CODE_OF_CONDUCT.md +27 -0
  3. CONTRIBUTING.md +46 -0
  4. Dockerfile +40 -0
  5. LICENSE +21 -0
  6. README.md +275 -7
  7. SECURITY.md +28 -0
  8. dns-fix.js +19 -0
  9. health-server.js +27 -0
  10. keep-alive.sh +34 -0
  11. start.sh +237 -0
  12. workspace-sync.sh +40 -0
CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.0.0] - 2026-03-30
6
+
7
+ ### πŸŽ‰ Initial Release
8
+
9
+ #### Features
10
+ - **Any LLM provider** β€” Anthropic (Claude), OpenAI (GPT-4), Google (Gemini)
11
+ - **Telegram integration** β€” connect via @BotFather, supports multiple users
12
+ - **Built-in keep-alive** β€” self-pings to prevent HF Spaces 48h sleep
13
+ - **Auto-sync workspace** β€” commits + pushes to HF Dataset every 10 min
14
+ - **Auto-create backup** β€” creates HF Dataset automatically on first run
15
+ - **Graceful shutdown** β€” saves workspace before container stops
16
+ - **Health endpoint** β€” `/health` on port 7861 for monitoring
17
+ - **DNS fix** β€” bypasses HF Spaces internal DNS restrictions
18
+ - **Version pinning** β€” lock OpenClaw to a specific version
19
+ - **Startup banner** β€” clean summary of all running services
20
+ - **Zero-config defaults** β€” just 2 secrets to get started
21
+
22
+ #### Architecture
23
+ - `start.sh` β€” config generator + validation + orchestrator
24
+ - `keep-alive.sh` β€” self-ping background service
25
+ - `workspace-sync.sh` β€” periodic workspace backup
26
+ - `health-server.js` β€” lightweight health endpoint
27
+ - `dns-fix.js` β€” DNS override for HF network restrictions
CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ ## Our Standards
8
+
9
+ **Positive behavior includes:**
10
+ - Using welcoming and inclusive language
11
+ - Being respectful of differing viewpoints
12
+ - Gracefully accepting constructive criticism
13
+ - Focusing on what is best for the community
14
+
15
+ **Unacceptable behavior includes:**
16
+ - Trolling, insulting, or derogatory comments
17
+ - Public or private harassment
18
+ - Publishing others' private information without permission
19
+ - Other conduct which could reasonably be considered inappropriate
20
+
21
+ ## Enforcement
22
+
23
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting the maintainer. All complaints will be reviewed and investigated.
24
+
25
+ ## Attribution
26
+
27
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.0.
CONTRIBUTING.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to HuggingClaw
2
+
3
+ Thanks for your interest in contributing! 🦞
4
+
5
+ ## How to Contribute
6
+
7
+ ### Bug Reports
8
+ - Open an issue with a clear description
9
+ - Include your HF Space logs if possible
10
+ - Mention which LLM provider you're using
11
+
12
+ ### Feature Requests
13
+ - Open an issue with the `enhancement` label
14
+ - Describe the use case β€” why is this needed?
15
+
16
+ ### Pull Requests
17
+ 1. Fork the repo
18
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
19
+ 3. Make your changes
20
+ 4. Test locally with Docker: `docker build -t huggingclaw . && docker run -p 7860:7860 --env-file .env huggingclaw`
21
+ 5. Commit with a clear message
22
+ 6. Push and open a PR
23
+
24
+ ### Code Style
25
+ - Shell scripts: use `set -e`, quote variables, comment non-obvious logic
26
+ - Keep it simple β€” this project should stay easy to understand
27
+ - No unnecessary dependencies
28
+
29
+ ### Testing
30
+ - Test with at least one LLM provider (Anthropic, OpenAI, or Google)
31
+ - Test with and without Telegram enabled
32
+ - Test with and without workspace backup enabled
33
+ - Verify keep-alive and auto-sync work
34
+
35
+ ## Development Setup
36
+
37
+ ```bash
38
+ cp .env.example .env
39
+ # Fill in your values
40
+ docker build -t huggingclaw .
41
+ docker run -p 7860:7860 --env-file .env huggingclaw
42
+ ```
43
+
44
+ ## Questions?
45
+
46
+ Open an issue or start a discussion. We're friendly! 🀝
Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:22-slim
2
+
3
+ # Version pinning (default: latest)
4
+ ARG OPENCLAW_VERSION=latest
5
+
6
+ # Install git, ca-certificates, jq, and curl
7
+ RUN apt-get update && apt-get install -y \
8
+ git \
9
+ ca-certificates \
10
+ jq \
11
+ curl \
12
+ --no-install-recommends && \
13
+ rm -rf /var/lib/apt/lists/*
14
+
15
+ # Reuse existing node user (UID 1000)
16
+ RUN mkdir -p /home/node/app /home/node/.openclaw && \
17
+ chown -R 1000:1000 /home/node
18
+
19
+ # Install OpenClaw (version configurable via build arg)
20
+ RUN npm install -g openclaw@${OPENCLAW_VERSION}
21
+
22
+ # Copy files
23
+ COPY --chown=1000:1000 dns-fix.js /opt/dns-fix.js
24
+ COPY --chown=1000:1000 health-server.js /home/node/app/health-server.js
25
+ COPY --chown=1000:1000 start.sh /home/node/app/start.sh
26
+ COPY --chown=1000:1000 keep-alive.sh /home/node/app/keep-alive.sh
27
+ COPY --chown=1000:1000 workspace-sync.sh /home/node/app/workspace-sync.sh
28
+ RUN chmod +x /home/node/app/start.sh /home/node/app/keep-alive.sh /home/node/app/workspace-sync.sh
29
+
30
+ USER node
31
+
32
+ ENV HOME=/home/node \
33
+ PATH=/home/node/.local/bin:/usr/local/bin:$PATH \
34
+ NODE_OPTIONS="--require /opt/dns-fix.js"
35
+
36
+ WORKDIR /home/node/app
37
+
38
+ EXPOSE 7860
39
+
40
+ CMD ["/home/node/app/start.sh"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Somrat Sorkar (@somratpro)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,12 +1,280 @@
1
  ---
2
  title: HuggingClaw
3
- emoji: πŸ“Š
4
- colorFrom: gray
5
- colorTo: green
6
  sdk: docker
7
- pinned: false
8
- license: mit
9
- short_description: 🦞 Run your own always-on AI assistant on HuggingFace Spaces
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: HuggingClaw
3
+ emoji: 🦞
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: docker
7
+ app_port: 7860
8
+ pinned: true
 
9
  ---
10
 
11
+ <!-- Badges -->
12
+ [![GitHub Stars](https://img.shields.io/github/stars/somratpro/huggingclaw?style=flat-square)](https://github.com/somratpro/huggingclaw)
13
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
14
+ [![HF Space](https://img.shields.io/badge/πŸ€—%20HuggingFace-Space-blue?style=flat-square)](https://huggingface.co/spaces)
15
+ [![OpenClaw](https://img.shields.io/badge/OpenClaw-Gateway-red?style=flat-square)](https://github.com/openclaw/openclaw)
16
+
17
+ # 🦞 HuggingClaw
18
+
19
+ Run your own **always-on AI assistant** on HuggingFace Spaces β€” for free.
20
+
21
+ Works with **any LLM** (Anthropic, OpenAI, Google), connects via **Telegram**, and persists your workspace to **HF Datasets** automatically.
22
+
23
+ ### ✨ Features
24
+
25
+ - **Zero-config** β€” just add 2 secrets and deploy
26
+ - **Any LLM provider** β€” Claude, GPT-4, Gemini, etc.
27
+ - **Built-in keep-alive** β€” self-pings to prevent HF sleep (no external cron needed)
28
+ - **Auto-sync workspace** β€” commits + pushes changes every 10 min
29
+ - **Auto-create backup** β€” creates the HF Dataset for you if it doesn't exist
30
+ - **Graceful shutdown** β€” saves workspace before container dies
31
+ - **Multi-user Telegram** β€” supports comma-separated user IDs for teams
32
+ - **Health endpoint** β€” `/health` for monitoring
33
+ - **Version pinning** β€” lock OpenClaw to a specific version
34
+ - **100% HF-native** β€” runs entirely on HuggingFace infrastructure
35
+
36
+ ---
37
+
38
+ ## πŸš€ Quick Start
39
+
40
+ ### 1. Duplicate this Space
41
+ Click **"Duplicate this Space"** β†’ name it β†’ set to **Private**
42
+
43
+ ### 2. Add Required Secrets
44
+ Go to **Settings β†’ Secrets**:
45
+
46
+ | Secret | Value |
47
+ |--------|-------|
48
+ | `LLM_API_KEY` | Your API key ([Anthropic](https://console.anthropic.com/) / [OpenAI](https://platform.openai.com/) / [Google](https://ai.google.dev/)) |
49
+ | `GATEWAY_TOKEN` | Run `openssl rand -hex 32` to generate |
50
+
51
+ ### 3. Deploy
52
+ That's it! The Space builds and starts automatically.
53
+
54
+ ### 4. (Optional) Add Telegram
55
+ | Secret | Value |
56
+ |--------|-------|
57
+ | `TELEGRAM_BOT_TOKEN` | From [@BotFather](https://t.me/BotFather) |
58
+ | `TELEGRAM_USER_ID` | Your user ID ([how to find](https://t.me/userinfobot)) |
59
+
60
+ ### 5. (Optional) Enable Workspace Backup
61
+ | Secret | Value |
62
+ |--------|-------|
63
+ | `HF_USERNAME` | Your HuggingFace username |
64
+ | `HF_TOKEN` | [HF token](https://huggingface.co/settings/tokens) with write access |
65
+
66
+ The backup dataset (`huggingclaw-backup`) is **created automatically** β€” no manual setup needed.
67
+
68
+ ---
69
+
70
+ ## πŸ“‹ All Configuration Options
71
+
72
+ See **`.env.example`** for the complete reference with examples.
73
+
74
+ #### Required
75
+
76
+ | Variable | Purpose |
77
+ |----------|---------|
78
+ | `LLM_API_KEY` | LLM provider API key |
79
+ | `GATEWAY_TOKEN` | Gateway auth token |
80
+
81
+ #### LLM
82
+
83
+ | Variable | Default | Purpose |
84
+ |----------|---------|---------|
85
+ | `LLM_PROVIDER` | `anthropic` | Provider: `anthropic`, `openai`, `google` |
86
+ | `LLM_MODEL` | `anthropic/claude-haiku-4-5` | Model to use |
87
+
88
+ #### Telegram
89
+
90
+ | Variable | Purpose |
91
+ |----------|---------|
92
+ | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather |
93
+ | `TELEGRAM_USER_ID` | Single user allowlist |
94
+ | `TELEGRAM_USER_IDS` | Multiple users (comma-separated): `123,456,789` |
95
+
96
+ #### Workspace Backup
97
+
98
+ | Variable | Default | Purpose |
99
+ |----------|---------|---------|
100
+ | `HF_USERNAME` | β€” | Your HF username |
101
+ | `HF_TOKEN` | β€” | HF token (write access) |
102
+ | `BACKUP_DATASET_NAME` | `huggingclaw-backup` | Dataset name (auto-created!) |
103
+ | `WORKSPACE_GIT_USER` | `openclaw@example.com` | Git commit email |
104
+ | `WORKSPACE_GIT_NAME` | `OpenClaw Bot` | Git commit name |
105
+
106
+ #### Background Services
107
+
108
+ | Variable | Default | Purpose |
109
+ |----------|---------|---------|
110
+ | `KEEP_ALIVE_INTERVAL` | `300` (5 min) | Self-ping interval. `0` = disable |
111
+ | `SYNC_INTERVAL` | `600` (10 min) | Auto-sync interval |
112
+
113
+ #### Advanced
114
+
115
+ | Variable | Default | Purpose |
116
+ |----------|---------|---------|
117
+ | `OPENCLAW_VERSION` | `latest` | Pin OpenClaw version |
118
+ | `HEALTH_PORT` | `7861` | Health endpoint port |
119
+
120
+ ---
121
+
122
+ ## πŸ€– LLM Provider Setup
123
+
124
+ ### Anthropic (Claude)
125
+ ```
126
+ LLM_PROVIDER=anthropic
127
+ LLM_API_KEY=sk-ant-v0-...
128
+ LLM_MODEL=anthropic/claude-haiku-4-5
129
+ ```
130
+ Models: `claude-opus-4-6` Β· `claude-sonnet-4-5` Β· `claude-haiku-4-5`
131
+
132
+ ### OpenAI
133
+ ```
134
+ LLM_PROVIDER=openai
135
+ LLM_API_KEY=sk-...
136
+ LLM_MODEL=gpt-4
137
+ ```
138
+ Models: `gpt-4-turbo` Β· `gpt-4` Β· `gpt-3.5-turbo`
139
+
140
+ ### Google (Gemini)
141
+ ```
142
+ LLM_PROVIDER=google
143
+ LLM_API_KEY=AIzaSy...
144
+ LLM_MODEL=gemini-pro
145
+ ```
146
+ Models: `gemini-pro` Β· `gemini-pro-vision`
147
+
148
+ ---
149
+
150
+ ## πŸ“± Telegram Setup
151
+
152
+ 1. Message [@BotFather](https://t.me/BotFather) β†’ `/newbot` β†’ copy the token
153
+ 2. Message [@userinfobot](https://t.me/userinfobot) to get your user ID
154
+ 3. Add secrets: `TELEGRAM_BOT_TOKEN` and `TELEGRAM_USER_ID`
155
+ 4. Restart the Space β†’ DM your bot πŸŽ‰
156
+
157
+ **Multiple users?** Use `TELEGRAM_USER_IDS=123,456,789` (comma-separated)
158
+
159
+ ---
160
+
161
+ ## πŸ’Ύ Workspace Backup
162
+
163
+ Set `HF_USERNAME` + `HF_TOKEN` and HuggingClaw handles everything:
164
+
165
+ 1. **Auto-creates** the dataset if it doesn't exist
166
+ 2. **Restores** workspace on every startup
167
+ 3. **Auto-syncs** changes every 10 minutes (configurable)
168
+ 4. **Saves** on shutdown (graceful SIGTERM handling)
169
+
170
+ Custom dataset name: `BACKUP_DATASET_NAME=my-custom-backup`
171
+
172
+ ---
173
+
174
+ ## πŸ’“ How It Stays Alive
175
+
176
+ HF Spaces sleeps after 48h of no HTTP requests. HuggingClaw prevents this with:
177
+
178
+ - **Self-ping** β€” pings its own URL every 5 min (uses HF's `SPACE_HOST` env var)
179
+ - **Health endpoint** β€” returns `200 OK` with uptime info
180
+ - **Zero dependencies** β€” no external cron, no third-party pinger
181
+
182
+ Your Space runs forever, powered entirely by HF. 🎯
183
+
184
+ ---
185
+
186
+ ## πŸ’» Local Development
187
+
188
+ ```bash
189
+ git clone https://github.com/somratpro/huggingclaw.git
190
+ cd huggingclaw
191
+ cp .env.example .env
192
+ nano .env # fill in your values
193
+ ```
194
+
195
+ **Docker:**
196
+ ```bash
197
+ docker build -t huggingclaw .
198
+ docker run -p 7860:7860 --env-file .env huggingclaw
199
+ ```
200
+
201
+ **Without Docker:**
202
+ ```bash
203
+ npm install -g openclaw@latest
204
+ export $(cat .env | xargs)
205
+ bash start.sh
206
+ ```
207
+
208
+ ---
209
+
210
+ ## πŸ”— Connect via CLI
211
+
212
+ ```bash
213
+ npm install -g openclaw@latest
214
+ openclaw channels login --gateway https://YOUR-SPACE-URL.hf.space
215
+ # Enter your GATEWAY_TOKEN when prompted
216
+ ```
217
+
218
+ ---
219
+
220
+ ## πŸ—οΈ Architecture
221
+
222
+ ```
223
+ HuggingClaw/
224
+ β”œβ”€β”€ Dockerfile # Runtime: Node.js + OpenClaw + curl + jq
225
+ β”œβ”€β”€ start.sh # Config generator + validation + orchestrator
226
+ β”œβ”€β”€ keep-alive.sh # Self-ping to prevent HF sleep
227
+ β”œβ”€β”€ workspace-sync.sh # Periodic workspace commit + push
228
+ β”œβ”€β”€ health-server.js # Health endpoint (/health)
229
+ β”œβ”€β”€ dns-fix.js # DNS override for HF network restrictions
230
+ β”œβ”€β”€ .env.example # Complete configuration reference
231
+ β”œβ”€β”€ .gitignore # Keeps secrets out of version control
232
+ └── README.md # You are here
233
+ ```
234
+
235
+ **Startup flow:**
236
+ 1. Validate secrets β†’ fail fast with clear errors
237
+ 2. Validate HF token β†’ warn if expired
238
+ 3. Auto-create backup dataset if missing
239
+ 4. Restore workspace from HF Dataset
240
+ 5. Generate `openclaw.json` config from env vars
241
+ 6. Print startup summary
242
+ 7. Start background services (keep-alive, auto-sync)
243
+ 8. Launch OpenClaw gateway
244
+ 9. On SIGTERM β†’ save workspace β†’ exit cleanly
245
+
246
+ ---
247
+
248
+ ## πŸ› Troubleshooting
249
+
250
+ **Missing secrets** β†’ Check **Settings β†’ Secrets** for `LLM_API_KEY` and `GATEWAY_TOKEN`
251
+
252
+ **Telegram not working** β†’ Verify bot token is valid, check logs for `πŸ“± Enabling Telegram`
253
+
254
+ **Workspace not restoring** β†’ Check `HF_USERNAME` and `HF_TOKEN` are set, token has write access
255
+
256
+ **Space sleeping** β†’ Check logs for `πŸ’“ Keep-alive started`. If missing, `SPACE_HOST` might not be set
257
+
258
+ **Control UI blocked** β†’ The Space URL is auto-allowlisted. Check logs for origin errors
259
+
260
+ **Version issues** β†’ Pin with `OPENCLAW_VERSION=2026.3.24` in secrets
261
+
262
+ ---
263
+
264
+ ## πŸ“š Links
265
+
266
+ - [OpenClaw Docs](https://docs.openclaw.ai) Β· [OpenClaw GitHub](https://github.com/openclaw/openclaw) Β· [HF Spaces Docs](https://huggingface.co/docs/hub/spaces)
267
+
268
+ ---
269
+
270
+ ## 🀝 Contributing
271
+
272
+ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
273
+
274
+ ## πŸ“„ License
275
+
276
+ MIT β€” see [LICENSE](LICENSE) for details.
277
+
278
+ ---
279
+
280
+ Made with ❀️ by [@somratpro](https://github.com/somratpro) for the [OpenClaw](https://github.com/openclaw/openclaw) community
SECURITY.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ If you discover a security vulnerability, please report it responsibly:
6
+
7
+ 1. **Do NOT open a public issue**
8
+ 2. Email the maintainer or open a private security advisory on GitHub
9
+ 3. Include steps to reproduce if possible
10
+
11
+ We'll respond within 48 hours and work on a fix.
12
+
13
+ ## Security Best Practices
14
+
15
+ When deploying HuggingClaw:
16
+
17
+ - **Set your Space to Private** β€” prevents unauthorized access to your gateway
18
+ - **Use a strong `GATEWAY_TOKEN`** β€” generate with `openssl rand -hex 32`
19
+ - **Keep your HF token scoped** β€” use fine-grained tokens with minimum permissions
20
+ - **Don't commit `.env` files** β€” the `.gitignore` already excludes them
21
+ - **Use `TELEGRAM_USER_ID`** β€” restricts bot access to your account only
22
+ - **Review logs regularly** β€” check for unauthorized access attempts
23
+
24
+ ## Supported Versions
25
+
26
+ | Version | Supported |
27
+ |---------|-----------|
28
+ | 1.0.x | βœ… |
dns-fix.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Fix HF Spaces DNS: internal resolver can't resolve discord.com / api.telegram.org
2
+ // Override dns.lookup (used by http/https) to use Google/Cloudflare DNS
3
+ const dns = require('dns');
4
+ const { Resolver } = dns;
5
+ const resolver = new Resolver();
6
+ resolver.setServers(['8.8.8.8', '1.1.1.1']);
7
+
8
+ const origLookup = dns.lookup;
9
+ dns.lookup = function(hostname, options, callback) {
10
+ if (typeof options === 'function') { callback = options; options = { family: 0 }; }
11
+ resolver.resolve4(hostname, (err, addresses) => {
12
+ if (err || !addresses || !addresses.length) return origLookup.call(dns, hostname, options, callback);
13
+ if (options && options.all) {
14
+ callback(null, addresses.map(a => ({ address: a, family: 4 })));
15
+ } else {
16
+ callback(null, addresses[0], 4);
17
+ }
18
+ });
19
+ };
health-server.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Lightweight health endpoint on port 7861
2
+ // OpenClaw runs on 7860, this runs alongside it
3
+ // Returns 200 OK for keep-alive pings and external monitoring
4
+ const http = require('http');
5
+
6
+ const PORT = process.env.HEALTH_PORT || 7861;
7
+ const startTime = Date.now();
8
+
9
+ const server = http.createServer((req, res) => {
10
+ if (req.url === '/health' || req.url === '/') {
11
+ const uptime = Math.floor((Date.now() - startTime) / 1000);
12
+ res.writeHead(200, { 'Content-Type': 'application/json' });
13
+ res.end(JSON.stringify({
14
+ status: 'ok',
15
+ uptime: uptime,
16
+ uptimeHuman: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`,
17
+ timestamp: new Date().toISOString()
18
+ }));
19
+ } else {
20
+ res.writeHead(404);
21
+ res.end();
22
+ }
23
+ });
24
+
25
+ server.listen(PORT, '0.0.0.0', () => {
26
+ console.log(`πŸ₯ Health server listening on port ${PORT}`);
27
+ });
keep-alive.sh ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Self-ping keep-alive for HF Spaces
3
+ # HF Spaces sleeps after 48h of inactivity (no HTTP requests)
4
+ # This script pings the Space's own URL to prevent that
5
+ #
6
+ # HF provides SPACE_HOST env var automatically (e.g., "username-spacename.hf.space")
7
+ # Runs as a background process alongside the gateway
8
+
9
+ INTERVAL="${KEEP_ALIVE_INTERVAL:-300}" # Default: every 5 minutes
10
+
11
+ if [ "$INTERVAL" = "0" ]; then
12
+ echo "⏸️ Keep-alive: disabled (KEEP_ALIVE_INTERVAL=0)"
13
+ exit 0
14
+ fi
15
+
16
+ if [ -z "$SPACE_HOST" ]; then
17
+ echo "⏸️ Keep-alive: SPACE_HOST not set (not on HF Spaces?), skipping."
18
+ exit 0
19
+ fi
20
+
21
+ # Ping the Space URL β€” any HTTP response (even 404) counts as activity
22
+ PING_URL="https://${SPACE_HOST}"
23
+
24
+ echo "πŸ’“ Keep-alive started: pinging ${PING_URL} every ${INTERVAL}s"
25
+
26
+ while true; do
27
+ sleep "$INTERVAL"
28
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$PING_URL" 2>/dev/null)
29
+ if [ "$HTTP_CODE" = "000" ]; then
30
+ echo "πŸ’“ Keep-alive: ping failed (network error), retrying next cycle..."
31
+ else
32
+ echo "πŸ’“ Keep-alive: OK"
33
+ fi
34
+ done
start.sh ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # ════════════════════════════════════════════════════════════════
5
+ # HuggingClaw β€” OpenClaw Gateway for HF Spaces
6
+ # ════════════════════════════════════════════════════════════════
7
+
8
+ # ── Startup Banner ──
9
+ OPENCLAW_VERSION="${OPENCLAW_VERSION:-latest}"
10
+ echo ""
11
+ echo " ╔══════════════════════════════════════════╗"
12
+ echo " β•‘ 🦞 HuggingClaw Gateway β•‘"
13
+ echo " β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"
14
+ echo ""
15
+
16
+ # ── Validate required secrets ──
17
+ ERRORS=""
18
+ if [ -z "$LLM_API_KEY" ]; then
19
+ ERRORS="${ERRORS} ❌ LLM_API_KEY is not set\n"
20
+ fi
21
+ if [ -z "$GATEWAY_TOKEN" ]; then
22
+ ERRORS="${ERRORS} ❌ GATEWAY_TOKEN is not set (generate: openssl rand -hex 32)\n"
23
+ fi
24
+ if [ -n "$ERRORS" ]; then
25
+ echo "Missing required secrets:"
26
+ echo -e "$ERRORS"
27
+ echo "Add them in HF Spaces β†’ Settings β†’ Secrets"
28
+ exit 1
29
+ fi
30
+
31
+ # ── Set LLM env based on provider ──
32
+ LLM_PROVIDER="${LLM_PROVIDER:-anthropic}"
33
+ LLM_MODEL="${LLM_MODEL:-anthropic/claude-haiku-4-5}"
34
+
35
+ case "$LLM_PROVIDER" in
36
+ anthropic) export ANTHROPIC_API_KEY="$LLM_API_KEY" ;;
37
+ openai) export OPENAI_API_KEY="$LLM_API_KEY" ;;
38
+ google) export GOOGLE_API_KEY="$LLM_API_KEY" ;;
39
+ *) export LLM_API_KEY="$LLM_API_KEY" ;;
40
+ esac
41
+
42
+ # ── Setup directories ──
43
+ mkdir -p /home/node/.openclaw/agents/main/sessions
44
+ mkdir -p /home/node/.openclaw/credentials
45
+ mkdir -p /home/node/.openclaw/workspace
46
+ chmod 700 /home/node/.openclaw
47
+
48
+ # ── Validate HF token (if provided) ──
49
+ if [ -n "$HF_TOKEN" ]; then
50
+ echo "πŸ”‘ Validating HF token..."
51
+ HF_AUTH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $HF_TOKEN" https://huggingface.co/api/repos/create --max-time 10 2>/dev/null || echo "000")
52
+ if [ "$HF_AUTH_STATUS" = "401" ]; then
53
+ echo " ⚠️ HF token is invalid or expired! Workspace backup will not work."
54
+ echo " Get a new token: https://huggingface.co/settings/tokens"
55
+ else
56
+ echo " βœ… HF token is valid"
57
+ fi
58
+ fi
59
+
60
+ # ── Auto-create + Restore workspace from HF Dataset ──
61
+ if [ -n "$HF_USERNAME" ] && [ -n "$HF_TOKEN" ]; then
62
+ BACKUP_DATASET="${BACKUP_DATASET_NAME:-huggingclaw-backup}"
63
+ BACKUP_URL="https://${HF_USERNAME}:${HF_TOKEN}@huggingface.co/datasets/${HF_USERNAME}/${BACKUP_DATASET}"
64
+
65
+ # Auto-create the dataset if it doesn't exist
66
+ echo "πŸ“¦ Checking HF Dataset: ${HF_USERNAME}/${BACKUP_DATASET}..."
67
+ DATASET_CHECK=$(curl -s -o /dev/null -w "%{http_code}" \
68
+ -H "Authorization: Bearer $HF_TOKEN" \
69
+ "https://huggingface.co/api/datasets/${HF_USERNAME}/${BACKUP_DATASET}" \
70
+ --max-time 10 2>/dev/null || echo "000")
71
+
72
+ if [ "$DATASET_CHECK" = "404" ]; then
73
+ echo " πŸ“ Dataset not found, creating ${HF_USERNAME}/${BACKUP_DATASET}..."
74
+ CREATE_RESULT=$(curl -s -w "\n%{http_code}" \
75
+ -X POST "https://huggingface.co/api/repos/create" \
76
+ -H "Authorization: Bearer $HF_TOKEN" \
77
+ -H "Content-Type: application/json" \
78
+ -d "{\"type\":\"dataset\",\"name\":\"${BACKUP_DATASET}\",\"private\":true}" \
79
+ --max-time 15 2>/dev/null || echo "error")
80
+ CREATE_STATUS=$(echo "$CREATE_RESULT" | tail -1)
81
+ if [ "$CREATE_STATUS" = "200" ] || [ "$CREATE_STATUS" = "201" ]; then
82
+ echo " βœ… Dataset created: ${HF_USERNAME}/${BACKUP_DATASET} (private)"
83
+ else
84
+ echo " ⚠️ Could not create dataset (HTTP $CREATE_STATUS). Create it manually:"
85
+ echo " https://huggingface.co/datasets/create"
86
+ fi
87
+ elif [ "$DATASET_CHECK" = "200" ]; then
88
+ echo " βœ… Dataset exists"
89
+ else
90
+ echo " ⚠️ Could not check dataset (HTTP $DATASET_CHECK)"
91
+ fi
92
+
93
+ # Restore workspace
94
+ echo "πŸ“¦ Restoring workspace..."
95
+ WORKSPACE="/home/node/.openclaw/workspace"
96
+ GIT_USER_EMAIL="${WORKSPACE_GIT_USER:-openclaw@example.com}"
97
+ GIT_USER_NAME="${WORKSPACE_GIT_NAME:-OpenClaw Bot}"
98
+
99
+ cd "$WORKSPACE"
100
+ if [ ! -d ".git" ]; then
101
+ git init -q
102
+ git remote add origin "$BACKUP_URL"
103
+ else
104
+ git remote set-url origin "$BACKUP_URL"
105
+ fi
106
+
107
+ git config user.email "$GIT_USER_EMAIL"
108
+ git config user.name "$GIT_USER_NAME"
109
+
110
+ if git fetch origin main 2>/dev/null; then
111
+ git reset --hard origin/main 2>/dev/null && echo " βœ… Workspace restored!"
112
+ else
113
+ echo " ⚠️ No remote data yet, starting fresh."
114
+ fi
115
+ cd /
116
+ fi
117
+
118
+ # ── Build config ──
119
+ CONFIG_JSON=$(cat <<'CONFIGEOF'
120
+ {
121
+ "gateway": {
122
+ "mode": "local",
123
+ "port": 7860,
124
+ "bind": "lan",
125
+ "auth": {
126
+ "token": ""
127
+ },
128
+ "controlUi": {
129
+ "allowInsecureAuth": true
130
+ },
131
+ "trustedProxies": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
132
+ },
133
+ "channels": {},
134
+ "plugins": {
135
+ "entries": {}
136
+ }
137
+ }
138
+ CONFIGEOF
139
+ )
140
+
141
+ # Gateway token + model
142
+ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".gateway.auth.token = \"$GATEWAY_TOKEN\"")
143
+ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".agents.defaults.model = \"$LLM_MODEL\"")
144
+
145
+ # Control UI origin (allow HF Space URL for web UI access)
146
+ if [ -n "$SPACE_HOST" ]; then
147
+ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".gateway.controlUi.allowedOrigins = [\"https://${SPACE_HOST}\"]")
148
+ fi
149
+
150
+ # Telegram (supports multiple user IDs, comma-separated)
151
+ if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
152
+ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq '.plugins.entries.telegram = {"enabled": true}')
153
+ export TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN"
154
+
155
+ if [ -n "$TELEGRAM_USER_IDS" ]; then
156
+ # Convert comma-separated IDs to JSON array
157
+ IDS_JSON=$(echo "$TELEGRAM_USER_IDS" | tr ',' '\n' | sed 's/^ *//;s/ *$//' | jq -R . | jq -s .)
158
+ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".channels.telegram = {\"dmPolicy\": \"allowlist\", \"allowFrom\": $IDS_JSON}")
159
+ elif [ -n "$TELEGRAM_USER_ID" ]; then
160
+ # Single user (backward compatible)
161
+ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".channels.telegram = {\"dmPolicy\": \"allowlist\", \"allowFrom\": [\"$TELEGRAM_USER_ID\"]}")
162
+ fi
163
+ fi
164
+
165
+ # Write config
166
+ echo "$CONFIG_JSON" > "/home/node/.openclaw/openclaw.json"
167
+
168
+ # ── Startup Summary ──
169
+ echo ""
170
+ echo " β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”"
171
+ echo " β”‚ πŸ“‹ Configuration Summary β”‚"
172
+ echo " β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€"
173
+ printf " β”‚ %-40s β”‚\n" "LLM: $LLM_PROVIDER"
174
+ printf " β”‚ %-40s β”‚\n" "Model: $LLM_MODEL"
175
+ if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
176
+ printf " β”‚ %-40s β”‚\n" "Telegram: βœ… enabled"
177
+ else
178
+ printf " β”‚ %-40s β”‚\n" "Telegram: ❌ not configured"
179
+ fi
180
+ if [ -n "$HF_USERNAME" ] && [ -n "$HF_TOKEN" ]; then
181
+ printf " β”‚ %-40s β”‚\n" "Backup: βœ… ${HF_USERNAME}/${BACKUP_DATASET:-huggingclaw-backup}"
182
+ else
183
+ printf " β”‚ %-40s β”‚\n" "Backup: ❌ not configured"
184
+ fi
185
+ if [ -n "$SPACE_HOST" ]; then
186
+ printf " β”‚ %-40s β”‚\n" "Keep-alive: βœ… every ${KEEP_ALIVE_INTERVAL:-300}s"
187
+ printf " β”‚ %-40s β”‚\n" "Control UI: https://${SPACE_HOST}"
188
+ else
189
+ printf " β”‚ %-40s β”‚\n" "Keep-alive: ⏸️ local mode"
190
+ fi
191
+ SYNC_STATUS="❌ disabled"
192
+ if [ -n "$HF_USERNAME" ] && [ -n "$HF_TOKEN" ]; then
193
+ SYNC_STATUS="βœ… every ${SYNC_INTERVAL:-600}s"
194
+ fi
195
+ printf " β”‚ %-40s β”‚\n" "Auto-sync: $SYNC_STATUS"
196
+ echo " β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜"
197
+ echo ""
198
+
199
+ # ── Trap SIGTERM for graceful shutdown ──
200
+ graceful_shutdown() {
201
+ echo ""
202
+ echo "πŸ›‘ Shutting down gracefully..."
203
+
204
+ # Commit any unsaved workspace changes
205
+ if [ -d "/home/node/.openclaw/workspace/.git" ]; then
206
+ echo "πŸ’Ύ Saving workspace before exit..."
207
+ cd /home/node/.openclaw/workspace
208
+ git add -A 2>/dev/null
209
+ if ! git diff --cached --quiet 2>/dev/null; then
210
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
211
+ git commit -m "Shutdown sync ${TIMESTAMP}" 2>/dev/null
212
+ git push origin main 2>/dev/null && echo " βœ… Workspace saved!" || echo " ⚠️ Push failed"
213
+ else
214
+ echo " βœ… No unsaved changes"
215
+ fi
216
+ fi
217
+
218
+ # Kill background processes
219
+ kill $(jobs -p) 2>/dev/null
220
+ echo "πŸ‘‹ Goodbye!"
221
+ exit 0
222
+ }
223
+ trap graceful_shutdown SIGTERM SIGINT
224
+
225
+ # ── Start background services ──
226
+ node /home/node/app/health-server.js &
227
+ /home/node/app/keep-alive.sh &
228
+ /home/node/app/workspace-sync.sh &
229
+
230
+ # ── Launch gateway ──
231
+ echo "πŸš€ Launching OpenClaw gateway on port 7860..."
232
+ echo ""
233
+ openclaw gateway run --port 7860 --bind lan --verbose &
234
+ GATEWAY_PID=$!
235
+
236
+ # Wait for gateway (allows trap to fire)
237
+ wait $GATEWAY_PID
workspace-sync.sh ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Periodic workspace sync to HF Dataset
3
+ # Commits and pushes workspace changes every SYNC_INTERVAL seconds
4
+ # Runs as a background process alongside the gateway
5
+
6
+ INTERVAL="${SYNC_INTERVAL:-600}" # Default: every 10 minutes
7
+ WORKSPACE="/home/node/.openclaw/workspace"
8
+
9
+ # Wait for workspace to be initialized
10
+ sleep 30
11
+
12
+ if [ ! -d "$WORKSPACE/.git" ]; then
13
+ echo "πŸ“ Workspace sync: no git repo found, skipping."
14
+ exit 0
15
+ fi
16
+
17
+ echo "πŸ”„ Workspace sync started: syncing every ${INTERVAL}s"
18
+
19
+ while true; do
20
+ sleep "$INTERVAL"
21
+
22
+ cd "$WORKSPACE" || continue
23
+
24
+ # Check if there are any changes
25
+ git add -A 2>/dev/null
26
+ if git diff --cached --quiet 2>/dev/null; then
27
+ # No changes
28
+ continue
29
+ fi
30
+
31
+ # Commit and push
32
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
33
+ if git commit -m "Auto-sync ${TIMESTAMP}" 2>/dev/null; then
34
+ if git push origin main 2>/dev/null; then
35
+ echo "πŸ”„ Workspace sync: pushed changes (${TIMESTAMP})"
36
+ else
37
+ echo "πŸ”„ Workspace sync: commit ok, push failed (will retry)"
38
+ fi
39
+ fi
40
+ done