Repository navigation
Description
Objetivo y motivo
Que Quorb en Linux lea sesiones y almacene sus propios secretos de forma segura y abra los logins oficiales en el mismo entorno Linux, sin confundirlo con WSL.
Alcance
- Resolver rutas Linux y sesiones locales de los proveedores presentes, con errores útiles para archivo ausente, formato inválido y sesión caducada; no prometer sesión local de todos los proveedores.
- Verificar Secret Service/keyring en la sesión de escritorio acordada, incluidos almacenamiento, recuperación, borrado y migración del servicio legado sin revelar valores.
- Abrir login de Claude, Codex y Gemini mediante terminal Linux con comandos y argumentos seguros, rutas con espacios/no ASCII y errores accionables; no depender ciegamente de
x-terminal-emulatorsi no está disponible. - Excluye leer credenciales WSL desde Linux, copiar tokens, instalar CLIs automáticamente, modificar cuentas ajenas y cambiar el contrato Windows/macOS.
Dependencias
#105 baseline Linux integrado y matriz/contrato aprobados. WSL #90–#93 es independiente; coordinar superficies compartidas, sin hacer depender Linux de WSL.
Criterios de aceptación
- Rutas y precedencia explícita entre configuración manual y defaults Linux, con pruebas de ausente, inválido, espacios/no ASCII y cuentas pertinentes.
- Secret Service/keyring probado en escritorio real: guardar/leer/borrar y ausencia/denegación del servicio se informan sin filtrar secretos; no degradar a texto plano.
- Login CLI y diagnóstico seguros y verificables; ausencia de terminal/CLI o cancelación producen resultado accionable y no se ejecuta una cadena arbitraria por interpolación.
- Proveedores soportados comprobados en Linux real; los no comprobados o dependientes de sesiones no disponibles quedan identificados, sin prometer paridad global.
- Suites focales y regresión Windows pertinentes, sin reescribir el flujo WSL.
Orientación
src-tauri/src/providers/, src-tauri/src/secrets.rs, src-tauri/src/login.rs, src-tauri/src/config.rs, src/Settings.tsx. Mantener lectura sin escritura en sesiones oficiales; no registrar secretos en logs/tests.
Comprobaciones y ODD
Observar comportamiento y rutas reales de la matriz #105 → decidir contrato de credenciales/terminal y fallos → desarrollar adaptadores con tests RED/GREEN y controles negativos → verificar CLI y Secret Service en Linux real, npm test, tipos/build y cargo test según superficie, más regresión Windows. Salida: evidencia funcional y límites por proveedor, no solo compilación.
Coordinación y entrega
Planificada y bloqueada por #105; puede desarrollarse en paralelo con la unidad de escritorio después de esa integración. Sin assignee inicial. Revalidar responsables, PR y dependencias antes de iniciar; coordinar textos con #66 sin duplicar i18n. Tests y docs junto al código; PR/merge/release requieren autorización separada.
Activity
feat(linux): port egdev6#106 CLI login + secrets + keyring hint (egde…
docs(linux): correcciones del review de baseline en egdev6#114
docs(linux): baseline, contrato y referencias Linux (#105) (#114)
feat(linux): port #106 CLI login + secrets + keyring hint (#106) (#122)
docs(linux): refresh CONTRIBUTING + §5 to match merged #127 and #122 (#…
docs(linux): evidencia W3 de verificación Linux real para egdev6#106
sosan commentedon Oct 22, 2026
W3 — Verificación en Linux real ejecutada. Registra el estado de los criterios con evidencia del runner, no solo de compilación.
Runs (workflow
linux-ci.yml,ref=feat-linux-l1-106-retake, Ubuntu 22.04):39698848192(with_bundle=tests):npm test(628 vitest + 221 node) ·npm run build·cargo test --locked(233 passed, 1 ignored) · smoke de Secret Service: 1 passed.39800266204(with_bundle=bundle): lo anterior + build de.deb/.AppImage+ arranque verificado de ambos en Ubuntu 22.04 limpia (60 s con ventana creada).Criterio 2 — Secret Service en sesión real (el que faltaba): el smoke
linux_secret_service_round_trip(marcado#[ignore], no corre en CI por diseño) se ejecuta explícitamente en el lab condbus-run-session+gnome-keyring-daemon --unlock. Guarda → lee → borra un valor de prueba vía el backendlinux-nativedel cratekeyring(libsecret/D-Bus), por la misma víaentry()/scoped()que la app. Resultado: OK, sin degradar a texto plano.Bug encontrado y corregido en el gate de bundles (relevante para la confianza en la evidencia): los scripts de
verifybuscabanquorb_*.deb/quorb_*.AppImageen minúscula, pero Tauri produceQuorb_0.7.0_amd64.*; ambos checks se saltaban y el orquestador imprimía «✔ Build-verify OK» sin arrancar nada. Corregido con búsqueda case-insensitive (find -iname) +exit 1si no hay bundles. El run39800266204ya arranca los binarios de verdad (.debinstalado →/usr/bin/quorbvivo;.AppImagevivo vía FUSE).Límites de esta evidencia (lo que sigue sin comprobarse automáticamente):
ubuntu-22.04.Evidencia completa (outputs, pasos, límites):
docs/linux-l1-evidence/README.mden la ramafeat-linux-l1-106-retake(commit72b18b8).egdev6 commentedon Oct 22, 2026
@sosan he verificado el comentario W3 contra GitHub y el código. La evidencia es real y se sostiene, con un matiz importante sobre dónde viven los runs.
✅ Verificado
Los runs existen y pasan — pero en el fork
sosan/quorb, no enegdev6/quorb(ahí los IDs dan 404):39698848192feat-linux-l1-106-retake5ca36e439800266204feat-linux-l1-106-retaked7a0f8ctest secrets::tests::linux_secret_service_round_trip ... ok(1 passed, 233 filtered out), condbus-run-session+gnome-keyring-daemon --unlockycargo test --locked -- --ignored. Coincide con el criterio 2.✔ .deb OK: quorb siguió vivo a los 60s bajo X virtual (ventana creada),✔ .AppImage OK: montó vía FUSE y siguió vivo 60sy✔ Build-verify OK: los bundles presentes en /artifacts han arrancado.d7a0f8c): el fix es correcto y relevante — los scripts buscabanquorb_*.deb(minúscula) pero Tauri produceQuorb_0.7.0_amd64.*; ambos checks se saltaban y el gate era un verde vacío. Ahorafind -iname+exit 1si no hay ningún bundle.docs/linux-l1-evidence/README.md(72b18b8) existe, es detallada y honesta con los límites (sin sesiones CLI reales, sin escritorio real, solo ubuntu-22.04).cargo test --locked= 233 passed + 1 ignored, coincide con lo declarado.🟡 Observaciones
--repo sosan/quorb). Quien auditeegdev6/quorbobtendrá 404. Conviene enlazar los runs con URL completa del fork. Además la ramafeat-linux-l1-106-retakeya no existe en el upstream y no hay PR abierto — la evidencia es difícil de reproducir para terceros.migrate_legacy()ensecrets.rsexiste pero no se ejercita en la evidencia.verify.sh: si falta solo uno de los dos tipos de bundle, ese check sigue haciendoexit 0con «AVISO» (el gate solo falla si no hay ninguno). El mensaje nuevo lo dice honestamente («los bundles presentes»), pero se podría endurecer por tipo.quorb-ci-testpass) en el YAML del smoke: aceptable (keyring efímero del runner, datos de prueba), pero el snippet del README muestra$PASSmientras el workflow la tiene fija. Trivial.Veredicto
Comentario fiable: todos los hechos declarados se confirman en logs y código. Sugerencias: (a) enlazar los runs con URL del fork explícita aquí, (b) abrir el PR upstream pronto para que rama y evidencia sean alcanzables, (c) registrar la migración legacy como lo pendiente del criterio 2.
test(linux): fixtures de sesión real por proveedor + tests determinis…
egdev6 commentedon Oct 22, 2026
@egdev6 Pendiente registrado del criterio 2 (Secret Service), tras revisar la evidencia W3:
La migración del servicio legado sigue sin ejercitarse. El smoke
linux_secret_service_round_tripvalida guardar → leer → borrar víaentry()/scoped(), peromigrate_legacy()(src-tauri/src/secrets.rs) — la lectura de la clave del servicio legado cuando el destino actual no la tiene — no aparece en la evidencia. El AC2 pide «almacenamiento, recuperación, borrado y migración del servicio legado sin revelar valores».Propuesta de cierre: extender el smoke (o añadir un segundo smoke
#[ignore]) que pueble una entrada en el espacio de nombres legado y verifique queentry()la recupera por la vía de migración, sin imprimir el valor. Criterio de aceptación: la clave legada se lee una vez, se reescribe en el destino actual y desaparece de futuras lecturas directas al legado.test(linux): gate de bundles por tipo + evidencia W3 directa (egdev6#106
sosan commentedon Oct 22, 2026
Respuesta a la revisión (4 observaciones) + nueva evidencia de fixtures
1 · Trazabilidad — runs con URL del fork (
sosan/quorb, donde viven los workflow_dispatch):39698848192(tests): https://github.com/sosan/quorb/actions/runs/3969884819239837767677(tests, nuevo): https://github.com/sosan/quorb/actions/runs/3983776767739800266204(bundle): https://github.com/sosan/quorb/actions/runs/39800266204La rama
feat-linux-l1-106-retakeestá empujada al fork; abro el PR upstream en paralelo para que rama y evidencia sean alcanzables desdeegdev6/quorb(lo enlazo aquí en cuanto esté abierto).2 · AC2 —
migrate_legacy()pendiente, registrado: la sección 4 dedocs/linux-l1-evidence/README.mdlo deja escrito como pendiente: poblarLEGACY_SERVICE, llamargetcon la clave, verificar que migra al servicio actual y que el legacy desaparece. Sin ejercitar en la evidencia (requiere Secret Service real con clave legacy), con la propuesta de validarlo en el mismo step del smoke o en máquina real.3 · Gate por tipo, endurecido: cada
verify-deb.sh/verify-appimage.shahora haceexit 1si falta su propio tipo de bundle (antesexit 0+ «AVISO»). El orquestador solo declara OK si ambos están presentes y arrancan. Commit2f33507.4 · Password alineada: el README documenta
KEYRING_PASSy el snippet de reproducción usa$KEYRING_PASS; el workflow sigue conquorb-ci-testpassfija (keyring efímero del runner, datos de prueba).Nueva evidencia — run
39837767677, log leído directamente:parses_real_session_fixture(×2) yparses_real_session_fixture_returns_valid_token_without_http→ todosok(cargo test --locked: 249 passed, 0 failed, 1 ignored = el smoke, ejecutado aparte y tambiénok).accessToken/refreshToken/expiresAt/scopes+subscriptionType). La evidencia ahora documenta dónde vive la sesión por plataforma: macOS → Keychain (Claude Code-credentials, ya cubierto porkeychain_fallback) vs Linux/WSL/Windows → JSON.unused variableentoken_needs_refresh, commit99ae704).Límites que se mantienen (sección 6 del README): sin llamada autenticada a ningún proveedor (los fixtures no tienen credenciales), sin escritorio real, solo
ubuntu-22.04, y Quorb no leeCLAUDE_CODE_OAUTH_TOKEN(solo archivo y Keychain).sosan commentedon Oct 22, 2026
PR upstream abierto: #176 (
feat-linux-l1-106-retake→egdev6/quorb:main). Rama y evidencia ya son alcanzables desdeegdev6/quorb; incluí también el merge con elmainactual (c2f7a12) para que el diff venga limpio.fix(linux): exec bit en verify-*.sh + regenerar inventario i18n (egde…
fix(linux): regenerar inventario i18n tras merge de main (egdev6#106)
test(linux): verificación W3 en Linux real — fixtures de sesión, smok…
feat(secrets): smoke de migración del servicio legado (egdev6#106, cr…
ci(linux): correr también el smoke de migración legacy en Secret Serv…
sosan commentedon Oct 28, 2026
@egdev6 el punto 2 de tu revisión (migración del servicio legado, criterio AC2) queda ejercitado con evidencia, no solo registrado.
AC2 —
migrate_legacy()ejercitado en Secret Service realAñadido un segundo smoke
#[ignore](linux_legacy_migrate_once_then_legacy_is_gone, ensrc-tauri/src/secrets.rs) hermano del round trip existente, y el paso "Secret Service smoke" delinux-ci.ymlahora ejecuta ambos bajo el mismodbus-run-session+gnome-keyringefímero.Run:
40651008234→ ✅ success, SHA0c75fab(ramafeat-linux-l1-106-retake), log directo:linux_secret_service_round_trip ... oklinux_legacy_migrate_once_then_legacy_is_gone ... okQué verifica el smoke de migración (tu criterio exacto, sin imprimir valores):
LEGACY_SERVICE=es.egdev.ai-usage) directamente, sin pasar porset/get(que migrarían o escribirían en el destino actual) — escenario de un secreto que quedó en el servicio anterior al cambiar el identificador.get()con el destino actual vacío lo recupera por la vía de migración (se lee una vez).es.egdev.quorb), comprobado leyendo el entry actual sin volver a migrar.NoEntrytras la migración).CleanupGuardlimpia ambos servicios al terminar.Detalle de diseño que el fallo del primer intento reveló (corregido en
0c75fab):get()no migra claves con sufijo de cuenta —if key.contains('@') … return current;. La migración legacy solo aplica a claves base. El smoke usanan_session(clave base de la lista cerradaALL), con asserts que exigen clave base + pertenencia aALL.Con esto, los 4 puntos de tu última revisión quedan con evidencia: trazabilidad (runs/PR #176), AC2 (smoke de migración, ahora ejecutado), gate por tipo endurecido (
2f33507) y password documentada. Quedo pendiente de tu contraste.egdev6 commentedon Oct 28, 2026
@sosan gracias, he contrastado el código y los logs del run 40651008234, con checkout en
0c75fab. Confirmo ambos smokes ejecutados y correctos: la migración legacy queda ejercitada en Secret Service real del runner, con escritura directa en legacy, recuperación medianteget(), lectura directa del destino actual yNoEntryen legacy. Usar una clave base es correcto porque las claves con@no migran.Antes de integrar, necesitamos proteger la ejecución manual del nuevo smoke: utiliza la clave real
nan_sessiony llama aget()antes de crear elCleanupGuard. En una sesión personal con una credencial legacy, esa llamada puede migrarla y después fallar el assert, habiendo modificado ya el llavero. Además,Noneno distingue ausencia de errores de lectura. Por favor, confina el smoke a un llavero desechable con aislamiento verificable del bus y del almacenamiento, y sustituye la indicación de ejecutar los tests ignorados directamente en el escritorio por un procedimiento aislado. Mover el guard no basta, porque su limpieza también podría borrar datos reales.Acepto esta evidencia concreta de migración en CI, sin extenderla al cierre global de #106. Para entregar el cambio, hace falta una nueva PR sobre
main(el smoke y su paso de workflow no entraron en #176, ya fusionada) y actualizar el README de evidencia, que todavía marca la migración como pendiente, enlazando este run y documentando la ejecución segura.feat(secrets): smoke de migración legacy aislado + README (issue egde…
fix(secrets): aplicar fixes del rust-review al smoke de migración (eg…
sosan commentedon Oct 28, 2026
@egdev6 aplicados los tres puntos de tu revisión sobre el smoke de migración. La entrega irá en una PR nueva sobre
main(el smoke no entró en #176), que abro ahora y enlazo aquí.Arreglos respondiendo a tu revisión (2026-10-28)
1. Confinamiento a llavero desechable con aislamiento verificable — el smoke
linux_legacy_migrate_once_then_legacy_is_goneahora:QUORB_SECRETS_SMOKE_ISOLATED=1y falla conassert!antes de tocar nada (ni siquiera crea elEntry) si no está definida. La variable solo se exporta dentro deldbus-run-session+gnome-keyringefímero en el paso "Secret Service smoke" delinux-ci.yml(bus aislado + almacenamiento efímero del runner). Fuera de ahí, el smoke se niega a ejecutarse: nunca en un escritorio con llavero real.CleanupGuardcreado antes de cualquier lectura y suDropre-verifica la variable de aislamiento antes de borrar (defensa en profundidad tras el rust-review): si la precondición falló en un entorno con llavero real (var filtrada), la limpieza no borra nada.NoEntryde error de lectura (víaentry().get_password(), noget()) —Noneya no puede confundir ausencia con fallo.linux-ci.yml, documentado en el README de evidencia.2. README de evidencia actualizado — la migración pasa de "pendiente" a "ejercitada" (run
40651008234), con la ejecución segura descrita y descartada la ejecución manual en escritorio.3. Revisión adicional (rust-reviewer) — 1 MEDIUM + 2 LOW, todos corregidos:
Dropdel guard (arriba).assert_eq!si algo falla, y es constante de prueba no secreta).Aceptando tu criterio de no extender esto al cierre global de #106: la PR nueva aporta solo el smoke + workflow + README.
sosan commentedon Oct 28, 2026
PR abierta con los arreglos de la revisión: #203 — smoke de migración legacy aislado + README de evidencia, sobre
mainactual (0.8.0), diff confinado a 3 archivos.feat(secrets): smoke de migración legacy aislado + README evidencia (#…