humanish 0.16.0 → 0.17.0
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/README.md +43 -3
- package/dist/observer-auth.d.ts +21 -0
- package/dist/observer-auth.js +92 -0
- package/dist/observer-auth.js.map +1 -0
- package/dist/observer-library.d.ts +19 -0
- package/dist/observer-library.js +189 -0
- package/dist/observer-library.js.map +1 -0
- package/dist/observer-serve.d.ts +111 -0
- package/dist/observer-serve.js +369 -0
- package/dist/observer-serve.js.map +1 -0
- package/dist/observer.d.ts +31 -0
- package/dist/observer.js +10 -5
- package/dist/observer.js.map +1 -1
- package/dist/program.js +242 -0
- package/dist/program.js.map +1 -1
- package/dist/serve-tunnel.d.ts +16 -0
- package/dist/serve-tunnel.js +104 -0
- package/dist/serve-tunnel.js.map +1 -0
- package/docs/architecture/observer.md +7 -0
- package/docs/architecture/serve.md +140 -0
- package/docs/contracts/schemas.md +23 -1
- package/docs/goals/current.md +2 -1
- package/docs/principles/invariants-and-defaults.md +1 -0
- package/docs/ramp/README.md +1 -1
- package/package.json +1 -1
|
@@ -71,6 +71,13 @@ sanitized transcripts, traces, and verdict events are available. This gives a
|
|
|
71
71
|
served Observer a truthful active state to poll while noninteractive local
|
|
72
72
|
actors are still running.
|
|
73
73
|
|
|
74
|
+
Watch is deliberately distinct from `humanish serve`. Watch serves ONE
|
|
75
|
+
attached run, and the process that created it may inject runtime stream URLs
|
|
76
|
+
(live hosted-desktop viewers) into the observer data it serves. Serve is the
|
|
77
|
+
LIBRARY surface — every run under `.humanish/runs/` — and never serves runtime
|
|
78
|
+
stream URLs in any mode; remote viewers see persisted evidence only. See
|
|
79
|
+
[Serve: the run library surface](serve.md).
|
|
80
|
+
|
|
74
81
|
## UI Shape
|
|
75
82
|
|
|
76
83
|
The Observer shell has:
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Serve: the run library surface
|
|
2
|
+
|
|
3
|
+
Date: 2026-08-02
|
|
4
|
+
|
|
5
|
+
Status: shipped in v1 — loopback, capability-link, and share-safe-open modes
|
|
6
|
+
(`src/observer-serve.ts`, `src/observer-auth.ts`, `src/observer-library.ts`,
|
|
7
|
+
`src/serve-tunnel.ts`; CLI wiring in `src/program.ts`). The `/_humanish/api/*`
|
|
8
|
+
control-plane namespace is reserved and answers `501`; no mutating route ships.
|
|
9
|
+
|
|
10
|
+
## What serve is
|
|
11
|
+
|
|
12
|
+
`humanish serve` is the third observer surface, and the first whose subject is
|
|
13
|
+
the LIBRARY rather than a run:
|
|
14
|
+
|
|
15
|
+
- `humanish watch` — one ATTACHED run: the process that created the run serves
|
|
16
|
+
it and may inject runtime stream URLs for live following;
|
|
17
|
+
- `humanish observe` — one FINISHED run, re-served read-only;
|
|
18
|
+
- `humanish serve` — the whole local library under `.humanish/runs/`: a
|
|
19
|
+
library index, per-run Observer pages, `/_humanish/history.json` polling, and
|
|
20
|
+
optional exposure beyond the machine.
|
|
21
|
+
|
|
22
|
+
The server binds `127.0.0.1` unconditionally (`serveObserverLibrary`); exposure
|
|
23
|
+
only ever happens through a tunnel or proxy forwarding to the loopback port.
|
|
24
|
+
`--expose` gates every request behind a capability link: a per-process secret
|
|
25
|
+
whose SHA-256 digest is compared timing-safe (`verifyTokenDigest`), minting an
|
|
26
|
+
HttpOnly viewer-session cookie with a bounded TTL. Ctrl-C revokes the link and
|
|
27
|
+
all sessions (`revokeAll` on close); restarting mints a new link.
|
|
28
|
+
|
|
29
|
+
## Threat model
|
|
30
|
+
|
|
31
|
+
**The tunnel-agent-connects-from-loopback trap.** The classic mistake on a
|
|
32
|
+
tunneled local server is "peer address is 127.0.0.1, so this is the local
|
|
33
|
+
operator — skip auth." The tunnel agent connects from loopback, so that
|
|
34
|
+
shortcut disables auth in exactly the deployment it exists to protect. Serve
|
|
35
|
+
auth therefore ignores source address entirely: every route below the auth
|
|
36
|
+
mint requires a valid session, loopback peer or not.
|
|
37
|
+
|
|
38
|
+
**Referer and unfurler leakage.** The capability URL embeds the secret in the
|
|
39
|
+
path. Every response carries `referrer-policy: no-referrer` (plus `no-store`
|
|
40
|
+
and `x-robots-tag: noindex`), so a link inside a served page can never leak the
|
|
41
|
+
origin or token onward. The residual channel is the operator pasting the link
|
|
42
|
+
into a chat app whose unfurler prefetches it server-side — which is why the
|
|
43
|
+
link is deliberately NOT single-use (a preview fetch must not burn it before
|
|
44
|
+
the phone taps it), and why the README steers sharing toward AirDrop or manual
|
|
45
|
+
entry.
|
|
46
|
+
|
|
47
|
+
**Host-only cookie scope on shared tunnel domains.** The session cookie sets
|
|
48
|
+
no `Domain` attribute (`buildSessionCookie`), so it is host-only. On a shared
|
|
49
|
+
tunnel apex (many customers under one provider domain), a `Domain` cookie
|
|
50
|
+
would be presented to sibling subdomains an attacker can rent; a host-only
|
|
51
|
+
cookie can never leave the exact public origin the operator declared.
|
|
52
|
+
|
|
53
|
+
**DNS rebinding and the strict Host allowlist.** A malicious page can point an
|
|
54
|
+
attacker-controlled DNS name at 127.0.0.1 and read a permissive local server
|
|
55
|
+
from the victim's browser. Serve keeps a strict Host allowlist in ALL modes —
|
|
56
|
+
loopback names plus the declared tunnel/public origin only — and answers
|
|
57
|
+
`421 Misdirected Request` otherwise. Even the unauthenticated loopback default
|
|
58
|
+
never trusts an arbitrary Host header.
|
|
59
|
+
|
|
60
|
+
**Token in phone history (residual).** After the tap, the capability URL
|
|
61
|
+
remains in the phone's browser history and share sheet. TTL-bounded sessions,
|
|
62
|
+
per-process minting, and Ctrl-C revocation bound the damage — a link recovered
|
|
63
|
+
from history after the process exits or restarts is dead — but while the same
|
|
64
|
+
process runs, a leaked history entry is a live credential. This risk is
|
|
65
|
+
accepted and documented, not solved.
|
|
66
|
+
|
|
67
|
+
## Mode-to-boundary mapping
|
|
68
|
+
|
|
69
|
+
| Mode | Invocation | Boundary class |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `loopback` | `humanish serve` | Capture-side trust: readable only by whoever can already read gitignored `.humanish/` on this machine; no new boundary is crossed. |
|
|
72
|
+
| `capability-link` | `--expose` (auth defaults to `link`) | Declared-friction exposure: publishing-adjacent, not publishing. Anyone holding the secret link reads everything it grants until Ctrl-C — the printed warning names the run count, including non-`share_ready` runs unless `--safe` composes in. |
|
|
73
|
+
| `share-safe-open` | `--expose --safe --auth none` | Genuine publishing, behind the feedback-grade `share_ready` gate: only runs that pass verify are served — admission is re-checked when a bundle changes and re-verified within a bounded window (default 30s), so it is fresh, not perfectly live; everything else is absent, 404ing byte-identically to a nonexistent run (no existence oracle). |
|
|
74
|
+
|
|
75
|
+
## Stream-URL doctrine
|
|
76
|
+
|
|
77
|
+
Live desktop stream URLs (auth-bearing hosted-VNC links) are never served by
|
|
78
|
+
this surface, in any mode, and the guarantee is layered:
|
|
79
|
+
|
|
80
|
+
- **structural** — runtime stream URLs live in a `WeakMap` keyed by the watch
|
|
81
|
+
process's in-memory `ObserverResult` (`src/observer.ts`) and are never
|
|
82
|
+
persisted into any bundle artifact; serve is a separate process reading disk,
|
|
83
|
+
so there is nothing for it to find;
|
|
84
|
+
- **defensive** — the serve handler passes an explicit empty array at its
|
|
85
|
+
`serveRunPath` call site, so a future refactor that makes injection ambient
|
|
86
|
+
would still serve zero stream URLs here;
|
|
87
|
+
- **tested** — the serve suite pins a no-injection test: observer data served
|
|
88
|
+
through the library surface carries no runtime stream URLs.
|
|
89
|
+
|
|
90
|
+
A `--live-streams` flag on serve was considered and deliberately refused: the
|
|
91
|
+
persisted bundle carries no stream URLs, so the flag would be a claim without a
|
|
92
|
+
mechanism (invariant 6). Remote live following belongs to a designed
|
|
93
|
+
`watch --expose` (see Future work), where the attached process actually holds
|
|
94
|
+
the URLs.
|
|
95
|
+
|
|
96
|
+
## The share_ready doctrine
|
|
97
|
+
|
|
98
|
+
`share_ready` was designed as the bar for FEEDBACK payloads: evidence eligible
|
|
99
|
+
to leave the machine inside a public issue draft. Serve's open mode extends
|
|
100
|
+
that same gate to arbitrary-audience BROWSING, which is a broader exposure of
|
|
101
|
+
the same artifacts. The honest caveat carries over unchanged: `humanish
|
|
102
|
+
verify` does not yet detect free-form PII/PHI (names, emails, medical
|
|
103
|
+
identifiers — see the README's public-safety boundary and issue #108), so
|
|
104
|
+
`share_ready` means the automated secret/path scan passed, not that a human
|
|
105
|
+
would publish every pixel. Maintainers should treat open mode accordingly:
|
|
106
|
+
synthetic data upstream, review before exposing, and treat `--safe --auth
|
|
107
|
+
none` as publishing because it is.
|
|
108
|
+
|
|
109
|
+
## v2 control-plane seam contract
|
|
110
|
+
|
|
111
|
+
The reserved `/_humanish/api/*` namespace answers `501` with
|
|
112
|
+
`HUMANISH_SERVE_CONTROL_PLANE_DISABLED` in v1 to any request that clears the
|
|
113
|
+
auth gate — so under `--expose --auth link` a session-less request gets the
|
|
114
|
+
uniform `401` first, and the `501` is what an authenticated (or loopback) caller
|
|
115
|
+
sees. No run artifact can ever shadow the namespace. The seam is already typed:
|
|
116
|
+
`createServeRequestHandler` accepts an optional `ServeControlPlane`, and v1
|
|
117
|
+
always passes `undefined`. Before any mutating route ships, the contract is:
|
|
118
|
+
|
|
119
|
+
- sessions grow a real scope split — today every session is scope `viewer`;
|
|
120
|
+
operators get a DISTINCT operator token, never an upgraded viewer cookie;
|
|
121
|
+
- mutating routes require double-submit CSRF on top of the operator session,
|
|
122
|
+
because a cookie alone is exactly what a cross-site request forges;
|
|
123
|
+
- the spend rule is invariant 3 applied to remote hands: a phone-initiated
|
|
124
|
+
LIVE run needs its own affirmative declaration at serve startup (an explicit
|
|
125
|
+
opt-in naming the lab and budget), never a default the viewer UI can reach.
|
|
126
|
+
|
|
127
|
+
## Future work
|
|
128
|
+
|
|
129
|
+
- **Persistent capability links.** A link that survives restart is a stored
|
|
130
|
+
secret and needs a real secret-storage design (keychain/agent integration,
|
|
131
|
+
rotation, revocation records) — not a `--token <value>` CLI string, which
|
|
132
|
+
would land in shell history and process listings.
|
|
133
|
+
- **Google Fonts inlining for per-run observer pages.** The library index is
|
|
134
|
+
self-contained; the per-run observer HTML still references remote Google
|
|
135
|
+
Fonts, which degrade gracefully offline but should be inlined (or dropped)
|
|
136
|
+
so a served run page makes no third-party requests from a viewer's browser.
|
|
137
|
+
- **`watch --expose --live-streams`.** Remote LIVE following is a separate
|
|
138
|
+
design: the attached watch process genuinely holds runtime stream URLs, so
|
|
139
|
+
it can expose them deliberately — with the same capability-link gate and its
|
|
140
|
+
own doctrine for auth-bearing hosted-desktop URLs.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Date: 2026-06-02 (current-state note updated 2026-07-14)
|
|
4
4
|
|
|
5
5
|
Status: reference map for the major contracts shipped through source version
|
|
6
|
-
`0.
|
|
6
|
+
`0.17.0`; it is not an exhaustive inventory of command/result envelopes. Exported types,
|
|
7
7
|
schema constants, parsers, and validators in `src/` are authoritative. Rows
|
|
8
8
|
marked "reserved" name layering intent only — no code emits or validates them
|
|
9
9
|
yet. Do not emit a reserved schema.
|
|
@@ -46,6 +46,8 @@ workflow without leaking private upstream truth into core.
|
|
|
46
46
|
| Adapter score | `humanish.adapter-score.v1` (`RunBundle.adapterScore`; namespaced; route-specific acceptance semantics) | see Product-Adapter Extension Seam below |
|
|
47
47
|
| Adapter artifact | `humanish.adapter-artifact.v1` (`RunBundle.adapterArtifacts[]`; namespaced; local relative proof references) | see Product-Adapter Extension Seam below |
|
|
48
48
|
| Shared-world evidence | `humanish.shared-world.v1` (additive `RunBundle.sharedWorld` + `RunBundle.attributionClass`; `topologyMode: sequential \| concurrent`) | see Shared-World Evidence below |
|
|
49
|
+
| Serve result | `humanish.serve-result.v1` (`src/observer-serve.ts` is authoritative) | none (command result envelope; see Serve Result below) |
|
|
50
|
+
| Serve control plane | reserved (`/_humanish/api/*` answers `501` `HUMANISH_SERVE_CONTROL_PLANE_DISABLED` in v1) | none |
|
|
49
51
|
|
|
50
52
|
## Lab Manifest
|
|
51
53
|
|
|
@@ -646,6 +648,26 @@ never shipped — no code emits or validates it. Substrate truth today lives
|
|
|
646
648
|
inside run bundles (per-stream transport and status) and lab execution config
|
|
647
649
|
(`execution.target: local | e2b-desktop`). Do not emit this schema.
|
|
648
650
|
|
|
651
|
+
## Serve Result And Reserved Control-Plane Namespace
|
|
652
|
+
|
|
653
|
+
`humanish serve` reports `humanish.serve-result.v1`. The exported `ServeResult`
|
|
654
|
+
type and `SERVE_SCHEMA` constant in `src/observer-serve.ts` are authoritative:
|
|
655
|
+
mode (`loopback | capability-link | share-safe-open`), the loopback host/port,
|
|
656
|
+
capability/public URLs, runs listed, computed warnings, and the
|
|
657
|
+
`ServeErrorCode` union. Capability URLs embed a live secret; they belong on the
|
|
658
|
+
operator's terminal and never in a persisted or committed artifact.
|
|
659
|
+
|
|
660
|
+
Reserved: `/_humanish/api/*` is the serve control-plane namespace. In v1 any
|
|
661
|
+
request under it that clears the auth gate answers `501` with error code
|
|
662
|
+
`HUMANISH_SERVE_CONTROL_PLANE_DISABLED`; under `--expose --auth link` a
|
|
663
|
+
session-less request answers the uniform `401` first. The typed `ServeControlPlane`
|
|
664
|
+
parameter exists in the handler options and is always `undefined` in v1; no
|
|
665
|
+
code dispatches into it yet. Do not build against the namespace; the
|
|
666
|
+
reservation guarantees only that no run artifact or observer asset will ever be
|
|
667
|
+
served under it. See
|
|
668
|
+
[`docs/architecture/serve.md`](../architecture/serve.md) for the v2 seam
|
|
669
|
+
contract.
|
|
670
|
+
|
|
649
671
|
## Terminal Cost Ledger And No-Spend Proof
|
|
650
672
|
|
|
651
673
|
The terminal-product lane (`src/e2b-terminal-lab.ts`) passes a real provider key
|
package/docs/goals/current.md
CHANGED
|
@@ -16,7 +16,7 @@ Humanish should be the open-source CLI that lets a maintainer ask:
|
|
|
16
16
|
The answer should be observable, verifiable, public-safe, and easy to turn into
|
|
17
17
|
actionable feedback.
|
|
18
18
|
|
|
19
|
-
## Current Program Truth (source `0.
|
|
19
|
+
## Current Program Truth (source `0.17.0`)
|
|
20
20
|
|
|
21
21
|
The package source and repository implementation in this tree agree on these
|
|
22
22
|
points:
|
|
@@ -32,6 +32,7 @@ The immutable 2026-06-10 proof-roadmap packet is paired with a
|
|
|
32
32
|
| Subject sources/routes | Six declared sources: `this-repo`, `clone`, `app-url`, `local-app`, `terminal-product`, and `local-tree`; support is route-specific and `this-repo` remains dry-run-only | One centralized run/resource lifecycle boundary across all routes |
|
|
33
33
|
| Public proof | Version-pinned synthetic Observer sample shipped in the npm payload | A legible Observer hero from one verified real public-application run |
|
|
34
34
|
| OSS meta-lab | Dry-run contract and separate disposable smoke harness | Live meta-lab execution; disabled until repository instructions and actor credentials have an isolated boundary |
|
|
35
|
+
| Observer serving | `watch`/`observe` loopback servers plus `serve` — the run-library surface with loopback default, capability-link exposure, `share_ready`-gated open mode, and optional operator-run tunnel; streams never served remotely | A remote live-stream (`--live-streams`) design; a persistent capability-link store; a control plane that can start runs |
|
|
35
36
|
|
|
36
37
|
Capability proof and adopter replacement are different gates. A deterministic
|
|
37
38
|
test or kept live receipt proves that a Humanish mechanism works. The depth-axis
|
|
@@ -74,6 +74,7 @@ silently drifting from one is not.
|
|
|
74
74
|
| Single lane | Cost + evidence simplicity | Declared fan-out where the backend supports it — `actors[0].count: N` (homogeneous), explicit `actors[0].lanes[]` (differentiated persona/device/instruction), or compact `actors[0].roster[]` groups that normalize into lanes on the computer-use E2B route (per-lane worlds, cap 16; `execution.concurrency` bounds concurrent paid lanes) |
|
|
75
75
|
| Stock `desktop` template | The stock E2B desktop image is right for most subjects; absent `execution.desktop.template` keeps `Sandbox.create(opts)` byte-stable | `execution.desktop.template` names a custom E2B desktop image (any name/id, no allowlist) for a subject needing baked-in runtimes the stock image lacks (e.g. node/bun/a local Postgres) — threaded to `Sandbox.create(template, opts)` on every desktop-creating route and recorded in the bundle as `desktopTemplate` (public-safe) |
|
|
76
76
|
| Desktop default URL opener | Preserve the route/image's historical browser/default opener behavior when unset | `execution.desktop.browser: chrome | chromium | firefox` makes hosted CUA/shared-world browser choice explicit, fail-closed, and recorded as `desktopBrowser` in the run bundle |
|
|
77
|
+
| Loopback-only observer serving | Run bundles are local by default (public-safety plank 3); the serve surface binds `127.0.0.1` unconditionally, and exposing the library is publishing-adjacent, so it carries declared friction | `humanish serve --expose` (secret capability link + a declared public origin via `--tunnel`/`--public-url`) or `--expose --safe --auth none` (open mode, gated on verify-`share_ready` runs only); both print computed warnings naming exactly what became reachable |
|
|
77
78
|
|
|
78
79
|
## The placement rule (worked example)
|
|
79
80
|
|
package/docs/ramp/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Status: public-safe contributor and agent ramp.
|
|
4
4
|
|
|
5
|
-
Package/source version in this tree: `0.
|
|
5
|
+
Package/source version in this tree: `0.17.0` (2026-08-02). The containment boundary introduced in
|
|
6
6
|
`0.15.1` remains in force: managed run and output paths bind to validated
|
|
7
7
|
physical filesystem identities, and stored provider IDs are evidence, not
|
|
8
8
|
cleanup authority. The bundled OSS meta-lab is dry-run only until
|
package/package.json
CHANGED