A family of models designed to get accurate and presice locations from normal pictures
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Reescritura completa de Lumi v1. Cliente de escritorio Tauri + servidor de inferencia autoalojado. Contexto completo en PRODUCT.md y DESIGN.md. Este README cubre solo el subsistema 1: instalador CLI y vinculación. Spec en docs/superpowers/specs/2026-08-03-instalador-y-pairing-design.md, plan en docs/superpowers/plans/2026-08-03-instalador-y-pairing.md. Si vas a probar desde WSL (recomendado)查看完整 README ↗
Reescritura completa de Lumi v1. Cliente de escritorio Tauri + servidor de inferencia autoalojado. Contexto completo en PRODUCT.md y DESIGN.md.
Este README cubre solo el subsistema 1: instalador CLI y vinculación. Spec en
docs/superpowers/specs/2026-08-03-instalador-y-pairing-design.md,
plan en docs/superpowers/plans/2026-08-03-instalador-y-pairing.md.
Todo lo de abajo asume Windows/Git Bash y por eso rodea systemd con un atajo de OpenSSL
(sección 1). En WSL con systemd habilitado no necesitas ese atajo: lumi install está
escrito para Linux y debería completar el flujo real.
# dentro de WSL, en la copia del repo (ojo: /mnt/e/... es lento; clona o copia
# el repo dentro del filesystem de WSL si el build se nota lento)
cd ~/lumi-station # o donde tengas el repo en WSL
cargo build
sudo target/debug/lumi install
Esto pregunta interactivamente por el modo (nativo/docker) y por la clave maestra
(automática/sellada), con el defecto recomendado marcado con › y aceptable con solo
pulsar Enter. Para saltarte las preguntas y que tome directamente los defectos
recomendados:
sudo target/debug/lumi install --yes
Esperado en ambos casos: las secciones de entorno, hardware, modo, capacidades y clave
maestra se imprimen (con ── título ──…, igual que el mockup), systemctl is-active lumid
responde active, y al final se imprime la clave de vinculación completa (lumi1_...).
Si tu WSL no tiene systemd (cat /proc/1/comm no dice systemd), habilítalo
en /etc/wsl.conf con [boot]\nsystemd=true y reinicia WSL (wsl --shutdown desde
PowerShell), o usa el atajo con OpenSSL de la sección 1 igual que en Windows puro.
Con lumi install completado de verdad, sáltate la sección 1 de abajo entera: ya tienes
.dev-data... salvo que ahora la ruta real sea /var/lib/lumi (la que usa el instalador en
Linux, no .dev-data). Ajusta LUMI_DATA=/var/lib/lumi en los comandos siguientes, o
copia el certificado a un .dev-data local si prefieres no tocar rutas de sistema mientras
pruebas.
lumi-proto: formato de clave de vinculación, huella de certificado, cifrado, matriz de
capacidades, tipos de la API.lumid: el daemon. TLS con certificado autofirmado, canje de clave, creación del primer
administrador, sesiones, clave maestra (automática y sellada), runner de tareas con log
persistente, telemetría por SSE.lumi-cli (binario lumi): detección de entorno/hardware, instalación (lumi install),
reemisión de clave (lumi key reissue), diagnóstico (lumi status).client: cliente Tauri. Fondo de planeta de la v1, wizard, verificación de huella TLS en
el lado Rust, franja de telemetría en vivo, los cuatro estados anómalos (reiniciando,
error, sellado, sin conexión).El daemon y el CLI están escritos para Linux (systemd, ufw, /etc/os-release,
/bin/sh). En Windows:
lumid arranca y sirve la API igual — TLS, SQLite, canje, tareas y telemetría no
dependen de nada de Linux. Es lo que puedes probar de verdad.lumi install no completa: falla adrede en el chequeo de systemd, con el mensaje
"este host no usa systemd...". Es el comportamiento correcto del guard, no un bug./bin/sh, que no
existe en Windows. El log recogerá el error de spawn — eso en sí prueba que el mecanismo
de log persistente funciona, aunque el comando sea Linux-only.Para probar el flujo de instalación de verdad (lumi install completo) necesitas una VM o
máquina Linux con systemd. Para probar el resto —API, cripto, wizard, huella, telemetría,
estados anómalos— Windows basta.
rustup default stable)curl (viene con Git Bash / WSL)python -m json.tool para leer JSON con formato, o jqlumi installlumi install comprueba systemd antes de generar nada (crates/lumi-cli/src/install.rs:39),
así que en Windows falla sin llegar a escribir el certificado. Si .dev-data/ ya tiene
cert.der y key.pem de una sesión anterior, sáltate este paso. Si no, genera un
certificado de prueba equivalente con OpenSSL (viene con Git Bash):
mkdir -p .dev-data
MSYS2_ARG_CONV_EXCL="/CN=" openssl req -x509 -newkey ed25519 \
-keyout .dev-data/key.pem -out .dev-data/cert.pem -days 3650 -nodes -subj "/CN=lumi"
openssl x509 -in .dev-data/cert.pem -outform der -out .dev-data/cert.der
(MSYS2_ARG_CONV_EXCL evita que Git Bash reescriba /CN=lumi como una ruta de Windows; sin
eso, openssl falla con "subject name is expected to be in the format...".)
lumid solo lee cert.der (para la huella) y key.pem (para TLS); no le importa cómo se
generaron.
LUMI_DATA=.dev-data cargo run -p lumid
Primera vez: compila (15-30 s). Deja la terminal abierta; verás:
lumid escuchando en https://0.0.0.0:7717
En otra terminal:
# Saludo público — sin autenticación, funciona también bloqueado
curl -sk https://localhost:7717/v1/hello | python -m json.tool
Esperado: JSON con "state":"unclaimed", tu huella de certificado (fingerprint), la
matriz de capacidades con motivos, y tus GPUs reales si tienes NVIDIA (usa NVML, funciona en
Windows).
# Telemetría en vivo (Ctrl+C para cortar)
curl -skN https://localhost:7717/v1/telemetry
Esperado: una línea JSON por segundo con uso de GPU/CPU/disco. Sigue funcionando aunque el servidor esté "sellado" — es deliberado, para poder monitorizar un servidor bloqueado.
La clave de vinculación tiene el formato lumi1_<host:puerto>_<huella>_<secreto>. Si no
tienes una impresa por lumi install, mira la tabla pair_key de .dev-data/lumi.db o
pide que se reemita:
sqlite3 .dev-data/lumi.db "SELECT secret_phc FROM pair_key;"
(El secreto en claro solo se imprime una vez, en la instalación real. Si perdiste esa
sesión, borra .dev-data/lumi.db y reinstala, o inserta manualmente una fila de prueba con
un secreto conocido usando lumi-proto.)
Con el secreto en $SECRET:
TOK=$(curl -sk -X POST https://localhost:7717/v1/claim \
-H 'content-type: application/json' -d "{\"secret\":\"$SECRET\"}" \
| python -c 'import sys,json;print(json.load(sys.stdin)["bootstrap_token"])')
curl -sk -X POST https://localhost:7717/v1/admin \
-H 'content-type: application/json' \
-d "{\"bootstrap_token\":\"$TOK\",\"username\":\"tu_usuario\",\"password\":\"una contraseña de 12+ caracteres\"}" \
-w '%{http_code}\n'
Esperado: 201. Repetir el canje con el mismo secreto debe dar 401 con
"la clave ya se canjeó".
T=$(curl -sk -X POST https://localhost:7717/v1/auth/login \
-H 'content-type: application/json' \
-d '{"username":"tu_usuario","password":"tu contraseña"}' \
| python -c 'import sys,json;print(json.load(sys.stdin)["token"])')
curl -sk -X POST https://localhost:7717/v1/tasks \
-H "authorization: Bearer $T" -H 'content-type: application/json' \
-d '{"kind":"database"}'
Guarda el id de la respuesta y sigue el log:
curl -skN "https://localhost:7717/v1/tasks/<ID>/log?from=0"
"kind":"database" funciona en Windows (es solo un echo). "kind":"inference_runtime"
fallará porque lanza /bin/sh: verás el error de spawn en el log, lo que confirma que el
runner en sí funciona.
Escotilla de emergencia y tarjeta pública, desde el host:
sudo lumi card # la tarjeta pública que se reparte al equipo
sudo lumi admin requests # solicitudes pendientes
sudo lumi admin reset-password ana # contraseña temporal + cambio obligatorio
sudo lumi admin unblock ana
sudo lumi admin accept-requests off # cierra la aceptación de nuevas solicitudes
Al iniciar sesión se elige un proyecto antes de entrar, como en Burp. Dentro hay casos, y dentro de un caso sus imágenes y sus análisis. Nada de un proyecto se ve desde otro. Los proyectos se crean privados; el dueño puede invitar a otros investigadores.
El motor de inferencia no existe todavía (subsistema 5): los análisis se crean en estado
pendiente y ahí se quedan. Es a propósito — son el trabajo que la cola del subsistema 4
encontrará esperando cuando arranque.
El mapa lo sirve el propio daemon: un administrador configura el proveedor (Mapbox u OpenStreetMap) y su clave, y el servidor hace de proxy de teselas con caché en disco. La clave nunca baja al cliente, y el proveedor ve una IP en vez de una por investigador.
El GPS que la cámara escribió en el EXIF se lee y se muestra aparte de lo inferido, en ámbar. El archivo original nunca se toca.
Configurar el mapa: panel de administración, sección Mapa. La caché de teselas vive en
{DATA}/tiles y se vacía borrando ese directorio.
Los análisis ya no se quedan en pendiente. Un trabajador por dispositivo —una GPU, o la CPU
si no hay ninguna— arranca con el daemon y se mantiene vivo entre trabajos con los pesos
cargados. La cola los reparte con esta política, en este orden: primero descarta lo que no
puede correr (dueño bloqueado, dueño desconectado sin background_jobs, dueño en su
max_concurrent), y luego ordena lo que queda por conectado antes que segundo plano,
queue_priority y llegada.
Lo que ya corre nunca se cancela. DELETE /v1/analyses/:id cancela lo pendiente y
devuelve 409 sobre lo que está en una GPU; lo mismo DELETE /v1/images/:id con una imagen que
se está analizando.
| Ruta | Quién | Qué |
|---|---|---|
GET /v1/queue/events | cualquier sesión | SSE con los cambios de sus análisis. Mientras está abierto, cuenta como conectado |
GET /v1/queue | administrador | Pendientes, en curso y estado de cada trabajador |
El motor todavía no existe (subsistema 5): workers/lumi_worker.py devuelve una
coordenada fija. Es el trabajador de referencia y también la documentación ejecutable del
contrato — quien escriba otro, lo lee.
Con lumid parado:
head -c 16 /dev/urandom > .dev-data/master.salt # o: openssl rand -out .dev-data/master.salt 16
LUMI_DATA=.dev-data cargo run -p lumid
curl -sk https://localhost:7717/v1/hello | grep -o '"locked":[a-z]*' # locked:true
curl -sk -X POST https://localhost:7717/v1/unseal \
-H 'content-type: application/json' -d '{"passphrase":"la frase que quieras"}' \
-w '%{http_code}\n' # 204
curl -sk https://localhost:7717/v1/hello | grep -o '"locked":[a-z]*' # locked:false
Reinicia lumid y prueba con otra frase: debe dar 401 con "frase incorrecta".
No hace falta abrir la ventana nativa de Tauri para ver la interfaz: el mismo código React corre en un navegador normal contra Vite.
cd client
npm install
npm run dev
Abre http://localhost:5173.
Esto te enseña el wizard, el fondo de planeta y el stepper, pero no los comandos
invoke() de Tauri (verificación de huella, telemetría en vivo, log de tareas) —
esos solo existen dentro del proceso nativo de Tauri, que necesita el runtime WebView2.
Requiere el WebView2 Runtime (suele venir con Windows 10/11) y Visual Studio Build Tools con el componente de C++.
cd client
npm install
npm run tauri dev
Esto abre la ventana real. Con lumid corriendo en otra terminal, pega tu clave de
vinculación en el paso «Vincular» del wizard.
La prueba que de verdad importa: cambia un solo carácter de la huella dentro de la clave antes de pegarla. Debe rechazar la conexión con "la huella del certificado no coincide" y el botón «Siguiente» debe seguir deshabilitado. Si en cambio conecta, el anclaje de huella no está funcionando.
Ver docs/superpowers/specs/2026-09-16-soporte-linux-cliente-design.md
para el diseño completo (empaquetado .deb/AppImage, auto-actualización). Prerrequisitos de
sistema para Tauri v2 en Ubuntu/Pop!_OS 24.04 (nombres de paquete de otras versiones pueden
variar, sobre todo libwebkit2gtk — 24.04 usa -4.1, 22.04 usa -4.0):
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev libssl-dev \
libayatana-appindicator3-dev patchelf build-essential xdg-utils
cd client
npm install
npm run tauri build -- --bundles deb,appimage
Deja los artefactos en client/src-tauri/target/release/bundle/{deb,appimage}/.
Si compilas dentro de WSL (como este proyecto ya hace para lumid), dos cosas que solo
salen ahí, no en un Pop!_OS real:
linuxdeploy, que es él mismo un AppImage y
necesita FUSE para montarse. WSL no lo trae por defecto:
sudo apt-get install -y libfuse2t64 (o libfuse2 en distros más viejas).$PATH hereda rutas de Windows: con la interoperabilidad de WSL activada (el
defecto), $PATH arrastra entradas como
/mnt/c/WINDOWS/system32/config/systemprofile/AppData/Local/Microsoft/WindowsApps, y
linuxdeploy revienta con un boost::filesystem::filesystem_error de permiso denegado al
toparse con una de esas rutas. Se esquiva con un $PATH limpio para ese comando:
env PATH="$HOME/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
npm run tauri build -- --bundles appimage
sudo target/debug/lumi uninstall
Pide confirmación (el mismo estilo de sección ── desinstalación ──… y advertencia antes
de borrar, porque /var/lib/lumi puede tener administradores y proyectos reales) y luego
detiene lumid.service, borra la unit, el binario y todo /var/lib/lumi. Con --yes se
salta la confirmación.
# Detén lumid con Ctrl+C en su terminal, o:
pkill -f "target.*lumid" 2>/dev/null || taskkill //F //IM lumid.exe
rm -rf .dev-data # borra certificado, base de datos y claves de prueba
Las únicas comprobaciones ejecutables del plan (ponytail: una por lógica no trivial, nada
más):
cargo test -p lumi-proto
Esperado: 3 tests en verde (key, crypto, caps).
cargo build # workspace completo: lumi-proto, lumid, lumi-cli
cd client && npm run build # frontend
AGPL-3.0-or-later (ver LICENSE). Al ser Lumi un servicio pensado para correr en un servidor propio, la AGPL cierra el hueco de la GPL normal: si alguien ofrece una versión modificada como servicio de red, también debe compartir su código fuente.