# Chromium en este equipo: dónde se toca y cómo Mapa de referencia de TODOS los puntos donde la configuración de Chromium se manipula en esta máquina (Linux nativo, Ubuntu 24.04, Chromium 148 del paquete `.deb` de xtradeb — **no snap**). Objetivo: no volver a redescubrir a ciegas de dónde sale cada comportamiento (extensiones, CDP, proxy). Reconstruido el 2026-06-05 desde el sistema en vivo + transcripts de sesiones anteriores. ## El wrapper que une todo `/usr/bin/chromium` es un **shell script** (no el binario). En cada arranque hace, en orden: 1. Sourcea **todos** los archivos de `/etc/chromium.d/*` (cada uno exporta/añade a `CHROMIUM_FLAGS`). 2. Lee las **managed policies** de `/etc/chromium/policies/managed/*.json`. 3. Aplica `master_preferences` (`/etc/chromium/master_preferences`) al crear un perfil nuevo. 4. Lanza el binario real `/usr/lib/chromium/chromium $CHROMIUM_FLAGS "$@"`. Por eso cualquier cosa en `/etc/chromium.d/` o en el policy aplica a **TODO** chromium que se lance, con cualquier perfil. Editar requiere `sudo` (`pass show claude/sudo | sudo -S ...`). ## Inventario de puntos de configuración | Path | Qué controla | Quién/cuándo lo creó | Cómo editar | |---|---|---|---| | `/etc/chromium/policies/managed/extensions.json` | **Extensiones force-instaladas en cualquier perfil** (`ExtensionInstallForcelist`). Hoy: solo uBlock Origin Lite. | Proyecto "Code", migración de PC (2026-05-31), 4 extensiones. Reducido a uBlock 2026-06-04. | `fn run apply_chromium_extension_policy --keep ...` (idempotente, backup auto) o `sudo` editar el JSON. Backup: `extensions.json.bak.20260605` (las 4 originales). | | `/etc/chromium.d/cdp` | **CDP global**: `--remote-debugging-port=9222 --remote-allow-origins=*` (bind loopback). | Proyecto web_scraping (2026-06-05), regla 8. | `fn run apply_chromium_cdp_flag [--remove]` o `sudo` editar/borrar el fragmento. | | `/etc/chromium.d/web_proxy_ext` | Carga la **extensión de captura del proxy** (`--load-extension=~/.web_proxy/extension`). | Proyecto web_proxy (2026-06-02). | Función `install_chromium_proxy_extension_bash_browser` lo reescribe; `--uninstall` lo borra. | | `/etc/chromium.d/extensions` | Pre-existente del `.deb`: `--enable-remote-extensions` + carga unpacked de `/usr/share/chromium/extensions/*` (vacío). | Paquete Debian (Jan 2025). | No tocar salvo necesidad. | | `/etc/chromium.d/default-flags` | Flags base del `.deb`: `--show-component-extension-options`, `--enable-gpu-rasterization`, `--no-default-browser-check`, `--disable-pings`, `--media-router=0`. | Paquete Debian. | No tocar. | | `/etc/chromium.d/dev-shm` | Añade `--disable-dev-shm-usage` si `/dev/shm` < 3.8 GB. | Paquete Debian. | No tocar. | | `/etc/chromium.d/apikeys` | `GOOGLE_API_KEY` de Debian (pública, para sync/gmail). | Paquete Debian. | No tocar (no es secreto). | | `/etc/chromium/master_preferences` | Estado inicial de perfiles nuevos (homepage, DuckDuckGo, signin off). **Sin extensiones.** | Distro. | No tocar para extensiones (no es el sitio). | | `~/.web_proxy/extension/` | Código de la extensión toggle del proxy (MV3, `chrome.proxy`). | App web_proxy. | Vía `install_chromium_proxy_extension`. | | `~/.mitmproxy/mitmproxy-ca-cert.pem` | CA de mitmproxy para HTTPS sin warnings. | mitmproxy. | `./web_proxy ca` para instrucciones. | **No existe** (verificado): external-extensions dir (`/usr/share/chromium/extensions` está vacío; `/opt/chromium.org/...` no existe), cron ni systemd-timer que regenere config de chromium, ni política `recommended/`. La forcelist es un archivo **estático sin regenerador automático del sistema**; se aplica de forma reproducible con `fn run apply_chromium_extension_policy` (idempotente, backup automático) o editándola a mano con sudo (también persistente). ## Extensiones: el mecanismo (LO QUE MÁS CONFUNDE) Las extensiones de la Chrome Web Store (uBlock, Dark Reader, NoScript, OneTab) se instalan en **cualquier perfil** vía `ExtensionInstallForcelist` en el policy managed. Chrome las descarga de `clients2.google.com/service/update2/crx` al arrancar cualquier perfil (incluido temporal/nuevo) y quedan "managed" (no removibles por la UI). Para cambiarlas se edita el JSON (sudo). - uBlock se usa en la variante **Lite** (`ddkjiahejlhfcafbddmgiahcphecmpfh`, MV3) porque el uBO clásico (MV2) no carga en Chromium 148. - IDs: uBlock Lite `ddkjiahejlhfcafbddmgiahcphecmpfh` · Dark Reader `eimadpbcbfnmbkopoojfekhnkhdbieeh` · NoScript `doojmbjmlfjjnbmnoijecmcbfeoakpjm` · OneTab `chphlpgkkbolifaimnlloiipkdnihall`. ### Anomalía resuelta (2026-06-05): backups dentro de `managed/` reinyectan extensiones Tras reducir la forcelist a solo uBlock **en disco**, los perfiles nuevos seguían instalando las 4 (incluso un `--user-data-dir` recién creado las recibía como `location=7` external_policy_download, activas). Causa raíz: **Chromium lee TODOS los archivos del directorio `policies/managed/` sin filtrar por extensión de nombre.** El backup `extensions.json.bak.20260605` (con las 4 originales) vivía dentro de `managed/` y Chromium lo mergeaba con `extensions.json`, así que el forcelist efectivo seguía teniendo las 4 — ganando incluso sobre un `ExtensionInstallBlocklist`. Mover el `.bak` fuera de `managed/` (a `policies/policy-backups/`) dejó el forcelist efectivo en solo uBlock y los perfiles nuevos pasaron a instalar únicamente uBlock. > Regla dura: en `policies/managed/` (y `recommended/`) **solo** archivos `.json` de política activa. > Cualquier `.bak`, `.old`, `.orig` ahí se carga como política. La función del registry > `apply_chromium_extension_policy_bash_browser` ya guarda sus backups fuera de `managed/`. El secundario (caché en memoria) también existe: aun con el disco correcto, hay que cerrar TODOS los Chromium (`pkill -9 chromium`) o pulsar `chrome://policy → Reload policies` para que el cambio surta efecto en runtime. **Para diagnosticar la forcelist EFECTIVA (no la del archivo):** abrir `chrome://policy` en el navegador y mirar `ExtensionInstallForcelist` (su columna *Source* y *Value*). No es scriptable por CDP (`chrome.send` no accesible vía `Runtime.evaluate`); hay que verlo en la UI y pulsar **"Reload policies"**. **Para que un cambio en la forcelist surta efecto de verdad:** cerrar **todos** los procesos chromium a la vez (`pkill -9 chromium`) y relanzar uno; o usar `chrome://policy → Reload policies`. Mientras quede un chromium vivo con la política vieja, los perfiles nuevos pueden heredarla. ### Perfil limpio para scraping Dos caminos (regla 9 de `CONVENTIONS.md`): 1. **Perfil nuevo desde cero** + forcelist=solo uBlock → nace solo con uBlock (una vez refrescada la política). Es lo más limpio; no arrastra nada. 2. **`prepare_chrome_profile`** (clona el perfil real conservando solo uBlock): útil cuando se necesitan las cookies/sesión del usuario. Borra carpetas de extensiones no-whitelist y purga sus refs de `Preferences`/`Secure Preferences` (si no, Chrome las re-descarga del Web Store). ## CDP: mapa de puertos | Puerto | Quién | Cómo se activa | |---|---|---| | 9222 | Chromium **diario** del usuario | Fragmento `/etc/chromium.d/cdp` (global, loopback). Todo chromium nuevo nace con él. | | 9333 (o el que se pida) | Navegador de **automatización** dedicado | `script-navegador launch --port 9333` o `systemd-run ... chromium --remote-debugging-port=9333`. Se pasa `--port` para no chocar con el 9222 global. | Dos procesos chromium **no** pueden compartir el mismo `--remote-debugging-port`. El `--port` en cmdline sobreescribe al del fragmento global. ## Proxy / captura mitm (web_proxy) - App: `projects/web_scraping/apps/web_proxy` (sub-repo Gitea `dataforge/web_proxy`). Compone funciones del registry: `start_mitm_capture`, `query_mitm_flows`, `rotate_capture_flows`, `launch_chromium_proxy`, `install_chromium_proxy_extension`. - Servicio `web_proxy.service` (systemd --user, `Restart=always`): mitmdump/mitmweb. - Puertos: **proxy de captura `127.0.0.1:8889`**, UI mitmweb `http://127.0.0.1:8081`. - Capturas: `~/captures/traffic-*.mitm`, rotación configurable (`--rotate-min`), retención 2048 MB / 7 días. El addon hace `flush()` por flujo (sobrevive a kill -9). - **Enrutar el navegador al proxy — dos vías:** 1. Extensión toggle `~/.web_proxy/extension`: popup ON → `chrome.proxy` a `127.0.0.1:8889`; OFF → directo. **Desde 2026-06-05 se instala vía managed policy, no con `--load-extension`** (Chromium 137+ desactiva `--load-extension` en cuanto hay una managed policy activa: "Loading of unpacked extensions is disabled by the administrator"). La extensión se empaquetó como `.crx` (`chromium --pack-extension`, clave en `~/.web_proxy/extension.pem` — NO perder, fija el id `nanldmckabfghgdebblpfbdbhphhbnde`), con un `~/.web_proxy/update.xml` (`file://`), y se fuerza con `apply_chromium_extension_policy --keep "=file://$HOME/.web_proxy/update.xml"`. Si se edita la extensión: re-empaquetar con la MISMA `.pem`, subir `version` en `manifest.json` y `update.xml`. `/etc/chromium.d/web_proxy_ext` ya NO usa `--load-extension`. 2. Perfil dedicado: `chromium --proxy-server=127.0.0.1:8889` (+ CA mitmproxy o `--ignore-certificate-errors` para HTTPS). - Consultar: `./web_proxy query "~d dominio.com" --last`, `./web_proxy har salida.har`, `./web_proxy inspect`. ## "Si quiero X, toco Y" (resumen accionable) | Quiero… | Toco… | |---|---| | Cambiar qué extensiones tiene cualquier perfil | `fn run apply_chromium_extension_policy --keep ...` (o editar el JSON con sudo) + cerrar TODO chromium + relanzar | | Activar/quitar CDP global | `fn run apply_chromium_cdp_flag [--remove]` (o editar `/etc/chromium.d/cdp` con sudo) | | Controlar un chromium con CDP | puerto 9222 (diario) o `script-navegador launch --port N` (dedicado) | | Capturar tráfico HTTP/HTTPS | `web_proxy` (proxy 8889, UI 8081); navegador con `--proxy-server=127.0.0.1:8889` o extensión toggle ON | | Perfil de scraping limpio | perfil nuevo desde cero (política da solo uBlock) o `prepare_chrome_profile --src ~/.config/chromium --dst ` | | Ver la política EFECTIVA | `chrome://policy` en la UI (no por CDP) → "Reload policies" | ## Selector de perfil — SIEMPRE pasar `--profile-directory` Este equipo tiene **varios perfiles** de chromium en `~/.config/chromium`: `Default`, `Personal`, `Profile 1`, `Automation` (+ `System Profile`). Si se lanza chromium **sin** `--profile-directory=`, Chrome se queda en el **selector de perfil** y NO carga ninguno. Consecuencias observadas: - CDP responde (`/json/version` OK) pero **no hay perfil activo** → los comandos operan sobre una ventana vacía. - Las extensiones **no se procesan**: las force-removed de la política no se desinstalan (fue la causa real de que las 3 extensiones "no se quitaran" — el diario estaba atascado en el picker). - La sesión (`--restore-last-session`) no se restaura. **Regla: SIEMPRE lanzar con `--profile-directory=""`.** - Navegador diario del usuario: `--profile-directory="Default"` (o el que use a diario). - Navegador de automatización/scraping: `--profile-directory="Automation"` (perfil dedicado), o un `--user-data-dir` nuevo (que tiene un solo perfil y por tanto no muestra picker). ```bash # Diario (visible, con su perfil y CDP global) systemd-run --user --unit=chromium-daily --collect --setenv=DISPLAY=:0.0 \ chromium --profile-directory="Default" --restore-last-session # Automatización (perfil dedicado, proxy mitm, CDP en puerto propio) systemd-run --user --unit=ws-explore --collect --setenv=DISPLAY=:0.0 \ chromium --profile-directory="Automation" --remote-debugging-port=9333 \ '--remote-allow-origins=*' --proxy-server=127.0.0.1:8889 --ignore-certificate-errors \ --window-size=1366,768 about:blank ``` > Nota: `script-navegador launch` con `--user-data-dir` propio evita el picker (perfil único). Para > apuntar al perfil real multi-perfil del usuario, añadir `--profile-directory`. ## Gotchas aprendidos (para no repetir) - **`.bak`/`.old`/`.orig` en directorios "source-todo" se activan**: tanto `/etc/chromium/policies/managed/` como `/etc/chromium.d/` se cargan ENTEROS (todos los archivos, sin filtrar por extensión de nombre). Un `extensions.json.bak` en `managed/` se mergea como política (y reinyecta sus extensiones); un `cdp.bak` en `chromium.d/` se sourcea como shell. Guardar SIEMPRE los backups FUERA de esos directorios (este equipo usa `policies/policy-backups/` y `chromium/chromiumd-backups/`). - **Perfil diario real = `~/.config/chromium-cdp`** (lo fuerza `/etc/chromium.d/cdp`), NO `~/.config/chromium`. Limpiar/inspeccionar el perfil del usuario = operar sobre `chromium-cdp`. - **Selector de perfil**: con varios perfiles, sin `--profile-directory` Chrome se atasca en el picker y no carga nada (ver sección arriba). SIEMPRE especificarlo. - **El archivo de policy en disco ≠ política efectiva**: Chrome cachea; verifica siempre en `chrome://policy`, no solo con `cat`. Para aplicar cambios, cierra todos los chromium. - **Lanzar chromium desde el Bash tool de Claude da exit-144** (el harness mata el cgroup). Usa `systemd-run --user --unit= --collect chromium ...` aislado. Ver memoria `harness-exit-144-chromium`. (No afecta a binarios Go ni al uso real del usuario.) - **`--remote-allow-origins=*` debe ir entre comillas en zsh** (el `*` se expande como glob). - **Dark Reader no toca páginas `chrome://`**: una captura de `chrome://version` sale clara aunque Dark Reader esté activo; usa una web normal para verificar si está oscureciendo. - **`prepare_chrome_profile` clonando**: borra carpetas Y purga `Preferences`, pero pueden quedar refs benignas en `commands`/pins; lo que causa re-descarga es `extensions.settings` (ya se purga). - **Lock por user-data-dir**: dos procesos chromium NO pueden compartir el mismo `--user-data-dir` (aunque sea con `--profile-directory` distinto). Si el diario tiene `~/.config/chromium` abierto, un segundo chromium sobre ese mismo user-data-dir muere/delegará al existente y su `--remote-debugging-port` no escucha. Para automatización en paralelo: usar un `--user-data-dir` dedicado (p.ej. `~/.local/share/web_scraping/scrape-fresh`). - **Ventana visible (headless=false) desde systemd-run**: hay que pasar el entorno gráfico: `--setenv=DISPLAY=:0 --setenv=XAUTHORITY=$HOME/.Xauthority`. Sin ellos chromium no conecta al X server y muere. (`DISPLAY` es `:0`, no `:0.0`.) - **Mover la ventana sin xdotool/wmctrl** (no instalados): usar CDP nativo — `Browser.getWindowForTarget` (sobre el ws browser-level de `/json/version`) → `windowId`, luego `Browser.setWindowBounds {left,top,width,height}`. Funciona en modo visible; `windowState:normal` confirma ventana real. - **Proxy mitm (8889) ralentiza/cuelga sitios externos**: con `--proxy-server=127.0.0.1:8889`, la navegación a sitios externos (p.ej. TED) puede tardar mucho o dejar el renderer atascado, y eso bloquea `open`/`eval`/`shot` sobre esa tab. Para scraping con captura: subir timeouts, o revisar la salud del upstream del proxy. Sin proxy, en este equipo la salida directa a internet puede dar `ERR_CONNECTION_RESET` (la red parece pasar por la infra de captura). A revisar.