Files
hapi/docs/guide/installation.md
T
28df974edd feat(settings): onboard hub provider credentials for dictation and voice (#1392)
* feat(settings): onboard hub transcription provider credentials in UI

Env-only keys made dictation invisible; Settings can now add/edit/clear
hub-side credentials (masked), with env still winning as override.
Refs tiann/hapi#1384.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(settings): onboard voice-assistant backends alongside dictation

Same Settings credential surface now covers ElevenLabs, Gemini Live, and
Qwen Realtime (alias env pairs), not only transcription providers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): address PR #1392 Major credential onboard findings

Alias env locks, non-destructive Save (omit empty fields), and
owner-only settings.json permissions for hub-stored provider secrets.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): harden credential onboard for second-pass Majors

Owner-namespace gate, stage-then-sync env after persist, and
per-field OpenAI-compatible editability under mixed env locks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): serialize settings RMW and clear partial compatible creds

Per-file settings lock for concurrent credential PUTs, and Clear shown
for partial OpenAI-compatible entries (key/url/model alone).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): serialize all settings writers via updateSettings

Route credentials, relay auth, generators, server settings, and CLI
token persistence through a locked RMW helper; reset Clear form state.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): share cross-process settings lock with CLI

Extract withSettingsFileLock for hub+CLI, keep owner-only 0o600
rewrites, and race hub credential updates against CLI-style writers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): keep UI secrets out of process.env; PID-own settings locks

Settings-backed provider credentials now live in an in-memory overlay
(getProviderEnvironment) so tunnel/ACP/Codex children do not inherit them.
Settings file locks record pid+token and only reclaim dead or legacy locks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): never reclaim ownerless settings lock sidecars

wx creates the lock path before the owner JSON is visible; unlinking
null owners let a waiter steal a live acquisition and collide on
settings.json.tmp (CI ENOENT). Only reclaim parsed owners with dead PIDs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): reclaim dead locks via rename; clean up failed publishes

Stale reclaim renames the sidecar to a unique break path and re-verifies
the expected dead owner before deleting it, so a loser cannot unlink a
successor's live lock. Failed owner writes unlink the wx sidecar.
Reclaim uses a sync owner read so contenders do not all observe one
dead owner across an await and race the exclusive create.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): reclaim dead locks under exclusive reaper sidecar

Stale reclaim now takes a fixed settings.json.lock.reap lock, re-validates
pid+token, then unlinks — so a delayed contender cannot move a successor's
live lock aside. Also document providerCredentials in settings.schema.json.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): fail closed on corrupt CLI settings; backoff busy reaper

CLI updateSettings now uses a strict read that rejects invalid JSON
instead of treating errors as {}, which could wipe providerCredentials.
Settings lock reclaim sleeps when another process holds .reap so retries
are not burned synchronously.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): publish locks via candidate+link; fix CLI vitest hoist

Acquire settings locks by writing a complete candidate then linkSync to
the fixed path so a crash cannot leave an empty live sidecar. Fix the
CLI persistence regression test to create its temp dir inside vi.hoisted.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): replace bespoke lock with proper-lockfile; hide tenant creds UI

Codex kept finding crash windows in hand-rolled lock sidecars. Switch the
shared settings lock to proper-lockfile's mkdir + mtime lease. Hide the
owner-only credentials editor from non-default namespaces on the voice page.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(settings): adapt sessionSummaryContract to outcome updateSettings

Rebase onto main brought #1376 unique tmp + outcome-shaped writers;
wire sessionSummaryContract and the write-failure credential test to match.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: retrigger CI after rebase onto upstream/main

Empty commit — Meta reported no checks on da0c6c258 after tip-forward rebase.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 13:20:03 +08:00

11 KiB

Installation

Install the HAPI CLI and set up the hub.

Prerequisites

  • At least one supported agent CLI installed (Claude Code, Codex, Cursor Agent, Grok Build, OpenCode, and more — see Supported Agents)

Verify your CLI is installed:

# For Claude Code
claude --version

# For OpenAI Codex CLI
codex --version

# For Cursor Agent CLI
agent --version

# For Grok Build CLI
grok --version

# For OpenCode CLI
opencode --version

Architecture

HAPI has three components:

Component Role Required
CLI Wraps AI coding agents, 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 │
              └───────────┘
  • 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 hub → hapi → work in terminal

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

Install the CLI

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:

brew install tiann/tap/hapi

Other install options

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

Download the latest release from GitHub Releases.

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

./cli/dist-exe/<target>/hapi

<target> is the Bun build target (e.g., bun-linux-x64, bun-darwin-arm64); it defaults to the host platform and architecture.

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
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

For relay key management, TCP fallback, and self-hosted tunnel alternatives, see Deployment.

Local Only

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
Variable Default settings.json Description
CLI_API_TOKEN Auto-generated cliApiToken Shared secret for authentication
HAPI_API_URL http://localhost:3006 apiUrl Hub URL for CLI connections
HAPI_EXTRA_HEADERS_JSON - extraHeaders JSON object of extra outbound headers for CLI → hub HTTP/WebSocket requests
HAPI_LISTEN_HOST 127.0.0.1 listenHost Hub HTTP bind address
HAPI_LISTEN_PORT 3006 listenPort Hub HTTP port
HAPI_PUBLIC_URL - publicUrl Public URL for external access
CORS_ORIGINS - corsOrigins Allowed CORS origins (comma-separated)
TELEGRAM_BOT_TOKEN - telegramBotToken Telegram Bot API token
TELEGRAM_NOTIFICATION true telegramNotification Enable Telegram notifications
SERVERCHAN_SENDKEY - serverChanSendKey Server酱 (ServerChan) SendKey for push notifications
SERVERCHAN_NOTIFICATION true serverChanNotification Enable ServerChan notifications
HAPI_RELAY_API relay.hapi.run - Relay API domain for the public relay
HAPI_RELAY_AUTH Per-hub key issued by the relay relayAuthKey Relay auth key override (set only when an operator provides a key)
HAPI_RELAY_FORCE_TCP false - Force TCP mode for relay
HAPI_OFFICIAL_WEB_URL https://app.hapi.run - Official web app origin, added to CORS when the relay is enabled
VAPID_SUBJECT mailto:admin@hapi.run - Web Push contact info
HAPI_HOME ~/.hapi - Config directory path
DB_PATH ~/.hapi/hapi.db - Database file path
HAPI_EXPERIMENTAL - - CLI: enable experimental features (true/1/yes)
ELEVENLABS_API_KEY - Settings / env ElevenLabs API key for voice + dictation
ELEVENLABS_AGENT_ID Auto-created - Custom ElevenLabs agent ID
OPENAI_API_KEY - Settings / env OpenAI API key for dictation (gpt-transcribe / gpt-live-transcribe)
DEEPGRAM_API_KEY - Settings / env Deepgram API key for dictation (nova-3)
GROQ_API_KEY - Settings / env Groq API key for dictation (whisper-large-v3)
TRANSCRIPTION_BASE_URL - Settings / env OpenAI-compatible/local transcription base URL
TRANSCRIPTION_MODEL - Settings / env Model for the OpenAI-compatible transcription endpoint
TRANSCRIPTION_API_KEY - Settings / env Optional bearer token for that endpoint
settings.json example

Configuration priority: ENV > settings.json > default

When ENV values are set and not present in settings.json, they are automatically saved. HAPI_EXTRA_HEADERS_JSON is not automatically saved, so access credentials are not persisted unexpectedly.

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

JSON Schema: settings.schema.json

CLI setup

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

export HAPI_API_URL="http://your-hub:3006"
export CLI_API_TOKEN="your-token-here"
export HAPI_EXTRA_HEADERS_JSON='{"Cookie":"CF_Authorization=..."}'

Or use interactive login:

hapi auth login

Authentication commands:

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

Diagnostics

Run hapi doctor for a full diagnostics report: configuration, runner status, logs, and relevant environment info.

hapi doctor          # Diagnostics report
hapi doctor clean    # Kill runaway hapi processes

Runner setup

Run a background service for remote session spawning:

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

Split hub + remote runner (peer discovery)

When the hub runs on one host and the runner on another, agents inside runner-spawned sessions should discover peers via MCP list_peers (same hub credentials as the session CLI). Prefer that over shelling hapi ping-peer --list.

[Hub host]  hapi hub          ← sessions DB + /api/sessions
     ▲
     │ HAPI_API_URL + CLI_API_TOKEN
     │
[Runner host]  hapi runner start  → spawns session CLIs
                     │
                     ▼
              agent session  → MCP list_peers / inspect_peer / ping_peer

On the runner host, configure the same hub URL and token the hub uses:

export HAPI_API_URL="http://your-hub:3006"   # or Tailscale / public URL
export CLI_API_TOKEN="your-token-here"
# or: hapi auth login   # saves the token; still set HAPI_API_URL for a remote hub
hapi runner start

Session CLI may export an explicit non-default HAPI_API_URL (from env or settings) into child env so shell helpers hit the same remote hub. It does not mirror CLI_API_TOKEN into wrapped agents (settings/prompt-backed secrets stay out of agent env; a fresh hapi re-reads ~/.hapi/settings.json, and systemd/env tokens already inherit). Prefer MCP list_peers inside a session. Web terminal PTYs still strip hub secrets. If --list fails with an auth/URL error, the message points at hapi auth login and the configured hub URL.

Additional runner commands:

hapi runner list                      # List active sessions
hapi runner stop-session <sessionId>  # Stop a single session managed by the runner

Use --workspace-root <path> to restrict which directories the runner can browse and spawn sessions in. Repeat the flag to allow multiple directories; supports ~ expansion:

hapi runner start --workspace-root ~/projects --workspace-root ~/work

For running the hub and runner as persistent background services (pm2, launchd, systemd), see Deployment.

Security notes

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