diff --git a/ct/browseros-neo.sh b/ct/browseros-neo.sh new file mode 100644 index 0000000000..773ae46607 --- /dev/null +++ b/ct/browseros-neo.sh @@ -0,0 +1,87 @@ +#!/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}" + +export var_tailscale="${var_tailscale:-}" +export var_ts_authkey="${var_ts_authkey:-}" + +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 +} + +# 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 + +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}" +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, show them with:${CL}" +echo -e "${TAB}pct exec ${CTID} -- cat /opt/browseros-neo_data/.env" +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 \"" +else + 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 new file mode 100644 index 0000000000..07762bd0b9 --- /dev/null +++ b/install/browseros-neo-install.sh @@ -0,0 +1,289 @@ +#!/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 + +[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 +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" + +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. 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 + +msg_info "Configuring Login Hints" +cat <<'EOF' >/etc/profile.d/browseros-neo-hint.sh +if [ "$(id -u)" -eq 0 ]; then + _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" + +motd_ssh +customize +cleanup_lxc diff --git a/json/browseros-neo.json b/json/browseros-neo.json new file mode 100644 index 0000000000..6ba27172cf --- /dev/null +++ b/json/browseros-neo.json @@ -0,0 +1,56 @@ +{ + "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" + } + } + ], + "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 + }, + "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" + } + ] +}