docs: align documentation with current implementation

This commit is contained in:
weishu
2026-09-12 22:19:32 +08:00
parent dd9cfdcee5
commit 1de9613df6
27 changed files with 360 additions and 227 deletions
+6 -6
View File
@@ -13,7 +13,7 @@ agent's integration; their supported syntax varies by agent.
| Agent | Command | Integration | Local | Remote | Permission modes | Resume |
|-------|---------|-------------|:-----:|:------:|------------------|:------:|
| Claude Code | `hapi claude` | Terminal wrapper (local) + Claude Agent SDK (remote) | ✓ | ✓ | `default` `acceptEdits` `auto` `bypassPermissions` `plan` | ✓ |
| Codex | `hapi codex` | TUI wrapper (local) + `codex app-server` JSON-RPC (remote) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` (+ `plan` collaboration mode) | ✓ |
| Codex | `hapi codex` | Native terminal + `codex app-server` (Codex 0.154.0+) | ✓ | ✓ | `default` `read-only` `yolo` (+ `plan` collaboration mode) | ✓ |
| Cursor Agent | `hapi cursor` | ACP (`agent acp`); legacy stream-json resume | ✓ | ✓ | `default` `plan` `ask` `debug` `autoReview` `yolo` | ✓ |
| Grok Build | `hapi grok` | ACP (`grok agent stdio`) | ✓ | ✓ | `default` `auto` `plan` `bypassPermissions` | ✓ |
| GitHub Copilot | `hapi copilot` | ACP (`copilot --acp --stdio`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ |
@@ -38,10 +38,10 @@ Permission modes are per-agent — each flavor exposes its own set (see the matr
### Local and remote mode
Every session is either **local** (driven from the terminal) or **remote** (driven from web/phone). DSH is remote-only because its ACP server has no local terminal surface. Switching is seamless and keeps the same session state for flavors that support both:
Work **locally** in the terminal or **remotely** from web/phone, keeping the same conversation when you hand off. The support matrix shows which interfaces each agent offers; DSH, Pi, and Antigravity accept input only through HAPI's remote interface.
- **Remote → local:** press double-space in the terminal.
- **Local → remote:** send a message from the web UI or phone; the session switches automatically.
- **Remote → local:** continue in the terminal. If it shows the remote-control screen, press double-space to return to local input.
- **Local → remote:** send a message from the web UI or phone; HAPI handles the handoff.
See [Seamless Handoff](./how-it-works.md#seamless-handoff) for details.
@@ -52,7 +52,7 @@ hapi resume # Interactive picker of resumable sessions on this ma
hapi resume <session-id> # Resume a specific HAPI session
```
`hapi resume` works for every resumable flavor except Gemini and fresh-session-only DSH. An active remote session is handed off to the local terminal first. Pi and Antigravity are the exceptions in the other direction: neither has a local input path, so their sessions always resume in remote mode.
`hapi resume` reopens the conversation on this machine, including active sessions you were using from your phone. Gemini and fresh-session-only DSH cannot be resumed. Pi and Antigravity resume with input still controlled from HAPI rather than the terminal.
## Cursor Agent
@@ -271,7 +271,7 @@ overall permission policy.
## Other agents
- **Claude Code** (`hapi claude`) — local sessions wrap the native TUI, remote sessions drive the Claude Agent SDK. [Claude Code docs](https://docs.anthropic.com/en/docs/claude-code)
- **Codex** (`hapi codex`) — OpenAI's Codex CLI; remote sessions talk to `codex app-server` over JSON-RPC, with a dedicated `plan` collaboration mode. [openai/codex](https://github.com/openai/codex)
- **Codex** (`hapi codex`) — OpenAI's Codex CLI, with terminal/Web control and a dedicated `plan` mode. See [Codex usage and limits](./codex-shared-sessions.md) for resume, terminal-exit behavior, and launch options. [openai/codex](https://github.com/openai/codex)
- **GitHub Copilot** (`hapi copilot`) — Copilot CLI over ACP (`copilot --acp --stdio`). [GitHub Copilot](https://github.com/features/copilot)
- **Kimi** (`hapi kimi`) — Moonshot AI's Kimi CLI over ACP (`kimi acp`). [MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli)
- **OpenCode** (`hapi opencode`) — the open-source OpenCode agent over ACP (`opencode acp`). [opencode.ai](https://opencode.ai)
+17 -6
View File
@@ -50,22 +50,32 @@ https://tailscale.com/download
```bash
sudo tailscale up
hapi hub
```
Access via your Tailscale IP:
The default hub listens only on loopback. For browser access directly via
your Tailscale IP, start it with:
```bash
HAPI_LISTEN_HOST=0.0.0.0 hapi hub
```
Restrict inbound access to the trusted network with your firewall, then open:
```
http://100.x.x.x:3006
```
For native apps and HTTPS-only browser features, use an HTTPS endpoint
(for example, Tailscale Serve forwarding to the loopback hub) instead.
</details>
<details>
<summary>Public IP / Reverse Proxy</summary>
If the hub has a public IP, access directly via `http://your-hub-ip:3006`.
Use HTTPS (via Nginx, Caddy, etc.) for production.
Keep the hub on its default `127.0.0.1:3006` and put an HTTPS reverse proxy
(Nginx, Caddy, etc.) in front of it. A public IP alone does not make the
loopback listener remotely accessible. Set `HAPI_LISTEN_HOST` only when you
need another bind address, and restrict direct access with your firewall.
**Self-signed certificates (HTTPS)**
@@ -104,6 +114,7 @@ Simple one-liner for quick background runs:
```bash
# Hub
mkdir -p ~/.hapi/logs
nohup hapi hub --relay > ~/.hapi/logs/hub.log 2>&1 &
# Runner
@@ -282,7 +293,7 @@ RestartSec=5
WantedBy=default.target
```
> **Why `KillMode=process`?** The runner spawns each agent session as a detached child process (`detached: true` in `cli/src/runner/run.ts`) so that sessions stay alive when the runner exits. Without `KillMode=process`, systemd's default `KillMode=control-group` sends SIGTERM to every PID in the runner's cgroup when the unit stops, defeating the detach and forcibly archiving every running session. `KillMode=process` preserves the contract: stopping or restarting the runner only signals the runner itself; agent sessions stay alive, and a fresh runner re-establishes control via the existing socket.io reconnect path. This applies to runner upgrades, manual restarts, and any reboot in which the runner unit is stopped before agents have finished.
> **Why `KillMode=process`?** Agent sessions are detached from the runner. The default `KillMode=control-group` would terminate them when the runner service stops; `KillMode=process` preserves them during runner restarts and upgrades. It does not keep processes running through an operating-system shutdown or sleep.
Enable and start:
+10 -8
View File
@@ -72,11 +72,12 @@ Yes. Telegram is optional. You can use the web app directly in any browser or in
### How do I receive notifications?
HAPI supports three methods:
HAPI supports these notification channels:
1. **PWA Push Notifications** - Enable when prompted, works even when app is closed
2. **Telegram Bot** - See [Telegram Setup](./notifications.md#telegram-setup)
3. **FCM native push** - Used by the Android/Wear OS companion apps; notifications are delivered via Firebase Cloud Messaging
3. **Native app notifications** - Official Android and iOS apps use encrypted push delivery; pair your hub and allow notifications, with no push-provider setup required
4. **ServerChan (Server酱)** - Send notifications to WeChat and other channels; see [ServerChan Setup](./notifications.md#serverchan-server酱-setup)
### Can I start sessions remotely?
@@ -99,7 +100,7 @@ Yes. Open any session and use the chat interface to send messages directly to th
### Why did my session look idle when the agent woke itself?
Some agents (especially Cursor) can resume after idle from harness signals such as background Shell `notify_on_output` or `/loop`, without you sending a new HAPI message. HAPI treats real ACP agent activity (and permission requests) as thinking again so the session list matches the agent - same keepalive path as a normal turn. This is different from session-attached jobs (`hapi job`), which show progress while the agent stays idle on purpose.
Some agents (especially Cursor) can resume after idle from harness signals such as background Shell `notify_on_output` or `/loop`, without you sending a new HAPI message. HAPI updates the session's thinking indicator when the agent resumes work or requests permission, so the list reflects that activity.
### Can I access a terminal remotely?
@@ -115,10 +116,11 @@ The voice assistant supports three backends: ElevenLabs, Gemini Live, and Qwen R
### Is my data safe?
Yes. HAPI is local-first:
- All data stays on your machine
- Nothing is uploaded to external servers
- The database is stored locally in `~/.hapi/`
HAPI keeps session history on the hub you operate, in `~/.hapi/` by default,
rather than on a central HAPI account server. Your devices connect to that hub.
Coding agents still use their configured model providers; optional voice,
title generation, and notification features also contact external services.
See the [Privacy Policy](../privacy.md) for data handling and encrypted native push.
### How secure is the token authentication?
@@ -261,7 +263,7 @@ hapi doctor clean
| Design | Cloud-first | Local-first |
| Users | Multi-user | Single user by default; lightweight multi-account isolation via [namespaces](./namespace.md) |
| Deployment | Multiple services | Single binary |
| Data | Encrypted on server | Never leaves your machine |
| Session history | Encrypted on server | Stored on your own hub |
See [Why HAPI](./why-hapi.md) for detailed comparison.
+13 -16
View File
@@ -1,9 +1,5 @@
# How it Works
**Codex 0.154.0+** uses a [shared app-server](./codex-shared-sessions.md):
terminal and Web/phone can act simultaneously, without switching ownership.
The local/remote handoff descriptions below apply to other agent integrations.
HAPI consists of three interconnected components that work together to provide remote AI agent control.
## Architecture Overview
@@ -66,7 +62,7 @@ hapi runner start # Run background service for remote session spawning
hapi ping-peer --list # Shell peer shortlist (prefer MCP list_peers in-session)
```
MCP peer tools (same hub/namespace as the session): `list_peers` (discover), `inspect_peer` (read), `ping_peer` (message). These work from runner-spawned sessions even when the hub is on another host - see [Installation → Split hub + remote runner](./installation.md#split-hub--remote-runner-peer-discovery).
MCP peer tools (same hub/namespace as the session): `list_peers` (discover), `inspect_peer` (read), `ping_peer` (message). These work from runner-spawned sessions even when the hub is on another host - see [Installation → Split hub + remote runner](./installation.md#split-hub-remote-runner-peer-discovery).
### HAPI Hub
@@ -86,9 +82,9 @@ A React-based PWA that provides the mobile interface:
- **Chat Interface** - Send messages and view agent responses
- **Permission Management** - Approve or deny tool access
- **File Browser** - Browse project files and view git diffs
- **Terminal View** - Watch the full terminal output of a session
- **Terminal View** - Run commands on the working machine from your browser
- **Voice Assistant** - Talk to your agent and approve permissions by voice (see [Voice input and assistant](./voice-assistant.md))
- **Session Sharing** - Share a read-only view of a session via a link
- **Session References** - Copy a session reference or mention another conversation for context
- **Remote Spawn** - Start new sessions on any connected machine
## Data Flow
@@ -96,10 +92,10 @@ A React-based PWA that provides the mobile interface:
### Starting a Session
```
1. User runs `hapi` in terminal
1. User runs `hapi` and chooses an agent
│
▼
2. CLI starts Claude Code (or other agent)
2. CLI starts the selected agent
│
▼
3. CLI connects to hub via Socket.IO
@@ -188,7 +184,7 @@ When working in local mode, you have the full terminal experience — it is the
- Direct keyboard input with instant response
- Full terminal UI with syntax highlighting
- Best for focused, uninterrupted coding sessions
- All AI processing happens locally on your machine
- Agent tools run on your machine; model requests use the provider configured in the agent
### Remote Mode
@@ -213,14 +209,15 @@ Switch to remote mode when you need to step away:
```
**Local → Remote:**
- Receive a message from phone/web
- Session automatically switches to remote mode
- Terminal shows "Remote mode - waiting for input"
- Open the session on your phone/web and send a message
- HAPI keeps the conversation going on the same working machine
**Remote → Local:**
- Press double-space in terminal
- Instantly regain local control
- Continue typing as if you never left
- Continue typing in the terminal
- If the terminal shows the remote-control screen, press double-space to return to local input
Some agents keep both interfaces available at once, so no switch is needed.
For Codex terminal-exit and resume behavior, see [Usage and limits](./codex-shared-sessions.md).
### Use Cases
+7
View File
@@ -196,11 +196,18 @@ On first run, HAPI:
| `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_ANDROID_PUSH` | `auto` | `androidPushMode` | Android push: official relay by default, direct FCM when private credentials are configured; also accepts `relay`, `fcm`, `off` |
| `HAPI_IOS_PUSH` | `relay` | `iosPushMode` | iOS push: `relay`, direct `apns`, or `off` |
| `HAPI_PUSH_RELAY_URL` | `https://push.hapi.run` | `iosPushRelayUrl` | Shared Android/iOS push relay, independent of the network tunnel |
| `FCM_SERVICE_ACCOUNT_PATH` | - | `fcmServiceAccountPath` | Direct FCM credentials for private builds using the same Firebase project |
| `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 |
| `GEMINI_API_KEY` / `GOOGLE_API_KEY` | - | Settings / env | Gemini Live voice assistant |
| `DASHSCOPE_API_KEY` / `QWEN_API_KEY` | - | Settings / env | Qwen Realtime voice assistant |
| `VOICE_BACKEND` | Auto-detected | - | Default assistant backend: `elevenlabs`, `gemini-live`, or `qwen-realtime` |
| `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`) |
+16 -3
View File
@@ -1,8 +1,20 @@
# Notifications
Get notified when sessions need input, request permissions, fail, or complete — via Telegram, Server酱 (ServerChan), Web Push, or voice.
Get notified when sessions need input, request permissions, fail, or complete — via native app notifications, Telegram, Server酱 (ServerChan), Web Push, or voice.
Web Push works out of the box once you [install the PWA](./pwa.md); no configuration needed. The channels below are optional.
Web Push needs no provider configuration: [install the PWA](./pwa.md) and allow notifications. The channels below are optional.
## Native app notifications
For official Android and iOS apps, pair an updated hub and allow notifications.
No Firebase project or Apple developer account is needed. Notification content
is end-to-end encrypted through the official push relay, which also works when
you access the hub through Tailscale or your own HTTPS setup. Android requires
Google Play services and FCM connectivity.
Private app builds can use their own matching Firebase/APNs credentials.
See the [native push contract](../api/native-companion-contract.md) for those
settings and delivery details.
## Telegram Setup
@@ -19,7 +31,8 @@ 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`.
Then message your bot with `/start`, open the app, and bind using
`CLI_API_TOKEN:<namespace>` (for example, `your-token:default`).
Related environment variables:
+3 -1
View File
@@ -26,7 +26,9 @@ Details and local-only mode: [Hub setup](./installation.md#hub-setup)
hapi
```
This starts Claude Code wrapped with HAPI. The session appears in the web UI.
Choose an installed agent from the picker. Its session appears in the web UI.
To start one directly, use `hapi claude`, `hapi codex`, or another
[supported agent command](./agents.md). Scripts must specify the agent explicitly.
## Open the UI
+14 -14
View File
@@ -2,7 +2,7 @@
[Happy](https://github.com/slopus/happy) is an excellent project. So why build HAPI?
**The short answer**: Happy uses a centralized server that stores your encrypted data. HAPI is decentralized — each user runs their own hub, and the relay server only forwards encrypted traffic without storing anything. These different goals lead to fundamentally different architectures.
**The short answer**: Happy uses a centralized server that stores your encrypted data. HAPI is decentralized — each user runs their own hub, and the optional network relay forwards encrypted traffic rather than hosting your conversation history. These different goals lead to fundamentally different architectures.
## TL;DR
@@ -10,8 +10,8 @@
|--------|-------|------|
| **Architecture** | Centralized (cloud server stores encrypted data) | Decentralized (each user runs own hub) |
| **Users** | Multi-user on shared server | Any number (each runs own hub) |
| **Data** | Encrypted on server (server cannot read) | Stays on your machine |
| **Encryption** | Application-layer E2EE (client encrypts before sending) | WireGuard + TLS via relay; or none needed if self-hosted |
| **Session history** | Encrypted on server (server cannot read) | Stored on your own hub |
| **Encryption** | Application-layer E2EE (client encrypts before sending) | WireGuard + TLS via relay; HTTPS for self-hosted remote access |
| **Deployment** | Multiple services (PostgreSQL, Redis, app server) | Single binary |
| **Complexity** | High (E2EE, key management, scaling) | Low (one command) |
@@ -56,14 +56,14 @@ The server stores encrypted data — it never sees plaintext, but it does hold y
Each user runs their own hub. HAPI offers two modes of remote access:
- **Self-hosted** (own server / Cloudflare Tunnel / Tailscale) — You control the full network path, no E2EE needed
- **Self-hosted** (own server / Cloudflare Tunnel / Tailscale) — You choose the host and HTTPS endpoint
- **Public relay** (`hapi hub --relay`) — E2E encrypted via tunwg (WireGuard + TLS); the relay only forwards opaque packets
- **Single embedded database** — SQLite, no external services
- **One-command deployment** — Single binary, zero config
#### Mode 1: Self-Hosted (own server or tunnel)
You control the entire path. No encryption beyond standard HTTPS is needed.
You operate the hub and choose how to expose it. Use HTTPS for remote access; a third-party proxy that terminates TLS is part of that trust boundary.
```
┌────────────────────────────────────────────────────────────────────────┐
@@ -134,10 +134,10 @@ The relay server only forwards encrypted packets — it cannot read your data.
| Aspect | Happy | HAPI |
|--------|-------|------|
| **Where data lives** | Cloud server (encrypted blobs) | Your own machine |
| **Who stores it** | Central server holds encrypted data | Only your hub, locally |
| **Where session history lives** | Cloud server (encrypted blobs) | Your own hub |
| **Who stores it** | Central server holds encrypted data | Your hub; clients may cache data |
| **Data at rest** | Encrypted (server cannot read) | Plaintext (protected by OS) |
| **Server's role** | Stores encrypted data + syncs devices | Relay only forwards (or no server at all if self-hosted) |
| **Server's role** | Stores encrypted data + syncs devices | Your hub stores history; optional relay forwards traffic |
### Deployment Model
@@ -203,14 +203,14 @@ Goal: Multi-user cloud platform
```
Goal: Self-hosted tool — each user runs their own hub
│
├──► Data never leaves your machine
│ └──► No application-layer E2EE needed
├──► History stored on your own hub
│ └──► No central HAPI history store
│
├──► Each user has their own hub
│ └──► No horizontal scaling needed; unlimited users in aggregate
│
├──► Self-hosted access (own server/tunnel)
│ └──► You control the full path — HTTPS sufficient
│ └──► Your hub behind your chosen HTTPS endpoint
│
└──► Public relay access
└──► WireGuard + TLS (tunwg) — relay forwards only
@@ -223,7 +223,7 @@ Goal: Self-hosted tool — each user runs their own hub
| Dimension | Happy | HAPI |
|-----------|-------|------|
| **Architecture** | Centralized cloud server | Decentralized (each user runs own hub) |
| **Server's role** | Stores encrypted data | Relay only forwards (or none if self-hosted) |
| **Server's role** | Stores encrypted data | Your hub stores history; optional relay forwards traffic |
| **Data location** | Server (encrypted, zero-knowledge) | Local (plaintext, your machine) |
| **Deployment** | Multiple services (PostgreSQL, Redis, Node.js) | Single binary (embedded SQLite) |
| **Encryption** | Application-layer E2EE (client-side) | WireGuard + TLS (relay) or HTTPS (self-hosted) |
@@ -236,6 +236,6 @@ The architectural differences stem from a centralized vs decentralized design:
- **Happy**: Centralized cloud server that stores your encrypted data. The server never sees plaintext (zero-knowledge), but it does hold your data. This requires application-layer E2EE, key management, and distributed infrastructure (PostgreSQL, Redis, scaling).
- **HAPI**: Decentralized — each user runs their own hub. Your data stays on your machine. For remote access, you can self-host (own server or tunnel — no E2EE needed since you control the path) or use the public relay (WireGuard + TLS via tunwg — the relay only forwards encrypted packets it cannot read). This achieves one-command deployment with zero external dependencies.
- **HAPI**: Decentralized — you run the hub that stores your session history, on your workstation or another host you control. Remote access uses your own HTTPS endpoint or the built-in encrypted network relay. The CLI, hub, web app, and SQLite database ship in one binary.
The core tradeoff: Happy solves the "untrusted server" problem with sophisticated encryption. HAPI avoids the problem entirely by keeping your data on your own machine.
The core tradeoff: Happy encrypts data for storage on its server; HAPI puts the history store under your control. Coding agents and optional voice, title-generation, and notification features still use their configured providers. See the [Privacy Policy](../privacy.md) for those data flows and native push relay metadata.