GTS Graph Transport
GTS (Graph Transport Substrate) is an ontology-independent binary
container and transport format for RDF 1.2 datasets and content-addressed
binary payloads. PurRDF hosts the reference Rust engine,
purrdf-gts (re-exported as purrdf::gts).
This chapter is a user-level tour. The wire format itself is specified in
docs/GTS-SPEC.md
— consult the spec for framing, fold semantics, registries, and conformance
classes; nothing here supersedes it.
The container model, at a high level
A GTS file is a CBOR Sequence of one or more append-only segments. Each segment is a deterministic CBOR header followed by deterministic CBOR frames chained by BLAKE3 content identifiers. The logical dataset is obtained by a deterministic fold over the segment sequence: quads, reifiers, annotations, and binary blobs all become rows of the folded container graph.
Properties that fall out of this design:
- Content-addressed and append-only — history is never rewritten; suppression is itself an appended record. Multi-segment files compose by simple concatenation.
- Partial readability, total reader — the reader verifies the BLAKE3 chain and folds what it can; undecodable frames (unknown codec, encrypted without a key) degrade to opaque nodes plus a diagnostic instead of aborting.
- Binary payloads ride along — the blobs a graph references travel in the same file, content-addressed like everything else.
- RDF 1.2-native — the spec formalizes the triple-term and
rdf:reifiesmapping, blank-node scoping, and multi-segment value union.
Reading and writing from Rust
The container engine (purrdf-gts) owns the wire-format machinery — reader,
writer, fold, verify, COSE, trust policy:
use purrdf::gts::reader;
// Fold GTS bytes into the container graph model, verifying the BLAKE3 chain.
let graph = reader::read(&bytes, /* allow_segments */ true, /* expected_head */ None);
// The fold is total: quads, reifiers, annotations, and blobs are all rows,
// and anything undecodable is preserved as an opaque node plus a diagnostic.
println!("{} quads, {} blobs", graph.quads.len(), graph.blobs.len());
The writer authors frames and produces byte-deterministic single-segment
snapshots (Writer::deterministic) — the GTS writer is under the same
determinism invariant as every PurRDF serializer.
The RdfDataset import/export path lives one layer up: the umbrella crate’s
gts module combines the container engine with the RDF-level adapter
(snapshot composition, content-chain verification), so RDF-facing GTS work
goes through purrdf directly.
Signing and encryption
Frames can be signed and encrypted with COSE; OpenPGP-based checks and a trust-policy layer are also part of the engine. All cryptography is pure Rust — no C toolchain, threads, or syscall dependencies — which is what keeps the whole engine wasm-friendly. An encrypted frame you cannot decrypt is simply an opaque node in the fold: the container remains readable.
Rust-library-only: compaction certificates, proofs and keyring verification
Beyond reading, writing and folding, purrdf-gts and the umbrella’s gts
module carry a verification stack that no other surface reaches — not the
CLI, Python, wasm or C:
- Streamable compaction with a certificate —
compact_streamablerewrites an accretive log into one delivery-ordered segment (leading stream index, trailing offset footer, in-band dictionaries), andcompact_and_certify/verify_compaction/composebind a content projection so that the pre- and post-compaction containers fold to byte-identical canonical content; the verifier recomputes from the pre bytes with its own keyring. Compaction never changes content and mints no content signatures. - Merkle mountain range proofs —
mmr::{root, prove, verify_proof, prove_file}(gts-mmr-proof-v1), andverify_content_chain, which walks COSE signature → head replay → digest inclusion. There is no transparency log and no signed checkpoint service. - OpenPGP transport keys — armored Ed25519 certificates and unencrypted
secret keys (
parse_transport_key,parse_secret_signing_key) withverify_file_with_keyringfor rotation; other algorithms, encrypted keys and v5/v6 packets are rejected, and there is no keyring or revocation store.
Where it stops, stated plainly: the files/tar profile, the nested
GTS-in-blob reader, the trust-policy layer and the deterministic ULID helper
have no direct tests in this repository — their oracle is upstream in
gmeow-gts — and the whole stack is exercised here only through the frozen
vectors and the compaction and certification test files.
Conformance vectors
GTS conformance is defined against a frozen, language-neutral vector
corpus (vectors/ in the repository), shared byte-exact across the
sibling GTS engines in other languages. The vectors are never regenerated or
“fixed” in this repository — the format is governed in the
gmeow-gts project,
alongside the specification and the other reference engines.
The C ABI’s star-layer round-trip
The GTS star-layer round-trip of a dataset containing quoted triples /
reifier bindings succeeds through the kernel
to_gts → read_graph → import_gts_graph path, the same as a star-free
round-trip; crates/rdf-capi/tests/abi.rs’s
gts_star_roundtrip_preserves_the_statement_layer pins it. See
Getting Started: C.
Related
docs/GTS-SPEC.md— the normative wire format (version 0.9-draft, wire-format major 1).- Slices, Mappings & Provenance — the RDF↔GTS loss ledger.
- Getting Started: Python — reading a container back as relational rows.