Reescribe el backend a FastAPI + uvicorn y añade los endpoints DAV
(CardDAV/CalDAV) sobre el servidor Xandikos, además de las vistas del vault
osint de Obsidian.
Vault (grupo obsidian del registry):
- /api/graph, /api/nodes, /api/node/{slug}, /api/attachment, /api/search,
/api/refresh. Allowlist estricta de path traversal en /api/attachment.
- Resolución de embeds por path relativo al vault y por basename (registry).
Xandikos (grupo dav del registry + pass_get_secret):
- /api/contacts, /api/contact/{uid} (CardDAV, parseo vCard a JSON).
- /api/calendar?from=&to= (CalDAV, parseo VEVENT a JSON, filtro por rango).
- Credencial vía pass dav/xandikos-enmanuel; degradación clara sin red (502/503).
Solo escucha en 127.0.0.1 (datos sensibles). 13 tests verdes (pytest).
frontend/README.md describe el montaje React+Vite+Mantine+sigma.js posterior.
App web local OSINT: explora el vault de Obsidian osint (grafo sigma.js, tablas por tipo, fichas con galería de attachments) y la agenda/calendario del servidor Xandikos (CardDAV/CalDAV). Backend FastAPI que orquesta los grupos obsidian y dav del registry; escucha solo en 127.0.0.1 (datos sensibles).
App del issue 0172 (project osint). Combina dos fuentes de datos en un frontend
web local:
El vault de Obsidian ~/Obsidian/osint (sin BD intermedia — decisión
KISS): grafo explorable (sigma.js), tablas filtradas por tipo y fichas con la
galería de attachments de cada nodo.
El servidor Xandikos (CardDAV/CalDAV): agenda de contactos y calendario de
eventos.
Registry-first: el backend NO parsea el vault ni habla DAV a mano — orquesta las
funciones del grupo obsidian (build_obsidian_graph, read_obsidian_note,
resolve_obsidian_embed, ...) y del grupo dav (dav_list_resources,
dav_get_resource, split_vcards) más pass_get_secret para la credencial,
todas declaradas en uses_functions.
Stack
Backend: FastAPI + uvicorn (venv propio en .venv/, deps en
pyproject.toml). Escucha solo en 127.0.0.1.
Frontend (pendiente — otro agente): React + Vite + Mantine v9 +
@fn_library + sigma.js / graphology. Ver frontend/README.md.
Arrancar el backend
cd projects/osint/apps/osint_web
.venv/bin/python 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). Los datos DAV
no se cachean (se piden a Xandikos en cada llamada).
Endpoints
Método
Ruta
Devuelve
GET
/api/health
estado + nº de nodos/aristas cacheados
GET
/api/graph
grafo completo {nodes, edges, counts} para sigma.js
GET
/api/nodes?tipo=persona
filas de la tabla de ese tipo (id, label, tipo, frontmatter)
GET
/api/node/<slug>
ficha: frontmatter + body Markdown + attachments + wikilinks
GET
/api/attachment?path=<rel>
binario del attachment (path relativo al vault, allowlist)
GET
/api/search?q=...
nodos cuyo contenido matchea la query
GET
/api/contacts
contactos del addressbook Xandikos (CardDAV) a JSON
GET
/api/contact/<uid>
un vCard concreto a JSON
GET
/api/calendar?from=&to=
eventos del calendario Xandikos (CalDAV) en el rango
POST
/api/refresh
re-escanea el vault y reconstruye la caché
Configuración Xandikos
Base URL: https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com
Usuario: enmanuel; password en pass dav/xandikos-enmanuel (vía
pass_get_secret, nunca hardcodeada).
El vault contiene datos personales sensibles (DNIs, fotos): el server escucha
solo en 127.0.0.1 — --host distinto avisa en stderr 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 (404 si
el path es legítimo dentro del vault pero el archivo no existe).
Vault inexistente al arrancar → error claro en stderr + exit 2 (nunca 500
silencioso).
Sin red / Xandikos caído → los endpoints DAV devuelven {"status":"error"}
con código 502/503, nunca un crash.
Tests
cd projects/osint/apps/osint_web
.venv/bin/python -m pytest tests -q
Cubren el DoD backend del issue 0172 + las extensiones DAV: grafo golden, tabla
por tipo, ficha con attachments (embed por path), wikilink dangling (nodo
fantasma), slug con acentos (María del Mar → maria-del-mar), path traversal
bloqueado, attachment legítimo servido, vault inexistente con error claro,
parseo vCard/iCalendar a JSON y degradación de los endpoints DAV sin red.
Estado / pendiente
Hecho: scaffold del sub-repo + backend FastAPI completo (vault + DAV) con
13 tests verdes.
Pendiente (siguiente agente): frontend/ React + Vite + Mantine v9 +
@fn_library con sigma.js + graphology (GraphView, TablesView, NodeCard,
ContactsView, CalendarView). Onboarding previsto: backend en 8470 +
pnpm dev en frontend/ → abrir http://127.0.0.1:5173. Ver
frontend/README.md.
Cuando exista el manifest de sub-repos del project (issue 0171), añadir esta
app a projects/osint/subrepos.yaml.