diff --git a/Caddyfile b/Caddyfile index 2a5fd914..d7889f7f 100644 --- a/Caddyfile +++ b/Caddyfile @@ -17,6 +17,12 @@ @api path /api/* /api reverse_proxy @api localhost:8090 + # OpenMCP relay (tronbrowser.dev/mcp/tron): same API process. Its descriptor + # is the static /.well-known/openmcp.json below, which is what makes the + # listing "verified" on a catalog (served from this origin, not registered). + @mcp path /mcp/* /mcp + reverse_proxy @mcp localhost:8090 + # Security headers on every response. header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -35,6 +41,8 @@ header /install.sh Content-Type "text/x-shellscript; charset=utf-8" header /sitemap.xml Content-Type "application/xml; charset=utf-8" header /manifest.json Content-Type "application/manifest+json; charset=utf-8" + header /.well-known/openmcp.json Content-Type "application/json; charset=utf-8" + header /.well-known/openmcp.json Access-Control-Allow-Origin "*" # Caching: long for images, SHORT for code/HTML so updates propagate fast # (no content hashing yet — don't let stale JS strand logged-in users). diff --git a/Dockerfile b/Dockerfile index 53aa94de..f0ae2fc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,67 @@ # Single container for tronbrowser.dev: Caddy serves the static site and -# reverse-proxies /api to the bundled Hono API (one service, one domain). -# Built from the monorepo root context. +# reverse-proxies /api and /mcp to the bundled Hono API (one service, one +# domain). Built from the monorepo root context. +# +# The API now carries the OpenMCP relay at /mcp/tron, so the image also holds +# its two engines: ungoogled-chromium (the portable Linux build, headless) and +# Obscura. Both are pinned below; bump the ARGs to move them. -# --- build the API --- -FROM node:24-slim AS api -WORKDIR /api -COPY services/api/package.json ./ -RUN npm install --no-audit --no-fund -COPY services/api/src ./src -RUN printf '%s' '{"compilerOptions":{"target":"ES2023","module":"NodeNext","moduleResolution":"NodeNext","outDir":"dist","rootDir":"src","strict":true,"skipLibCheck":true,"esModuleInterop":true},"include":["src"]}' > tsconfig.build.json \ - && npx tsc -p tsconfig.build.json \ - && npm prune --omit=dev +ARG UNGOOGLED_CHROMIUM_VERSION=152.0.7977.82-1 +ARG OBSCURA_VERSION=0.2.2 -# --- final: caddy + node --- -FROM caddy:2-alpine +# --- build the API (a pnpm workspace member: it imports @tronbrowser/sdk) --- +FROM node:24-bookworm-slim AS api +RUN corepack enable && corepack prepare pnpm@9.12.0 --activate +WORKDIR /repo +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml tsconfig.base.json ./ +COPY packages/browser-core/package.json packages/browser-core/ +COPY packages/agent-runtime/package.json packages/agent-runtime/ +COPY packages/sdk/package.json packages/sdk/ +COPY services/api/package.json services/api/ +RUN pnpm install --frozen-lockfile --filter @tronbrowser/api... +COPY packages/browser-core packages/browser-core +COPY packages/agent-runtime packages/agent-runtime +COPY packages/sdk packages/sdk +COPY services/api services/api +RUN pnpm --filter @tronbrowser/browser-core --filter @tronbrowser/agent-runtime --filter @tronbrowser/sdk --filter @tronbrowser/api build \ + && pnpm --filter @tronbrowser/api deploy --prod /out + +# --- engines: fetched once at build time, not at boot --- +FROM debian:bookworm-slim AS engines +ARG UNGOOGLED_CHROMIUM_VERSION +ARG OBSCURA_VERSION +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl xz-utils && rm -rf /var/lib/apt/lists/* +RUN set -eu; arch="$(uname -m)"; case "$arch" in x86_64) uc=x86_64; ob=x86_64 ;; aarch64) uc=arm64; ob=aarch64 ;; *) echo "unsupported arch $arch" >&2; exit 1 ;; esac; \ + mkdir -p /opt/ungoogled-chromium /opt/obscura; \ + curl -fsSL "https://github.com/ungoogled-software/ungoogled-chromium-portablelinux/releases/download/${UNGOOGLED_CHROMIUM_VERSION}/ungoogled-chromium-${UNGOOGLED_CHROMIUM_VERSION}-${uc}_linux.tar.xz" \ + | tar -xJ --strip-components=1 -C /opt/ungoogled-chromium; \ + test -x /opt/ungoogled-chromium/chrome; \ + curl -fsSL "https://github.com/h4ckf0r0day/obscura/releases/download/v${OBSCURA_VERSION}/obscura-${ob}-linux-stealth.tar.gz" \ + | tar -xz -C /opt/obscura; \ + test -x /opt/obscura/obscura + +# --- final: caddy + node + tor + the engines --- +# Debian rather than Alpine: the portable ungoogled-chromium and Obscura are +# glibc binaries. Caddy is a static binary, copied from its own image. +FROM node:24-bookworm-slim +COPY --from=caddy:2 /usr/bin/caddy /usr/bin/caddy # openssh-client: the store provisions BBS publisher accounts and generates # ed25519 keypairs via `ssh`/`ssh-keygen` (services/api/src/store/fileshost.ts). # tor: runs a Tor v3 hidden service in this same container so tronbrowser.dev is # reachable over a stable .onion (start.sh writes torrc and boots it). The onion # key persists on a Railway volume mounted at /var/lib/tor/hidden_service. -RUN apk add --no-cache nodejs openssh-client tor +# The lib* rows are what headless Chromium links against; fonts so text renders. +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates openssh-client tor \ + libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 \ + libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 libpango-1.0-0 \ + libcairo2 libatspi2.0-0 libxshmfence1 libx11-6 libx11-xcb1 libxcb1 libxext6 libglib2.0-0 \ + libdbus-1-3 libexpat1 fonts-liberation fonts-noto-color-emoji \ + && rm -rf /var/lib/apt/lists/* +COPY --from=engines /opt/ungoogled-chromium /opt/ungoogled-chromium +COPY --from=engines /opt/obscura /opt/obscura +ENV TRON_MCP_CHROMIUM_BIN=/opt/ungoogled-chromium/chrome \ + OBSCURA_BIN=/opt/obscura/obscura COPY Caddyfile /etc/caddy/Caddyfile COPY apps/web/public/ /srv/ # Extension store (tronbrowser.dev/store) — static frontend; dynamic bits hit @@ -29,9 +71,9 @@ COPY apps/extensions/public/ /srv/store/ # symlinks to them for local dev, but Docker COPY won't follow symlinks pointing # outside the copied dir — so copy the real files in (these override the links). COPY logo.svg favicon.svg hero.svg banner.png /srv/ -COPY --from=api /api/dist /api/dist -COPY --from=api /api/node_modules /api/node_modules -COPY --from=api /api/package.json /api/package.json +COPY --from=api /out/dist /api/dist +COPY --from=api /out/node_modules /api/node_modules +COPY --from=api /out/package.json /api/package.json # DB migrations run on boot (start.sh) so schema never drifts from the deploy. COPY scripts/db-migrate.mjs /api/db-migrate.mjs COPY packages/storage/migrations /api/migrations diff --git a/README.md b/README.md index ee8259d4..d5e6c65d 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ Linux phones (Librem 5 / PinePhone / Ubuntu Touch). See to the cloud SQLite (Turso) or your own self-hosted backend. - Keeps all Chromium features: extensions, profiles, bookmarks, history, PWAs, DevTools. +- **`tron automate`** — an MCP server for agents: Obscura renders pages first + (fast, light), the Chromium session takes over for full-JS pages and bot + walls. Stdio for a local host, HTTP with an OpenMCP descriptor for a catalog. + The same relay runs hosted at **tronbrowser.dev/mcp/tron** (keyless + `fetch_page` + `screenshot_page`, listed on openmcp.logicsrc.com). See + [docs/mcp.md](docs/mcp.md). ## Monorepo diff --git a/apps/web/public/.well-known/openmcp.json b/apps/web/public/.well-known/openmcp.json new file mode 100644 index 00000000..1224878c --- /dev/null +++ b/apps/web/public/.well-known/openmcp.json @@ -0,0 +1,12 @@ +{ + "openmcp": "0.1", + "mcp": "https://tronbrowser.dev/mcp/tron", + "name": "TronBrowser", + "description": "Fetch any public web page as markdown, text, links or html, or take a screenshot, through TronBrowser: Obscura renders first (fast, light) and the ungoogled-chromium engine takes over for JS-heavy pages and bot walls. Keyless; a few fetches at a time.", + "url": "https://tronbrowser.dev", + "auth": { "kind": "none", "open": ["fetch_page", "screenshot_page"] }, + "tags": ["browser", "fetch", "scrape", "screenshot", "chromium", "ungoogled-chromium", "obscura", "web", "hosted"], + "operator": "https://logicsrc.com/.well-known/openprofile.md", + "tools": ["fetch_page", "screenshot_page"], + "catalogs": ["https://openmcp.logicsrc.com"] +} diff --git a/apps/web/public/install.sh b/apps/web/public/install.sh index 63e8b322..29290ec2 100755 --- a/apps/web/public/install.sh +++ b/apps/web/public/install.sh @@ -93,6 +93,12 @@ Usage: tron run