Expose local web pages and HTTP services to the public internet via secure tunnels. Perfect for AI agents that build websites locally and need to share them...
---
name: aitun-tunnel
description: Expose local web pages and HTTP services to the public internet via secure tunnels. Perfect for AI agents that build websites locally and need to share them publicly.
version: 1.0.0
metadata:
openclaw:
requires:
bins:
- python3
envVars:
- name: AITUN_SERVER
required: false
description: "AiTun server address (default: aitun.cc:6639)"
- name: AITUN_SUBDOMAIN
required: false
description: "Custom subdomain to register (e.g., myapp)"
install:
- kind: uv
package: aitun
bins: [aitun]
emoji: "๐"
homepage: https://aitun.cc
---
# AiTun Tunnel - Expose Local Web Pages to the Public Internet
## When to Use
Use this skill when:
- You've built a web page, dashboard, or web app locally and need to share it publicly
- You need to test a local HTTP service from an external device
- You want to demo a locally-running application to someone
- You need a public webhook endpoint for an external service to call
- You want to expose a local API for integration testing
- You built a website for a user and they want to see it in their browser
Do NOT use this skill when:
- The content only needs to be viewed locally
- The user asks for a file download (use file sharing instead)
## Instructions
### Step 1: Install aitun
```bash
pip install aitun
```
Or if already installed, verify:
```bash
which aitun || pip show aitun
```
### Step 2: Start the local web service
Ensure the local web service is running on a port. For static HTML files:
```bash
# Serve a directory with a built HTML page
python3 -m http.server 8080 --directory /path/to/html/dir
```
For other frameworks, start their dev server:
- Next.js: `npm run dev -- -p 3000`
- Flask: `flask run --port 5000`
- Any HTTP service on any port
### Step 3: Create a tunnel
**Option A: Free instant tunnel (no sign-up, 24-hour limit)**
```bash
aitun -s aitun.cc:6639 -l <PORT> --free &
AITUN_PID=$!
sleep 3
```
The output will contain the public URL, e.g.:
- `https://abc123.t.aitun.cc`
- `https://aitun.cc/s/abc123`
**Option B: Via API (no CLI needed)**
```bash
curl -s -X POST https://aitun.cc/aitun-api/tunnel/free
```
Returns JSON with `tunnel_key`, `url`, and `subdomain_url`.
**Option C: Custom subdomain (requires Google sign-in at aitun.cc)**
```bash
# Check availability
curl -s https://aitun.cc/aitun-api/subdomain/check/<name>
# Register via website: https://aitun.cc
# Then use:
aitun -s aitun.cc:6639 -l <PORT> -sub <name>
```
### Step 4: Share the public URL
Tell the user their website is now publicly accessible:
```
Your website is now live at: https://abc123.t.aitun.cc
This free tunnel expires in 24 hours.
For a permanent URL (e.g., myapp.aitun.cc), register at https://aitun.cc
```
### Step 5: Clean up
When done, stop the tunnel:
```bash
kill $AITUN_PID 2>/dev/null
```
## API Reference
### Check subdomain availability
```
GET https://aitun.cc/aitun-api/subdomain/check/{name}
Response: { "available": true, "name": "...", "url": "https://name.aitun.cc" }
```
### Create free tunnel
```
POST https://aitun.cc/aitun-api/tunnel/free
Response: { "tunnel_key": "abc123", "url": "https://aitun.cc/s/abc123", "subdomain_url": "https://abc123.t.aitun.cc" }
```
### Google OAuth login
```
POST https://aitun.cc/aitun-api/auth/google
Body: { "access_token": "<google-access-token>" }
Response: { "user": { ..., "token": "<jwt-token>" } }
```
### Register subdomain (requires auth)
```
POST https://aitun.cc/aitun-api/subdomain/register
Headers: Authorization: Bearer <token>
Body: { "name": "myapp" }
```
## Notes
- Free tunnels expire after 24 hours
- Registered subdomains remain active for 30 days of inactivity; use heartbeat to renew
- Maximum 5 subdomains per registered user
- All traffic is encrypted end-to-end
- The client can optionally serve as a relay node (`--relay` flag) to improve network performance
- Reserved subdomains (www, api, admin, test, etc.) cannot be registered
don't have the plugin yet? install it then click "run inline in claude" again.