This guide covers installing HAPI, configuring the hub, and setting up optional features like Telegram notifications and the background runner.
Prerequisites
HAPI requires at least one AI agent CLI:
Claude Code
Cursor Agent
OpenCode
Codex
Gemini
npm install -g @anthropic-ai/claude-code
claude --version
macOS/Linux: curl https://cursor.com/install -fsS | bash
agent --version
Windows (PowerShell): irm 'https://cursor.com/install?win32=true' | iex
agent --version
# Install OpenCode CLI
npm install -g @opencode/cli
opencode --version
# Install Codex CLI
npm install -g @openai/codex-cli
codex --version
# Install Gemini CLI (via ACP)
npm install -g @google/gemini-cli
gemini --version
Install HAPI CLI
npm (Recommended)
Homebrew
npx (no install)
Prebuilt Binary
Build from Source
npm install -g @twsxtd/hapi --registry=https://registry.npmjs.org
Use the official npm registry for global install. Some mirrors may not sync platform packages in time.
brew install tiann/tap/hapi
# Run without installing
npx @twsxtd/hapi hub --relay
npx @twsxtd/hapi
Download from GitHub Releases : # macOS: Remove quarantine flag
xattr -d com.apple.quarantine ./hapi
chmod +x ./hapi
sudo mv ./hapi /usr/local/bin/
# Verify
hapi --version
git clone https://github.com/tiann/hapi.git
cd hapi
bun install
bun run build:single-exe
# Binary location
./cli/dist/hapi
Building from source requires Bun runtime.
Architecture Overview
HAPI has three components:
Component Role Required CLI Wraps AI agents (Claude/Codex/Cursor/Gemini/OpenCode), runs sessions Yes Hub Central coordinator: persistence, real-time sync, remote access Yes Runner Background service for remote session spawning Optional
How They Work Together
┌─────────────────────────────────────────────────────┐
│ Your Machine │
│ │
│ ┌─────────┐ Socket.IO ┌─────────────┐ │
│ │ CLI │◄───────────────►│ Hub │ │
│ │+ Agent │ │ + SQLite │ │
│ └─────────┘ └──────┬──────┘ │
│ ▲ │ SSE │
│ │ spawn ▼ │
│ ┌────┴────┐ ┌─────────────┐ │
│ │ Runner │◄────RPC────────►│ Web App │ │
│ │ (后台) │ └─────────────┘ │
│ └─────────┘ │
└─────────────────────────────────────────────────────┘
│
[Tunnel / Public URL]
│
┌─────▼─────┐
│ Phone/Web │
└───────────┘
Workflow:
CLI : Start a session with hapi. Wraps your AI agent and syncs with the hub.
Hub : Run hapi hub. Stores sessions, handles permissions, enables remote access.
Runner : Run hapi runner start. Spawn sessions from phone/web without keeping terminal open.
Hub Setup
Option 1: Public Relay (Recommended)
The public relay provides instant remote access with end-to-end encryption:
You’ll see:
┌─────────────────────────────────────────────────────────┐
│ │
│ HAPI Hub Started │
│ │
│ Local: http://localhost:3006 │
│ Remote: https://abc123.relay.hapi.run │
│ │
│ Access Token: hapi_1a2b3c4d5e6f... │
│ (saved to ~/.hapi/settings.json) │
│ │
│ [QR CODE] │
│ │
└─────────────────────────────────────────────────────────┘
The relay uses WireGuard + TLS for end-to-end encryption. Your data is encrypted from your device to your machine—the relay only forwards packets it cannot read.
UDP blocked? Some networks block UDP traffic. Force TCP mode:HAPI_RELAY_FORCE_TCP = true hapi hub --relay
Option 2: Local Only
Run the hub without remote access:
hapi hub
# or
hapi hub --no-relay
The hub listens on http://localhost:3006 by default.
Access from other devices on your local network:
http://<your-computer-ip>:3006
Option 3: Self-Hosted Tunnels
For lower latency or self-managed infrastructure, use your own tunnel:
Cloudflare Tunnel provides secure HTTPS access without exposing ports.Quick Tunnels not supported! Cloudflare Quick Tunnels (TryCloudflare) do not support SSE , which HAPI requires. Use a Named Tunnel instead.Setup: # Install cloudflared
# https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
# Create named tunnel
cloudflared tunnel create hapi
cloudflared tunnel route dns hapi hapi.yourdomain.com
# Start tunnel (use http2 protocol)
cloudflared tunnel --protocol http2 run hapi
Use --protocol http2 instead of QUIC (default) to avoid timeout issues with long-lived connections.
In a separate terminal: export HAPI_PUBLIC_URL = "https://hapi.yourdomain.com"
hapi hub
Tailscale creates a private mesh network between your devices.# Install and connect
sudo tailscale up
# Start hub
hapi hub
Access via your Tailscale IP: If your hub has a public IP, expose it directly: export HAPI_LISTEN_HOST = "0.0.0.0"
export HAPI_LISTEN_PORT = "3006"
hapi hub
Production Setup (HTTPS): Use a reverse proxy like Nginx or Caddy: # Nginx example
server {
listen 443 ssl http2;
server_name hapi.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3006;
proxy_http_version 1.1 ;
proxy_set_header Upgrade $ http_upgrade ;
proxy_set_header Connection "upgrade" ;
proxy_set_header Host $ host ;
proxy_set_header X-Real-IP $ remote_addr ;
}
}
Self-signed certificates? If using self-signed certs, the CLI may fail with “self signed certificate” error. Fix: # Preferred: Trust your CA
export NODE_EXTRA_CA_CERTS = "/path/to/your-ca.pem"
# Dev-only workaround (INSECURE)
export NODE_TLS_REJECT_UNAUTHORIZED = 0
CLI Configuration
If your hub is not on localhost, configure the CLI:
export HAPI_API_URL = "http://your-hub:3006"
export CLI_API_TOKEN = "your-token-here"
Or use interactive login:
Authentication Commands:
hapi auth status # Show current config
hapi auth login # Save token interactively
hapi auth logout # Clear saved credentials
Configuration Priority
ENV > settings.json > default
When ENV values are set, they’re automatically saved to settings.json.
Configuration Files
~/.hapi/
├── settings.json # Main configuration
├── hapi.db # SQLite database (hub)
├── runner.state.json # Runner process state
└── logs/ # Log files
settings.json example:
{
"$schema" : "https://hapi.run/docs/schemas/settings.schema.json" ,
"cliApiToken" : "hapi_1a2b3c4d5e6f..." ,
"apiUrl" : "http://localhost:3006" ,
"machineId" : "machine_xyz789" ,
"listenHost" : "0.0.0.0" ,
"listenPort" : 3006 ,
"publicUrl" : "https://your-domain.com"
}
Environment Variables
Required
Variable Default Description CLI_API_TOKENAuto-generated Shared secret for authentication HAPI_API_URLhttp://localhost:3006Hub URL for CLI connections
Optional
Variable Default Description HAPI_HOME~/.hapiConfig/data directory HAPI_LISTEN_HOST127.0.0.1Hub HTTP bind address HAPI_LISTEN_PORT3006Hub HTTP port HAPI_PUBLIC_URL- Public URL for external access CORS_ORIGINS- Allowed CORS origins (comma-separated) HAPI_RELAY_FORCE_TCPfalseForce TCP mode for relay HAPI_EXPERIMENTALfalseEnable experimental features HAPI_CLAUDE_PATH- Custom Claude executable path
Telegram
Variable Description TELEGRAM_BOT_TOKENToken from @BotFather TELEGRAM_NOTIFICATIONEnable notifications (default: true)
Voice Assistant
Variable Description ELEVENLABS_API_KEYElevenLabs API key ELEVENLABS_AGENT_IDCustom agent ID (auto-created if not set)
Runner
Variable Default Description HAPI_RUNNER_HEARTBEAT_INTERVAL60000Heartbeat interval (ms) HAPI_RUNNER_HTTP_TIMEOUT10000HTTP timeout (ms)
Runner Setup
The runner enables remote session spawning from your phone:
Benefits:
Spawn sessions remotely without terminal access
Sessions persist after terminal closes
Your machine appears in the “Machines” list
Runner Commands:
hapi runner start # Start runner as detached process
hapi runner stop # Stop runner gracefully
hapi runner status # Show diagnostics
hapi runner list # List active sessions
hapi runner logs # Print log file path
hapi runner stop-session < i d > # Terminate specific session
Check Status:
Output:
✓ Runner is running
PID: 12345
Port: 3007
Uptime: 2h 34m
Sessions: 3 active
Background Service Deployment
Keep HAPI running persistently across terminal closes and system restarts.
nohup (Quick)
pm2 (Recommended)
systemd (Linux)
launchd (macOS)
Simple one-liner for background runs: # Hub
nohup hapi hub --relay > ~/.hapi/logs/hub.log 2>&1 &
# Runner
nohup hapi runner start --foreground > ~/.hapi/logs/runner.log 2>&1 &
View logs: tail -f ~/.hapi/logs/hub.log
tail -f ~/.hapi/logs/runner.log
Stop: pkill -f "hapi hub"
pkill -f "hapi runner"
pm2 provides process management with auto-restart.# Install
npm install -g pm2
# Start services
pm2 start "hapi hub --relay" --name hapi-hub
pm2 start "hapi runner start --foreground" --name hapi-runner
# View status and logs
pm2 status
pm2 logs hapi-hub
pm2 logs hapi-runner
# Auto-restart on system reboot
pm2 startup # Follow printed instructions
pm2 save # Save current process list
Hub service (~/.config/systemd/user/hapi-hub.service):[Unit]
Description =HAPI Hub
After =network.target
[Service]
Type =simple
ExecStart =/usr/local/bin/hapi hub --relay
Restart =always
RestartSec =5
[Install]
WantedBy =default.target
Runner service (~/.config/systemd/user/hapi-runner.service):[Unit]
Description =HAPI Runner
After =network.target hapi-hub.service
[Service]
Type =simple
ExecStart =/usr/local/bin/hapi runner start --foreground
Restart =always
RestartSec =5
[Install]
WantedBy =default.target
Enable and start: # Reload systemd
systemctl --user daemon-reload
# Enable (auto-start on login)
systemctl --user enable hapi-hub
systemctl --user enable hapi-runner
# Start now
systemctl --user start hapi-hub
systemctl --user start hapi-runner
# View status/logs
systemctl --user status hapi-hub
journalctl --user -u hapi-hub -f
Persist after logout: loginctl enable-linger $USER
Hub plist (~/Library/LaunchAgents/com.hapi.hub.plist):<? xml version = "1.0" encoding = "UTF-8" ?>
<! DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
< plist version = "1.0" >
< dict >
< key > Label </ key >
< string > com.hapi.hub </ string >
< key > ProgramArguments </ key >
< array >
< string > /usr/local/bin/hapi </ string >
< string > hub </ string >
< string > --relay </ string >
</ array >
< key > RunAtLoad </ key >
< true />
< key > KeepAlive </ key >
< true />
< key > StandardOutPath </ key >
< string > /Users/YOUR_USERNAME/.hapi/logs/hub.log </ string >
< key > StandardErrorPath </ key >
< string > /Users/YOUR_USERNAME/.hapi/logs/hub.log </ string >
</ dict >
</ plist >
Runner plist (~/Library/LaunchAgents/com.hapi.runner.plist):<? xml version = "1.0" encoding = "UTF-8" ?>
<! DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
< plist version = "1.0" >
< dict >
< key > Label </ key >
< string > com.hapi.runner </ string >
< key > ProgramArguments </ key >
< array >
< string > /usr/local/bin/hapi </ string >
< string > runner </ string >
< string > start </ string >
< string > --foreground </ string >
</ array >
< key > RunAtLoad </ key >
< true />
< key > KeepAlive </ key >
< true />
< key > StandardOutPath </ key >
< string > /Users/YOUR_USERNAME/.hapi/logs/runner.log </ string >
< key > StandardErrorPath </ key >
< string > /Users/YOUR_USERNAME/.hapi/logs/runner.log </ string >
</ dict >
</ plist >
Load services: launchctl load ~/Library/LaunchAgents/com.hapi.hub.plist
launchctl load ~/Library/LaunchAgents/com.hapi.runner.plist
macOS sleep note: macOS may suspend background processes when display sleeps. Prevent with caffeinate:caffeinate -dimsu hapi hub --relay
Telegram Setup
Enable Telegram notifications and Mini App access:
Create Bot
Message @BotFather on Telegram: Follow prompts to create your bot and get the token.
Configure Hub
export TELEGRAM_BOT_TOKEN = "your-bot-token"
export HAPI_PUBLIC_URL = "https://your-public-url"
hapi hub --relay
Telegram Mini Apps require HTTPS and a public URL. Use the relay or a tunnel.
Bind Account
Message your bot with /start
Tap “Open App”
Enter your CLI_API_TOKEN when prompted
You’ll now receive notifications for permission requests and session events.
Voice Assistant Setup
Enable voice control with ElevenLabs:
Configure Hub
export ELEVENLABS_API_KEY = "your-api-key"
hapi hub --relay
HAPI will auto-create an agent if ELEVENLABS_AGENT_ID is not set.
Use Voice
In the web app:
Open a session
Click the microphone button
Start speaking
The AI will respond with voice and execute commands.
Diagnostics
Run full system diagnostics:
Output:
✓ Hub connectivity: OK (http://localhost:3006)
✓ Authentication: Valid token
✓ Claude CLI: Found at /usr/local/bin/claude (v1.2.3)
✓ Cursor Agent: Found at /usr/local/bin/agent (v0.9.1)
✓ OpenCode: Not found
✓ Runner: Running (PID 12345, port 3007)
✓ Sessions: 2 active, 15 archived
✓ Database: ~/.hapi/hapi.db (4.2 MB)
Recent logs:
[2024-03-06 10:32:15] Session started: session_abc123
[2024-03-06 10:35:42] Permission approved: edit file.ts
Clean up runaway processes:
Security Best Practices
Token Security:
Keep your CLI_API_TOKEN secret
Rotate tokens if compromised
Use strong, unique tokens for production
Network Security:
Always use HTTPS for public access
Restrict CORS origins in production
Use firewall rules to limit access
Firewall Example (ufw):
# Allow from local network only
ufw allow from 192.168.1.0/24 to any port 3006
Rotate Access Token:
# Generate new token
rm ~/.hapi/settings.json
hapi hub --relay
# Update CLI
hapi auth login
Next Steps
Quickstart Get up and running in 2 minutes
How It Works Understand the architecture
GitHub View source and contribute
Telegram Community Join the discussion