GraphRD is an RDF database written in Rust with zero third-party dependencies in its default
build (HTTPS is an optional tls feature backed by rustls): HTTP server, JSON,
RDF parsers and serializers, storage, SPARQL engine and reasoner are all implemented on the
standard library. It speaks the repository REST protocol and has its own management REST API, so
existing repository clients work against it, and it ships its own web UI inside the binary.
Documentation: https://graphrd.org — getting started, command line, HTTP API, SPARQL, inference, administration and more, under docs/.
Türkçe özet
GraphRD, Rust ile yazılmış, varsayılan derlemesinde hiç üçüncü parti bağımlılığı olmayan bir RDF veritabanıdır: HTTP sunucusu, JSON, RDF ayrıştırıcıları, depolama (LSM, WAL, MVCC), SPARQL motoru ve çıkarım motoru standart kütüphane üzerinde yazıldı. Tek ikili dosya; web arayüzü içinde gelir. Depo REST protokolünü ve SPARQL 1.1 Protocol'ü konuşur, mevcut istemciler çalışır.
- Sorgu: SPARQL 1.1/1.2 sorgu ve güncelleme, RDF-star, federasyon (paralel
SERVICE); tüm W3C RDF ve SPARQL test paketleri geçer. GraphQL ve PostgreSQL protokolüyle SQL. - Çıkarım: RDFS, OWL 2 RL ve yaygın kural setleri (ileri zincirleme, artımlı geri alma),
sorgu anında çıkarım (
infer=at-query), özel.piekuralları. - Arama: BM25 tam metin arama, GeoSPARQL ve uzamsal indeks, HNSW vektör araması, OpenSearch/Elasticsearch bağlayıcıları.
- Doğal dil:
--llm-url,--llm-key,--llm-model(ya da yalnızANTHROPIC_API_KEY/OPENAI_API_KEY) ile sorular düz cümleyle sorulur; dil modeli (Claude ya da OpenAI uyumlu herhangi bir sunucu) SPARQL yazar, GraphRD yalnız okuma sorgularını kabul eder. - Doğrulama ve geçmiş: SHACL Core + SHACL-SPARQL, zamanda yolculuk sorguları, artımlı yedek ve zamana dönük geri yükleme.
- İşletim: Raft kümesi (log sıkıştırma, okuma kopyaları), Prometheus metrikleri, kullanıcılar ve roller, API anahtarları, OpenID Connect, LDAP, denetim kaydı, isteğe bağlı HTTPS.
- Java istemciler:
org.graphrd:graphrd-client(saf Java, sıfır bağımlılık) veorg.graphrd:graphrd-spring-data(Spring Data repository'leri + starter) Maven Central'da.
Hızlı başlangıç: cargo build --release, sonra target/release/graphrd serve ve
http://127.0.0.1:7200. Belgeler docs/ altında (İngilizce); katkı kuralları
katkı rehberinde.
- One binary: the server, the web UI, the CLI and the offline tools — no runtime, no services to babysit.
- Full SPARQL 1.1 Query and Update, plus RDF 1.2 and SPARQL 1.2 (triple terms, annotations, base direction); the official W3C suites pass (see the table below).
- Reasoning included: RDFS to OWL 2 RL rulesets, incremental retraction, query-time
inference, custom
.pierules. - Search beyond patterns: BM25 full-text with typo tolerance and stemming (English, Turkish), vector (HNSW) and geospatial search with a spatial index, OpenSearch/Elasticsearch connectors, RDF Rank.
- Every way in: repository REST protocol, SPARQL 1.1 Protocol, GraphQL with a schema derived from the data, SQL over the PostgreSQL wire protocol, a web UI.
- AI queries: ask in plain language — a language model (Claude or any OpenAI-compatible server, local ones included) writes the SPARQL, GraphRD checks and runs it; read queries only, so the model can never change the data.
- Engineered for durability and concurrency: LSM storage with a fsynced write-ahead log, snapshot isolation (MVCC), time travel, Raft clustering.
- Java client libraries on Maven Central:
org.graphrd:graphrd-client(zero dependencies) andorg.graphrd:graphrd-spring-data(Spring Data repositories + Spring Boot starter).
cargo build --release
./target/release/graphrd serveOpen http://127.0.0.1:7200/, create a repository, import some RDF and run queries.
graphrd serve [OPTIONS]
--port <n> HTTP port (default 7200)
--host <addr> Bind address (default 127.0.0.1)
--data <dir> Data directory (default ./graphrd-data)
--ui-dir <dir> Serve another web UI build at / (GraphRD's UI moves to /ui/)
--auth <user:pass> Require this user; also read from GRAPHRD_AUTH
The server binds to localhost by default. Use --auth before exposing it on a network; it prints a
warning when bound elsewhere without authentication.
In a container (the image runs as an unprivileged user, with the data in a volume):
docker build -t graphrd .
docker run -d -p 7200:7200 -v graphrd-data:/data -e GRAPHRD_AUTH=admin:change-me graphrd# create a repository with RDFS-Plus inference
curl -X POST localhost:7200/rest/repositories -H 'Content-Type: application/json' \
-d '{"id":"zoo","params":{"ruleset":{"value":"rdfsplus-optimized"}}}'
# load Turtle
curl -X POST localhost:7200/repositories/zoo/statements -H 'Content-Type: text/turtle' \
--data-binary @animals.ttl
# query (prefixes declared in loaded Turtle are registered automatically)
curl -G localhost:7200/repositories/zoo -H 'Accept: text/csv' \
--data-urlencode 'query=SELECT ?s ?type WHERE { ?s a ?type }'
# update
curl -X POST localhost:7200/repositories/zoo/statements \
--data-urlencode 'update=PREFIX ex: <http://example.org/> INSERT DATA { ex:a ex:b ex:c }'Offline tools work on a data directory without a server (it is locked while they run). load
adds statements in bulk: files are parsed on all cores and each index gets one sorted run, without
the write-ahead log; the new manifest commits each batch at once, so a crash leaves the data as
it was (about 1.5 million lines/s on 4 cores; repositories that validate SHACL load through the
checked path):
graphrd load --data db --repo zoo animals.ttl more.nq # create the repository if needed
graphrd load --data db --lenient wikidata-dump.nt # skip and count unparsable statements
graphrd query --data db --repo zoo --format csv --explain true 'SELECT ...' # plan on stderr
graphrd update --data db --repo zoo --base http://example.org/ 'INSERT DATA { <a> <b> <c> }'
graphrd dump --data db --repo zoo --graph default --output zoo.ttl
graphrd convert --from trig --to nt --from-graph http://example.org/g data.trig
graphrd backup --data db /backups/db-2026-10-14 # a data directory of its own
graphrd optimize --data db # compact every index
graphrd serve --data db --read-only --cors --timeout 30 # query-only public endpoint
graphrd serve --memory # nothing on disk; data ends with the process- SPARQL 1.1 Query and Update: SELECT, ASK, CONSTRUCT, DESCRIBE; OPTIONAL, UNION, MINUS,
FILTER, BIND, VALUES, sub-queries, property paths (
/ | ^ * + ? !), GROUP BY / HAVING with COUNT, SUM, AVG, MIN, MAX, SAMPLE, GROUP_CONCAT, and the built-in function library (string, numeric, date/time, hash, regex). All update operations, including DELETE/INSERT WHERE, WITH, USING, CLEAR, DROP, ADD, MOVE and COPY. Federated queries:SERVICE <endpoint> { … }(andSERVICE ?var,SERVICE SILENT) send the block to any SPARQL 1.1 endpoint and join its results. - RDF 1.2 and SPARQL 1.2: triple terms
<<( s p o )>>, reified triples<< s p o ~ :id >>(a reifier withrdf:reifies), annotations{| … |}with any number of reifiers, literals with a base direction ("text"@ar--rtl,rdf:dirLangString),VERSION, in every syntax (Turtle, TriG, N-Triples, N-Quads, RDF/XML withrdf:parseType="Triple",rdf:annotationandits:dir) and in queries, templates and updates, withTRIPLE,SUBJECT,PREDICATE,OBJECT,isTRIPLE,LANGDIR,hasLANG,hasLANGDIRandSTRLANGDIR. As in RDF 1.2,<< s p o >>is a reified triple (it denotes a reifier), not the triple itself as in the older RDF-star drafts; data stored with RDF-star subjects stays readable.
- Inference by forward chaining with the rulesets
rdf,rdfs,rdfsplus,owl-horst,owl2-rl, their-optimizedvariants, andowl-max/owl2-*aliases. Explicit and inferred statements are kept apart: queries takeinfer=false, and thegraph:explicit/graph:implicitpseudo-graphs are supported. Deleting a fact retracts what was derived from it.infer=at-queryinfers at query time by backward chaining instead, with nothing stored. - Custom rulesets in the
.pierule format (Prefices,Axioms,RuleswithId:blocks and[Constraint a != b]): set the repository's ruleset to the path of a.piefile.
- Full-text search: set
enableFtsIndexon a repository and query with?literal g:fts "query"(the prefixesg:=http://graphrd.org/fn#,graph:andrank:are predefined). The query language supports words (all must match, any inflection: English Porter and Turkish stemming),"exact phrases",prefix*,word~(typo-tolerant, edit distance up to 2),a OR band-exclusions; a query that finds nothing is retried typo-tolerant.g:ftsScore(?literal, "query")gives the BM25 score,g:ftsHighlight(?literal, "query")the text with the matching words marked, andGET /rest/repositories/{id}/search?query=…&facet=<predicate>hits, highlights and facet counts. Postings live in their own LSM index, are maintained on every write, and can be built over existing data. - Vector search: embeddings stored as
g:vectorliterals are indexed in an HNSW graph;?v g:knn ?qfinds the nearest (p95 1.5 ms over a million vectors),g:similarityscores them, and both combine with full-text search for hybrid ranking. See docs/vectors.md. - GeoSPARQL 1.1 functions (
geof:=http://www.opengis.net/def/function/geosparql/) overgeo:wktLiteralandgeo:geoJSONLiteralvalues (CRS84, EPSG:4326/4258/4269, Web Mercator and WGS 84 / UTM zones): the Simple Features (sfEquals,sfDisjoint,sfIntersects,sfTouches,sfCrosses,sfWithin,sfContains,sfOverlaps), Egenhofer (eh*) and RCC8 (rcc8*) relation families,relatewith a DE-9IM pattern,intersection,union,difference,symDifference,envelope,convexHull,centroid,boundary,dimension,coordinateDimension,spatialDimension,isEmpty,isSimple,asWKT,asGeoJSON,getSRID,minX/minY/maxX/maxY,numGeometries, anddistance,area,length,perimeterin metres (or anotheruom:unit) on the sphere. The geometry engine is GraphRD's own and is checked against GEOS on random geometries (DE-9IM matrices and set operations agree on 10,000 pairs). Topology is planar in coordinate space, as with most GeoSPARQL implementations. A spatial index over every geometry literal answers relation anddistance < rfilters against a geometry without trying every geometry (100,000 geometries: 0.4-3 ms instead of ~500 ms; see docs/geosparql.md). - Search connectors: entities of chosen classes kept in an OpenSearch or Elasticsearch index
as the data changes, and searched from SPARQL with
?e <http://graphrd.org/connector/NAME> "query". See docs/connectors.md. - RDF Rank:
POST /rest/rank/computeruns PageRank over the links between resources; query scores with?node rank:score ?score(orscore3/score5for rounded values). The status turns OUTDATED when data changes. - Autocomplete:
/rest/autocomplete/query?q=suggests IRIs by local-name prefix (used by the Explore search box), from an index rebuilt only after data changes.
With an endpoint, key and model (--llm-url, --llm-key, --llm-model), or just
ANTHROPIC_API_KEY / OPENAI_API_KEY in the environment, a language model (Claude through the
Anthropic Messages API, or any OpenAI-compatible server, local ones included) writes the SPARQL
for a question from a summary of the repository's classes and properties; GraphRD accepts only
read queries without SERVICE, retries once on a parse error, and can run the query:
POST /rest/repositories/{id}/nl-query (docs/nl-query.md). In the web UI
this is the AI Assistant page: the query appears in the editor as the model writes it, and the
answer arrives with the query's results. The boundary is deliberate: the model can read through
GraphRD, never write — write paths (SPARQL updates, SQL, mutations) stay model-free.
- GraphQL without writing a schema:
POST /rest/repositories/{id}/graphqlwith{"query": "...", "variables": {...}}. The schema is derived from the data: every class is a query field, every predicate a field named by its local name, links nest. Root fields takeid,where,limit,offset,orderByanddesc;?sdlreturns the derived schema. MutationscreateT/updateT/deleteTwrite through the same schema, one transaction per request (SHACL-checked); the web UI has a GraphQL page. - SQL over the PostgreSQL protocol (
--pg-port 5432): connect with psql, psycopg, JDBC or BI tools; the database name is the repository. Tables are classes, columns areidplus predicates:SELECT name, age FROM Person WHERE age > 30 ORDER BY age DESC LIMIT 10,COUNT(*),LIKE,IS NULL,SHOW TABLES,information_schema, andSELECT * FROM sparql('…')for any SPARQL query,[LEFT] JOIN … ON a = b,CREATE VIEW(kept in the repository), andINSERT/UPDATE/DELETE(one transaction each, SHACL-checked, replicated in a cluster). Simple and extended (prepared) protocol, text and binary parameters; same users and permissions as HTTP. - HTTP API: the repository protocol (
/repositories) — query and update endpoint,/statements,/size,/contexts,/namespaces, the SPARQL Graph Store protocol at/rdf-graphs/service— the standard SPARQL 1.1 Protocol at/sparqland/repositories/{id}/sparql(a GET without a query returns the Service Description), and a management REST API under/rest(repositories, import, saved queries, locations, security and login, version, monitoring). See docs/http-api.md. - Loading from the web: SPARQL
LOAD <url> [INTO GRAPH <g>]and import-from-URL download RDF (content type, then file extension decide the format; redirects are followed).
- Formats: Turtle(-star), TriG(-star), N-Triples(-star), N-Quads(-star), RDF/XML (including DTD entities) and JSON-LD for import and export, and Notation3 (N3) for import, with formulas as blank-node named graphs; SPARQL results as JSON, XML, CSV and TSV.
- SHACL validation (
isShaclrepository setting): shapes are loaded into the shapes graph (urn:graphrd:shapes). Every write is validated for the nodes it touches (and nodes linking to them); a violating write is rolled back and answered with a 400 and the validation report. All of SHACL Core (complex paths,and/or/not/xone,node, qualified value shapes, property pairs,closed,languageIn,uniqueLang, severities) and SHACL-SPARQL (sh:sparql, SPARQL-based constraint components); the W3C SHACL test suite passes 120/120.graphrd validate --shapes S DATA...validates files offline.GET /rest/repositories/{id}/shacl/validatereports on the whole repository (JSON, or Turtlesh:ValidationReport). - Backup and restore:
GET /rest/recovery/backupreturns a tar archive with every repository's configuration, namespaces and statements;POST /rest/recovery/restorebrings them back (also from the Repositories page).
- Disk-based storage: dictionary-encoded quads in four LSM indexes (SPOG, POSG, OSPG, GSPO). Writes go to a write-ahead log and an in-memory memtable; checkpoints fold them into immutable, block-indexed run files that are merged in a size-tiered way. Terms live in an on-disk dictionary with a hash index. Memory use is bounded by the memtable and caches, not by the data size, and inferred statements are persisted, so restarts are instant.
- Snapshot isolation (MVCC): every query runs on a consistent snapshot of the repository and never waits for a writer; writers never wait for readers. A snapshot costs a few reference-count increments: memtables are frozen into immutable sorted levels and run files are shared.
- Time travel: with the
historyRetentionrepository setting (e.g.7d), a query takesas-of=<dateTime>and runs on the repository as it was then (graphrd query --as-ofoffline); see docs/sparql.md.
- Web UI at
/: repositories and rulesets, a SPARQL editor with highlighting and saved queries, file/text/server-side import, a resource and class-hierarchy explorer, named graphs, namespaces, light and dark themes, and a visual graph explorer of its own (canvas rendering, Barnes–Hut force layout plus radial and hierarchy layouts, neighbourhood highlighting, search, minimap, context menu and PNG export; thousands of nodes) for a resource, a named graph or the schema. - Security (optional,
--auth admin:password): multiple users with roles (ROLE_ADMIN,ROLE_REPO_MANAGER,ROLE_USER) and per-repositoryREAD_REPO_<id>/WRITE_REPO_<id>grants, managed through/rest/security/usersor the Users page; HTTP Basic orAuthorization: Bearer <token>from/rest/login. Passwords are stored salted and iterated. Also API keys for programs, OpenID Connect access tokens (--oidc-issuer, tested with Keycloak), LDAP sign-in (--ldap-url, tested with OpenLDAP) and a JSONL audit log (--audit-log); see docs/security.md. - HTTPS (build with
--features tls, which adds rustls): serve HTTPS with--tls-cert cert.pem --tls-key key.pem, and fetchhttps://sources (SSL_CERT_FILEadds certificate authorities to trust, such as a company CA). The library also has an ACME client (graphrd::acme: certificates from Let's Encrypt with HTTP-01 challenges) and an HTTPS server that picks the certificate by server name. The default build stays free of third-party crates and still fetcheshttp://URLs. - Monitoring:
GET /metricsin the Prometheus text format (query/update/WAL fsync latency histograms, responses, block cache hits, statements per repository, cluster state);serve --slow-query <ms>logs slow queries and updates (/rest/monitor/slow-queries, Monitoring page). - Clustering:
--cluster,--node-idand--cluster-secretmake a Raft cluster: every server holds all the data, followers forward writes to the leader, and a change counts once a majority has it. Followers answer reads from their own copy, which may lag a moment behind; a read with the headerX-GraphRD-Consistency: leaderis answered by the leader and sees every acknowledged write.
src/
main.rs CLI
http.rs HTTP/1.1 server (thread per connection, keep-alive, chunked, multipart)
json.rs JSON parser/serializer
geo.rs GeoSPARQL geometry: WKT/GeoJSON, DE-9IM, overlay, metrics
rdf/ term model, Turtle/TriG/N-Triples/N-Quads parser, RDF/XML (incl. XML parser),
JSON-LD reader, serializers
store.rs indexed quad store, snapshot + WAL persistence, inferred pseudo-graph
reason.rs semi-naive forward-chaining rule engine and rulesets
sparql/ lexer/parser to algebra, evaluator, functions, regex engine, hashes, result formats
server.rs repositories, repository protocol, management REST API, auth, embedded UI
ui/ the web UI (compiled into the binary with include_str!)
Measured on an M2 Pro, with inference and full-text search on:
| Operation | Result |
|---|---|
| Load 3 million triples | 81k triples/s |
| Point lookups, joins, property paths, full-text phrases | 0.1–0.3 ms |
| Durable single write (fsynced before acknowledged) | 1–3 ms |
| Group commit, 8 concurrent writers | 850 commits/s |
Write with --sync off |
0.3 ms |
| Three servers loading combined | 285k triples/s |
| Three-node Raft cluster, mixed workload | 4,942 QPS across nodes |
The full 12-query suite on a second setup (Docker, 4 CPUs, 6 GB, 900k triples — from a 0.58 ms point lookup to a 35 ms join over 198k rows), bulk-load and concurrency numbers, tuning notes and the measuring tools are in docs/performance.md.
Every transaction is appended to a write-ahead log with a commit marker and fsynced before the
client gets its answer, so an acknowledged write survives a crash of the process or of the
machine. A transaction cut short by a crash is dropped whole on restart. --sync off skips the
fsync: writes then take 0.3 ms and survive a crash of the process, but not a power loss.
A SPARQL update request is applied completely or not at all: if one of its operations fails,
the ones before it are undone. Embedded use can also commit without waiting for the disk
(StoreLock::write_relaxed, for records such as logs) and wait for everything with
StoreLock::sync.
- Users and roles with per-repository read and write grants; passwords salted and iterated.
- A user is locked for 15 minutes after 10 wrong passwords; login tokens expire after 12 hours.
serve --max-body <mb> --max-connections <n>(orset_limitswhen embedding) bound the largest request body and the connections served at once, for a server facing the internet.- XML input: entity expansion is capped (no "billion laughs"), nesting depth too.
- The UI never triggers the browser's own login prompt; it shows its sign-in form.
- Search connectors cover OpenSearch and Elasticsearch, not Solr.
- GeoSPARQL: no rewriting of the relation properties (
?a geo:sfWithin ?bas a triple pattern). - Queries have no default time limit unless the server is started with
--timeout; passtimeoutper request.
cargo test --releasepython3 tests/docs/check_examples.py runs the curl examples of docs/ against a fresh server.
Unit tests cover the parsers, serializers, regex and hash code, the store and its persistence, inference and retraction, and SPARQL evaluation. HTTP integration tests drive the server end to end, including the authentication flow.
tests/w3c/run.py runs the official W3C test suites (w3c/rdf-tests)
against GraphRD:
| Suite | Result |
|---|---|
| N-Triples, N-Quads, Turtle, TriG, RDF/XML (RDF 1.1) | 993 / 993 |
| N-Triples, N-Quads, Turtle, TriG, RDF/XML (RDF 1.2) | 1,334 / 1,334 |
| SPARQL 1.0 syntax and query evaluation | 487 / 487 |
| SPARQL 1.1 Query | 353 / 353 |
| SPARQL 1.1 Update | 157 / 157 |
| SPARQL 1.1 result formats (CSV, TSV) | 10 / 10 |
| SPARQL 1.2 (syntax, evaluation, triple terms, base direction) | 271 / 271 |
| SPARQL 1.1 Federated Query (SERVICE, local stand-in endpoints) | 7 / 7 |
| SPARQL 1.1 Entailment Regimes (RDF, RDFS, D, OWL 2 RDF-Based via OWL 2 RL) | 46 / 48 (22 OWL Direct/RIF-only skipped) |
| RDF Dataset Canonicalization (RDFC-1.0, w3c/rdf-canon) | 86 / 86 |
| JSON-LD 1.1 to RDF (w3c/json-ld-api) | 456 / 456 |
| Notation3 grammar and extended grammar (w3c/N3) | 1,083 / 1,085 |
git clone --depth 1 https://github.com/w3c/rdf-tests /tmp/rdf-tests
python3 tests/w3c/run.py /tmp/rdf-testsThe other suites are cloned next to rdf-tests and run by name (rdfc10, jsonld-toRdf, n3). The
two N3 cases not passed are a 1,080-level nesting of formulas, beyond GraphRD's nesting guard
against hostile input, and a test whose expected file holds a cwm-specific absolute file: IRI.
The suites run with unionDefaultGraph = false (a separate default graph, as the tests assume).
The SPARQL 1.1 Protocol and Service Description suites (37 tests) run with
uv run --with rdflib python3 tests/w3c/protocol.py /tmp/rdf-tests.
Run them by name: entailment, service. Not covered: OWL 2 Direct Semantics and RIF entailment
(they need a description logic reasoner). RDF 1.2
and SPARQL 1.2 are W3C drafts; the suites are those of the drafts at the time of testing.
The production suite (tests/prod/run.sh, about 2 minutes; FULL=1 for long runs) drives real
server processes:
| Test | What it checks |
|---|---|
differential.py |
110+ queries and 18 updates compared with rdflib (spec-checked where rdflib is wrong) |
regex_diff.py |
thousands of random regex patterns compared with Python's re; pathological patterns stay linear |
robustness.py |
protocol abuse, slow clients, deeply nested queries and Turtle, 2,300 fuzzed inputs, PostgreSQL port garbage |
crash.py |
SIGKILL at random moments under concurrent writes and bulk loads: no acknowledged write lost, no transaction half-applied |
geo_diff.py |
GeoSPARQL geometry vs GEOS: DE-9IM matrices and set operations on random geometries |
soak.py |
concurrent bank transfers (no lost updates), atomic pairs seen by readers, latency, memory growth, restart |
Java, on Maven Central — source, documentation and tests in their own repositories:
- graphrd-client — the pure-Java client,
zero dependencies: SPARQL, statements in any RDF format, repository management and a
JPA-shaped object mapper (
org.graphrd:graphrd-client). - graphrd-spring-data — Spring Data
repositories and the Spring Boot starter on top of the client
(
org.graphrd:graphrd-spring-data; the client comes with it).
<dependency>
<groupId>org.graphrd</groupId>
<artifactId>graphrd-spring-data</artifactId>
<version>1.0.0</version>
</dependency>