Ctrl K
ComunidadGuías…

Dockerfile

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Build the shared Settings UI from the same commit as the server.
FROM ghcr.io/cirruslabs/android-sdk:34 AS console-build
WORKDIR /console-src
COPY mobile ./
RUN bash gradlew :shared:wasmJsBrowserDistribution --no-daemon

# Its own image, with Chromium in it.
#
# This is the reason the service is a separate deployment rather than another app on the shared
# runner image: the runner has no browser, and installing one at container start would mean a
# 300 MB download and a system-dependency install on every pod restart. Microsoft's Playwright
# image already has Chromium at exactly the version the npm package expects, on glibc — the
# Alpine/musl route does not launch Chromium at all.
#
# The tag MUST track the playwright version in package.json, and that version MUST be pinned exactly
# — no caret. Caught on the first deploy: `^1.59.1` resolved to 1.59.1 locally and 1.62.1 in the
# build, so the package went looking for a Chromium the base image does not contain and every
# session failed with "Executable doesn't exist at /ms-playwright/chromium_headless_shell-1234".
# The image and the package are one thing; a range pretends they are two.
FROM mcr.microsoft.com/playwright:v1.59.1-noble

# Tailscale, so the browser can exit through a device its OWNER controls.
#
# The pods exit from a Hetzner IP that public databases already flag as a proxy, and Facebook
# refuses a login from it while accepting the same account from a home connection seconds later.
# That is not fixable in the browser; it is fixable by not exiting from here.
#
# The static binaries, not the apt repo: this needs two files, not a systemd unit, an init script
# and a package manager's opinion about how a VPN should start.
ARG TAILSCALE_VERSION=1.78.1
RUN set -eux;     arch="$(dpkg --print-architecture)";     case "$arch" in amd64) ts_arch=amd64 ;; arm64) ts_arch=arm64 ;; *) echo "unsupported $arch" >&2; exit 1 ;; esac;     curl -fsSL "https://pkgs.tailscale.com/stable/tailscale_${TAILSCALE_VERSION}_${ts_arch}.tgz" -o /tmp/ts.tgz;     tar -xzf /tmp/ts.tgz -C /tmp;     mv /tmp/tailscale_${TAILSCALE_VERSION}_${ts_arch}/tailscale /tmp/tailscale_${TAILSCALE_VERSION}_${ts_arch}/tailscaled /usr/local/bin/;     rm -rf /tmp/ts.tgz /tmp/tailscale_${TAILSCALE_VERSION}_${ts_arch};     tailscale version

# ffmpeg — so the browser can encode its OWN screen recordings (the CDP screencast) into a video
# asset. This is what lets GhostBrowser film itself: a live tour of the console becomes an MP4 clip
# that goes straight into an edit. apt here, not a static binary — ffmpeg pulls a web of codecs.
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*
# The Modal client, for the GPU nodes the hub starts by itself (nodes.js). Its own venv: the
# image's python is Ubuntu's and refuses pip installs into the system.
RUN apt-get update && apt-get install -y --no-install-recommends python3-venv && rm -rf /var/lib/apt/lists/* \
 && python3 -m venv /opt/modal && /opt/modal/bin/pip install --no-cache-dir modal

# PulseAudio — so a page can be HEARD. Nothing runs at start: a screen recording starts its own
# private sound server with one null sink (recorder/sidecar.js), points its own Chromium at it,
# and ffmpeg records that sink's monitor. The pool's browsers stay silent and untouched.
RUN apt-get update && apt-get install -y --no-install-recommends pulseaudio pulseaudio-utils && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY package.json package-lock.json* ./
RUN npm install --omit=dev --no-audit --no-fund

COPY src ./src
# The set builder runs as a spawned child of the server, and the round scripts are served to any
# laptop that offers to train. Neither lives on a developer machine: without them in the image the
# nightly loop fails at the first step, and the second machine can never be set up at all.
COPY scripts ./scripts
COPY training ./training
COPY docs ./docs
COPY public ./public
COPY --from=console-build /console-src/shared/build/dist/wasmJs/productionExecutable/ ./public/console/
COPY public/compose-index.html ./public/index.html
RUN node scripts/prepare-console.js
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# The image already ships a non-root `pwuser`; Chromium runs unprivileged and nothing is written
# to disk, so there is nothing to chown beyond the app itself.
RUN chown -R pwuser:pwuser /app
USER pwuser

ENV NODE_ENV=production
EXPOSE 3000
# The browser runs HEADFUL, so it needs a display.
#
# reCAPTCHA Enterprise scores a headless browser as automated before the puzzle is even shown —
# which is why solving Facebook's fire hydrants correctly still returned to the login page. A real
# X display is not something a stealth patch can fake.
#
# The display is started by entrypoint.sh rather than `xvfb-run`: that wrapper failed once here with
# nothing listening and no output at all, which is unfixable without guessing. Eight explicit lines
# print what happened.
CMD ["/usr/local/bin/entrypoint.sh"]