Skip to content

Installation

Install the HAPI CLI and set up the hub.

Prerequisites

  • Claude Code, OpenAI Codex CLI, Cursor Agent CLI, Google Gemini CLI, or OpenCode CLI installed

Verify your CLI is installed:

bash
# For Claude Code
claude --version

# For OpenAI Codex CLI
codex --version

# For Cursor Agent CLI
agent --version

# For Google Gemini CLI
gemini --version

# For OpenCode CLI
opencode --version

Architecture

HAPI has three components:

ComponentRoleRequired
CLIWraps AI agents (Claude/Codex/Cursor/Gemini/OpenCode), runs sessionsYes
HubCentral coordinator: persistence, real-time sync, remote accessYes
RunnerBackground service for remote session spawningOptional

How they work together

┌─────────────────────────────────────────────────────┐
│              Your Machine                           │
│                                                     │
│  ┌─────────┐    Socket.IO    ┌─────────────┐       │
│  │  CLI    │◄───────────────►│    Hub      │       │
│  │+ Agent  │                 │  + SQLite   │       │
│  └─────────┘                 └──────┬──────┘       │
│       ▲                             │ SSE          │
│       │ spawn                       ▼              │
│  ┌────┴────┐                 ┌─────────────┐       │
│  │ Runner  │◄────RPC────────►│   Web App   │       │
│  │(背景)   │                 └─────────────┘       │
│  └─────────┘                                       │
└─────────────────────────────────────────────────────┘

           [Tunnel / Public URL]

              ┌─────▼─────┐
              │ Phone/Web │
              └───────────┘
  • CLI: Start a session with hapi. The CLI 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. Lets you spawn sessions from phone/web without keeping a terminal open.

Typical workflows

Local only: hapi hubhapi → work in terminal

Remote access: hapi hub --relayhapi runner start → control from phone/web

Install the CLI

bash
npm install -g @twsxtd/hapi --registry=https://registry.npmjs.org

Recommendation: use the official npm registry for global install. Some mirrors may not sync platform packages in time.

Or with Homebrew:

bash
brew install tiann/tap/hapi

Other install options

npx (no install)
bash
npx @twsxtd/hapi
Prebuilt binary

Download the latest release from GitHub Releases.

bash
xattr -d com.apple.quarantine ./hapi
chmod +x ./hapi
sudo mv ./hapi /usr/local/bin/
Build from source
bash
git clone https://github.com/tiann/hapi.git
cd hapi
bun install
bun build:single-exe

./cli/dist/hapi

Hub setup

The hub can be deployed on:

  • Local desktop (default) - Run on your development machine
  • Remote host - Deploy the hub on a VPS, cloud host, or any machine with network access
bash
hapi hub --relay

The terminal displays a URL and QR code. Scan to access from anywhere.

hapi server remains supported as an alias.

  • End-to-end encrypted with WireGuard + TLS
  • No configuration needed
  • Works behind NAT, firewalls, and any network

Tip: The relay uses UDP by default. If you experience connectivity issues, set HAPI_RELAY_FORCE_TCP=true to force TCP mode.

Local Only

bash
hapi hub
# or
hapi hub --no-relay

The hub listens on http://localhost:3006 by default.

On first run, HAPI:

  1. Creates ~/.hapi/
  2. Generates a secure access token
  3. Prints the token and saves it to ~/.hapi/settings.json
Config files
~/.hapi/
├── settings.json      # Main configuration
├── hapi.db           # SQLite database (hub)
├── runner.state.json  # Runner process state
└── logs/             # Log files
Environment variables
VariableDefaultsettings.jsonDescription
CLI_API_TOKENAuto-generatedcliApiTokenShared secret for authentication
HAPI_API_URLhttp://localhost:3006apiUrlHub URL for CLI connections
HAPI_LISTEN_HOST127.0.0.1listenHostHub HTTP bind address
HAPI_LISTEN_PORT3006listenPortHub HTTP port
HAPI_PUBLIC_URL-publicUrlPublic URL for external access
CORS_ORIGINS-corsOriginsAllowed CORS origins (comma-separated)
TELEGRAM_BOT_TOKEN-telegramBotTokenTelegram Bot API token
TELEGRAM_NOTIFICATIONtruetelegramNotificationEnable Telegram notifications
HAPI_RELAY_FORCE_TCPfalse-Force TCP mode for relay
VAPID_SUBJECTmailto:[email protected]-Web Push contact info
HAPI_HOME~/.hapi-Config directory path
DB_PATH~/.hapi/hapi.db-Database file path
ELEVENLABS_API_KEY--ElevenLabs API key for voice
ELEVENLABS_AGENT_IDAuto-created-Custom ElevenLabs agent ID
settings.json example

Configuration priority: ENV > settings.json > default

When ENV values are set and not present in settings.json, they are automatically saved.

json
{
  "$schema": "https://hapi.run/docs/schemas/settings.schema.json",
  "listenHost": "0.0.0.0",
  "listenPort": 3006,
  "publicUrl": "https://your-domain.com"
}

JSON Schema: settings.schema.json

CLI setup

If the hub is not on localhost, set these before running hapi:

bash
export HAPI_API_URL="http://your-hub:3006"
export CLI_API_TOKEN="your-token-here"

Or use interactive login:

bash
hapi auth login

Authentication commands:

bash
hapi auth status
hapi auth login
hapi auth logout

Each machine gets a unique ID stored in ~/.hapi/settings.json. This allows:

  • Multiple machines to connect to one hub
  • Remote session spawning on specific machines
  • Machine health monitoring

Operations

Self-hosted tunnels

If you prefer not to use the public relay (e.g., for lower latency or self-managed infrastructure), you can use these alternatives:

Cloudflare Tunnel

https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/

Note: Cloudflare Quick Tunnels (TryCloudflare) are not supported because they do not support SSE, which HAPI uses for real-time updates. Use a Named Tunnel instead.

Named tunnel setup:

bash
# Install cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/

# Create and configure a named tunnel
cloudflared tunnel create hapi
cloudflared tunnel route dns hapi hapi.yourdomain.com

# Run the tunnel
cloudflared tunnel --protocol http2 run hapi

Tip: Use --protocol http2 instead of QUIC (the default) to avoid potential timeout issues with long-lived connections.

Tailscale

https://tailscale.com/download

bash
sudo tailscale up
hapi hub

Access via your Tailscale IP:

http://100.x.x.x:3006
Public IP / Reverse Proxy

If the hub has a public IP, access directly via http://your-hub-ip:3006.

Use HTTPS (via Nginx, Caddy, etc.) for production.

Self-signed certificates (HTTPS)

If HAPI_API_URL is set to an https://... URL with a self-signed (or otherwise untrusted) certificate, the CLI may fail with:

Error: self signed certificate

Recommended fixes (in order):

  1. Use a publicly trusted certificate (e.g., Let's Encrypt)
  2. Trust your private CA (recommended for private networks)
  3. Dev-only workaround: disable TLS verification (insecure)
bash
# Preferred: trust your own CA
export NODE_EXTRA_CA_CERTS="/path/to/your-ca.pem"

# Dev-only workaround: disable TLS verification (INSECURE)
export NODE_TLS_REJECT_UNAUTHORIZED=0

If you use the dev-only workaround, assume MITM risk; do not use on public networks.

Telegram setup

Enable Telegram notifications and Mini App access:

  1. Message @BotFather and create a bot
  2. Set the bot token and public URL
  3. Start the hub and bind your account
bash
export TELEGRAM_BOT_TOKEN="your-bot-token"
export HAPI_PUBLIC_URL="https://your-public-url"

hapi hub

Then message your bot with /start, open the app, and enter your CLI_API_TOKEN.

Troubleshooting:

  • If binding fails, verify HAPI_PUBLIC_URL is accessible from the internet
  • Telegram Mini App requires HTTPS (not HTTP)

Runner setup

Run a background service for remote session spawning:

bash
hapi runner start
hapi runner status
hapi runner logs
hapi runner stop

With the runner running:

  • Your machine appears in the "Machines" list
  • You can spawn sessions remotely from the web app
  • Sessions persist even when the terminal is closed
Alternative: pm2

If you prefer pm2 for process management:

bash
pm2 start "hapi runner start --foreground" --name hapi-runner
pm2 save

Background service deployment

Keep HAPI running persistently so it survives terminal closes, system restarts, and continues running in the background.

Quick: nohup

Simple one-liner for quick background runs:

bash
# 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:

bash
tail -f ~/.hapi/logs/hub.log
tail -f ~/.hapi/logs/runner.log

Stop processes:

bash
pkill -f "hapi hub"
pkill -f "hapi runner"
pm2 (recommended for Node.js users)

pm2 provides process management with auto-restart on crashes and system reboot.

bash
# Install pm2
npm install -g pm2

# Start hub and runner
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 the printed instructions
pm2 save       # Save current process list
macOS: launchd

Create plist files for automatic startup on macOS.

Hub (~/Library/LaunchAgents/com.hapi.hub.plist):

xml
<?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 (~/Library/LaunchAgents/com.hapi.runner.plist):

xml
<?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/unload services:

bash
# Load (start)
launchctl load ~/Library/LaunchAgents/com.hapi.hub.plist
launchctl load ~/Library/LaunchAgents/com.hapi.runner.plist

# Unload (stop)
launchctl unload ~/Library/LaunchAgents/com.hapi.hub.plist
launchctl unload ~/Library/LaunchAgents/com.hapi.runner.plist

macOS sleep note: macOS may suspend background processes when the display sleeps. Use caffeinate to prevent this:

bash
caffeinate -dimsu hapi hub --relay

Or run caffeinate -dimsu in a separate terminal while HAPI is running.

Linux: systemd

Create user-level systemd services for automatic startup.

Hub (~/.config/systemd/user/hapi-hub.service):

ini
[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 (~/.config/systemd/user/hapi-runner.service):

ini
[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:

bash
# 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: To keep services running even when not logged in:

bash
loginctl enable-linger $USER

Voice assistant setup

Enable voice control:

  1. Get an API key from elevenlabs.io
  2. Set the environment variable:
bash
export ELEVENLABS_API_KEY="your-api-key"
hapi hub --relay

See Voice Assistant for usage details.

Security notes

  • Keep tokens secret and rotate if needed
  • Use HTTPS for public access
  • Restrict CORS origins in production
Firewall example (ufw)
bash
ufw allow from 192.168.1.0/24 to any port 3006

Released under the LGPL-3.0 License.