feat(serve): humanish serve — run-library surface with capability-link exposure - #291
Merged
Conversation
…afety gating Adds the serve core: a loopback-only run-library HTTP surface with three composable modes — loopback (trust class of today's watch server plus Host allowlist and security headers), capability-link (cookie-minting secret URL, digest-only token storage, per-tap viewer sessions, no loopback-source bypass), and share-safe-open (share_ready-verified runs only, fail-closed admission with stat-identity caching). Auth is origin-scoped by mechanism: the observer client mixes page-relative and root-relative fetches, so a cookie survives both where a path-prefix token cannot. Uniform 401 bodies carry no run-existence oracle; filtered runs 404 byte-identically to nonexistent ones. Runtime E2B stream URLs are structurally absent (separate process) and defensively pinned by an explicit empty array at the serveRunPath call site, with a no-injection test. observer.ts changes are export-only; the watch path is byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Registers 'humanish serve' with a fail-closed flag conflict matrix (exposure requires --expose plus a declared public origin; --auth none requires --safe; tunnel failures tear the server down rather than silently serving loopback-only under declared exposure intent). The optional ngrok tunnel spawns the operator's own external binary — no npm dependency — and is killed by exact child pid on close. The library index is a mobile-first, self-contained page (system fonts, zero external requests) polling the filtered history feed. Docs cover the threat model, mode-to-boundary mapping, stream-URL doctrine, the share_ready doctrinal note, and the reserved v2 control-plane seam. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e scheme, exposure counts - Admission cache gains a bounded TTL (default 30s) so an out-of-band edit to a scanned artifact that leaves run.json untouched is re-verified rather than served on a stale share_ready verdict (defense-in-depth on the open mode). - Cookie Secure now follows the declared public origin scheme, not the spoofable X-Forwarded-Proto header; the tunnel's https origin is declared post-start via addPublicOrigin. - Exposure warning counts derive from the uncapped run list, not the 80-item index, so 'grants access to all N runs' is truthful for large libraries. - admit is required on the handler options (no silent fail-open path). - Auto-open suppressed under --expose (keeps the capability token out of an opener process argv); explicit --open still honored. - Session store sweeps expired records on mint; server force-closes keep-alive connections on shutdown; formatServeHuman gains the conventional trailing newline. - public-surface scan gains Linux host-home-path and ngrok-authtoken patterns. - Docs corrected: /_humanish/api answers 501 behind the auth gate (401 first for session-less exposed requests); safe admission is bounded-fresh, not live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Minor bump for the new `humanish serve` command (loopback run library, capability-link exposure, share_ready-gated open mode, optional ngrok tunnel). Bumps package + doc version pins and records serve in the program-truth table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
humanish serve— a read-only run-library HTTP surface, so the Observer can be watched from another device (e.g. a phone) through an operator-run tunnel, and so runs/replays can be browsed in one place. Complements the existingwatch(attached run) andobserve(one finished run) commands; the whole thing binds127.0.0.1and only reaches beyond loopback through an explicit--expose.Three composable modes:
humanish servehumanish serve --expose --tunnel ngrok --tunnel-domain <domain>--safeto restrict)humanish serve --safe --expose --auth none --tunnel ngrokshare_readyruns only, fail-closedNo password prompt: exposure uses a capability link (256-bit token → HttpOnly, host-only, SameSite=Lax session cookie). The token is per-process and digest-only in memory; Ctrl-C revokes it and every session; restart rotates it.
Design provenance
Designed via a three-lens workshop (minimal-delta / product-vision / security-doctrine) judged by a doctrine critic and a pragmatism judge, then synthesized. Deliberately not shipped (fatal-flawed in judging): no
--hostnon-loopback bind (would send the secret in cleartext over a LAN), no--secretpin flag (defeats restart-rotation revocation), no mutually-exclusive--exposeenum (auth and--safecompose). Exposure always requires--exposeplus a declared origin (--tunnelor--public-url); the Host allowlist stays strict in every mode.Safety properties (each tested)
runtimeStreamUrlsat theserveRunPathcall site) + a no-injection test.--auth nonerequires--safe; a requested tunnel that can't start tears the server down (exit 2) rather than silently serving loopback-only.--saferidesverifyRunshareSafety at the publish/verify boundary, with a bounded (30s) admission TTL so an out-of-band artifact edit can't ride a staleshare_readyverdict.Verification
pnpm release:checkgreen: 1125 tests pass (+67 new across auth, server integration, tunnel, CLI), public-surface scan clean (467 files, 2 binary assets), skill check + pack dry-run good./_humanish/api→ 501; POST → 405; SIGTERM → clean teardown, port closed. (No live tunnel or provider spend in the test suite; the ngrok JSONL fixture is a real captured wire shape with the domain genericized toobserver.example.dev.)Securederived from the declared origin scheme rather than a spoofable header, honest exposure counts,admitmade required, auto-open kept out of process argv under--expose, session-store sweep, keep-alive shutdown, scanner patterns for host paths + ngrok tokens, and two doc/mechanism corrections) are all folded into thefix(serve):commit.For maintainer sign-off
share_readywas designed as the bar for feedback payloads, and the--safe --auth noneopen mode extends it to arbitrary-audience browsing.verifydoes not yet detect free-form PII/PHI (issue #108), so open mode is only as safe as that gate. The capability-link mode (which does not widen the bar — the link holder is you) is the recommended default for phone viewing; the open mode is opt-in and documented as such indocs/architecture/serve.md. Flagging for your explicit call on whether the open mode should ship enabled or be held.Two commits: serve core + auth, then CLI/tunnel/library/docs; plus the review-fix and
0.17.0release commits.🤖 Generated with Claude Code