chore: auto-commit (4 archivos)
- .gitignore - CAPABILITIES_TODO.md - CHROMIUM_SYSTEM.md - hoppscotch/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -98,6 +98,95 @@ Dos caminos (regla 9 de `CONVENTIONS.md`):
|
||||
Dos procesos chromium **no** pueden compartir el mismo `--remote-debugging-port`. El `--port` en
|
||||
cmdline sobreescribe al del fragmento global.
|
||||
|
||||
## Navegador gestionado por systemd — sesión persistente (cierre limpio)
|
||||
|
||||
**Problema resuelto (2026-06-10):** el navegador diario perdía la sesión de Google (y de cualquier
|
||||
sitio) cada vez que se cerraba y reabría — aparecía deslogueado al volver a abrirlo.
|
||||
|
||||
### Causa raíz: el cierre sucio, NO el CDP
|
||||
|
||||
Cuando el proceso **master** de Chromium se mata con `SIGKILL` (en vez de `SIGTERM`), no llega a
|
||||
vaciar a disco las cookies de sesión recién escritas, marca `profile.exit_type = "Crashed"` y al
|
||||
reabrir la sesión está perdida. Esto pasaba porque el navegador, lanzado por rofi/wrapper, no estaba
|
||||
atado a nada que le mandara `SIGTERM` con margen: al logout/apagado de XFCE o al morir el proceso
|
||||
lanzador recibía `SIGKILL` directo, sin tiempo de flush.
|
||||
|
||||
Descartado tras experimento controlado (reusando la sesión real del usuario, sin re-login):
|
||||
|
||||
- Con CDP activo (`--remote-debugging-port=9222 --remote-allow-origins=*`) **y** cliente CDP
|
||||
conectado, `myaccount.google.com` seguía **logueado**. No hay deslogueo por tener CDP.
|
||||
- `navigator.webdriver` es **`false`** con CDP → no hay detección de automatización por JS ni
|
||||
server-side. El flag `--disable-blink-features=AutomationControlled` no cambia nada aquí.
|
||||
- Tras un cierre **limpio** (`SIGTERM`, `exit_type=SessionEnded`), las cookies de auth
|
||||
(`SID`, `SAPISID`, `__Secure-1PSID`, `__Secure-3PSID`) **persisten** a disco con CDP puesto.
|
||||
|
||||
Callejones descartados (no eran la causa, no repetir): un supuesto "borrar cookies al cerrar" (no
|
||||
existía), `restore_on_startup` (se aplicó policy `RestoreOnStartup:1`, no resolvió), y el cifrado
|
||||
OSCrypt fallando en XFCE (`os_crypt.portal.prev_init_success:false`; se aplicó
|
||||
`--password-store=basic` vía `/etc/chromium.d/password-store`, no resolvió — inofensivo, se deja).
|
||||
|
||||
### Solución: systemd user service `chromium-personal`
|
||||
|
||||
`~/.config/systemd/user/chromium-personal.service` mantiene el navegador como servicio gestionado,
|
||||
de modo que **cada cierre es SIGTERM con margen de flush**. Claves del unit:
|
||||
|
||||
| Directiva | Valor | Por qué |
|
||||
|---|---|---|
|
||||
| `ExecStart` | `/usr/lib/chromium/chromium --user-data-dir=%h/.config/chromium-cdp --profile-directory=Personal --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --remote-allow-origins=* --no-first-run --no-default-browser-check` | Binario **real** (controlamos los flags). CDP en **loopback** explícito. |
|
||||
| `KillSignal=SIGTERM` + `KillMode=mixed` | — | SIGTERM solo al master; él apaga sus hijos y vacía cookies. |
|
||||
| `TimeoutStopSec=30` | 30 s | Margen para flushear antes de cualquier SIGKILL. |
|
||||
| `PartOf=graphical-session.target` | — | systemd lo para ordenado en el **logout**, antes de que XFCE mate la sesión. |
|
||||
| `Restart=always` + `RestartSec=3` | — | Siempre activo; se recupera de crashes. |
|
||||
| `[Install] WantedBy=default.target` (`enable`) | — | Arranca al iniciar sesión. |
|
||||
|
||||
`DISPLAY=:0` y `XAUTHORITY=%h/.Xauthority` van en `Environment=` (sin ellos no conecta al X server).
|
||||
|
||||
**Seguridad:** el CDP escucha **solo en `127.0.0.1:9222`** (verificado con `ss`: `127.0.0.1:9222`,
|
||||
nunca `0.0.0.0`). El `--remote-allow-origins=*` se mantiene porque lo necesita el cliente MCP local
|
||||
para el websocket; no expone nada a la red porque el bind es loopback.
|
||||
|
||||
### Todos los perfiles heredan el cierre limpio
|
||||
|
||||
Chromium usa **un único master por `user-data-dir`**. Como el service mantiene ese master
|
||||
(perfil inicial `Personal`), cualquier otro perfil que se abra (rofi, menú, selector de Chromium) se
|
||||
**engancha al mismo master**. Por eso `systemctl --user stop` (o el logout) hace un shutdown global
|
||||
que vacía las cookies de **todos** los perfiles abiertos. No hace falta un service por perfil (de
|
||||
hecho no funcionaría: no pueden coexistir dos masters sobre el mismo `user-data-dir`).
|
||||
|
||||
### rofi y escritorio usan el service
|
||||
|
||||
- Script `~/.local/bin/chromium-launch`: hace `systemctl --user start chromium-personal`, espera a
|
||||
que el CDP loopback responda y reenvía al master con el binario real
|
||||
(`/usr/lib/chromium/chromium --user-data-dir=$HOME/.config/chromium-cdp "$@"`). Así nunca se crea
|
||||
un master no gestionado.
|
||||
- Override `~/.local/share/applications/chromium.desktop` (precedencia sobre
|
||||
`/usr/share/applications/`): su `Exec` apunta a ese script. rofi y el menú XFCE lo usan
|
||||
automáticamente. Trae `Actions` para nueva ventana / incógnito.
|
||||
|
||||
### Uso
|
||||
|
||||
```bash
|
||||
systemctl --user start chromium-personal # abrir (o desde rofi/menú "Chromium")
|
||||
systemctl --user stop chromium-personal # cerrar de verdad (no dispara Restart)
|
||||
systemctl --user status chromium-personal # estado
|
||||
```
|
||||
|
||||
Cerrar la ventana con la `X` o `Ctrl+Q` también es limpio (SIGTERM normal de Chromium). **Gotcha
|
||||
UX de `Restart=always`:** si se cierran TODAS las ventanas, el master sale y systemd lo relanza en
|
||||
~3 s (reabre `Personal`); para apagarlo del todo usar `stop`. Si molesta, cambiar a
|
||||
`Restart=on-failure`.
|
||||
|
||||
### Control por el MCP browser (lifecycle por perfil)
|
||||
|
||||
El MCP `browser` (`apps/browser_mcp`) tiene tres tools para gestionar instancias por perfil sin
|
||||
comandos a mano (añadidas 2026-06-10, rama `quick/browser-lifecycle`):
|
||||
|
||||
- `browser_list` — lista los masters chromium corriendo: `{pid, profile, user_data_dir, cdp_port, has_cdp}`.
|
||||
- `browser_launch_profile` — lanza por perfil; `cdp:false` usa el binario real **sin** remote-debugging
|
||||
(navegador "humano" que Google no toca), `cdp:true` añade el puerto. Desacopla con `setsid` para
|
||||
sobrevivir a la muerte del MCP.
|
||||
- `browser_close` — cierra limpio por `profile`/`cdp_port`/`pid` (SIGTERM; SIGKILL solo como último recurso).
|
||||
|
||||
## Proxy / captura mitm (web_proxy)
|
||||
|
||||
- App: `projects/web_scraping/apps/web_proxy` (sub-repo Gitea `dataforge/web_proxy`). Compone
|
||||
|
||||
Reference in New Issue
Block a user