Añade alta, edición y borrado de contactos (personas y organizaciones) a la
app osint_web. La fuente de verdad es la ficha .md del vault Obsidian
(CONVENTIONS.md §3b/§6); Xandikos es el retransmisor al móvil.
Backend (server/main.py):
- POST /api/contact: genera slug, escribe la ficha .md con el frontmatter
canónico + PUT del vCard a Xandikos. 409 si el slug ya existe.
- PUT /api/contact/{slug}: merge del frontmatter (preserva campos heredados)
+ re-PUT del vCard. 404 si no existe.
- DELETE /api/contact/{slug}: borra la ficha .md + DELETE del vCard. 404 si
no existe.
Cada escritura invalida la caché DAV para que el cambio se vea ya en la app.
Registry-first: orquesta create/update/delete_obsidian_note del grupo obsidian
y carddav_put_vcard/dav_delete_resource del grupo dav (sin reimplementar
parseo ni HTTP). Mapea los campos OSINT a propiedades X-OSINT-* del vCard.
Frontend (ContactsView.tsx + api.ts + format.ts):
- Botón "Nuevo contacto" → modal con formulario Mantine (TextInput,
TagsInput aliases, Select contexto, Textarea notas).
- Detalle: botones "Editar" (formulario precargado) y "Borrar" (con
confirmación). Tras guardar refresca la lista.
- Helper slugify (replica slugify_obsidian_name) para resolver la ficha.
Tests: 6 nuevos casos (ciclo crear→editar→borrar con .md real + reflejo vCard
mockeado, organización, 404s, tipo inválido, preserva campos heredados). Suite
27 passed. Ciclo e2e real verificado contra Xandikos + vault (vCard creado,
editado y borrado; slug zz-test-crud limpiado). pnpm build verde (React 19 +
Mantine v9).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Frontend web de lectura del vault osint + agenda/calendario Xandikos.
- Stack: React 19 + Vite 6 + TypeScript + Mantine v9 (React 19 obligatorio para
que Mantine v9 monte). Grafo con sigma v3 + graphology + forceatlas2 en web
worker. Markdown con react-markdown, calendario con @mantine/dates.
- AppShell con navbar de 4 secciones + botón global de refresco (POST /api/refresh).
- GraphView: force-directed, color por tipo, tamaño por grado, panel lateral con
toggles de tipo + dangling + buscador (centra el nodo). Guard de WebGL: si el
navegador no lo expone, avisa en vez de crashear.
- TablesView: una pestaña por tipo, tabla ordenable/filtrable con columnas del
frontmatter. Click en fila -> ficha.
- NodeCard (modal): frontmatter clave-valor (fechas europeas), cuerpo Markdown,
galería de imágenes con lightbox, PDFs/docs como enlace, wikilinks navegables.
- ContactsView: agenda con buscador + detalle (teléfonos, correos, bloque osint,
nota). CalendarView: mini-calendario con días marcados + eventos agrupados por
día (hora local).
- Vite proxya /api -> 127.0.0.1:8470. Verificado end-to-end contra el backend
real: 1199 nodos / 618 aristas, 539 personas en tabla, 1064 contactos, 98
eventos; grafo renderiza con WebGL y NodeCard abre con frontmatter+body.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.