hop2 0.11.47 → 0.11.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Jian Zhou
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README-MCP.md CHANGED
@@ -221,8 +221,9 @@ Helper tools (`hopx_`): convenience wrappers built on top of core tools.
221
221
  - `hopx_agents_overview` — fleet status in one call: every agent-created/agent-permitted session with state (running/busy/idle), cwd, foreground program, Claude turn count, bell counters, last activity, and pending wait jobs; `include_user_sessions=false` narrows to agent-created only, `include_ports=true` adds proxy sessions
222
222
  - `hopx_wait_any` — race several background waits (wait_ids and/or terminal_ids, auto-starting `until_agent_done` waits); returns the first real completion(s) plus the still-pending set; expired watches are re-armed automatically (new wait_id in `pending`) rather than reported as completions
223
223
  - `hopx_spawn_agent` — one-call subagent bring-up: create terminal + launch agent CLI (claude/codex/gemini/custom) + wait until ready + optionally run a first task with an automatic completion contract; `isolation="worktree"` gives the worker its own git worktree + fleet/ branch for overlapping-file fleets
224
- - the Claude preset uses the `sonnet` model alias and `bypassPermissions` mode by default, and removes inherited Claude Code process credentials before launch, so a delegated worker uses the host's durable `claude auth login` without parking on approval prompts; pass `permission_mode="manual"` for a restricted worker, `args="--model ..."` to choose another model, or an explicit `command` when inherited credentials are intentional
225
- - the Codex preset runs autonomously and bypasses workspace-trust, approval, and self-update startup gates; Hop rejects detected login, trust, or update screens instead of dispatching a task into them
224
+ - the Claude preset uses the `opus` model alias and `bypassPermissions` mode by default, and removes inherited Claude Code process credentials before launch, so a delegated worker uses the host's durable `claude auth login` without parking on approval prompts; pass `permission_mode="manual"` for a restricted worker, `args="--model ..."` to choose another model, or an explicit `command` when inherited credentials are intentional
225
+ - the host operator can cap the mode with `hop config agent-ceiling <mode>` (or `HOP_AGENT_PERMISSION_CEILING`); a request above the cap is clamped, the applied mode is returned as `permission_mode`, and the clamp is explained in `permission_note`
226
+ - the Codex preset runs autonomously and bypasses workspace-trust, approval, and self-update startup gates when the ceiling is `bypassPermissions` (the default); under any lower ceiling it runs `--full-auto` (sandboxed, asks on request). Hop rejects detected login, trust, or update screens instead of dispatching a task into them
226
227
  - MCP and `hopa spawn --task` wait by default and return the transcript-backed `assistant_reply`; `async=true` is for fleet dispatch through a long-lived MCP connection, while the short-lived `hopa` CLI rejects async waits
227
228
  - `hopx_task_ledger` — the durable orchestration ledger (~/.hop2/orchestration): async dispatches with contracts survive manager/MCP restarts; pending entries reconcile lazily from turn counters + transcripts; acknowledge to consume
228
229
 
package/README.md CHANGED
@@ -290,6 +290,8 @@ Paths:
290
290
  - workspace-backed sessions: `~/.hop2/workspaces/<workspace>/history/<internal-session>.history`
291
291
  - workspace-backed audit logs: `~/.hop2/workspaces/<workspace>/logs/<internal-session>/audit.ndjson`
292
292
 
293
+ Retention: a session's live `audit.ndjson` rotates at 64 MB (`HOP_SESSION_AUDIT_MAX_MB`, keep `HOP_SESSION_AUDIT_KEEP`=5 rotations), and a *dead* session's whole log directory is deleted 90 days after its last write (`HOP_SESSION_AUDIT_RETENTION_DAYS`; `0` disables). Live sessions are never swept.
294
+
293
295
  Key tunables:
294
296
 
295
297
  - `HOP_SESSION_HISTORY_SIZE`
@@ -298,6 +300,8 @@ Key tunables:
298
300
  - `HOP_AGENT_POST_START_HISTORY_INIT`
299
301
  - `HOP_POST_START_HISTORY_INIT`
300
302
  - `HOP_SESSION_AUDIT_INLINE_MAX_BYTES`
303
+ - `HOP_SESSION_AUDIT_MAX_MB`, `HOP_SESSION_AUDIT_KEEP`, `HOP_SESSION_AUDIT_RETENTION_DAYS`
304
+ - `HOP_SESSION_TTL_DAYS` (login session lifetime, default 30)
301
305
  - `HOP_SESSION_TUI_KEYFRAME_INTERVAL_MS`
302
306
  - `HOP_SESSION_TUI_KEYFRAME_TAIL_CHARS`
303
307
  - `HOP_SESSION_TUI_KEYFRAME_MAX_LINES`
@@ -360,13 +364,15 @@ Notes:
360
364
 
361
365
  ### Security
362
366
 
363
- - Password + TOTP is supported; password is required for custom domains
364
- - Login cookies are `httpOnly`, `secure`, and `sameSite=lax`
365
- - Browser sessions persist across `hop stop/start` by default through a stable session secret
366
- - The local server binds only to `127.0.0.1`
367
- - Cloudflare Tunnel provides end-to-end TLS
367
+ - Login is password + TOTP, or a passkey (Touch ID / Face ID / security key). **A password is required before the public tunnel opens**; `hop config totp-only on` opts out.
368
+ - Every login gets its **own per-device session** (random token, stored hashed), valid for 30 days (`HOP_SESSION_TTL_DAYS`). `hop auth` lists logged-in devices, `hop auth revoke <id>` logs one out, `hop auth revoke --all` logs everyone out, `hop auth token` mints a revocable token for an app or script.
369
+ - Login cookies are `httpOnly`, `secure`, `sameSite=lax`, and host-scoped; they survive `hop stop/start`.
370
+ - The daemon's own secret is only for local processes (CLI, MCP, hay) as a Bearer header; it is never a cookie.
371
+ - The local server binds only to `127.0.0.1`; Cloudflare Tunnel provides end-to-end TLS.
372
+ - Audit logs rotate at 64 MB and dead sessions' logs are removed after 90 days (see Logging).
373
+ - `hop config agent-ceiling <mode>` caps the permission mode spawned agents may run with.
368
374
 
369
- Secrets are stored in `~/.hop2/`. Set `HOP_PERSIST_SESSION_SECRET=0` if you want login cookies invalidated on every daemon restart.
375
+ Secrets are stored in `~/.hop2/`. Full threat model, what an authenticated user can do, and how to report a hole: [SECURITY.md](SECURITY.md).
370
376
 
371
377
  ### Runtime and Recovery
372
378
 
package/SECURITY.md ADDED
@@ -0,0 +1,102 @@
1
+ # Security
2
+
3
+ Hop puts a shell on the public internet on purpose. This page says exactly what
4
+ stands between the internet and that shell, what an attacker who gets past it
5
+ can do, and how to report a hole.
6
+
7
+ ## Reporting
8
+
9
+ Email **jianzhou@uchicago.edu** with "hop security" in the subject. Please do
10
+ not open a public issue for anything exploitable. You will get an
11
+ acknowledgement within a few days and a fix or a plan within two weeks for
12
+ anything confirmed.
13
+
14
+ ## Threat model
15
+
16
+ - **In scope:** an attacker on the internet reaching your tunnel URL; a
17
+ stranger who obtained a device's cookie; a lab member on a shared landing
18
+ host trying to reach another member's instance; an agent (yours or a
19
+ prompt-injected one) doing more than you meant through the MCP surface.
20
+ - **Out of scope:** an attacker who already has your local user account, your
21
+ Cloudflare account, or root on the machine. Hop runs as you; it cannot
22
+ protect you from you.
23
+
24
+ ## How a request gets in
25
+
26
+ 1. **TLS and the tunnel.** The daemon binds `127.0.0.1` only. Cloudflare
27
+ Tunnel terminates TLS at Cloudflare and connects to the daemon over
28
+ loopback. Nothing listens on a public port.
29
+ 2. **Login.** One of:
30
+ - password (scrypt, salted) **plus** a TOTP code (`otplib`, 30-second
31
+ steps), or
32
+ - a passkey (WebAuthn; Touch ID, Face ID, security key), enrolled from an
33
+ already-authenticated session and bound to the hostname.
34
+ Failed attempts back off per IP: 5 failures → 30 s, doubling.
35
+ 3. **Per-device sessions.** A successful login mints a random 256-bit token
36
+ for *that device* (stored only as a SHA-256 hash in
37
+ `~/.hop2/.auth-sessions.json`, mode 0600), set as an `httpOnly`, `Secure`,
38
+ `SameSite=Lax`, host-scoped cookie valid for **30 days**
39
+ (`HOP_SESSION_TTL_DAYS`). `hop auth` lists devices; `hop auth revoke <id>`
40
+ logs one out; `hop auth revoke --all` logs everyone out. Logging out
41
+ revokes the session server-side.
42
+ 4. **Local processes.** The hop CLI, MCP server and hay talk to the daemon
43
+ with a per-install secret (`~/.hop2/.session_secret`, 0600) as an
44
+ `Authorization: Bearer` header. That secret is **never** accepted as a
45
+ cookie, and in a WebSocket `?token=` query only on a direct loopback
46
+ connection (never through the tunnel). `hop auth token` mints a
47
+ revocable device token for apps and scripts instead.
48
+
49
+ ## Password is required for remote access
50
+
51
+ TOTP alone is one factor. Hop will not open the public tunnel until a
52
+ password is set (`hop password set`). If you genuinely want
53
+ authenticator-only login — say, behind Cloudflare Access — opt out with
54
+ `hop config totp-only on`; `hop status` shows the policy in force.
55
+
56
+ ## What an authenticated user can do
57
+
58
+ Everything you can do in a terminal on that machine, as your user. There is
59
+ no sandbox between the browser and the PTY. Treat the login page as the only
60
+ boundary, and treat MCP access the same way: an MCP client that can reach the
61
+ daemon can run `hopx_exec`.
62
+
63
+ ## Agents
64
+
65
+ `hopx_spawn_agent` launches Claude Code with `--permission-mode
66
+ bypassPermissions` by default so delegated work never parks on a prompt.
67
+ Cap that on any host you do not fully trust with
68
+ `hop config agent-ceiling <mode>` (for example `acceptEdits`); requests above
69
+ the ceiling are clamped and the clamp is reported in the tool result. The
70
+ Codex preset drops its approval bypass and runs `--full-auto` (sandboxed)
71
+ under any ceiling below the top. This is a safety rail against agent
72
+ accidents, not a security boundary: MCP access already means shell access.
73
+
74
+ ## Audit logs
75
+
76
+ Every session's input and output is recorded to
77
+ `~/.hop2/**/logs/<session>/audit.ndjson`. It is a plaintext record of
78
+ everything typed, including anything pasted into the terminal. The daemon
79
+ rotates each file at 64 MB (`HOP_SESSION_AUDIT_MAX_MB`), keeps 5 rotations
80
+ (`HOP_SESSION_AUDIT_KEEP`), and deletes a *dead* session's log directory 90
81
+ days after its last write (`HOP_SESSION_AUDIT_RETENTION_DAYS`; 0 disables).
82
+ Live sessions are never swept. If you need shorter retention for compliance,
83
+ lower the day count; if you need none, set it to 0 and manage the directory
84
+ yourself.
85
+
86
+ ## Multi-user hosts and self-service signup
87
+
88
+ Each lab member runs their own daemon on their own machine. The admin's
89
+ daemon only issues subdomains and tunnels. Cookies are host-scoped (no
90
+ `Domain=`), so one member's session never reaches another's host. Signup is
91
+ off by default, requires an explicit allowed email domain
92
+ (`hop registration domain <d>`), and gates identity through either a mailed
93
+ confirmation link or a verified Cloudflare Access JWT. An approved user can
94
+ serve anything they like at their subdomain; only approve people you would
95
+ give a subdomain of your own domain to.
96
+
97
+ ## Files that are secrets
98
+
99
+ Under `~/.hop2/` (all 0600): `.auth_secret` (TOTP seed), `.password_hash`,
100
+ `.session_secret` (local IPC), `.auth-sessions.json` (session hashes),
101
+ `.passkeys.json` (public keys only), tunnel credentials/tokens, and the
102
+ `workspaces/**/logs` audit trail. Back them up like passwords.