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.2 — 2026-10-14
|
||||||
|
|
||||||
**v1.11.1 was tagged and never published.** Run 724 failed one smoke assertion
|
**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)
|
### Reaching the UI from your browser (the container caveat)
|
||||||
|
|
||||||
pi-studio **hard-binds its server to `127.0.0.1` inside the container**
|
By default pi-studio **binds its server to `127.0.0.1` inside the
|
||||||
(`index.ts`: `.listen(port, "127.0.0.1")`) and serves a tokenized URL.
|
container** and serves a tokenized URL. This matters for a container: a plain
|
||||||
There is no `--host`/bind flag. This matters for a container: a plain
|
|
||||||
`docker run -p 8765:8765` publish forwards to the container's *external*
|
`docker run -p 8765:8765` publish forwards to the container's *external*
|
||||||
interface, **not** its loopback, so it will not reach Studio. Two paths
|
interface, **not** its loopback, so it will not reach a default Studio.
|
||||||
work:
|
Three paths work (A and B keep Studio itself on loopback; C does not):
|
||||||
|
|
||||||
**A. Host networking (simplest — OrbStack / single-host, no bridge).**
|
**A. Host networking (simplest — OrbStack / single-host, no bridge).**
|
||||||
Run the container with host networking so the container's loopback is the
|
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
|
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
|
Then the URL Studio prints (`http://127.0.0.1:8765/?token=…`) works in a
|
||||||
host. This is the most secure option (Studio never leaves loopback). Note:
|
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
|
host networking changes `host.docker.internal` semantics, so weigh it
|
||||||
against the LAN-jump SSH feature if you use that.
|
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
|
> 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**.
|
> `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)
|
### Remote host (SSH / mosh)
|
||||||
|
|
||||||
When the Docker host is remote, keep Studio on localhost and forward the
|
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:
|
TUI, not a shell command:
|
||||||
```
|
```
|
||||||
/studio --no-browser --port 8765
|
/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
|
/studio --status # reprint the URL anytime
|
||||||
```
|
```
|
||||||
Copy the `http://…:8765/?token=<token>` it prints. **This** is where
|
**This** is where the real token comes from — not `studio-expose`.
|
||||||
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:
|
3. **On your laptop** — open the ssh port-forward alongside mosh:
|
||||||
```bash
|
```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>`
|
Only the **left** port is yours to choose. If `8765` is already taken on
|
||||||
(keep the port and token verbatim; only the host part is `127.0.0.1`).
|
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`
|
> **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
|
> 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
|
> PDF export (`/studio-pdf`, `studio_export_pdf`) uses **`typst`**, shipped in
|
||||||
> the base image as the pandoc PDF engine (`pandoc --pdf-engine=typst`). For
|
> the base image as the pandoc PDF engine (`pandoc --pdf-engine=typst`). For
|
||||||
|
|||||||
Reference in New Issue
Block a user