A binary property graph format for decentralized knowledge networks.
GRC-20 is designed for encoding, decoding, and synchronizing graph data across distributed systems with support for event-sourced architecture, efficient binary transmission, and cross-language interoperability.
- Property Graph Model — Entities, relations, and typed properties with 13 data types
- Event Sourced — All state changes expressed as append-only operations
- Binary Optimized — Dictionary interning and zstd compression for minimal wire size
- Deterministic — Canonical encoding for content addressing and signatures
- Cross-Platform — Rust and TypeScript implementations with identical behavior
[dependencies]
grc-20 = { git = "https://github.com/geobrowser/grc-20" }npm install @geoprotocol/grc-20use grc_20::{EditBuilder, encode_edit, decode_edit};
use grc_20::genesis::properties;
// Create an edit with entities and relations
let edit = EditBuilder::new(edit_id)
.name("My Edit")
.author(author_id)
.create_entity(entity_id, |e| e.text(properties::name(), "Hello", None))
.build();
// Encode to binary
let bytes = encode_edit(&edit)?;
// Decode back
let decoded = decode_edit(&bytes)?;import { EditBuilder, encodeEdit, decodeEdit, properties } from '@geoprotocol/grc-20';
// Create an edit
const edit = new EditBuilder(editId)
.setName('My Edit')
.addAuthor(authorId)
.createEntity(entityId, (e) => e.text(properties.name(), 'Hello'))
.build();
// Encode to binary
const bytes = encodeEdit(edit);
// Decode back
const decoded = decodeEdit(bytes);| Type | Description |
|---|---|
BOOLEAN |
Boolean value |
INTEGER |
64-bit signed integer (with optional unit) |
FLOAT |
IEEE 754 double precision (with optional unit) |
DECIMAL |
Arbitrary-precision decimal (with optional unit) |
TEXT |
UTF-8 string (with optional language) |
BYTES |
Opaque byte array |
DATE |
Calendar date with timezone |
TIME |
Time of day with timezone |
DATETIME |
Timestamp with timezone |
SCHEDULE |
RFC 5545/7953 iCalendar schedule |
POINT |
WGS84 coordinates (lat, lon, optional alt) |
RECT |
Axis-aligned bounding box |
EMBEDDING |
Dense vectors for semantic search |
| Operation | Description |
|---|---|
CreateEntity |
Create or upsert an entity with values |
UpdateEntity |
Modify entity values (set/unset) |
DeleteEntity |
Tombstone an entity |
RestoreEntity |
Restore a deleted entity |
CreateRelation |
Create a directed edge with optional position and space/version pins |
UpdateRelation |
Update relation position or mutable fields |
DeleteRelation |
Tombstone a relation |
RestoreRelation |
Restore a deleted relation |
CreateValueRef |
Create a referenceable value for use in relations |
Value references allow creating referenceable values that can be used as relation endpoints:
// Create a value ref that can be targeted by relations
.addOp({ type: 'createValueRef', id: valueRefId, entity: entityId, property: propId })TEXT values support language variants via BCP 47 language IDs:
// Set text with language
e.text(nameProp, "Hello", languages.english())
e.text(nameProp, "Hola", languages.spanish())
// Unset specific language variant
u.unsetLanguage(nameProp, languages.english())Relations support advanced features for knowledge graphs:
- Space Pins — Pin relation to a specific space version
- Version Pins — Pin endpoints to specific entity versions
- Position — Lexicographic ordering for relation lists
- Reification — Relations can target value refs for statement-level metadata
For content addressing and deterministic hashing:
const bytes = encodeEdit(edit, { canonical: true });Canonical mode ensures identical edits produce identical bytes regardless of construction order.
GRC-20 uses a custom binary format optimized for size and decode speed:
- GRC2 — Uncompressed format with dictionary interning
- GRC2Z — zstd compressed format
Both formats support canonical encoding for deterministic content addressing.
Run the comparison benchmark:
cd rust/crates/grc-20-compare
cargo run --releaseExample output comparing GRC-20 vs Protocol Buffers on 153k cities:
╔══════════════════════════════════════════════════════════════════════════════╗
║ GRC-20 vs Proto Benchmark Comparison ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Dataset: 153728 cities | JSON size: 193.7 MB ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ SIZE ║
║ ┌─────────────────┬─────────────────┬─────────────────┬───────────────────┐ ║
║ │ │ GRC-20 │ Proto │ Winner │ ║
║ ├─────────────────┼─────────────────┼─────────────────┼───────────────────┤ ║
║ │ Uncompressed │ 73.5 MB │ 252.6 MB │ GRC-20 3.4x │ ║
║ │ Compressed │ 25.2 MB │ 34.3 MB │ GRC-20 1.4x │ ║
║ │ vs JSON │ 13.0% │ 17.7% │ │ ║
║ └─────────────────┴─────────────────┴─────────────────┴───────────────────┘ ║
╠──────────────────────────────────────────────────────────────────────────────╣
║ ENCODE TIME ║
║ ┌─────────────────┬─────────────────┬─────────────────┬───────────────────┐ ║
║ │ │ GRC-20 │ Proto │ Winner │ ║
║ ├─────────────────┼─────────────────┼─────────────────┼───────────────────┤ ║
║ │ Uncompressed │ 120.0 ms │ 180.0 ms │ GRC-20 1.5x │ ║
║ │ Compressed │ 320.0 ms │ 360.0 ms │ GRC-20 1.1x │ ║
║ └─────────────────┴─────────────────┴─────────────────┴───────────────────┘ ║
╠──────────────────────────────────────────────────────────────────────────────╣
║ DECODE TIME ║
║ ┌─────────────────┬─────────────────┬─────────────────┬───────────────────┐ ║
║ │ │ GRC-20 │ Proto │ Winner │ ║
║ ├─────────────────┼─────────────────┼─────────────────┼───────────────────┤ ║
║ │ Uncompressed │ 145.0 ms │ 710.0 ms │ GRC-20 4.9x │ ║
║ │ Compressed │ 295.0 ms │ 850.0 ms │ GRC-20 2.9x │ ║
║ └─────────────────┴─────────────────┴─────────────────┴───────────────────┘ ║
╚══════════════════════════════════════════════════════════════════════════════╝
grc-20/
├── spec.md # GRC-20 v2 specification
├── docs/ # Design documentation
│ ├── requirements.md # P0-P3 requirements
│ └── design-faq.md # Design rationale
├── rust/ # Rust implementation
│ └── crates/
│ ├── grc-20/ # Core library
│ ├── grc-20-bench/ # Benchmarks
│ ├── grc-20-compare/ # Format comparison tool
│ └── grc-20-proto-bench/# Protobuf baseline
├── typescript/ # TypeScript implementation
│ └── src/
│ ├── builder/ # EditBuilder API
│ ├── codec/ # Encoder/decoder
│ ├── types/ # Type definitions
│ ├── genesis/ # Well-known IDs
│ └── util/ # Utilities
└── data/ # Sample datasets (compressed)
cd rust
cargo build --release
cargo testcd typescript
npm install
npm run build
npm test- Specification — Complete binary format specification
- Requirements — Design requirements and priorities
- Design FAQ — Rationale for design decisions
Conforms to GRC-20 v2 specification version 0.19.0.
- Determinism — GRC-20 supports canonical encoding for reproducible content hashes
- Size — Dictionary interning saves ~12 bytes per UUID reference
- Simplicity — ~200 lines to implement vs 500-1000 for Protobuf
- No pre-write state validation needed
- O(1) append vs O(log N) disk reads
- Supports offline-first workflows
- Enables CRDT-style convergence
- Optimized specifically for property graph operations
- Native support for multi-value properties and language variants
- Built-in compression with zstd
- Designed for content addressing from the start
MIT OR Apache-2.0