Killer
Explorador de archivos desktop (Tauri + Solid + Go + Rust)
Aplicación desktop con Tauri 2 + SolidJS donde el filesystem real vive en Go (sidecar) y Rust actúa como puente IPC por stdio con JSON line-delimited. Incluye ListDir, ReadFile, Tree, Stat, Write y cache reactivo con fallback a mock en modo web. Documentada en README, ARCHITECTURE.md y go/README.md.
Contexto y propósito
Killer es mi proyecto más “sistemas”: un explorador de archivos desktop con Tauri 2 + SolidJS donde el filesystem real está en Go y Rust solo es el puente IPC. Nació del template Tauri + Solid + TS y evolucionó a arquitectura tri-lenguaje documentada en ARCHITECTURE.md y go/README.md.
Para un reclutador: demuestra IPC real, manejo de procesos hijo, tipos compartidos entre 3 lenguajes y estrategia de fallback web vs desktop.
Propósito declarado: Explorar el filesystem real desde una app desktop ligera, separando responsabilidades: Go para I/O, Rust para IPC seguro y SolidJS para UI reactiva.
Arquitectura
SolidJS --invoke--> Rust (Tauri go_bridge) --stdio JSON--> Go (internal/fs). Go expone ping/list_dir/read_file/tree/stat/write/delete; Rust lo spawnea como hijo y expone comandos fs_*; frontend usa fsService + fsCache con fallback a mock.
┌──────────────┐ invoke("fs_*") ┌──────────────┐ stdin JSON ┌──────────────┐
│ SolidJS (TS) │ ────────────────▶ │ Rust (Tauri) │ ───────────▶ │ Go (fs.SVC) │
│ fsService + │ ◀──────────────── │ go_bridge/ │ ◀─────────── │ internal/fs + │
│ fsCache │ JSON Response │ sidecar.rs │ stdout JSON │ internal/ipc │
└──────────────┘ └──────────────┘ └──────────────┘
└────────────── TÚNEL IPC stdio (sin HTTP ni sockets) ──────────────┘
stderr solo logs humanos [go]/[rust]. - Go (killer/go): Service stateless con ListDir, ReadFile (límite 10 MB, detección binarios), Tree recursivo con MaxDepth, Stat, WriteFile, Exists, EnsureDir, Delete. Tipos Directory/FileDeck/File/Stat idénticos a src/types/fs.ts.
- Rust (src-tauri/src/go_bridge): GoBridge spawnea el binario (busca en binaries/killer-go*, ../go/killer-go, /tmp/killer-go o KILLER_GO_BIN), mantiene Child + pipes con Mutex para llamadas secuenciales; comandos fs_ping/list_dir/read_file/stat/tree/write/delete son proxies delgados. build.rs compila Go automáticamente.
- Frontend (src/): fsService (invoke + isTauri), fileService híbrido real/mock, fsCache reactivo, mock en cache/file.ts (archs) para vite dev. App.tsx carga mock y luego reemplaza con árbol real.
Stack detallado
| Capa | Tecnología | Detalle |
|---|---|---|
| UI | SolidJS 1.9 + Vite 6 | Signals; lucide-react icons |
| Desktop | Tauri 2 + Tokio + UUID | Commands, setup con manage(), opener plugin |
| Filesystem | Go 1.22+ (os/filepath) | Sin dependencias pesadas; JSON line-delimited |
| Contrato | types/fs.ts ↔ types.go | Mismo JSON; trailing slash en dirs |
| Build | pnpm + cargo + go build | Triple linux-gnu para sidecar |
Endpoints principales
| Método | Ruta | Qué hace | Auth |
|---|---|---|---|
invoke | fs_ping | Health del túnel | Tauri |
invoke | fs_list_dir {path} | Un nivel de carpeta | Tauri |
invoke | fs_read_file {path} | Contenido o placeholder binario | Tauri |
invoke | fs_tree {root,maxDepth,showHidden} | Árbol recursivo limitado | Tauri |
invoke | fs_stat · fs_write_file · fs_delete | Metadata y escritura | Tauri |
Datos y conexiones
Request Rust→Go (stdin, una línea):
{"id":"uuid","method":"list_dir","params":{"path":"/src"}}
Response Go→Rust (stdout, una línea):
{"id":"uuid","result":{"type":"dir","path":"/src/","name":"src",...},"error":null} - Sin sockets ni HTTP: el sidecar es hijo del proceso Tauri y muere con él (SIGINT/SIGTERM).
- Subdirs inaccesibles se saltean sin tumbar el tree completo.
Despliegue
Dónde: Desktop Linux/Win/Mac (Tauri bundle) + modo web mock en Vite
- go vet ./... && go build -o src-tauri/binaries/killer-go ./go
- cp a killer-go-x86_64-unknown-linux-gnu para el bundler
- pnpm tauri dev (real) vs pnpm dev (mock web en :1420)
Metodología y decisiones
- Contrato primero: tipos TS y Go se escriben a la par y se prueban con echo JSON por pipe antes de tocar UI.
- Fallback como feature: la app siempre arranca (web o sin Go) y degrada a mock documentado.
- Docs vivas: README + ARCHITECTURE.md + go/README.md con comandos copiables.
Retos y soluciones
Qué aprendí
- IPC por stdio entre Rust y Go.
- Empaquetado Tauri con binarios externos.
- Diseño tolerante a fallos con mocks.
Ejecutar en local
cd killer && pnpm install && pnpm dev (mock web → :1420)
Con Go real: cd killer/go && go build -o ../src-tauri/binaries/killer-go . && cp a triple linux-gnu
cd killer && pnpm tauri dev
Probar Go solo: echo '{"id":"1","method":"ping","params":{}}' | /tmp/killer-goPróximos pasos
- Búsqueda full-text y watcher de cambios.
- Permisos y operaciones batch.
- Firmado y auto-update Tauri.