From 39067787fcf3debf24bb24fe35800c9de27a8f4f Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 05:59:24 +0300 Subject: [PATCH 01/10] BrowserOS Neo (#new script) Bare-metal install of BrowserOS Neo from its GitHub .deb release (browserclaw/v* tags): TigerVNC + openbox + noVNC under supervisor, nginx in front with basic auth for the viewer and a bearer token + Host rewrite for the MCP server. Co-Authored-By: Claude Opus 5.5 --- ct/browseros-neo.sh | 60 +++++++++ install/browseros-neo-install.sh | 218 +++++++++++++++++++++++++++++++ json/browseros-neo.json | 50 +++++++ 3 files changed, 328 insertions(+) create mode 100644 ct/browseros-neo.sh create mode 100644 install/browseros-neo-install.sh create mode 100644 json/browseros-neo.json diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh new file mode 100644 index 0000000000..60d6bfdf96 --- /dev/null +++ b/ct/browseros-neo.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +_CS_DEFAULT_URL="https://raw.githubusercontent.com/community-scripts/DevScripts/main" +_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func" +source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func") +# Copyright (c) 2021-2026 community-scripts ORG +# Author: masterde +# License: MIT | https://github.com/community-scripts/DevScripts/raw/main/LICENSE +# Source: https://github.com/browseros-ai/BrowserOS + +APP="BrowserOS-Neo" +var_tags="${var_tags:-browser;ai;mcp}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-no}" # GitHub releases ship an amd64 .deb only +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /etc/systemd/system/browseros-neo.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "browseros-neo" "browseros-ai/BrowserOS" "" "" "browserclaw/v"; then + msg_info "Stopping BrowserOS Neo" + systemctl stop browseros-neo + msg_ok "Stopped BrowserOS Neo" + + fetch_and_deploy_gh_release "browseros-neo" "browseros-ai/BrowserOS" "binary" "latest" "" "" "browserclaw/v" + + msg_info "Starting BrowserOS Neo" + systemctl start browseros-neo + msg_ok "Started BrowserOS Neo" + msg_ok "Updated successfully!" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Open the browser viewer (user: neo) using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6080${CL}" +echo -e "${INFO}${YW} Connect MCP clients to the following URL with an Authorization: Bearer header:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9010/mcp${CL}" +echo -e "${INFO}${YW} The viewer password and MCP token are in /opt/browseros-neo_data/.env${CL}" diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh new file mode 100644 index 0000000000..1be562579e --- /dev/null +++ b/install/browseros-neo-install.sh @@ -0,0 +1,218 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: masterde +# License: MIT | https://github.com/community-scripts/DevScripts/raw/main/LICENSE +# Source: https://github.com/browseros-ai/BrowserOS + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +# libasound2t64 goes first: the .deb's unversioned "Depends: libasound2" can +# otherwise resolve to an OSS shim, and BrowserOS Neo then fails to start. +msg_info "Installing Dependencies" +$STD apt install -y libasound2t64 +$STD apt install -y \ + tigervnc-standalone-server \ + tigervnc-tools \ + openbox \ + novnc \ + websockify \ + supervisor \ + nginx \ + openssl \ + fonts-liberation \ + fonts-noto-core \ + fonts-noto-color-emoji +msg_ok "Installed Dependencies" +systemctl disable -q --now supervisor + +fetch_and_deploy_gh_release "browseros-neo" "browseros-ai/BrowserOS" "binary" "latest" "" "" "browserclaw/v" + +msg_info "Configuring BrowserOS Neo" +mkdir -p /opt/browseros-neo_data/{profile,browserclaw,downloads} /etc/browseros-neo /var/log/browseros-neo /root/.config/gtk-3.0 +ln -sfn /opt/browseros-neo_data/downloads /root/Downloads +VIEWER_PASS=$(random_password) +MCP_TOKEN=$(openssl rand -hex 32) +cat </opt/browseros-neo_data/.env +VIEWER_USER=neo +VIEWER_PASSWORD=${VIEWER_PASS} +MCP_TOKEN=${MCP_TOKEN} +EOF +chmod 600 /opt/browseros-neo_data/.env + +# Openbox only keeps BrowserOS Neo maximized and re-fits it when noVNC resizes the +# screen; there is no desktop or taskbar, so the minimize button is hidden too. +cat <<'EOF' >/etc/browseros-neo/openbox-rc.xml + + + + yes + no + + + Smart +
yes
+
+ + 1 + + + + + no + yes + + +
+EOF +cat <<'EOF' >/root/.config/gtk-3.0/settings.ini +[Settings] +gtk-decoration-layout=:maximize,close +EOF +# --no-sandbox is required to run Chromium as root; drop its warning bar for that flag. +mkdir -p /etc/chromium/policies/managed +cat <<'EOF' >/etc/chromium/policies/managed/browseros-neo.json +{"CommandLineFlagSecurityWarningsEnabled": false} +EOF +msg_ok "Configured BrowserOS Neo" + +msg_info "Configuring Nginx" +cat </etc/nginx/browseros-neo.htpasswd +neo:$(openssl passwd -apr1 "${VIEWER_PASS}") +EOF +chown root:www-data /etc/nginx/browseros-neo.htpasswd +chmod 640 /etc/nginx/browseros-neo.htpasswd +# Port 9010 rewrites Host to loopback: BrowserOS Neo's MCP server only accepts +# Host: localhost/127.0.0.1 (DNS-rebinding guard) and has no setting to widen it. +cat </etc/nginx/sites-available/browseros-neo +map \$http_upgrade \$connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 6080; + auth_basic "BrowserOS Neo"; + auth_basic_user_file /etc/nginx/browseros-neo.htpasswd; + + location = / { + return 302 /vnc.html?autoconnect=1&resize=remote&reconnect=1&reconnect_delay=1000; + } + + location / { + proxy_pass http://127.0.0.1:6081; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection \$connection_upgrade; + proxy_read_timeout 1d; + } +} + +server { + listen 9010; + + location / { + if (\$http_authorization != "Bearer ${MCP_TOKEN}") { + return 401; + } + proxy_pass http://127.0.0.1:19010; + proxy_http_version 1.1; + proxy_set_header Host 127.0.0.1:19010; + proxy_set_header Origin ""; + proxy_set_header Connection ""; + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 1d; + } +} +EOF +nginx_enable_site "browseros-neo" +msg_ok "Configured Nginx" + +msg_info "Creating Service" +cat <<'EOF' >/etc/browseros-neo/supervisord.conf +[supervisord] +nodaemon=true +user=root +logfile=/var/log/browseros-neo/supervisord.log +pidfile=/run/browseros-neo/supervisord.pid + +[program:xvnc] +command=/usr/bin/Xvnc :1 -geometry 1600x1000 -depth 24 -SecurityTypes None -localhost -rfbport 5900 -AlwaysShared -nolisten tcp +priority=100 +autorestart=true +stdout_logfile=/var/log/browseros-neo/xvnc.log +redirect_stderr=true + +[program:openbox] +environment=DISPLAY=":1",HOME="/root" +command=/usr/bin/openbox --config-file /etc/browseros-neo/openbox-rc.xml +priority=200 +autorestart=true +stdout_logfile=/var/log/browseros-neo/openbox.log +redirect_stderr=true + +[program:vncconfig] +environment=DISPLAY=":1" +command=/usr/bin/vncconfig -nowin +priority=200 +autorestart=true +stdout_logfile=/var/log/browseros-neo/vncconfig.log +redirect_stderr=true + +[program:websockify] +command=/usr/bin/websockify --web /usr/share/novnc 127.0.0.1:6081 127.0.0.1:5900 +priority=200 +autorestart=true +stdout_logfile=/var/log/browseros-neo/websockify.log +redirect_stderr=true + +[program:browseros-neo] +environment=DISPLAY=":1",HOME="/root",LANG="C.UTF-8",XDG_RUNTIME_DIR="/run/browseros-neo",BROWSERCLAW_DIR="/opt/browseros-neo_data/browserclaw",BROWSERCLAW_MODE="hosted" +command=/usr/bin/browserclaw --no-sandbox --user-data-dir=/opt/browseros-neo_data/profile --no-first-run --no-default-browser-check --password-store=basic --browseros-proxy-port=19010 --browseros-cdp-port=9110 --browseros-server-port=9210 --hide-crash-restore-bubble --start-maximized +priority=300 +autorestart=true +startsecs=5 +stopwaitsecs=20 +stdout_logfile=/var/log/browseros-neo/browseros-neo.log +redirect_stderr=true +EOF +# The profile lock outlives an unclean stop; nothing else uses this profile. +cat <<'EOF' >/etc/systemd/system/browseros-neo.service +[Unit] +Description=BrowserOS Neo +After=network.target + +[Service] +Type=simple +RuntimeDirectory=browseros-neo +RuntimeDirectoryMode=0700 +ExecStartPre=/bin/rm -f /opt/browseros-neo_data/profile/SingletonLock /opt/browseros-neo_data/profile/SingletonSocket /opt/browseros-neo_data/profile/SingletonCookie +ExecStart=/usr/bin/supervisord -c /etc/browseros-neo/supervisord.conf -n +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now browseros-neo +msg_ok "Created Service" + +msg_info "Waiting for BrowserOS Neo" +for _ in $(seq 1 120); do + curl -fsS http://127.0.0.1:19010/system/ready >/dev/null 2>&1 && break + sleep 1 +done +curl -fsS -X PUT http://127.0.0.1:19010/api/v1/settings/agent \ + -H 'Content-Type: application/json' -d '{"humanHelpEnabled":false}' >/dev/null +msg_ok "BrowserOS Neo is ready" + +motd_ssh +customize +cleanup_lxc diff --git a/json/browseros-neo.json b/json/browseros-neo.json new file mode 100644 index 0000000000..8b5b473c0c --- /dev/null +++ b/json/browseros-neo.json @@ -0,0 +1,50 @@ +{ + "name": "BrowserOS Neo", + "slug": "browseros-neo", + "categories": [ + 20 + ], + "date_created": "2026-10-22", + "type": "ct", + "updateable": true, + "privileged": false, + "architectures": ["amd64"], + "interface_port": 6080, + "documentation": "https://docs.browseros.com/", + "website": "https://www.browseros.com/", + "repository": "https://github.com/browseros-ai/BrowserOS", + "logo": "https://cdn.jsdelivr.net/gh/browseros-ai/BrowserOS@main/packages/browseros-agent/apps/app/assets/browserclaw_logo.png", + "description": "BrowserOS Neo is an open-source Chromium-based browser built for AI agents. Agents such as Claude Code or Codex drive it over its MCP server using the accounts you signed in with, while you watch or take over in a browser tab.", + "install_methods": [ + { + "type": "default", + "script": "ct/browseros-neo.sh", + "config_path": "/opt/browseros-neo_data/.env", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 10, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The viewer (port 6080, user neo) password and the MCP token for port 9010 are in /opt/browseros-neo_data/.env. Send the token as an Authorization: Bearer header.", + "type": "info" + }, + { + "text": "Open the viewer once and sign in to the sites your agents should use; logins persist across updates.", + "type": "info" + }, + { + "text": "Anyone with the MCP token or viewer password controls a browser signed in to your accounts, over plain HTTP. Keep it on a trusted network or behind HTTPS.", + "type": "warning" + } + ] +} From 760b4de72332065b6027e1481ba5711ca8129a90 Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 06:19:07 +0300 Subject: [PATCH 02/10] BrowserOS Neo: enable supervisorctl Co-Authored-By: Claude Opus 5.5 --- install/browseros-neo-install.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh index 1be562579e..2bdb1b5313 100644 --- a/install/browseros-neo-install.sh +++ b/install/browseros-neo-install.sh @@ -143,6 +143,16 @@ user=root logfile=/var/log/browseros-neo/supervisord.log pidfile=/run/browseros-neo/supervisord.pid +[unix_http_server] +file=/run/browseros-neo/supervisor.sock +chmod=0700 + +[supervisorctl] +serverurl=unix:///run/browseros-neo/supervisor.sock + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + [program:xvnc] command=/usr/bin/Xvnc :1 -geometry 1600x1000 -depth 24 -SecurityTypes None -localhost -rfbport 5900 -AlwaysShared -nolisten tcp priority=100 From 7b7c035b953ee2df4520cabe947a6b1129bc5c2f Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 06:25:31 +0300 Subject: [PATCH 03/10] BrowserOS Neo: optional Tailscale (var_tailscale / var_ts_authkey) Asks before creating the container (prompt_confirm, skipped when unattended); 'yes' enables var_tun, installs Tailscale from its deb822 repo, logs in with the auth key or prints the login URL, and serves the viewer (8443) and MCP server (443) over HTTPS on the tailnet. A failed login only warns. Co-Authored-By: Claude Opus 5.5 --- ct/browseros-neo.sh | 20 ++++++++++++++++++ install/browseros-neo-install.sh | 36 ++++++++++++++++++++++++++++++++ json/browseros-neo.json | 6 ++++++ 3 files changed, 62 insertions(+) diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh index 60d6bfdf96..cc1a7a38f2 100644 --- a/ct/browseros-neo.sh +++ b/ct/browseros-neo.sh @@ -17,6 +17,9 @@ var_version="${var_version:-13}" var_arm64="${var_arm64:-no}" # GitHub releases ship an amd64 .deb only var_unprivileged="${var_unprivileged:-1}" +export var_tailscale="${var_tailscale:-}" +export var_ts_authkey="${var_ts_authkey:-}" + header_info "$APP" variables color @@ -47,6 +50,16 @@ function update_script() { exit } +# Decided before the container exists: Tailscale needs the TUN device (var_tun). +if [[ -z "$var_tailscale" ]] && command -v pveversion >/dev/null 2>&1; then + if [[ -n "$var_ts_authkey" ]] || prompt_confirm "Add BrowserOS Neo to your Tailscale tailnet for HTTPS access?" "n" 120; then + var_tailscale="yes" + else + var_tailscale="no" + fi +fi +[[ "$var_tailscale" == "yes" ]] && export var_tun="yes" + start build_container description @@ -57,4 +70,11 @@ echo -e "${INFO}${YW} Open the browser viewer (user: neo) using the following UR echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6080${CL}" echo -e "${INFO}${YW} Connect MCP clients to the following URL with an Authorization: Bearer header:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9010/mcp${CL}" +TS_NAME="" +[[ "$var_tailscale" == "yes" ]] && TS_NAME=$(pct exec "$CTID" -- bash -c "tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // empty'" | sed 's/\.$//') +if [[ -n "$TS_NAME" ]]; then + echo -e "${INFO}${YW} On your tailnet:${CL}" + echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}:8443${CL} (viewer)" + echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}/mcp${CL} (MCP)" +fi echo -e "${INFO}${YW} The viewer password and MCP token are in /opt/browseros-neo_data/.env${CL}" diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh index 2bdb1b5313..70f0cfc5f8 100644 --- a/install/browseros-neo-install.sh +++ b/install/browseros-neo-install.sh @@ -223,6 +223,42 @@ curl -fsS -X PUT http://127.0.0.1:19010/api/v1/settings/agent \ -H 'Content-Type: application/json' -d '{"humanHelpEnabled":false}' >/dev/null msg_ok "BrowserOS Neo is ready" +if [[ "${var_tailscale:-no}" == "yes" ]]; then + setup_deb822_repo \ + "tailscale" \ + "https://pkgs.tailscale.com/stable/$(get_os_info id)/$(get_os_info codename).noarmor.gpg" \ + "https://pkgs.tailscale.com/stable/$(get_os_info id)/" \ + "$(get_os_info codename)" + msg_info "Installing Tailscale" + $STD apt install -y tailscale + msg_ok "Installed Tailscale" + + # A Tailscale failure must not fail the install: BrowserOS Neo already works on the LAN. + if [[ -n "${var_ts_authkey:-}" ]]; then + # The key goes through a mode-600 file so it never shows up in the process list. + ( + umask 077 + cat </root/.ts-authkey +${var_ts_authkey} +EOF + ) + tailscale up --auth-key=file:/root/.ts-authkey --hostname="$(hostname)" || true + rm -f /root/.ts-authkey + else + echo -e "${INFO}${YW} Open the URL below and approve the device to add BrowserOS Neo to your tailnet:${CL}" + tailscale up --hostname="$(hostname)" --timeout=10m || true + fi + + if [[ "$(tailscale status --json 2>/dev/null | jq -r '.BackendState')" == "Running" ]]; then + msg_info "Serving BrowserOS Neo over HTTPS on your tailnet" + $STD tailscale serve --bg --https=443 http://127.0.0.1:9010 + $STD tailscale serve --bg --https=8443 http://127.0.0.1:6080 + msg_ok "Serving BrowserOS Neo over HTTPS on your tailnet" + else + msg_warn "Tailscale login did not complete; run 'tailscale up' in the container to finish it" + fi +fi + motd_ssh customize cleanup_lxc diff --git a/json/browseros-neo.json b/json/browseros-neo.json index 8b5b473c0c..6ba27172cf 100644 --- a/json/browseros-neo.json +++ b/json/browseros-neo.json @@ -29,6 +29,12 @@ } } ], + "app_vars": [ + { "name": "var_tailscale", "label": "Join Tailscale Tailnet", "type": "boolean", "default": "no", + "help": "Adds HTTPS addresses on your tailnet; without an auth key the install prints a login URL" }, + { "name": "var_ts_authkey", "label": "Tailscale Auth Key", "type": "password", "secret": true, + "help": "Optional; joins the tailnet without the login URL" } + ], "default_credentials": { "username": null, "password": null From 54d72da80c72f43114dd7df7cb0f2867c1ab856d Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 07:28:06 +0300 Subject: [PATCH 04/10] BrowserOS Neo: always set a root password Prompt for it before setup with prompt_password (Enter or unattended generates one, shown in the final message). With var_pw set the engine keeps console autologin off, which matters for a container holding logged-in browser sessions. Co-Authored-By: Claude Opus 5.5 --- ct/browseros-neo.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh index cc1a7a38f2..4aa1a35eba 100644 --- a/ct/browseros-neo.sh +++ b/ct/browseros-neo.sh @@ -50,6 +50,15 @@ function update_script() { exit } +# The container holds logged-in browser sessions, so it always gets a root +# password; with var_pw set the engine also leaves console autologin off. +ROOT_PW_GENERATED="" +if [[ -z "${var_pw:-}" ]] && command -v pveversion >/dev/null 2>&1; then + ROOT_PW_GENERATED=$(random_password 16) + var_pw=$(prompt_password "Root password for the BrowserOS Neo container, Enter to generate one" "$ROOT_PW_GENERATED" 120 5) + export var_pw +fi + # Decided before the container exists: Tailscale needs the TUN device (var_tun). if [[ -z "$var_tailscale" ]] && command -v pveversion >/dev/null 2>&1; then if [[ -n "$var_ts_authkey" ]] || prompt_confirm "Add BrowserOS Neo to your Tailscale tailnet for HTTPS access?" "n" 120; then @@ -78,3 +87,6 @@ if [[ -n "$TS_NAME" ]]; then echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}/mcp${CL} (MCP)" fi echo -e "${INFO}${YW} The viewer password and MCP token are in /opt/browseros-neo_data/.env${CL}" +if [[ -n "$ROOT_PW_GENERATED" && "${var_pw:-}" == "$ROOT_PW_GENERATED" ]]; then + echo -e "${INFO}${YW} Generated root password (save it now):${CL} ${BGN}${ROOT_PW_GENERATED}${CL}" +fi From 82e5f2202283fb86556ce366e55940188a2d2103 Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 07:28:50 +0300 Subject: [PATCH 05/10] BrowserOS Neo: show credentials and the claude mcp add command at the end Co-Authored-By: Claude Opus 5.5 --- ct/browseros-neo.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh index 4aa1a35eba..48d7f2d652 100644 --- a/ct/browseros-neo.sh +++ b/ct/browseros-neo.sh @@ -86,7 +86,17 @@ if [[ -n "$TS_NAME" ]]; then echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}:8443${CL} (viewer)" echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}/mcp${CL} (MCP)" fi -echo -e "${INFO}${YW} The viewer password and MCP token are in /opt/browseros-neo_data/.env${CL}" +VIEWER_PASS=$(pct exec "$CTID" -- sed -n 's/^VIEWER_PASSWORD=//p' /opt/browseros-neo_data/.env) +MCP_TOKEN=$(pct exec "$CTID" -- sed -n 's/^MCP_TOKEN=//p' /opt/browseros-neo_data/.env) +echo -e "${INFO}${YW} Credentials (also in /opt/browseros-neo_data/.env):${CL}" +echo -e "${TAB}Viewer: ${BGN}neo${CL} / ${BGN}${VIEWER_PASS}${CL}" +echo -e "${TAB}MCP token: ${BGN}${MCP_TOKEN}${CL}" if [[ -n "$ROOT_PW_GENERATED" && "${var_pw:-}" == "$ROOT_PW_GENERATED" ]]; then - echo -e "${INFO}${YW} Generated root password (save it now):${CL} ${BGN}${ROOT_PW_GENERATED}${CL}" + echo -e "${TAB}Root: ${BGN}${ROOT_PW_GENERATED}${CL} (generated, save it now)" +fi +echo -e "${INFO}${YW} Add it to Claude Code:${CL}" +if [[ -n "$TS_NAME" ]]; then + echo -e "${TAB}claude mcp add --transport http browseros-neo https://${TS_NAME}/mcp --header \"Authorization: Bearer ${MCP_TOKEN}\"" +else + echo -e "${TAB}claude mcp add --transport http browseros-neo http://${IP}:9010/mcp --header \"Authorization: Bearer ${MCP_TOKEN}\"" fi From a3893f5bf5f62430405fc5d71655e38c95bb4ec9 Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 07:30:45 +0300 Subject: [PATCH 06/10] BrowserOS Neo: point to the secrets instead of printing them The final message and a login hint (/etc/profile.d, as in hermesagent) show the viewer/MCP addresses, the tailnet ones when connected, and where the viewer password and MCP token live. Only a generated root password is printed once, since it is never stored in the container. Co-Authored-By: Claude Opus 5.5 --- ct/browseros-neo.sh | 16 +++++++--------- install/browseros-neo-install.sh | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh index 48d7f2d652..e46378ce92 100644 --- a/ct/browseros-neo.sh +++ b/ct/browseros-neo.sh @@ -86,17 +86,15 @@ if [[ -n "$TS_NAME" ]]; then echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}:8443${CL} (viewer)" echo -e "${TAB}${GATEWAY}${BGN}https://${TS_NAME}/mcp${CL} (MCP)" fi -VIEWER_PASS=$(pct exec "$CTID" -- sed -n 's/^VIEWER_PASSWORD=//p' /opt/browseros-neo_data/.env) -MCP_TOKEN=$(pct exec "$CTID" -- sed -n 's/^MCP_TOKEN=//p' /opt/browseros-neo_data/.env) -echo -e "${INFO}${YW} Credentials (also in /opt/browseros-neo_data/.env):${CL}" -echo -e "${TAB}Viewer: ${BGN}neo${CL} / ${BGN}${VIEWER_PASS}${CL}" -echo -e "${TAB}MCP token: ${BGN}${MCP_TOKEN}${CL}" +echo -e "${INFO}${YW} The viewer password and MCP token are in /opt/browseros-neo_data/.env, show them with:${CL}" +echo -e "${TAB}pct exec ${CTID} -- cat /opt/browseros-neo_data/.env" if [[ -n "$ROOT_PW_GENERATED" && "${var_pw:-}" == "$ROOT_PW_GENERATED" ]]; then - echo -e "${TAB}Root: ${BGN}${ROOT_PW_GENERATED}${CL} (generated, save it now)" + # Generated on the host and never stored in the container, so this is the only chance to see it. + echo -e "${INFO}${YW} Generated root password (save it now):${CL} ${BGN}${ROOT_PW_GENERATED}${CL}" fi -echo -e "${INFO}${YW} Add it to Claude Code:${CL}" +echo -e "${INFO}${YW} Add it to Claude Code (token from the .env file):${CL}" if [[ -n "$TS_NAME" ]]; then - echo -e "${TAB}claude mcp add --transport http browseros-neo https://${TS_NAME}/mcp --header \"Authorization: Bearer ${MCP_TOKEN}\"" + echo -e "${TAB}claude mcp add --transport http browseros-neo https://${TS_NAME}/mcp --header \"Authorization: Bearer \"" else - echo -e "${TAB}claude mcp add --transport http browseros-neo http://${IP}:9010/mcp --header \"Authorization: Bearer ${MCP_TOKEN}\"" + echo -e "${TAB}claude mcp add --transport http browseros-neo http://${IP}:9010/mcp --header \"Authorization: Bearer \"" fi diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh index 70f0cfc5f8..2f3b1e6371 100644 --- a/install/browseros-neo-install.sh +++ b/install/browseros-neo-install.sh @@ -259,6 +259,20 @@ EOF fi fi +msg_info "Configuring Login Hints" +cat <<'EOF' >/etc/profile.d/browseros-neo-hint.sh +if [ "$(id -u)" -eq 0 ]; then + echo " BrowserOS Neo viewer: http://$(hostname -I | awk '{print $1}'):6080 (user neo)" + echo " BrowserOS Neo MCP: http://$(hostname -I | awk '{print $1}'):9010/mcp" + if command -v tailscale >/dev/null 2>&1 && [ -n "$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // empty')" ]; then + echo " On your tailnet: https://$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'):8443 (viewer), /mcp on port 443" + fi + echo " Viewer password and MCP token: cat /opt/browseros-neo_data/.env" + echo "" +fi +EOF +msg_ok "Configured Login Hints" + motd_ssh customize cleanup_lxc From f53c4d80a80eee099b2f9ca3765f54cfdbde7c51 Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 08:06:44 +0300 Subject: [PATCH 07/10] BrowserOS Neo: show the serve commands when the Tailscale login times out Approving the login URL after the timeout still joins the tailnet, but the HTTPS serve step was skipped; the warning now lists the commands. Co-Authored-By: Claude Opus 5.5 --- install/browseros-neo-install.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh index 2f3b1e6371..7c8a634684 100644 --- a/install/browseros-neo-install.sh +++ b/install/browseros-neo-install.sh @@ -255,7 +255,10 @@ EOF $STD tailscale serve --bg --https=8443 http://127.0.0.1:6080 msg_ok "Serving BrowserOS Neo over HTTPS on your tailnet" else - msg_warn "Tailscale login did not complete; run 'tailscale up' in the container to finish it" + msg_warn "Tailscale login did not complete. Once the device is approved, run in the container:" + echo -e "${TAB}tailscale serve --bg --https=443 http://127.0.0.1:9010" + echo -e "${TAB}tailscale serve --bg --https=8443 http://127.0.0.1:6080" + echo -e "${TAB}(and 'tailscale up' first if you never opened the login URL)" fi fi From 8ac2825ebfbb79589416761e227b9895f1d6e6c5 Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 08:09:07 +0300 Subject: [PATCH 08/10] BrowserOS Neo: keep the engine's default root login Drop the root password prompt so the script behaves like every other community script: no password means console autologin, and var_pw or the advanced settings set one. Co-Authored-By: Claude Opus 5.5 --- ct/browseros-neo.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh index e46378ce92..773ae46607 100644 --- a/ct/browseros-neo.sh +++ b/ct/browseros-neo.sh @@ -50,15 +50,6 @@ function update_script() { exit } -# The container holds logged-in browser sessions, so it always gets a root -# password; with var_pw set the engine also leaves console autologin off. -ROOT_PW_GENERATED="" -if [[ -z "${var_pw:-}" ]] && command -v pveversion >/dev/null 2>&1; then - ROOT_PW_GENERATED=$(random_password 16) - var_pw=$(prompt_password "Root password for the BrowserOS Neo container, Enter to generate one" "$ROOT_PW_GENERATED" 120 5) - export var_pw -fi - # Decided before the container exists: Tailscale needs the TUN device (var_tun). if [[ -z "$var_tailscale" ]] && command -v pveversion >/dev/null 2>&1; then if [[ -n "$var_ts_authkey" ]] || prompt_confirm "Add BrowserOS Neo to your Tailscale tailnet for HTTPS access?" "n" 120; then @@ -88,10 +79,6 @@ if [[ -n "$TS_NAME" ]]; then fi echo -e "${INFO}${YW} The viewer password and MCP token are in /opt/browseros-neo_data/.env, show them with:${CL}" echo -e "${TAB}pct exec ${CTID} -- cat /opt/browseros-neo_data/.env" -if [[ -n "$ROOT_PW_GENERATED" && "${var_pw:-}" == "$ROOT_PW_GENERATED" ]]; then - # Generated on the host and never stored in the container, so this is the only chance to see it. - echo -e "${INFO}${YW} Generated root password (save it now):${CL} ${BGN}${ROOT_PW_GENERATED}${CL}" -fi echo -e "${INFO}${YW} Add it to Claude Code (token from the .env file):${CL}" if [[ -n "$TS_NAME" ]]; then echo -e "${TAB}claude mcp add --transport http browseros-neo https://${TS_NAME}/mcp --header \"Authorization: Bearer \"" From 5ce72a95836c82d22b70360532a1b9274697f55d Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 08:17:07 +0300 Subject: [PATCH 09/10] BrowserOS Neo: tailnet viewer and MCP on separate login-hint lines Co-Authored-By: Claude Opus 5.5 --- install/browseros-neo-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh index 7c8a634684..76b9959496 100644 --- a/install/browseros-neo-install.sh +++ b/install/browseros-neo-install.sh @@ -268,7 +268,8 @@ if [ "$(id -u)" -eq 0 ]; then echo " BrowserOS Neo viewer: http://$(hostname -I | awk '{print $1}'):6080 (user neo)" echo " BrowserOS Neo MCP: http://$(hostname -I | awk '{print $1}'):9010/mcp" if command -v tailscale >/dev/null 2>&1 && [ -n "$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // empty')" ]; then - echo " On your tailnet: https://$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'):8443 (viewer), /mcp on port 443" + echo " Tailnet viewer: https://$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'):8443" + echo " Tailnet MCP: https://$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//')/mcp" fi echo " Viewer password and MCP token: cat /opt/browseros-neo_data/.env" echo "" From 9c1c9cff7443766bbaa525d3c7c7fd4f62a2bd83 Mon Sep 17 00:00:00 2001 From: Baker Date: Thu, 22 Oct 2026 08:22:20 +0300 Subject: [PATCH 10/10] BrowserOS Neo: claude mcp add command in the login hint Uses the tailnet MCP URL when Tailscale is connected, else the LAN one, with a placeholder; the helper variables are unset so they do not leak into the login shell. Co-Authored-By: Claude Opus 5.5 --- install/browseros-neo-install.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/install/browseros-neo-install.sh b/install/browseros-neo-install.sh index 76b9959496..07762bd0b9 100644 --- a/install/browseros-neo-install.sh +++ b/install/browseros-neo-install.sh @@ -265,14 +265,21 @@ fi msg_info "Configuring Login Hints" cat <<'EOF' >/etc/profile.d/browseros-neo-hint.sh if [ "$(id -u)" -eq 0 ]; then - echo " BrowserOS Neo viewer: http://$(hostname -I | awk '{print $1}'):6080 (user neo)" - echo " BrowserOS Neo MCP: http://$(hostname -I | awk '{print $1}'):9010/mcp" - if command -v tailscale >/dev/null 2>&1 && [ -n "$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // empty')" ]; then - echo " Tailnet viewer: https://$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'):8443" - echo " Tailnet MCP: https://$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//')/mcp" + _bn_ip=$(hostname -I | awk '{print $1}') + _bn_ts="" + command -v tailscale >/dev/null 2>&1 && _bn_ts=$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // empty' | sed 's/\.$//') + _bn_mcp="http://${_bn_ip}:9010/mcp" + echo " BrowserOS Neo viewer: http://${_bn_ip}:6080 (user neo)" + echo " BrowserOS Neo MCP: ${_bn_mcp}" + if [ -n "$_bn_ts" ]; then + _bn_mcp="https://${_bn_ts}/mcp" + echo " Tailnet viewer: https://${_bn_ts}:8443" + echo " Tailnet MCP: ${_bn_mcp}" fi echo " Viewer password and MCP token: cat /opt/browseros-neo_data/.env" + echo " Claude Code: claude mcp add --transport http browseros-neo ${_bn_mcp} --header \"Authorization: Bearer \"" echo "" + unset _bn_ip _bn_ts _bn_mcp fi EOF msg_ok "Configured Login Hints"