Files
osint/CONVENTIONS.md
T
egutierrez 0e7b615a1e chore: auto-commit (2 archivos)
- CONVENTIONS.md
- tools/gen_osint_tools.py

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-14 23:55:18 +02:00

11 KiB

Convenciones de archivos — vault osint

Estándar para organizar la información de personas (y, a futuro, dominios y casos) en el vault osint (/home/enmanuel/Obsidian/osint). Toda extracción desde otros vaults o desde sesiones de navegación OSINT debe dejar los archivos siguiendo este estándar.

1. Estructura física (estándar: plano + subcarpeta de documentos)

osint/
  README.md
  _self.md                              # ficha del titular (Enmanuel), referencia, NO es objetivo
  personas/
    <slug>.md                           # ficha de la persona (su "index")
    <slug>/                             # carpeta con las notas-documento de esa persona
      <doc-slug>.md
  dominios/
  organizaciones/                       # empresas/entidades (mismo patrón <slug>.md + <slug>/)
  lugares/                              # direcciones/lugares de interés
  casos/
  inbox/
  attachments/
    personas/
      <slug>/
        <doc-slug>-NN.<ext>             # imágenes/PDFs de esa persona

Regla: la ficha vive como .md suelto en personas/ (todas las fichas listables en un nivel); sus documentos en una subcarpeta homónima; los binarios en attachments/personas/<slug>/.

2. Slug

El slug es la clave estable de una persona. Se deriva del nombre completo:

  • minúsculas, sin acentos ni ñ (transliteración: ñn, áa, …)
  • espacios y separadores → -
  • solo [a-z0-9-], sin guiones dobles ni al inicio/fin

Ejemplo: Enmanuel Gutiérrez Pérezenmanuel-gutierrez-perez.

3. Ficha de persona — personas/<slug>.md

Frontmatter:

tipo: persona
nombre: "Enmanuel Gutierrez Perez"        # nombre legible original
aliases: ["Enmanuel Gutierrez Perez"]     # nombres viejos para que wikilinks previos resuelvan
slug: enmanuel-gutierrez-perez
sexo: hombre                              # hombre|mujer|null
fecha_nacimiento: 1997-03-05              # ISO o null
dni: 54370345R                           # o null
direccion: "Calle Eugenia Rios 14, 29718 Almachar, Malaga"   # texto plano, no wikilink
pais: españa
tags: [persona, osint]
fuente: "NotasDeObsidian/personas/Enmanuel Gutierrez Perez.md"   # origen de la extracción

Body, secciones en este orden (omitir las vacías):

## Documentos
- [[enmanuel-gutierrez-perez/dni|DNI]]
- [[enmanuel-gutierrez-perez/certificado-digital|Certificado digital]]

## Relaciones
- [[manuel-gutierrez-gamez|Manuel Gutierrez Gamez]] — hermano

## Notas
Texto libre de investigación.

3b. Esquema canónico del frontmatter de persona

Todas las fichas personas/<slug>.md comparten el mismo conjunto de campos, en este orden. Campos sin valor se dejan como null (o [] para listas) — nunca se omiten, para que el cálculo de datapoints y el score de completitud sean consistentes.

tipo: persona
nombre: "Nombre Apellidos"
slug: nombre-apellidos
aliases: []                 # otros nombres por los que aparece
sexo: null                  # hombre | mujer | null
fecha_nacimiento: null      # ISO YYYY-MM-DD | null
dni: null
telefono: null
email: null
direccion: null             # texto plano
pais: null
relaciones: []              # ["[[slug]] — parentesco/rol"]
contexto: null              # familia | aurgiobsidian | ... (origen/circulo)
fuente: ""                  # nota/vault de procedencia
tags: [persona, osint]

Campos extra heredados (p.ej. horoscopo) se conservan al final del frontmatter.

Datapoints y score de fiabilidad. Los campos de identidad que cuentan para el score de completitud son: sexo, fecha_nacimiento, dni, telefono, email, direccion, pais (7). El score de una ficha es campos_identidad_presentes / 7 * 100. Una ficha por debajo del 100% tiene datapoints faltantes. El total de datapoints de una persona suma además sus documentos, attachments y relaciones. Lo calcula projects/osint/tools/person_datapoints.py.

4. Nota-documento — personas/<slug>/<doc-slug>.md

Una nota-documento agrupa los attachments de un tipo de documento de la persona.

<doc-slug> canónico (deriva del título original quitando el nombre de la persona y normalizando):

Título original (ejemplos) doc-slug doc_tipo
Dni X, DNI de X dni dni
Certificado Digital X certificado-digital certificado
Carnet de conducir de X, Documentos Carnet Conducir X carnet-conducir carnet
Fotos X, Fotos de X fotos fotos
Vida Laboral X vida-laboral laboral
Nominas <empresa> X nominas-<empresa> laboral
Documentos <banco> X (Abanca, BBVA, Cajamar…) documentos-<banco> banco
Modelo 100 X, Alta autonomo X modelo-100, alta-autonomo fiscal
Datos empadronamiento X empadronamiento otro
Contrato <x> X contrato-<x> contrato
(sin patrón claro) slug del título sin el nombre otro

Frontmatter:

tipo: documento
doc_tipo: dni                            # dni|certificado|carnet|fotos|banco|fiscal|laboral|contrato|otro
persona: "[[enmanuel-gutierrez-perez]]"
fuente: "NotasDeObsidian/Dni Enmanuel Gutierrez.md"

Body: los embeds reescritos a la ruta de attachments de la persona:

![[attachments/personas/enmanuel-gutierrez-perez/dni-1.jpg]]
![[attachments/personas/enmanuel-gutierrez-perez/dni-2.jpeg]]

5. Attachments — attachments/personas/<slug>/

  • Cada binario referenciado por una nota-documento se mueve a attachments/personas/<slug>/.
  • Se renombra a <doc-slug>-NN.<ext> (1-indexed, en el orden en que aparece en la nota), preservando la extensión original en minúsculas. Ej: dni-1.jpg, dni-2.jpeg, documentos-abanca-1.pdf.
  • Si el nombre original es relevante, se anota en el body de la nota-documento como comentario.

6. Entidades que NO son personas

Notas como FenixFood SL, Documento Endesa, Documentos Aduanas … van a organizaciones/; las direcciones/lugares (Calle Eugenia Rios …) van a lugares/. Ambas carpetas siguen el mismo patrón que personas/: <slug>.md como ficha + subcarpeta <slug>/ para sus documentos + attachments/<organizaciones|lugares>/<slug>/ para sus binarios. El frontmatter usa tipo: organizacion / tipo: lugar en vez de tipo: persona.

7. Relaciones y direcciones

  • Relaciones entre personas (Hermano de [[X]]) se normalizan a la sección ## Relaciones de la ficha, enlazando al slug de la otra persona con alias legible.
  • Direcciones: en el frontmatter direccion como texto plano (no wikilink). Si una dirección es entidad de investigación propia, tiene además su ficha en lugares/<slug>.md, y la persona la enlaza con direccion: "[[lugares/<slug>|texto legible]]" si se quiere navegable.

8. Idempotencia de la extracción

El migrador debe ser re-ejecutable: si una persona ya existe en osint con su slug, se actualiza (no se duplica). Los attachments ya movidos no se vuelven a mover.

9. Scans de red (recon)

Todo escaneo de red de una investigación —WHOIS, RDAP, DNS, nmap, traceroute, ping— se archiva SIEMPRE en OSINT. No existen scans sueltos: el resultado queda como nota navegable en el vault y como fila consultable en la base de datos. Lo gestionan las funciones del grupo de capacidad recon del registry (dominio cybersecurity); ver docs/capabilities/recon.md.

9.1 Nota del scan en el vault

Cada scan produce una nota Markdown bajo la carpeta del dominio escaneado:

dominios/<slug>/recon/<scan_type>-<YYYYMMDD-HHMM>.md

donde <scan_type> es uno de whois | rdap | dns | nmap | traceroute | ping y el timestamp tiene granularidad de minuto. Frontmatter de la nota:

tipo: scan-red
scan_tipo: whois                 # whois|rdap|dns|nmap|traceroute|ping
target: "ejemplo.com"            # objetivo original (dominio, host o IP)
slug: ejemplo.com                # slug del target (clave de la carpeta)
fecha: 2026-06-14T13:18:00       # ISO, momento del scan
herramienta: whois               # CLI usada (whois, dig, nmap, ...)
tags: [scan-red, whois, recon]

Body: cabecera con target/tipo/herramienta/fecha, un ## Resumen opcional con los campos destacados del scan, y la salida cruda completa (raw) dentro de un bloque de código. La nota es la capa crítica: si no se puede escribir, el guardado falla.

9.2 Tabla network_scans (DuckDB, service osint_db)

Además de la nota, cada scan se registra en la tabla network_scans (schema main) de la base DuckDB que posee el service osint_db (single-writer), vía POST http://127.0.0.1:8771/api/scan. Columnas:

Columna Qué
id Identificador del scan
target Objetivo original (dominio/host/IP)
target_slug Slug del target (clave de agrupación)
scan_type whois | rdap | dns | nmap | traceroute | ping
tool CLI usada (whois, dig, nmap, ...)
scan_ts Timestamp ISO del scan
note_path Ruta relativa de la nota en el vault
summary JSON con los campos resumidos del scan
created_at Timestamp de inserción

Es la capa best-effort: si osint_db está caído o no expone el endpoint, el guardado degrada a solo-nota (registered=False + aviso) sin fallar. El re-ingest del vault NO borra network_scans —es una tabla de datos vivos, no derivada de las notas.

9.3 Cómo lanzar y guardar

El camino canónico es el pipeline one-shot del registry, que escanea y archiva en una sola llamada:

cd /home/enmanuel/fn_registry
./fn run recon_osint <target> <scan_type>      # p.ej.  ./fn run recon_osint ejemplo.com whois

Para un nmap pesado (full-tcp, vuln, udp-top) lanzar en segundo plano por la duración:

nohup ./fn run recon_osint scanme.nmap.org nmap --profile full-tcp --timeout-s 7200 \
  > /tmp/recon-fulltcp.log 2>&1 &

Alternativa atómica (controlas el scan y lo guardas aparte) desde Python, importando las funciones del registry —no se reescriben:

import sys; sys.path.insert(0, "python/functions")
from cybersecurity import dns_records
from cybersecurity.save_scan_to_osint import save_scan_to_osint

scan = dns_records("ejemplo.com")
if scan["status"] == "ok":
    save_scan_to_osint("ejemplo.com", "dns", scan["raw"],
                       summary={"A": scan["records"].get("A")}, tool="dig")

9.4 Cómo consultar scans guardados

Desde una nota del vault, con un bloque osintdb (plugin osint-db) que consulta la tabla:

```osintdb
SELECT scan_type, tool, scan_ts, note_path
FROM network_scans
WHERE target_slug = 'ejemplo.com'
ORDER BY scan_ts DESC
```

O contra el service directamente vía /api/query (mismo SQL). El slug del target se deriva igual que en todo el vault:

import re
slug = re.sub(r"[^a-z0-9._-]+", "-", target.lower())

Nota: el slug de un dominio/host (p.ej. ejemplo.com, 192.168.1.10) conserva puntos y guiones porque el set permitido es [a-z0-9._-]; difiere del slug de persona de la sección 2, que solo admite [a-z0-9-].