El cliente Git que te ayuda a entender qué va a pasar antes de ejecutar — grafo, staging y red de seguridad, en el navegador, en tu máquina, sin suscripción.
Abyssan es un cliente gráfico auto-hospedable. Un backend Node opera los repositorios locales;
una SPA React muestra el DAG, prepara el commit y —en el horizonte Identidad— enseña el efecto de un merge antes de ejecutarlo.
Inicio rápido · Arquitectura · Capacidades · Documentación · Licencia
Git no se paga por “hacer git”. Los clientes gráficos maduros ya cubren el ratón. Abyssan se juega otra tesis: Git visual + seguridad + comprensión.
| Pilar | Problema | Promesa de Abyssan |
|---|---|---|
| Ver historia | El log lineal no explica merges, HEAD ni el remoto de un vistazo. | Un DAG con lanes, refs y contexto. |
| Preparar el commit | git add -p es potente y frágil. |
Staging visual, diff inmediato, commit consciente. |
| Entender antes | Un botón Merge no dice qué va a pasar. | Preview: mini-DAG, conflictos estimados, cancelar sin costo. |
| No romper el repo | Un reset o un merge mal leído cuesta horas. | Confirmación contextual, journal de undo, auditoría. |
No es un host de repositorios, un GitHub web, ni un clon de Boards / Insights / Teams. No es un producto de IA. Es un cliente Git local que enseña el efecto de una operación antes de ejecutarla.
Norte del producto. Escalera ya recorrida: local → power → forjas. El siguiente “listo” es Identidad: preview, undo serio, modo aprendizaje y grafo que explica. Después: worktrees y forja como contexto de rama. Distro y plugins más tarde.
Grafo DAG del repo abyssan-demo generado por pnpm demo:repo. Layout de tres columnas, tema oscuro, escritorio-first (≥ 1280 px). Si ves un placeholder con «Captura real pendiente», genera la real con los pasos de «Capturas reales» más abajo.
Modal de confirmación con preview no mutante de merge sobre el mismo repo demo: antes de ejecutar, Abyssan muestra la rama, la dirección (fix/choca-con-main → main), los riesgos (incluido el conflicto en src/core/tenant.js) y los cambios que entrarían.
| Zona | Rol |
|---|---|
| Barra superior | Selector de repositorio, sync (fetch / pull / push) y acciones globales. |
| Sidebar | Ramas locales y remotas, tags, checkout y creación de referencias. |
| Grafo | Historia como DAG: commits, padres múltiples, etiquetas HEAD / rama / tag. |
| Staging + diff | Unstaged / staged, visor de cambios y formulario de commit. |
Superficie verificada en el código (no inventario de intenciones). Fases 0–3 cerradas, Fase 4 Identidad en curso. Cada fila declara su estado: Hecho (presente en API y UI), Parcial (parte del flujo cableado, parte pendiente) o Pendiente (no está).
| Dominio | Capacidad | Estado |
|---|---|---|
Repositorios bajo PROJECTS_ROOT |
Listado, clone, init; validación con realpath |
Hecho |
Repositorios bajo PROJECTS_ROOT |
Progreso async de clone/fetch/push/pull vía OperationManager + WS |
Hecho |
Repositorios bajo PROJECTS_ROOT |
Multi-tab de repos | Pendiente (Fase 5) |
| Grafo DAG | Virtualizado, lanes, búsqueda por texto, HEAD con ahead/behind | Hecho |
| Grafo DAG | merge-base, comparar A…B (BranchCompareModal) |
Hecho |
| Grafo DAG | Highlight de camino en modo aprendizaje (semántica cableada) | Parcial |
| Stage / commit | Archivo completo, stage-all, amend con detección de commit publicado |
Hecho |
| Stage / commit | Stage por hunk y por línea | Pendiente (Fase 4.x) |
| Diff | Shiki + unified / split, copiar diff, comparación por commit o rango | Hecho |
| Diff | «Ver cambios» desde el preview | Pendiente |
| Ramas y tags | CRUD, rename, fetch, pull (merge/rebase), delete seguro, tag en commit | Hecho |
| Merge | Preview no mutante (clon temporal) con detección de conflictos | Hecho |
| Merge | Ejecutar, abort, continuar | Hecho |
| Rebase | Pull en modo rebase | Parcial — rebase interactivo/visual pendiente |
| Cherry-pick / Revert / Reset | Preview + confirmación tipada en reset hard sucio | Hecho |
| Undo | Journal persistente (JournalOperaciones), timeline en UI, recovery ref refs/abyssan/recovery/ para reset |
Hecho |
| Aprendizaje | Explain Mode con plantillas (PanelExplicacion, sin IA) |
Hecho |
| Seguridad | validarRutaRepositorio con realpath, token de instancia, rate limit, CORS/Origin, auditoría JSONL |
Hecho |
| Forjas | OAuth GitHub/GitLab, token cifrado en disco, listar/crear PR/MR, PAT para push HTTPS en Docker | Hecho |
| Blame | — | Pendiente |
| Worktrees | — | Pendiente (Fase 5) |
| Preview rebase / force-push | — | Pendiente |
Operaciones Git disponibles en API hoy (verificables en GitRoutes y GitUseCases): status, log, diff, stage y unstage (archivo completo), commit, amend, checkout, branch, rename-branch, delete-branch, tag, stash, merge, cherry-pick, revert, reset, fetch, push, pull (merge/rebase), remotos (add/remove/list), conflictos (parseo 3-way), preview no mutante de merge/reset/cherry-pick/revert, forjas (OAuth + PR/MR). No hay endpoints de blame, rebase interactivo ni stage por hunk/línea.
Monorepo pnpm workspaces. Sin base de datos en v1: estado en memoria, localStorage en el cliente y archivos de configuración locales.
┌─────────────────────────────────────────────────────────────────┐
│ apps/web · :5174 │
│ React 19 · TypeScript · Vite 6 · Tailwind 4 │
│ Lucide · date-fns · WebSocket client │
└────────────────────────────┬────────────────────────────────────┘
│ REST /api/git/* WS REPO_CHANGED
┌────────────────────────────▼────────────────────────────────────┐
│ apps/server · :3001 │
│ Node.js · Express · TypeScript · simple-git │
│ ws · chokidar · validación de rutas │
└────────────────────────────┬────────────────────────────────────┘
│
▼
filesystem ⊆ PROJECTS_ROOT
| Capa | Tecnología | Notas |
|---|---|---|
| Presentación | React 19, Vite 6, Tailwind CSS 4 | Tema oscuro #0f111a / paneles #181c2d / acento #10b981 |
| Aplicación (web) | Hooks + HttpGitApi |
Un solo camino HTTP hacia el backend |
| Dominio (server) | Entidades Git + casos de uso | DDD ligero: sin Nest, sin GraphQL, sin DB |
| Infraestructura Git | simple-git |
Prohibido child_process.exec genérico |
| Tiempo real | ws + chokidar |
El socket notifica cambios; no envía contenido de archivos |
| Calidad | Vitest, oxlint | pnpm test · pnpm lint · pnpm build |
| Empaque | Docker Compose | SPA + API; volumen de proyectos configurable |
Gestor de paquetes: solo pnpm. No usar npm, npx (en el runtime del proyecto) ni otros lockfiles.
El backend no es un wrapper de comandos. Las mutaciones Git atraviesan un único camino:
HTTP → GitController → GitUseCases → SimpleGitAdapter
flowchart TB
subgraph web["apps/web"]
UI["UI: grafo, staging, diff, ramas"]
Hook["useGitRepository"]
API["HttpGitApi"]
UI --> Hook --> API
end
subgraph server["apps/server"]
Rutas["GitRoutes /api/git"]
Ctrl["GitController"]
UC["GitUseCases"]
Adapter["SimpleGitAdapter"]
Watch["ChokidarWatcherAdapter"]
Val["validarRutaRepositorio"]
Log["InMemoryCommandLog"]
Rutas --> Ctrl --> UC --> Adapter
UC --> Log
Watch -.-> Rutas
Val -.-> Ctrl
Val -.-> Watch
end
API -->|"REST JSON"| Rutas
API -->|"WATCH_REPO"| Watch
Adapter --> FS[("Repos ⊆ PROJECTS_ROOT")]
Watch --> FS
Envelope único. El cliente (HttpGitApi) habla el mismo contrato que el servidor.
{
"exito": true,
"mensaje": "Commit creado",
"datos": {},
"meta": {}
}Healthcheck: GET /health → { "status": "ok", ... }.
Abyssan/
├── apps/
│ ├── web/ # SPA React
│ │ └── src/
│ │ ├── application/ # hooks de orquestación
│ │ ├── domain/ # modelos
│ │ ├── infrastructure/ # HttpGitApi, WebSocket
│ │ └── components/ # UI de producto
│ └── server/ # API Express
│ └── src/
│ ├── domain/
│ ├── application/
│ ├── infrastructure/ # git · seguridad · watcher · logging
│ └── interfaces/http
├── docs/ # PRD, plan, assets
├── docker-compose.yml
└── pnpm-workspace.yaml
| Herramienta | Versión |
|---|---|
| Node.js | 22.13 LTS o superior |
| pnpm | 11.25.0 (vía Corepack) |
| Git | En el PATH del sistema |
| Docker | Opcional, para Compose |
package.jsondeclaraengines.node: ">=22.13.0"yengines.pnpm: ">=11.25.0"para coincidir con lo que exige elpackageManagerfijado. CI y las imágenes Docker usannode:22-alpinedesde Fase 3.
pnpm installCopia .env.example a .env en la raíz y define la raíz de repositorios. Abyssan no opera fuera de ese directorio.
PROJECTS_ROOT=C:\Users\<usuario>\proyectos
PORT=3001
BIND_HOST=127.0.0.1
NODE_ENV=development
VITE_API_URL=http://localhost:3001
VITE_WS_URL=ws://localhost:3001| Variable | Rol |
|---|---|
PROJECTS_ROOT |
Única raíz permitida para listar y mutar repos |
PORT |
HTTP y WebSocket del servidor (3001) |
BIND_HOST |
Default 127.0.0.1. Si no es loopback, hay que definir token |
ABYSSAN_API_TOKEN |
Token de instancia (obligatorio fuera de localhost). No se embebe en Vite |
VITE_API_URL |
Origen REST del frontend |
VITE_WS_URL |
Origen WebSocket del frontend |
En Linux o dentro de Docker: PROJECTS_ROOT=/workspace/proyectos.
Un solo comando levanta API y SPA en paralelo:
pnpm dev # API :3001 + web :5174Si necesitas solo uno de los dos:
pnpm dev:server # http://localhost:3001
pnpm dev:web # http://localhost:5174Abre http://localhost:5174, elige un repositorio bajo PROJECTS_ROOT y trabaja sobre el grafo.
| Script | Qué hace |
|---|---|
pnpm dev |
API + SPA en paralelo (recomendado) |
pnpm dev:server |
Solo API + WebSocket (tsx watch) |
pnpm dev:web |
Solo Vite HMR |
pnpm build |
Compila server y web |
pnpm typecheck |
tsc --noEmit en server y web |
pnpm lint |
oxlint |
pnpm test |
Vitest |
pnpm test:seguridad |
Subconjunto de perímetro de seguridad |
pnpm demo:repo |
Crea abyssan-demo bajo PROJECTS_ROOT (ver abajo) |
Para evaluar Abyssan sin un repo real a mano, hay un script que genera uno bajo PROJECTS_ROOT:
pnpm demo:repo # crea abyssan-demo (falla si ya existe)
pnpm demo:repo mi-demo # elige otro nombre
pnpm demo:repo abyssan-demo --force # sobreescribe el existenteEl repo trae 20 commits en main, rama feature/pagos fusionada con merge-commit, rama fix/choca-con-main con un commit que choca con el final de main, dos tags anotados (v0.1.0 sobre el merge, v0.2.0 sobre HEAD) y, al terminar, un archivo staged y otro unstaged para que el panel de Staging no esté vacío. Útil para capturas, QA manual y para probar el preview de merge en un conflicto reproducible.
El README muestra placeholders SVG bajo assets/capturas/ para no publicar en el repo imágenes derivadas de proyectos personales. Para generar las capturas reales usando el repo demo:
# 1. Repo de demo bajo PROJECTS_ROOT
pnpm demo:repo
# 2. API + SPA en paralelo (en otra terminal)
pnpm dev
# 3. Chromium para Playwright (primera vez; .npmrc trae ignore-scripts=true)
pnpm --filter @abyssan/web exec playwright install chromium
# 4. Capturas (headless, no muta el repo: el preview es no mutante)
pnpm --filter @abyssan/web capturasEl script apps/web/scripts/capturar-pantallas.mjs abre Chromium a 1280×800, selecciona el repo demo, captura el grafo y después abre el preview de merge de fix/choca-con-main → main. Al terminar deja dos PNG en assets/capturas/grafo.png y assets/capturas/preview-merge.png. Si quieres que el README apunte a las PNG en vez de a los SVG de placeholder, cambia la extensión en los <img> de la sección Interfaz.
Variables opcionales:
| Variable | Default | Qué cambia |
|---|---|---|
ABYSSAN_URL |
http://localhost:5174 |
URL base de la SPA |
DEMO_REPO |
abyssan-demo |
Nombre del repo bajo PROJECTS_ROOT |
DEMO_RAMA_ORIGEN |
fix/choca-con-main |
Rama que se fusiona en el preview |
DEMO_RAMA_BASE |
main |
Rama destino del preview |
DEMO_TIMEOUT_MS |
15000 |
Timeout por paso en milisegundos |
| Atajo | Acción |
|---|---|
Ctrl+Enter |
Commit (con el formulario enfocado) |
Ctrl+Shift+A |
Stage all |
Ctrl+Shift+P |
Paleta mínima (fetch / pull / push / commit / PRs) |
Deshacer está en el header (hoy: última operación). El horizonte Identidad es un journal persistente + timeline, no una pila Ctrl+Z ciega. El reflog corto vive en el drawer de consola.
No se obtiene de un servicio externo: lo inventas tú (una contraseña larga y aleatoria). El servidor la exige cuando no escucha solo en localhost; Docker siempre la exige porque dentro del contenedor BIND_HOST=0.0.0.0.
Define el token solo en el servidor. La SPA abre una sesión (POST /api/sesion) y no embebe el secreto:
| Variable | Quién la usa |
|---|---|
ABYSSAN_API_TOKEN |
Servidor (valida Bearer permanente, o emite una sesión de 12 h) |
Generar un secreto (ejemplo):
# Windows (PowerShell)
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))# Linux / macOS / Git Bash
openssl rand -base64 32Pégalo en .env:
ABYSSAN_API_TOKEN=pega-aqui-el-secreto-generadoEn Docker/LAN la UI pedirá ese valor una vez. No uses VITE_ABYSSAN_API_TOKEN: todo VITE_* termina en el bundle.
Con pnpm dev en tu máquina (BIND_HOST=127.0.0.1) no suele hacer falta el token. Con docker compose up sí.
Desde la raíz del repositorio:
docker compose down
docker compose up --buildSolo el frontend (más rápido si el API ya está bien):
docker compose up --build -d webCuando ya esté reconstruido y solo quieras reiniciar procesos (sin instalar nada nuevo):
docker compose restart web
docker compose restart serverO por nombre de contenedor:
docker restart abyssan-web
docker restart abyssan-serverVer que existen:
docker compose ps| Servicio | URL |
|---|---|
| Interfaz | http://127.0.0.1:5174 |
| API | http://127.0.0.1:3001 |
El contenedor del servidor monta proyectos en PROJECTS_ROOT=/workspace/proyectos en lectura-escritura (ABYSSAN_PROJECTS_HOST, por defecto el checkout). Compose publica los puertos solo en localhost del host. Como el proceso dentro del contenedor escucha 0.0.0.0, hace falta ABYSSAN_API_TOKEN en .env (sin default). No re-publiques los puertos sin 127.0.0.1.
Abyssan ejecuta Git sobre el filesystem del host. El modelo de amenaza de Daily Driver es deliberadamente estrecho.
| Control | Comportamiento |
|---|---|
| Sandbox de rutas | Todo repoPath pasa por validarRutaRepositorio. Fuera de PROJECTS_ROOT → 403. |
| Superficie Git | Solo operaciones vía simple-git. Sin shell arbitrario. |
| Operaciones destructivas | Confirmación en UI y confirmado: true en el API (reset hard, discard, borrar rama, abortar merge). |
| CORS / Origin | Solo la SPA en :5174 (o CORS_ORIGINS). Mutación con Origin ajeno → 403. |
| WebSocket | Handshake AUTH (sin token en la query). Valida WATCH_REPO y Origin. Progreso acotado al repo vigilado. No emite diffs ni secretos. |
| Credenciales | No se versionan. SSH usa el agent del sistema. |
| Exposición de red | Default localhost. Vite en 127.0.0.1. Si BIND_HOST no es loopback, ABYSSAN_API_TOKEN es obligatorio. |
No copies .env al repositorio. No registres diffs completos en logs de producción.
Estimaciones en semanas-persona de trabajo enfocado, no en calendario. Fases 0–3 cerradas.
0–3 hechas Fase 4 Identidad Fase 5 Fase 6
higiene · Daily Driver ──► preview · undo serio ──► worktrees ──► distro
power · forjas explain · grafo PR contexto auth / Tauri
▲
│
SIGUIENTE LISTO
(entender antes de ejecutar; no más botones)
| Fase | Entregable | Estado |
|---|---|---|
| 0 Higiene | Un env, un cliente HTTP, tests verdes, Docker RW, identidad Abyssan | Hecha |
| 1 Daily Driver | Clone/init, discard, 3-way, ramas, fetch, undo mínimo, grafo virtualizado | Hecha |
| 2 Power | Stage por hunk/línea, command palette, tabs, blame, rebase visual | Parcial — hunk/línea, blame, tabs y rebase visual siguen pendientes |
| 3 Forjas | OAuth GitHub/GitLab y cola de pull/merge requests | Hecha |
| 4 Identidad | Preview, journal de undo, Explain Mode, grafo que enseña, seguridad | Ahora — preview, journal, explain y seguridad en código; highlight de grafo parcial |
| 5 Superficie | Worktrees bajo PROJECTS_ROOT; PR/MR como contexto de rama |
Después |
| 6 Plataforma | Compose prod; usuarios/roles si LAN real; Tauri opcional; plugins | Después |
Fuera de este horizonte: IA, LFS, submódulos, GPG, GitKraken Cloud. Plugins solo en Fase 6 con sandbox.
- Un adaptador Git.
SimpleGitAdapter→GitUseCases→GitController. Sin servicios paralelos. - Una raíz.
PROJECTS_ROOTes la única frontera del filesystem. - pnpm exclusivo. Lockfile
pnpm-lock.yaml. - Español de producto. UI, mensajes y nombres de negocio en español; términos Git de industria se mantienen cuando son estándar.
- Confirmación antes de destruir. Hard reset, discard y force-with-lease no son un
window.confirmaccidental. - Honestidad de estado. Daily Driver + power + forjas ya corren. El siguiente listo no es “más botones”: es entender la operación antes de ejecutarla. El preview no miente (informe Git, no una VM).
Los packages internos usan el scope @abyssan/*. El nombre comercial del producto es Abyssan.
La documentación técnica versionada está en docs/wiki/. La política de reporte de vulnerabilidades está en SECURITY.md.
- Home de la documentación
- Instalación y configuración
- Arquitectura
- Referencia de API
- Seguridad (política de reporte)
- Seguridad técnica
- Contribución
- Roadmap
El detalle de producto y fases sigue en docs/PLAN-TRABAJO.md. No trates el roadmap como inventario de funciones ya terminadas.
Distribuido bajo MIT. Uso, copia, modificación y redistribución libres, con atribución.
Abyssan — historia legible, operaciones comprendidas, repos intactos.