Repository navigation
Conversation
Opt-in mixin that gives a session a contained GUI: a virtual X display (Xvnc) running a fullscreened Chromium, served over VNC (RFB). A supervisor keeps Xvnc, matchbox-window-manager, and the browser alive with per-component restart loops. The RFB port is published on the host loopback so any VNC client can attach, and Xvnc enforces a per-session password (VncAuth) at the RFB layer. Exporting DISPLAY and BROWSER=vnc-open routes "open in browser" flows onto the contained display, where browsing stays on the session's gateway-restricted network. commands/host/vnc-viewer adds `enclave vnc-viewer`. It resolves the session's published port from `enclave ps --json`, reads the password through the container engine's non-TTY exec, and launches a host-installed viewer with the password in the environment rather than argv. It runs on the host, outside the sandbox, because driving a host GUI is what a session cannot do. It needs jq and takes its viewer command from the vnc_viewer key in enclave's config.json.
tortmayr
left a comment
There was a problem hiding this comment.
Amazing feature 👍🏼
Tested it with the corresponding enclave PR and it works almost as expected.
During testing I noticed that for me the $DISPLAY and $BROWSER env vars never reached the container.
As a consequence xdg-open inside the container did not work as descriped in the readme i.e. it did not use vnc-open.
I had to manually declare them in the spec.yaml
environment:
variables:
DISPLAY: ":99"
BROWSER: /usr/local/bin/vnc-openNeeds to be double checked. If we really need the spec declaration we probably cannot use $VNC_DISPLAY and have to hardcode it to :99
| --hide-crash-restore-bubble \ | ||
| --password-store=basic \ | ||
| --start-maximized \ | ||
| --user-data-dir=/tmp/enclave-vnc/chromium \ |
There was a problem hiding this comment.
The profile lives in /tmp, so cookies, logins, and open tabs are gone whenever the container is recreated. Persistence is the other topic the repo rules ask every extension README to cover (link) and the vnc README does not mention it.
| # TigerVNC's, which takes the password from the environment. | ||
| if [ "${#viewer[@]}" -eq 0 ]; then | ||
| if [ "$(uname -s)" = "Darwin" ]; then | ||
| viewer=(open "vnc://:{password}@{host}:{port}") |
There was a problem hiding this comment.
The macOS default puts the password in argv, which contradicts the access-control section of the README ("hands it to the client through the environment ... rather than argv", features/vnc/README.md L113-117).
|
|
||
| # vnc-open <url> opens a URL in the session's contained (VNC) browser. It | ||
| # shares the supervisor's Chromium profile, so a running instance adopts the | ||
| # URL as a new (fullscreened) window instead of a second browser starting. |
There was a problem hiding this comment.
URL is adopted as tab.It not a window. With the supervisor's Chromium running, vnc-open <url> prints Opening in existing browser session. and adds a tab to the existing window (verified in a live session). Same wording in features/vnc/README.md L81.
| apt-get clean | ||
| rm -rf /var/lib/apt/lists/* | ||
|
|
||
| install -D -m 755 "$dir/bin/vnc-supervisor" /usr/local/bin/vnc-supervisor |
There was a problem hiding this comment.
Two naming points. The scripts land as bare vnc-* on the image-wide PATH while both existing extensions prefix theirs (dsh, openclaw), and this PR's own desktop file is already enclave-vnc-open.desktop. Line 33 also plants the waiting page in /usr/local/share/enclave/, next to enclave's own runtime assets (kit-init.sh, net.sh, tmux-session.conf); that made sense when this was a built-in, less so for an installed extension.
| shift | ||
| ( | ||
| while :; do | ||
| "$@" >> "$LOG_DIR/$_name.log" 2>&1 |
There was a problem hiding this comment.
Fixed 2s backoff with no cap, appending to /tmp every cycle. A component that can never start (Xvnc failing to bind 5900, say) grows its log for the life of the session.
| set -u | ||
|
|
||
| exec chromium \ | ||
| --no-sandbox \ |
There was a problem hiding this comment.
--no-sandbox makes Chromium show the "You are using an unsupported command-line flag" infobar across the top of the waiting page, so it is the first thing anyone attaching a viewer sees.
Could be supressed with --test-type suppresses it.
|
|
||
| Holding that password is what grants control of the display, and nothing else | ||
| does. It is generated per session, so it reaches exactly one session's display | ||
| and no other — which is why the (untrusted) agent knowing it is harmless, and |
There was a problem hiding this comment.
Em dash here and on line 110. Nothing else in the repo uses them, commas or a period would match.
| | select((.protocol // "") as $proto | $proto == "" or $proto == "tcp") | ||
| | [$session.name, ($session.sessionName // ""), ($session.projectHash // ""), (.hostIP // ""), .hostPort] | ||
| | @tsv | ||
| ' | sort) |
There was a problem hiding this comment.
One row per binding, not per container. Docker gives a wildcard publish two bindings on an IPv6-enabled daemon (0.0.0.0 and [::]), which is exactly the case the host_ip switch below anticipates, and then both paths bail: auto-select hits "multiple running enclave containers with a VNC display" and passing the name hits "several containers match", both listing the same name twice with no way out. Dedupe by container name before counting.
| inside the container's network namespace. Under network isolation that | ||
| namespace belongs to the session's gateway container on a bridge shared with | ||
| other containers of the same engine, so those (including other sessions' | ||
| gateways) can reach the RFB port directly, with VncAuth as the only gate. |
There was a problem hiding this comment.
VncAuth authenticates, it does not encrypt. Since this bullet already says other containers on the bridge can reach the RFB port, it should also say the framebuffer, keystrokes and clipboard travel in the clear.
I wonder why this works for me out of the box. I have to check what's going on |
I now retested the extension in an isolated enclave environment and there the env vars are set in the container. |
|
Also just a heads up for Ubunut/Gnome users: |
EclipseSourceAI
left a comment
There was a problem hiding this comment.
Note
Autonomous AI review.
This review was done by an AI agent and therefore may contain mistakes. Feel free to ignore any comment you disagree with. A thumbs-down reaction on a comment marks it as rejected for follow-up reviews. Noting why in a reply helps, since replies are read too.
Resolving all AI comments does not lead to an automatic approval. A maintainer still needs to review and sign off on the overall architecture and design.
To get an updated review after pushing changes, a maintainer may re-request a review from this account.
Running in Eclipse Enclave, submitted via review-guard-mcp
Adds the repo's first feature extension: an opt-in vnc mixin that gives a session a contained GUI (Xvnc, matchbox, fullscreened Chromium), published as raw RFB on the host loopback with a per-session VncAuth password, plus a commands/host/vnc-viewer host command that resolves the port from enclave ps --json, reads the password through docker/podman exec, and launches a host viewer.
The approach fits enclave's extension model well. spec.yaml uses documented mixin fields only (aptPackages, commands.startup with background: true, ports with hostAllocation: auto), feature-entrypoint.d/setup.sh matches the built-in features' style, and the commands/host/ layout matches what enclave#95 defines. I verified the CLI contracts the host command leans on against enclave main: ps --json emits containerPort/hostPort/hostIP/projectHash/sessionName as strings and lists running sessions only, config --json exposes paths.global/paths.project, unknown config keys like vnc_viewer are ignored rather than rejected, and enclave exec really does hardcode a TTY, so the direct engine exec is justified. The scripts are shellcheck-clean, the VncAuth fallback key and bit-reversal are correct, and the macOS path avoids the bash 3.2 empty-array trap.
Where attention is worth spending: the relationship to the still-open enclave core PRs #24/#29, which would make enclave vnc-viewer and the vnc_viewer config key built-ins and shadow this extension's verb; supervisor lifecycle (no trap, so children outlive the supervisor, which compounds the unbounded restart-log growth already raised); and the README, which still has no egress coverage for a feature whose whole point is a browser on the session's gateway-restricted network.
| # | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| # `enclave vnc-viewer [container-or-session-name]` |
There was a problem hiding this comment.
eclipse-enclave/enclave#29 is still open and adds enclave vnc-viewer plus the vnc_viewer config key as built-ins. Per #95's own rules a built-in verb wins and the extension's command is skipped with a warning, so a maintainer should decide which of the two ships before this merges.
|
|
||
| log "stack started (display $DISPLAY_NUM, RFB port $RFB_PORT)" | ||
|
|
||
| wait |
There was a problem hiding this comment.
No trap here, so killing the supervisor leaves Xvnc, matchbox and Chromium running orphaned, and a manual restart then spawns a second Xvnc that can never bind 5900. The openclaw session wrapper solves the same problem with a trap ... EXIT (link).
| `xvnc.log`, `wm.log`, `browser.log`, and `vnc-open.log` for the individual | ||
| components. | ||
|
|
||
| ## Residual risks |
There was a problem hiding this comment.
Nothing in the README covers egress, which the repo rules ask every extension README for (link) and both existing extensions have a section for (dsh, openclaw). It matters for this feature: the contained Chromium browses through the session gateway, so any URL outside the allowlist just fails in the viewer with no hint that --allow-domain is the lever.
| enclave_verb=$(basename "$enclave_bin") | ||
|
|
||
| die() { | ||
| printf 'vnc-viewer: %s\n' "$*" >&2 |
There was a problem hiding this comment.
Tab-indented, while every other script in the repo uses 4 spaces, including this PR's own bin/vnc-* and openclaw/install.sh.
There was a problem hiding this comment.
This PR fills features/ with features/vnc/ in the same commit, so the placeholder has no job. Drop it.
What it does
Opt-in mixin that gives a session a contained GUI: a virtual X display (Xvnc) running a fullscreened Chromium, served over VNC (RFB). A supervisor keeps Xvnc, matchbox-window-manager, and the browser alive with per-component restart loops. The RFB port is published on the host loopback so any VNC client can attach, and Xvnc enforces a per-session password (VncAuth) at the RFB layer.
Exporting DISPLAY and BROWSER=vnc-open routes "open in browser" flows onto the contained display, where browsing stays on the session's gateway-restricted network.
commands/host/vnc-viewer adds
enclave vnc-viewer. It resolves the session's published port fromenclave ps --json, reads the password through the container engine's non-TTY exec, and launches a host-installed viewer with the password in the environment rather than argv. It runs on the host, outside the sandbox, because driving a host GUI is what a session cannot do. It needs jq and takes its viewer command from the vnc_viewer key in enclave's config.json.Derived from
How to test
To test the
vnc-viewercommand you need enclave withhostCommandsupport: eclipse-enclave/enclave#95