
Un conjunto de herramientas de graficación para la investigación de amenazas - y otras cosas
Este proyecto fue mayoritariamente vibecodeado, así que tome las precauciones adecuadas. El código ha sido revisado por programadores reales, pero no está endurecido contra vulnerabilidades. No lo exponga externamente sin una revisión de seguridad exhaustiva.
Quantickle es un conjunto de herramientas interactivo, centrado en el navegador, para construir y explorar grafos de red. El front-end (Cytoscape.js + interfaz personalizada) maneja el renderizado, la edición y la ejecución de diseños, mientras que el servidor ligero Express sirve la interfaz, realiza llamadas de integración proxy y, opcionalmente, almacena grafos en Neo4j. En otras palabras, el navegador posee el estado del grafo y la visualización, mientras que el servidor existe para proporcionar recursos e integraciones cuando sea necesario.
bezier), rectas, agrupadas (unbundled-bezier), taxi y taxi redondeado.haystack y segments mediante estilos personalizados.Aquí hay algunos ejemplos de lo que Quantickle puede hacer:

Quantickle se basa en un pequeño conjunto de conceptos compartidos que conectan la documentación:
Sistema de coordenadas y diseños espaciales — Los diseños absolutos y conscientes de profundidad de Quantickle usan un cubo de 0 a 1000 para las coordenadas x, y y z. Consulte COORDINATE_SYSTEM.md para conocer las reglas espaciales completas y el comportamiento de iluminación.
Gestión de archivos de grafo y archivos de proyecto — Los archivos .qut son el estado guardado canónico; almacenan nodos, aristas, metadatos y jerarquía de contenedores.
Integración con Neo4j — el servidor puede persistir y recuperar grafos de Neo4j, incluyendo instantáneas de metadatos. Consulte NEO4J_INTEGRATION_README.md para conocer la configuración y los detalles del flujo de trabajo.
Un grafo de Quantickle es un conjunto de nodos y aristas con metadatos que impulsan el diseño, el estilo y la agrupación.
metadata, matriz de nodos, matriz de aristas y estado opcional de diseño o vista.id obligatorio más atributos opcionales como label, type, size, color y coordenadas. Los nodos también pueden incluir info en Markdown o propiedades personalizadas utilizadas por las integraciones.source + target y metadatos opcionales de label, type, weight y estilo.El JSON de grafo interno de Quantickle es una estructura aplanada con matrices de nodes y edges. Las relaciones y clases de contenedores se conservan como metadatos de nodo.
Quantickle acepta varios formatos de importación desde Archivo → Importar Datos y desde integraciones automatizadas. Cada importador alimenta el mismo proceso de grafo utilizado al guardar archivos de proyecto .qut, por lo que las estructuras a continuación también representan formas de exportación.
El importador CSV reconoce dos diseños:
Secciones de nodos + aristas – formato de exportación utilizado por Quantickle y la opción más segura al preparar datos manualmente. El archivo contiene una tabla de nodos seguida de una fila en blanco y una tabla de aristas: ```csv node_id,node_label,node_type,node_size,node_color,node_x,node_y srv-1,Gateway,server,40,#2dd4bf,100,250 cli-1,Analyst,client,28,#38bdf8,320,210
source,target,label,weight,type srv-1,cli-1,allows,1,connection
Se toleran columnas adicionales—el importador normaliza encabezados como Node Label, nodeLabel o label, conserva cualquier valor explícito de color/size y mantiene coordenadas cuando se proporcionan.
source y target. Las columnas opcionales como label, type, weight, source_type, target_type, source_label o color se fusionan en los nodos y aristas generados cuando están presentes: ```csv
source,target,label,source_type,target_type
srv-1,cli-1,allows,server,client
srv-1,sensor-2,monitors,server,sensor
Quantickle analiza los encabezados sin distinción de mayúsculas/minúsculas, acepta tanto nombres en snake_case como separados por espacios, y omite filas vacías automáticamente.
.edges)Se admiten pares de aristas separados por espacios en blanco para gráficos esqueléticos rápidos. Los nodos faltantes se crean automáticamente:```text srv-1 cli-1 srv-1 sensor-2
> **Nota:** Los libros de Excel (`.xlsx`) ya no son compatibles. Exporta o guarda
> los datos como CSV antes de importarlos en Quantickle.
#### JSON
`File → Import Data` también acepta exportaciones JSON producidas por Quantickle o
colecciones de elementos sin procesar de Cytoscape. Cuando el archivo contiene
`elements` con entradas `data` anidadas, se normalizan en la estructura interna
de Quantickle.
#### Grafo de Quantickle (`.qut`)
Los archivos `.qut` son documentos JSON con objetos de nodo y arista aplanados.
Un gráfico mínimo tiene este aspecto:```json
{
"metadata": { "name": "My graph" },
"nodes": [
{ "id": "srv-1", "label": "Gateway", "type": "server", "size": 40 }
],
"edges": [
{ "id": "srv-1_cli-1", "source": "srv-1", "target": "cli-1", "label": "allows" }
]
}
Los archivos heredados que almacenan nodos/aristas dentro de un objeto data aún se aceptan—el cargador los aplana automáticamente y conserva las coordenadas, las clases y los metadatos de la jerarquía de contenedores.
Al intercambiar datos con la API HTTP o la integración de Neo4j, envía la misma forma aplanada utilizada por los archivos .qut:```json
{
"nodes": [
{ "id": "srv-1", "label": "Gateway", "type": "server", "size": 40 }
],
"edges": [
{ "id": "srv-1_cli-1", "source": "srv-1", "target": "cli-1", "label": "allows" }
]
}
El campo opcional `info` aún admite Markdown y se persiste junto con cualquier
propiedad personalizada.
<a id="layouts"></a>
## Diseños
Quantickle agrupa los diseños en familias prácticas para que puedas elegir el adecuado para la tarea:
- **Dirigido por fuerza** (force, cose, fcose) — bueno para descubrimiento exploratorio de estructuras y cuando quieres agrupación natural.
- **Jerárquico y flujo** (breadthfirst, dagre) — mejor para árboles de dependencias, grafos de llamadas o flujos causales.
- **Cuadrícula y círculo** — diseños rápidos y deterministas para paneles, grafos pequeños y exportaciones.
- **Basado en tiempo** (timeline, timeline-scatter, temporal-attraction) — úsalo cuando el orden temporal o la actualidad sean el enfoque.
- **Espacial/absoluto** (absolute, depth-aware) — úsalo para diseños similares a mapas o diagramas con coordenadas conocidas.
- **Cluster/radial** (radial-recency) — úsalo cuando quieras agrupar por tipo o anillos de actualidad.
### Opciones de diseño```javascript
// js/layouts.js
const layoutOptions = {
'force': {
name: 'force',
animate: true,
randomize: false,
infinite: false
},
'grid': {
name: 'grid',
rows: undefined,
cols: undefined
}
// ... more layouts
}
El diseño personalizado timeline posiciona nodos a lo largo de un eje temporal. Puedes personalizar la barra central mediante la opción barStyle:```javascript
cy.layout({
name: 'timeline',
barStyle: {
color: '#3498db', // bar color
height: 15, // bar height in pixels
className: 'my-timeline-bar' // optional CSS class
}
}).run();
When `className` is provided, the timeline bar receives that class and its default color and height styling are removed so you can target it via CSS.
### Radial Recency Layout
Use `radial-recency` to plot newest items closest to the center and older ones on outer rings. The layout maps angle to a secondary attribute (type/cluster/group) so related nodes stay aligned around the circle. You can tune the rings with a few options:```javascript
cy.layout({
name: 'radial-recency',
ringThickness: 140, // radial spacing between time rings
minSeparation: 80, // minimum distance between neighbors along a ring
angleJitter: 0.15, // optional jitter (radians) to break perfect symmetry
angleStrategy: 'grouped' // or 'alphabetical' for deterministic ordering
}).run();
Ver graphs/radial_time_rings.qut para un pequeño fixture que demuestra bandas de tiempo concéntricas agrupadas por metadatos de clúster/tipo.
Use el diseño timeline-scatter para mapear marcas de tiempo directamente a posiciones en x mientras distribuye nodos verticalmente por similitud, categoría o comunidad. Acepta escalas ajustables para ambos ejes y un jitter opcional para reducir superposición:```javascript
cy.layout({
name: 'timeline-scatter',
xScale: 0.5, // pixels per millisecond (auto-calculated when omitted)
yScale: 60, // spread for similarity/category lanes
jitter: 4, // optional per-node jitter to minimise overlap
barStyle: { // applied when timeline bars already exist
color: '#222',
height: 12,
className: 'scatter-bar'
}
}).run();
Los nodos con puntuaciones de similitud numéricas (`similarity`, `similarityScore` o `similarity_score`) se centran alrededor de la media, mientras que las etiquetas categóricas o de comunidad crean carriles espaciados uniformemente a lo largo del eje y.
### Disposición de Atracción Temporal
Usa la distancia de la marca de tiempo para dirigir las fuerzas del resorte manteniendo la repulsión ligera:```javascript
cy.layout({
name: 'temporal-attraction',
timeMode: 'gaussian', // or 'bucket'
timeSigma: 60 * 60 * 1000, // Gaussian falloff window (in ms)
bucketSize: 24 * 60 * 60 * 1000, // bucket size when using bucket mode
repulsionStrength: 12 // minimal node repulsion
}).run();
Select Layout → Temporal Attraction - Time Weighted en la interfaz de usuario o pasa name: 'temporal-attraction' a través de la configuración de diseño de CLI/API para habilitarlo.
Quantickle puede extraer datos de sistemas externos o sincronizarse con ellos. Las integraciones generalmente fluyen a través del servidor porque requieren credenciales, reglas de proxy o persistencia.
http://localhost:3000.Para tutoriales más detallados y capturas de pantalla, consulta la Guía de uso.
.qut para una instantánea del proyecto con fidelidad completa (diseño, metadatos, contenedores).Quantickle se inicializa a través del objeto global window.QuantickleApp definido en js/main.js. La aplicación llama automáticamente a window.QuantickleApp.init() cuando el DOM está listo.
// js/config.js const config = { performance: { nodeLimit: 1000, // Max nodes to render at once batchSize: 100, // Nodes to add per batch webgl: true, // Enable WebGL rendering hideEdgesOnViewport: false // Hide edges while dragging } }
<a id="api"></a>
## API
### HTTP API
El servidor Express (`server.js`) sirve el front-end estático y expone varios endpoints JSON utilizados por la interfaz de usuario. Todas las rutas tienen el prefijo `/api`:
| Method & Path | Description |
| --- | --- |
| `GET /api/domain-files` | Lista las definiciones de dominio JSON presentes en `assets/domains/`. |
| `GET /api/examples` | Devuelve metadatos sobre los grafos `.qut` de ejemplo incluidos. |
| `GET /api/serpapi` | Actúa como proxy de las solicitudes de Google Search a SerpApi; requiere `SERPAPI_API_KEY` en la cadena de consulta o en el entorno. |
| `GET /api/openai/models` | Actúa como proxy de las solicitudes de listado de modelos de OpenAI a `api.openai.com`; requiere un encabezado `Authorization: Bearer ...`. |
| `GET /api/proxy?url=…` | Reenvía solicitudes HTTP/HTTPS a hosts permitidos con encabezados similares a los de un navegador. |
| `POST /api/neo4j/graph` | Persiste un grafo en Neo4j. Acepta el cuerpo JSON del grafo Quantickle aplanado descrito anteriormente. |
| `POST /api/neo4j/node-graphs` | Encuentra grafos guardados que contienen nodos que coinciden con el array `labels` proporcionado. |
| `GET /api/neo4j/graphs` | Lista los grafos almacenados en Neo4j junto con metadatos resumidos. |
| `GET /api/neo4j/graph/:name` | Obtiene un grafo guardado, devolviendo `metadata`, `nodes` y `edges`. |
| `DELETE /api/neo4j/graph/:name` | Elimina un grafo almacenado de Neo4j. |
Todos los endpoints de Neo4j aceptan credenciales a través de los encabezados `X-Neo4j-Url`, `X-Neo4j-Username` y `X-Neo4j-Password` (o las variables de entorno `NEO4J_URL`, `NEO4J_USER` y `NEO4J_PASSWORD` en el servidor). Cuando se proporcionan, el objeto `metadata` se guarda junto con el grafo y se añade automáticamente una marca de tiempo `savedAt`.
<a id="api-keys"></a>
### Claves API
Algunas funciones requieren claves API. Estas se almacenan en el almacenamiento local del navegador a través del panel de Integraciones.
- **SerpAPI** — necesario para la búsqueda web del pipeline RAG. Agrega tu clave en el diálogo de Integraciones y será utilizada por el pipeline RAG del lado del cliente.
- **VirusTotal** — se utiliza para enriquecer nodos de dominio/IP/archivo/URL y extraer grafos de relaciones.
- **CIRCL-LU** — credenciales de autenticación opcionales si tu feed de MISP las requiere.
Para uso desde la línea de comandos, puedes alternativamente configurar `SERPAPI_API_KEY` en el entorno.
<a id="backend-proxy"></a>
### Proxy del Backend
El servidor expone un proxy que sortea CORS y reenvía solicitudes HTTP(S) a los hosts listados en la lista blanca del proxy. Usa `/api/proxy` con un parámetro URL:```
curl "http://localhost:3000/api/proxy?url=https%3A%2F%2Fopentip.kaspersky.com%2F"
La lista blanca base se encuentra en config/proxy-allowlist.json. Debes proporcionar este archivo (o establecer la variable de entorno PROXY_ALLOWLIST separada por comas antes de iniciar el servidor); de lo contrario, el proxy registra un error de configuración fatal y rechaza cada solicitud con HTTP 403.
Los hosts específicos de integración (para adaptadores de integración de backend como OpenAI y VirusTotal) se rigen por config/integration-allowlist.json (o INTEGRATION_ALLOWLIST). En tiempo de ejecución, ambas listas blancas se fusionan. Cada entrada debe listar un host o patrón comodín al que el proxy pueda acceder. Se admiten comodines con * en cualquier parte de una entrada, por lo que *.example.com permite cualquier subdominio de example.com, y máscaras como news-* se comportan según lo esperado. Los subdominios también heredan la entrada de su dominio padre, por lo que agregar example.com automáticamente permite www.example.com.
Un archivo de lista blanca mínimo se ve así:```json { "allowlist": ["otx.alienvault.com", "feeds.example.org"] }
Si prefieres variables de entorno, establece `PROXY_ALLOWLIST="otx.alienvault.com,feeds.example.org"` antes de iniciar el servidor.
Cuando el proxy reenvía una solicitud, ahora envía un conjunto de encabezados similares a los de un navegador (incluyendo valores modernos de Chrome `User-Agent`, `Accept`, `Accept-Language` y `Sec-Fetch-*`) para que los sitios que bloquean contenido con filtros anti-bot respondan de la misma manera que lo harían ante una carga de página normal. Cualquiera de estos encabezados puede ser anulado proporcionando anulaciones `x-proxy-<header>` desde la solicitud del cliente cuando sea necesario.
<a id="storage"></a>
## Almacenamiento
Quantickle mantiene el estado del gráfico en el navegador mientras trabajas y luego lo persiste cuando exportas o sincronizas.
- **Almacenamiento del navegador** — la configuración y las claves de API se almacenan localmente en el navegador.
- **Archivos de proyecto (`.qut`)** — guardados en tu carpeta de espacio de trabajo para instantáneas de gráficos de fidelidad completa. Consulta [GRAPH_FILE_MANAGEMENT_README.md](https://github.com/rsac-labs/quantickle/blob/main/GRAPH_FILE_MANAGEMENT_README.md) para conocer las reglas del espacio de trabajo y el ciclo de vida de los archivos.
- **Neo4j** — persistencia opcional en el lado del servidor para colaboración y búsqueda, configurada a través de la integración de Neo4j.
<a id="project-structure"></a>
## Estructura del Proyecto```
quantickle/
├── assets/ # Static assets
│ ├── backgrounds/ # Background graphics
│ ├── icons/ # Mostly empty now; icons are colocated with node types
│ ├── domains/ # Node type definitions
│ ├── help/ # Help pages
│ ├── examples/ # Example graphs
│ └── css/ # Stylesheets referenced by index.html
├── config/ # Proxy allowlist
├── data_retrieval/ # SerpAPI/web search helpers used by the RAG pipeline
├── graphs/ # Workspace folder for bundled and test .qut graphs
├── js/ # Front-end source modules
│ ├── main.js # Application bootstrap
│ ├── ai-input-manager.js # Currently unused
│ ├── api.js # HTTP client for server endpoints
│ ├── graph.js # Graph rendering and management
│ ├── graph-manager.js # High-level graph state orchestration
│ ├── graph-reference-resolver.js # Normalization of graph references
│ ├── layouts.js # Layout registration and options
│ ├── 3d-globe-layout.js # 3D Layout
│ ├── absolute-layout.js # Absolute coordinate layout
│ ├── custom-layouts.js # Other custom layouts
│ ├── aggressive-performance-fix.js # Aggressive Performance Fix for Large Graph Panning
│ ├── non-invasive-performance-fix.js # Non-Invasive Performance Fix for Large Graphs
│ ├── lod-system.js # Level of Detail (LOD) System
│ ├── auto-refresh.js # Auto-refresh functionality when new data arrives
│ ├── config.js # Default settings
│ ├── domain-loader.js # Loading and managing domain-specific node type configurations
│ ├── edge-editor.js # Editing edge styles
│ ├── extensions.js # Loading and registration of Cytoscape extensions
│ ├── integrations.js # Configuration and connection to external services
│ ├── rag-pipeline.js # Handles AI-assisted data ingestion
│ ├── secure-storage.js # Encrypts sensitive values in sessionStorage
│ ├── source-editor.js # Editor for the JSON graph source
│ ├── tables.js # Data table updates, filtering, and display
│ ├── ui.js # User interface interactions and notifications
│ ├── validation.js # Validation of all data inputs
│ ├── workspace-manager.js # Workspace file functionality
│ ├── utils.js # Shared browser utilities
│ ├── integrations/ # Integration-specific helpers (MISP/CIRCL-LU, etc.)
│ └── features/ # Feature modules (node editor, callouts, timeline, ...)
├── tests/ # Automated regression tests covering UI flows, imports, and APIs
├── utils/ # Node helpers (Neo4j client, readability shim, CLI scripts)
├── public/
│ ├── index.html # Static front-end
│ └── favicon.ico # Main app icon
├── package.json # Node dependencies and scripts
└── server.js # Express server exposing the HTTP API
El gráfico no se renderiza
Rendimiento deficiente
Problemas de diseño
Este proyecto no se mantiene activamente como un repositorio canónico. Es probable que las solicitudes de extracción (PR) sean ignoradas. Sin embargo, los comentarios, informes de errores y sugerencias son bienvenidos.
Este proyecto está bajo la licencia Apache 2.0; consulte el archivo LICENSE para obtener más detalles.