docs(readme): pi-studio has --listen-all; the remote recipe describes the 0.10 URL
README "Using pi-studio" said pi-studio hard-binds 127.0.0.1 with no bind flag.
It has had /studio --listen-all (binds 0.0.0.0) since 0.9.60; listed now as path
C, with a warning not to combine it with the studio-expose bridge on one port:
with STUDIO_EXPOSE=1, `/studio --listen-all --port 8765` fails with
"listen EADDRINUSE: address already in use 0.0.0.0:8765" (measured, tor-ms22,
v1.11.2).
The remote recipe described a 0.9 URL. Since 0.10 the printed URL also carries
studioTabState + hostedWorkspace and a token-only URL is answered 409; the
recipe now says to open the whole URL, names the wrapped-URL copy hazard
("This link looks damaged..."), says Studio must run on the published port
(only the laptop's side of ssh -L is free) and that --port does not move a
running server, adds --watch <file>, and gives an on-host curl that tells
"chain up" (403) from "Studio down or on another port" (empty reply).
All of it measured on tor-ms22 2026-10-14 (pi-studio 0.10.1): tunnel through
the host sshd + headless browser load, both modes; --listen-all reached on the
container's eth0 (200 / 403 without token). Docs only; no pin changes.
This commit is contained in:
@@ -11,6 +11,32 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
|
||||
|
||||
---
|
||||
|
||||
## Unreleased
|
||||
|
||||
Docs only: this entry changes no pin.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **README "Using pi-studio" said pi-studio has no bind flag.** It has had
|
||||
`/studio --listen-all` (binds `0.0.0.0`) since pi-studio 0.9.60, so the
|
||||
section now lists it as path **C**, next to host networking (A) and the
|
||||
`studio-expose` bridge (B). It also warns not to combine B and C on one port.
|
||||
With `STUDIO_EXPOSE=1` the bridge holds the egress IP on `$STUDIO_PORT`
|
||||
from boot, and `/studio --listen-all --port 8765` then fails with
|
||||
`listen EADDRINUSE: address already in use 0.0.0.0:8765` (measured on
|
||||
tor-ms22, v1.11.2).
|
||||
- **The remote recipe described a pi-studio 0.9 URL.** Since 0.10 the printed
|
||||
URL carries `studioTabState` and `hostedWorkspace` as well as the token, and
|
||||
a token-only URL is answered `409`. The recipe now says to open the whole
|
||||
URL, and names the copy hazard: the URL wraps, a side panel's text can land
|
||||
inside it, and Studio answers *"This link looks damaged…"*. It also says
|
||||
Studio must run on the **published** port (only the laptop's side of
|
||||
`ssh -L` is free to choose) and that `--port` does not move a running server
|
||||
(`/studio --stop` first). Two more additions: `--watch <file>` for a rendered,
|
||||
read-only preview, and an on-host `curl` that tells "chain up" (`403`) from
|
||||
"Studio down or on another port" (empty reply). A remote session hit exactly
|
||||
that: Studio was started on `18765` while only `8765` is published.
|
||||
|
||||
## v1.11.2 — 2026-10-14
|
||||
|
||||
**v1.11.1 was tagged and never published.** Run 724 failed one smoke assertion
|
||||
|
||||
@@ -198,12 +198,11 @@ Inside a pi session in the container:
|
||||
|
||||
### Reaching the UI from your browser (the container caveat)
|
||||
|
||||
pi-studio **hard-binds its server to `127.0.0.1` inside the container**
|
||||
(`index.ts`: `.listen(port, "127.0.0.1")`) and serves a tokenized URL.
|
||||
There is no `--host`/bind flag. This matters for a container: a plain
|
||||
By default pi-studio **binds its server to `127.0.0.1` inside the
|
||||
container** and serves a tokenized URL. This matters for a container: a plain
|
||||
`docker run -p 8765:8765` publish forwards to the container's *external*
|
||||
interface, **not** its loopback, so it will not reach Studio. Two paths
|
||||
work:
|
||||
interface, **not** its loopback, so it will not reach a default Studio.
|
||||
Three paths work (A and B keep Studio itself on loopback; C does not):
|
||||
|
||||
**A. Host networking (simplest — OrbStack / single-host, no bridge).**
|
||||
Run the container with host networking so the container's loopback is the
|
||||
@@ -215,8 +214,9 @@ services:
|
||||
network_mode: host # container 127.0.0.1 == host 127.0.0.1
|
||||
```
|
||||
|
||||
Then `http://127.0.0.1:8765/?token=…` works in a browser on the Docker
|
||||
host. This is the most secure option (Studio never leaves loopback). Note:
|
||||
Then the URL Studio prints (`http://127.0.0.1:8765/?token=…`) works in a
|
||||
browser on the Docker host. This is the most secure option (Studio never
|
||||
leaves loopback). Note:
|
||||
host networking changes `host.docker.internal` semantics, so weigh it
|
||||
against the LAN-jump SSH feature if you use that.
|
||||
|
||||
@@ -253,6 +253,19 @@ studio-expose & # bridges $STUDIO_PORT (default 8765); --help for detai
|
||||
> its tokenized URL is the only auth. Keep the host-side publish on
|
||||
> `127.0.0.1:` and use `ssh -L` for remote access. Default is **off**.
|
||||
|
||||
**C. `/studio --listen-all` (pi-studio ≥ 0.9.60, no bridge).** pi-studio has
|
||||
its own opt-in flag: `/studio --no-browser --listen-all --port 8765` binds
|
||||
`0.0.0.0`, so the published port reaches it without `socat`. The URL it
|
||||
prints still says `127.0.0.1`, so it works verbatim through a same-port
|
||||
publish. Keep the publish on `127.0.0.1:` as in B: anyone who can reach the
|
||||
port and has the full URL controls Studio, including running prompts and
|
||||
reading or writing files, and there is no TLS.
|
||||
|
||||
> **Use B or C on a port, not both.** With `STUDIO_EXPOSE=1` the bridge holds
|
||||
> the container's egress IP on `$STUDIO_PORT` from boot, and `--listen-all` on
|
||||
> that same port fails with `EADDRINUSE` (`0.0.0.0` includes that address).
|
||||
> If the bridge is running, start Studio *without* `--listen-all`.
|
||||
|
||||
### Remote host (SSH / mosh)
|
||||
|
||||
When the Docker host is remote, keep Studio on localhost and forward the
|
||||
@@ -298,20 +311,38 @@ Assuming the compose file publishes `127.0.0.1:8765:8765` (see method B):
|
||||
TUI, not a shell command:
|
||||
```
|
||||
/studio --no-browser --port 8765
|
||||
/studio --no-browser --port 8765 --watch notes.md # or: a rendered, read-only preview of a file
|
||||
/studio --status # reprint the URL anytime
|
||||
```
|
||||
Copy the `http://…:8765/?token=<token>` it prints. **This** is where
|
||||
the real token comes from — not `studio-expose`.
|
||||
**This** is where the real token comes from — not `studio-expose`.
|
||||
The port must be the **published** one (`8765` here): the tunnel, the
|
||||
publish and the bridge all lead to it, and Studio on any other port is
|
||||
unreachable from outside. `--port` does not move a running server (Studio
|
||||
only warns), so run `/studio --stop` first to change it.
|
||||
3. **On your laptop** — open the ssh port-forward alongside mosh:
|
||||
```bash
|
||||
ssh -L 8765:127.0.0.1:8765 user@docker-host
|
||||
ssh -N -o ExitOnForwardFailure=yes -L 8765:127.0.0.1:8765 user@docker-host
|
||||
```
|
||||
4. **In your laptop browser** — open `http://127.0.0.1:8765/?token=<token>`
|
||||
(keep the port and token verbatim; only the host part is `127.0.0.1`).
|
||||
Only the **left** port is yours to choose. If `8765` is already taken on
|
||||
the laptop (for example by the laptop's own devbox publish), use
|
||||
`-L 18765:127.0.0.1:8765` and change only the port in the URL.
|
||||
4. **In your laptop browser** — open the **whole** URL Studio printed. It
|
||||
already says `127.0.0.1`, and it carries more than the token
|
||||
(`token`, `studioTabState`, `hostedWorkspace` since pi-studio 0.10, or
|
||||
`docPath`/`watchFile`/… for `--watch`). A URL cut down to just the token
|
||||
is answered `409`.
|
||||
The URL is long and wraps across TUI lines; a side panel next to it (such
|
||||
as the pi-atelier sidebar) can end up inside the copied text, and Studio
|
||||
then answers *"This link looks damaged…"*. Rejoin the lines without the
|
||||
panel text, or widen the terminal and copy it again.
|
||||
|
||||
> **Order check:** nothing listens on the container's `127.0.0.1:8765`
|
||||
> until step 2 runs. If the browser can't connect, verify Studio is up
|
||||
> (`/studio --status`) and the bridge is running (`ps aux | grep socat`).
|
||||
> **on the published port** (`/studio --status`) and the bridge is running
|
||||
> (`ps aux | grep socat`). On the Docker host,
|
||||
> `curl -s http://127.0.0.1:8765/` answers `403` when the whole chain is up;
|
||||
> an empty reply means the bridge has nothing behind it, i.e. Studio is down
|
||||
> or on another port.
|
||||
|
||||
> PDF export (`/studio-pdf`, `studio_export_pdf`) uses **`typst`**, shipped in
|
||||
> the base image as the pandoc PDF engine (`pandoc --pdf-engine=typst`). For
|
||||
|
||||
Reference in New Issue
Block a user