--- name: osint_web lang: py domain: tools version: 0.1.0 description: "App web local para explorar el vault OSINT de Obsidian: grafo sigma.js, tablas por tipo y fichas con galería de attachments. Backend Python stdlib que orquesta el grupo obsidian; escucha solo en 127.0.0.1 (datos sensibles)." tags: [osint, web, graph, sigma, obsidian, vault, dashboard, mantine] uses_functions: - build_obsidian_graph_py_obsidian - list_obsidian_notes_py_obsidian - read_obsidian_note_py_obsidian - extract_obsidian_embeds_py_obsidian - resolve_obsidian_embed_py_obsidian - slugify_obsidian_name_py_obsidian - search_obsidian_notes_py_obsidian uses_types: [] framework: "react-vite-mantine" entry_point: "server/main.py" dir_path: "projects/osint/apps/osint_web" repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/osint_web" e2e_checks: - id: tests cmd: "../../../../python/.venv/bin/python3 -m pytest server -q" timeout_s: 120 - id: vault_missing cmd: "../../../../python/.venv/bin/python3 server/main.py --vault /no/existe" expect_exit: 2 timeout_s: 30 --- ## Qué es App del issue 0172 (project `osint`). Lee directamente los `.md` del vault de Obsidian `~/Obsidian/osint` (sin BD intermedia — decisión KISS) y ofrece tres vistas: grafo explorable (sigma.js), tablas filtradas por tipo y fichas con la galería de attachments de cada nodo. Registry-first: el backend NO parsea el vault — orquesta las funciones del grupo de capacidad `obsidian` (`build_obsidian_graph`, `read_obsidian_note`, `resolve_obsidian_embed`, ...) declaradas en `uses_functions`. ## Arrancar el backend ```bash cd projects/osint/apps/osint_web ../../../../python/.venv/bin/python3 server/main.py --vault ~/Obsidian/osint --port 8470 ``` El servidor cachea el grafo en memoria al arrancar; `POST /api/refresh` re-escanea el vault bajo demanda (botón "refrescar" del frontend). ## Endpoints | Método | Ruta | Devuelve | |---|---|---| | GET | `/api/health` | estado + nº de nodos/aristas cacheados | | GET | `/api/graph` | grafo completo `{nodes, edges}` para sigma.js | | GET | `/api/nodes?tipo=persona` | filas de la tabla de ese tipo (id, label, tipo, frontmatter) | | GET | `/api/node/` | ficha: frontmatter + body Markdown + attachments + wikilinks | | GET | `/api/attachment?path=` | binario del attachment (path relativo al vault, allowlist) | | GET | `/api/search?q=...` | nodos cuyo contenido matchea la query | | POST | `/api/refresh` | re-escanea el vault y reconstruye la caché | ## Seguridad - El vault contiene datos personales sensibles (DNIs, fotos): el server escucha **solo en `127.0.0.1`** — no hay flag para exponerlo a red y NO es un service desplegable a VPS (sin tag `service`). - `/api/attachment` bloquea path traversal: `realpath` del candidato debe quedar estrictamente bajo el `realpath` del vault; cualquier otro caso → 403. - Vault inexistente al arrancar → error claro en stderr + exit 2 (nunca 500 silencioso). ## Tests ```bash cd projects/osint/apps/osint_web ../../../../python/.venv/bin/python3 -m pytest server -q ``` Cubren el DoD backend del issue 0172: grafo golden, tabla por tipo, ficha con attachments, wikilink dangling (nodo fantasma), slug con acentos (`[[María del Mar Pérez]]` → `maria-del-mar-perez`), path traversal bloqueado, vault inexistente y un e2e HTTP contra el server real en puerto efímero. ## Estado / pendiente - **Hecho (fase 5b)**: scaffold del sub-repo + backend completo con tests. - **Pendiente (fase siguiente)**: `frontend/` React + Vite + Mantine v9 + `@fn_library` con sigma.js + graphology (GraphView, TablesView, NodeCard). Onboarding previsto: `pnpm dev` en `frontend/` + backend en 8470 → abrir `http://127.0.0.1:5173`. - Cuando exista el manifest de sub-repos del project (issue 0171), añadir esta app a `projects/osint/subrepos.yaml`.