Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
| id | title | status | type | domain | scope | priority | depends | blocks | related | created | updated | tags | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0172 | App web OSINT: grafo sigma.js + tablas por tipo + fichas con imágenes sobre el vault osint | pendiente | app |
|
app-scoped | media |
|
2026-06-10 | 2026-06-10 |
|
0172 — App web OSINT: grafo sigma.js + tablas por tipo + fichas con imágenes
APP Metadata
| Campo | Valor |
|---|---|
| ID | 0172 |
| Estado | pendiente (solo plan — se construye cuando el vault tenga más datos) |
| Prioridad | media |
| Tipo | app — nueva app web en projects/osint/apps/osint_web |
| Project | osint (projects/osint/) |
Contexto
El project osint guarda sus investigaciones en el vault de Obsidian
/home/enmanuel/Obsidian/osint (sub-repo dataforge/osint). Hoy ese vault tiene:
- ~82 nodos repartidos en carpetas tipadas:
personas/(45),organizaciones/(25),lugares/(10),dominios/(1),casos/(1). - Datos tabulares en el frontmatter YAML de cada ficha:
tipo,nombre,sexo,fecha_nacimiento,dni,direccion,pais,aliases,tags, etc. - Aristas implícitas: los wikilinks
[[...]]en las seccionesRelaciones,LugaresyDocumentosconectan unas fichas con otras (y con sus attachments). - ~240 attachments: fotos, DNIs, certificados y PDFs en
attachments/<tipo>/<slug>/, embebidos en las notas con![[...]].
Obsidian es bueno para escribir la investigación, pero malo para explorarla de un vistazo:
no da un grafo navegable de todos los objetivos, ni una tabla filtrable, ni una ficha-resumen
con la galería de imágenes de cada persona. Metabase/Grafana no encajan: leen BD SQL (no .md),
y no muestran ni grafo de nodos ni imágenes inline.
Decisión del usuario (10/06/2026): construir una app web propia que lea el vault y ofrezca tres vistas — grafo explorable con sigma.js, tablas filtradas por tipo, y fichas con imágenes. Este issue es solo el plan: la recopilación de datos en Obsidian continúa primero; la app se implementa cuando haya suficiente material que justifique la inversión.
Objetivo
Una app web local que, leyendo directamente los .md del vault osint (sin BD intermedia
obligatoria en v1), permita:
- Explorar el grafo de nodos (personas, organizaciones, lugares, dominios, casos) y sus conexiones por wikilinks, con sigma.js: zoom, pan, click en nodo → ficha, colores por tipo, filtro de tipos visibles, búsqueda de nodo.
- Ver tablas filtradas por tipo: una tabla por categoría (personas, organizaciones, ...) con las columnas del frontmatter, ordenable y filtrable (por dni, lugar, fecha, tag).
- Abrir la ficha de cualquier nodo: frontmatter renderizado + cuerpo Markdown + galería de sus attachments (fotos, DNIs, PDFs) servidos por el backend.
Arquitectura propuesta
projects/osint/apps/osint_web/ (sub-repo Gitea dataforge/osint_web)
app.md frontmatter de registro (framework: react-vite-mantine)
server/ backend Python (lee el vault, sirve JSON + attachments)
main.py FastAPI o stdlib http
frontend/ React + Vite + Mantine + sigma.js
src/
views/GraphView.tsx sigma.js + graphology
views/TablesView.tsx Mantine DataTable filtrable por tipo
views/NodeCard.tsx ficha + galería de attachments
Backend (Python — máximo reuso del grupo obsidian)
Python porque el grupo de capacidad obsidian (11 funciones, dominio obsidian) ya cubre casi
todo el parseo del vault. Registry-first: el backend orquesta estas funciones, no reimplementa
el parseo.
Funciones del registry a reutilizar:
| Función | Uso en la app |
|---|---|
list_obsidian_notes_py_obsidian |
enumerar nodos por carpeta/tipo |
read_obsidian_note_py_obsidian |
leer ficha: {frontmatter, body, wikilinks, tags} |
parse_obsidian_frontmatter_py_obsidian |
datos tabulares de cada nodo |
extract_obsidian_wikilinks_py_obsidian |
aristas del grafo |
extract_obsidian_embeds_py_obsidian |
attachments embebidos en cada nota |
resolve_obsidian_embed_py_obsidian |
resolver ![[foto.jpg]] → path real en disco para servir la imagen |
slugify_obsidian_name_py_obsidian |
normalizar nombre de wikilink → id de nodo |
search_obsidian_notes_py_obsidian |
búsqueda global en el grafo |
Funciones nuevas a delegar a fn-constructor (no escribir inline en la app):
build_obsidian_graph_py_obsidian(impure) — dadovault_dir, devuelve{"nodes": [{id, tipo, label, frontmatter}], "edges": [{source, target, kind}]}. Resuelve cada wikilink a un nodo existente (vía slug / nombre de archivo); los wikilinks que no resuelven a un.mddel vault se marcan como aristas "dangling" o se descartan según flag. Tag de grupo:obsidian. Es la pieza que el grupo declara como frontera no cubierta ("No indexa el grafo agregado") — esta función la cierra.
Endpoints HTTP (JSON salvo el de attachments):
| Método | Ruta | Devuelve |
|---|---|---|
| GET | /api/graph |
grafo completo {nodes, edges} para sigma.js |
| GET | /api/nodes?tipo=persona |
filas de la tabla de ese tipo (frontmatter aplanado) |
| GET | /api/node/{slug} |
ficha: frontmatter + body (HTML/markdown) + lista de attachments |
| GET | /api/attachment?path=... |
sirve el binario del attachment (image/pdf), con allowlist al vault |
| GET | /api/search?q=... |
nodos que matchean |
Seguridad: el backend solo sirve archivos dentro del vault osint (path traversal bloqueado).
El vault contiene datos personales sensibles (DNIs) → la app escucha solo en 127.0.0.1, sin
exponer a red. No es un service desplegable a VPS.
Frontend (React + Vite + Mantine + sigma.js)
- Sistema del registry: React + Vite + Mantine v9 +
@fn_library(grupomantine, 63 funciones). Componentes propios de@fn_libraryantes que HTML nativo (reglafrontend_theming.md). - Grafo:
sigma.js+graphology. Color portipo, tamaño por grado, layout force-directed (graphology-layout-forceatlas2). Click en nodo → abreNodeCard. Panel lateral con toggles de tipos visibles y caja de búsqueda. - Tablas: una pestaña por tipo, Mantine
Table/DataTable con columnas del frontmatter, orden y filtro por columna (dni, lugar, fecha_nacimiento, tags). - Fichas:
NodeCardcon frontmatter en formato clave-valor (fechas en formato europeo DD/MM/AAAA — memoriaformato-fecha-europeo), cuerpo Markdown, y galería de attachments (imágenes con lightbox; PDFs como enlace/embed).
sigma.js y graphology son dependencias nuevas del frontend (no en @fn_library). KISS:
añadir solo esas dos; el resto (tabla, layout, modales) sale de Mantine/@fn_library.
Decisiones abiertas
- ¿BD intermedia o lectura directa del vault? v1 lee el vault en cada arranque (cachea el
grafo en memoria). Si el vault crece mucho o se quiere histórico/diff, evaluar un
operations.dbconentities/relations(encaja con el bucle reactivo). Recomendado: empezar sin BD (KISS), añadirla solo si el rendimiento o un caso de uso lo exige. - Backend FastAPI vs stdlib http: FastAPI da validación y OpenAPI gratis; stdlib evita una dependencia. Como el backend es fino (orquesta funciones del registry), decidir al construir.
- Live-reload del vault: ¿re-escanear bajo demanda (botón "refrescar") o watcher de filesystem? v1: botón refrescar (simple). Watcher si molesta.
- Aristas dangling: wikilinks a notas que aún no existen — ¿mostrarlos como nodos fantasma (útil para ver "objetivos pendientes de fichar") o esconderlos? Propuesta: nodo fantasma con estilo atenuado, toggle para ocultar.
Definition of Done
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|---|---|---|---|
| Golden: grafo carga el vault | e2e | GET /api/graph con el vault osint real |
nodes ≥ nº de .md, edges con los wikilinks resueltos; sigma.js los pinta |
| Golden: ficha con imágenes | e2e | GET /api/node/<persona con fotos> + abrir NodeCard |
frontmatter + cuerpo + galería con las imágenes de attachments/personas/<slug>/ |
| Edge: tabla filtrada por tipo | e2e | GET /api/nodes?tipo=organizacion |
solo nodos de ese tipo, columnas del frontmatter |
| Edge: wikilink dangling | unit | nota con [[Persona-Inexistente]] |
arista marcada dangling / nodo fantasma, sin crash |
| Edge: nombre con mayúsculas/acentos | unit | wikilink [[María del Mar]] → slug |
resuelve a maria-del-mar-...md vía slugify_obsidian_name |
| Error: path traversal en attachment | e2e | GET /api/attachment?path=../../etc/passwd |
403/404, jamás sirve fuera del vault |
| Error: vault inexistente | e2e | arrancar con --vault /no/existe |
error claro al arrancar, no 500 silencioso |
| Cobertura | audit | uses_functions del app.md |
declara todas las funciones del grupo obsidian consumidas |
Vida útil (cuando se construya): usar la app de verdad sobre el vault osint durante ≥7 días en investigaciones reales; medir que el grafo sigue cargando sin romperse al crecer el vault.
Notas
Estado actual: solo plan. No construir todavía — la recopilación de datos en Obsidian
continúa; cuando el vault tenga masa crítica de objetivos/relaciones, se arranca con
/new-cpp-app no aplica (es web): se hace git init del sub-repo dataforge/osint_web dentro de
projects/osint/apps/osint_web/ antes de limpiar cualquier worktree (regla apps_subrepo.md),
scaffolding de frontend con el stack Mantine del registry, y backend Python orquestando el grupo
obsidian.
Onboarding (para cuando exista): arrancar backend python server/main.py --vault /home/enmanuel/Obsidian/osint --port 8470 y pnpm dev en frontend/; abrir
http://127.0.0.1:5173. Pestañas: Grafo / Tablas / (ficha al click). Solo localhost por los
datos sensibles del vault.
Relación con #0171 (manifest de sub-repos): cuando esta app exista será un hijo del project
osint y debe entrar en su subrepos.yaml para re-clonarse en otros PCs.