Graph-Loom
A lightweight, local-first graph notebook and visualizer built with Rust and egui. Features physics-assisted layout, OpenCypher query support, embedded API servers, and enterprise-grade analytics.

Key Features
- Physics-Assisted Layout: Auto-clustering with smooth interaction for hundreds of nodes
- OpenCypher Query Console: Full support for MATCH, CREATE, MERGE, SET, DELETE, WHERE, RETURN, WITH, UNWIND
- Graph Algorithms: PageRank, Betweenness Centrality, Shortest Path, A* Pathfinding
- Temporal Queries: Time-travel visualization with timestamp-based filtering
- Local Embeddings: TF-IDF based semantic similarity search (no external API required)
- LLM Integration: Optional OpenAI/Anthropic/Ollama support for entity extraction
- Embedded APIs: HTTP, WebSocket, and gRPC interfaces for automation
- SQLite Storage: ACID-compliant persistence with FTS5 full-text search
- Local-First: All data stored locally with automatic backups
Installation
Prerequisites
- Rust toolchain (stable, edition 2024)
Build from Source
# Clone the repository
# Build and run (release mode recommended)
Build with CLI Shell
# Build both GUI and CLI tools
Quick Start
Creating Nodes and Relationships
Using the Query Console (sidebar → Query tab):
-- Create nodes
CREATE (p:Person {name: 'Alice', role: 'Engineer'})
CREATE (c:Company {name: 'TechCorp', industry: 'Software'})
-- Create relationship
CREATE (p)-[:WORKS_AT {since: '2020'}]->(c)
Or use the sidebar tools for point-and-click creation.
Querying Data
-- Find all people
MATCH (p:Person) RETURN p
-- Find relationships
MATCH (p:Person)-[r:WORKS_AT]->(c:Company)
WHERE p.name = 'Alice'
RETURN p, r, c
-- Pattern matching with filters
MATCH (n) WHERE n.name CONTAINS 'Tech' RETURN n
Query Language Reference
OpenCypher Support
| Clause | Description | Example |
|---|---|---|
MATCH |
Find patterns | MATCH (n:Person) RETURN n |
CREATE |
Create nodes/relationships | CREATE (n:Label {key: 'value'}) |
MERGE |
Create if not exists | MERGE (n:Person {name: 'Bob'}) |
WHERE |
Filter results | WHERE n.age > 30 |
RETURN |
Output results | RETURN n.name, n.age |
SET |
Update properties | SET n.status = 'active' |
REMOVE |
Delete properties | REMOVE n.temp_field |
DELETE |
Delete nodes/relationships | DELETE n |
DETACH DELETE |
Delete with relationships | DETACH DELETE n |
WITH |
Chain queries | WITH n ORDER BY n.name |
UNWIND |
Expand lists | UNWIND [1,2,3] AS x |
ORDER BY |
Sort results | ORDER BY n.name DESC |
SKIP/LIMIT |
Pagination | SKIP 10 LIMIT 5 |
DISTINCT |
Unique results | RETURN DISTINCT n.label |
WHERE Operators
-- Comparison
WHERE n.age > 30 AND n.status = 'active'
WHERE n.name <> 'Unknown'
-- String operations
WHERE n.name CONTAINS 'Smith'
WHERE n.name STARTS WITH 'A'
WHERE n.name ENDS WITH 'son'
-- Null checks
WHERE n.email IS NOT NULL
WHERE n.phone IS NULL
-- List membership
WHERE n.role IN ['Admin', 'Manager']
-- Logical
WHERE n.active = true OR n.role = 'Admin'
WHERE NOT n.archived
Graph Algorithms
Access via CALL procedures in the Query Console:
PageRank (Node Importance)
CALL algo.pageRank()
-- Returns nodes sorted by importance score
Betweenness Centrality (Bridge Detection)
CALL algo.betweenness()
-- Identifies nodes that connect different graph regions
Shortest Path
CALL algo.shortestPath("node-uuid-1", "node-uuid-2")
-- Returns path between two nodes
A* Pathfinding
CALL algo.astar("from-uuid", "to-uuid")
-- Heuristic-based pathfinding using node positions
All Paths
CALL algo.allPaths("from-uuid", "to-uuid", 5)
-- Find all paths up to depth 5
Levenshtein Distance (Fuzzy String Matching)
CALL string.levenshtein("search text", 2)
-- Find nodes within edit distance 2 (default)
CALL string.levenshtein("search text", 3, "name")
-- Search specific field with custom max distance
Returns _levenshtein_distance and _matched_field in metadata.
Temporal Queries
Nodes and relationships include created_at and updated_at timestamps.
Timeline
CALL temporal.timeline()
-- Returns chronological list of all graph events
Timestamp Range
CALL temporal.range()
-- Returns min/max timestamps in the graph
Nodes in Time Range
CALL temporal.nodesInRange(1704067200000, 1706745600000)
-- Find nodes created within Unix timestamp range (milliseconds)
Graph State at Time
CALL temporal.atTime(1705000000000)
-- Returns graph state as it existed at that timestamp
Semantic Search & Embeddings
Local Embeddings (No API Required)
Graph-Loom includes local embeddings for semantic similarity with three options:
- ONNX (default): Best quality using all-MiniLM-L6-v2 transformer model. Downloads ~90MB on first use.
- TF-IDF: Fast, lightweight term-frequency based vectors, no downloads required
- Word2Vec: Learns semantic word relationships from your graph data (pure Rust implementation)
Configure in Preferences → Embeddings to switch between models. On startup, if the node_embeddings table is empty, all nodes are automatically re-embedded using the selected model.

ONNX Examples (Default - Best Quality)
-- High-quality semantic search using transformer embeddings
CALL embedding.similar("machine learning engineer", 10)
-- Understands synonyms and related concepts
CALL embedding.threshold("software development", 0.7)
TF-IDF Examples
-- Find nodes with similar keywords
CALL embedding.similar("database engineer", 10)
-- Good for exact term matching
CALL embedding.threshold("python developer", 0.3)
Word2Vec Examples
-- Find semantically related nodes (understands word relationships)
CALL embedding.similar("machine learning", 10)
-- May return nodes about "AI", "neural networks", "deep learning"
-- Find neighbors of a node by semantic similarity
CALL embedding.neighbors("node-uuid", 5)
-- Discover related concepts above similarity threshold
CALL embedding.threshold("data science", 0.5)
Word2Vec learns that words appearing in similar contexts are related, enabling discovery of semantic relationships between nodes even when they don't share exact terms.
Fast Approximate Search (HNSW)
CALL embedding.ann("search text", 10)
-- O(log n) approximate nearest neighbor search using HNSW index
Faster than exact search for large graphs. Results include _search_type: hnsw_ann in metadata.
Re-Embedding
CALL embedding.reembed()
-- Clears and rebuilds all embeddings using the selected model
Use this after changing the embedding model in Preferences, or click "Re-Embed All Nodes" in Preferences → Embeddings.
Results include _similarity (cosine) and _distance (L2) in metadata.
Full-Text Search
CALL db.search("search query")
-- Searches node labels and metadata
Graph Schema
CALL db.schema()
-- Returns labels, relationship types, and query suggestions
LLM-Powered Entity Extraction
Configure in Preferences → LLM Settings:
CALL semantic.extract("John works at Acme Corp as a senior developer")
-- Extracts entities using configured LLM or heuristic fallback
Supported providers: OpenAI, Anthropic, Ollama (local)
API & Integration
Enable APIs
Go to Settings → Preferences → API Settings:

- HTTP/WebSocket: Default
127.0.0.1:8787 - gRPC: Default port
50051 - API Key: Optional authentication
HTTP API
Health Check
# Returns: {"status":"healthy","components":{...},"version":"1.15.1"}
Prometheus Metrics
# Returns Prometheus-compatible metrics:
# graph_loom_queries_total{status="success"} 42
# graph_loom_queries_total{status="error"} 3
# graph_loom_query_duration_seconds_sum 12.5
# graph_loom_api_up 1
# graph_loom_nodes_total 150
# graph_loom_relationships_total 200
WebSocket REPL
Connect to ws://127.0.0.1:8787/ws for interactive queries.
gRPC
High-performance interface using Protocol Buffers. See proto/graph_loom.proto for schema.
Available RPCs:
Execute(QueryRequest) -> QueryResponse- Standard request/responseExecuteStream(QueryRequest) -> stream QueryRow- Streaming for large result sets
CLI Shell (glsh)
# Build CLI
# Connect to running instance
The CLI provides an interactive REPL for executing queries against a running Graph-Loom instance:

Python Client
Example client in examples/python_client/. See Python Client README.
Headless Mode
Run as a pure API server without GUI:
Command Line Options
| Flag | Description |
|---|---|
--background, -b |
Run without GUI (background/headless mode) |
--api-enable |
Enable HTTP/WebSocket API |
--api-bind <addr> |
Bind address (default: 127.0.0.1) |
--api-port <port> |
HTTP port (default: 8787) |
--api-key <key> |
Set API authentication key |
--grpc-enable |
Enable gRPC server |
--grpc-port <port> |
gRPC port (default: 50051) |
User Interface
Canvas Navigation
- Pan: Drag background
- Zoom: Scroll wheel (cursor over canvas)
- Select: Click node/relationship
- Multi-select: Rectangle drag or Shift+click
Sidebar Modes
- Tooling: Node/relationship creation, layout controls
- Query: OpenCypher console with autocomplete
- Notes: Markdown notes linked to graph nodes
Node Details
Click on any node to view and edit its details, metadata, and relationships:

Note Editor
Create and manage markdown notes that can be linked to graph nodes:

Keyboard Shortcuts
- Ctrl/Cmd+S: Save
- Ctrl/Cmd+Space: Force autocomplete popup
- Tab/Enter: Accept autocomplete suggestion
- Escape: Dismiss autocomplete/dialogs
- Arrow keys: Navigate autocomplete suggestions
Query Autocomplete
The query console provides IDE-style autocomplete:
- Type to filter suggestions
- Arrow keys to navigate
- Tab/Enter to accept
- Trailing space hides suggestions
- Ctrl/Cmd+Space shows all options
Data Storage
SQLite Backend (Default)
- Location: OS-specific app data directory
- File:
state.db - Features: ACID compliance, FTS5 full-text search, R-tree spatial indexing
Performance Optimizations
- WAL Mode: Write-Ahead Logging for 10-100x faster writes and concurrent reads
- Query Plan Caching: LRU cache (1000 plans) reduces parse overhead for repeated queries
- Property Indexes: B-tree indexes on metadata for fast property lookups
- Incremental Embeddings: Add/remove nodes without full index rebuild
- Batch Operations: Optimized bulk imports (1000-10000 items per transaction)
Versioned Backups
Automatic timestamped backups in RON format for human readability.
Settings
Stored in settings.json:
- macOS:
~/Library/Application Support/Graph-Loom/ - Windows:
%APPDATA%\Graph-Loom\ - Linux:
~/.config/Graph-Loom/
System Tray & Background Mode
When API/gRPC is enabled:
- Close to Tray: Window hides, service continues running
- Tray Menu: Show window or quit application
- Multi-Instance Detection: Second launch brings existing instance to foreground
- CPU Efficiency: Near-zero CPU when backgrounded
Contributing
Development Setup
Running Tests
Code Style
- Follow existing patterns in the codebase
- Use
cargo fmtbefore committing - Run
cargo clippyfor lints
Pull Requests
- Fork the repository
- Create a feature branch
- Make changes with tests
- Submit PR with clear description
Project Structure
src/
├── api/ # HTTP, WebSocket, gRPC servers
│ ├── auth.rs # Authentication & RBAC
│ ├── grpc.rs # gRPC service
│ └── server.rs # HTTP/WebSocket server
├── gql/ # Query language
│ ├── cypher_spec.rs # OpenCypher parser
│ └── query_interface.rs # Query execution
├── graph_utils/ # Core graph operations
│ ├── algorithms.rs # PageRank, centrality, pathfinding
│ ├── graph.rs # Node/Relationship structures
│ └── temporal.rs # Time-travel queries
├── gui/ # User interface
│ └── frontend.rs # egui application
├── persistence/ # Data storage
│ ├── persist.rs # Save/load logic
│ ├── settings.rs # App configuration
│ └── sqlite_backend.rs # SQLite storage
├── semantic/ # AI/ML features
│ ├── embeddings.rs # Local TF-IDF embeddings
│ ├── extraction.rs # Entity extraction
│ ├── llm_client.rs # LLM API client
│ └── rag.rs # Graph RAG
└── main.rs # Application entry point
License
Acknowledgments
Built with: