Skip to content

Commit 4e4727e

Browse files
authored
Extending docs (#205)
1 parent 8cb57dc commit 4e4727e

File tree

2 files changed

+252
-14
lines changed

2 files changed

+252
-14
lines changed

docs/docs/core-concepts.md

Lines changed: 246 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,245 @@ Hypergraph re-imagines traditional client–server apps as **local-first**, **pe
1111

1212
## Table of Contents
1313

14+
- [Knowledge Graphs and GRC-20](#knowledge-graphs-and-grc-20)
1415
- [Spaces](#spaces)
1516
- [Identities](#identities)
1617
- [Inboxes](#inboxes)
17-
- [Knowledge Graph](#knowledge-graph)
1818
- [Events & CRDTs](#events--crdts)
1919
- [Security Model](#security-model)
2020

2121
---
22+
## Knowledge Graphs and GRC-20
23+
24+
Hypergraph adopts **GRC-20** as its canonical data format. Every mutation you perform through the Hypergraph SDK—whether it's adding a note, uploading a photo, or inviting a collaborator—ultimately becomes a set of GRC-20 values bundled into an edit. Once the edit is posted, it becomes part of the global knowledge graph—instantly connecting your data to a world of interoperable apps, spaces, and users. From that moment the edit is immutable and immediately queryable via Hypergraph's hooks and GraphQL APIs.
25+
26+
### 1. The GRC-20 Standard
27+
The GRC-20 standard defines how knowledge is structured, shared, and connected in a decentralized, composable way—enabling interoperability across web3 applications. It specifies the core building blocks: entities, types, properties, relations, and values. Read the [GRC-20 spec on GitHub](https://github.com/graphprotocol/graph-improvement-proposals/blob/main/grcs/0020-knowledge-graph.md).
28+
29+
### 2. Core Data Model Concepts
30+
31+
To illustrate the core pieces of a knowledge graph, we'll break down a single sentence:
32+
33+
> **"Teresa, a photographer, owns a Fujifilm camera."**
34+
35+
#### The Value Model
36+
In GRC-20, each **entity** is a node in the graph with a list of **values**. Each value attaches a **property** (by ID) and a literal value (plus options). Properties define the data type and constraints for their values. **Relations** are first-class objects that connect entities and can have their own properties and metadata.
37+
38+
**Example property definition:**
39+
```json
40+
{
41+
"id": "PROFESSION_ATTR_ID",
42+
"data_type": "TEXT"
43+
}
44+
```
45+
46+
**Example entity with values:**
47+
```json
48+
{
49+
"id": "Teresa_ID",
50+
"values": [
51+
{ "property": "PROFESSION_ATTR_ID", "value": "photographer" }
52+
]
53+
}
54+
```
55+
56+
**Example in code:**
57+
```ts
58+
Graph.createEntity({
59+
name: 'Teresa',
60+
types: [PERSON_TYPE_ID],
61+
values: [
62+
{ property: PROFESSION_ATTR_ID, value: 'photographer' }
63+
]
64+
});
65+
```
66+
67+
#### IDs: Where Do They Come From?
68+
Every entity, attribute, and relation has a unique ID (usually a string, e.g. `PERSON_TYPE_ID`). These are generated per your schema or space, and are required for all operations.
69+
70+
#### Entities & Types
71+
**Entity:** A unique thing in the graph (e.g., `Teresa`, `Camera`).
72+
**Type:** A category for entities (e.g., `Person`, `Device`).
73+
74+
```ts
75+
const PERSON_TYPE_ID = 'PERSON_TYPE_ID';
76+
const DEVICE_TYPE_ID = 'DEVICE_TYPE_ID';
77+
const PROFESSION_ATTR_ID = 'PROFESSION_ATTR_ID';
78+
const BRAND_ATTR_ID = 'BRAND_ATTR_ID';
79+
80+
const { id: cameraId, ops: cameraOps } = Graph.createEntity({
81+
name: 'Camera',
82+
types: [DEVICE_TYPE_ID],
83+
values: [
84+
{ property: BRAND_ATTR_ID, value: 'Fujifilm' },
85+
],
86+
});
87+
88+
const { id: teresaId, ops: teresaOps } = Graph.createEntity({
89+
name: 'Teresa',
90+
types: [PERSON_TYPE_ID],
91+
values: [
92+
{ property: PROFESSION_ATTR_ID, value: 'photographer' },
93+
],
94+
});
95+
```
96+
97+
#### Properties vs. Relations
98+
- **Property:** Attaches data to a single entity (e.g., `Camera``brand``Fujifilm`).
99+
- **Relation:** Connects two entities (e.g., `Teresa``owns``Camera`). Relations are themselves entities and can have their own properties (e.g., `date_acquired`).
100+
101+
```ts
102+
const OWNS_REL_TYPE_ID = 'OWNS_REL_TYPE_ID';
103+
const DATE_ACQUIRED_ATTR_ID = 'DATE_ACQUIRED_ATTR_ID';
104+
105+
import { getEntityRelations } from '@graphprotocol/grc-20';
106+
107+
// 1️⃣ Fetch existing owns relations for Teresa
108+
const existingOwns = getEntityRelations(teresaId, PersonSchema, doc).owns;
109+
110+
// 2️⃣ Only create if none exists pointing to this camera
111+
if (!existingOwns.find(rel => rel.id === cameraId)) {
112+
const { ops: ownsOps } = Graph.createRelation({
113+
fromEntity: teresaId,
114+
toEntity: cameraId,
115+
relationType: OWNS_REL_TYPE_ID,
116+
values: [
117+
{ property: DATE_ACQUIRED_ATTR_ID, value: Graph.serializeDate(new Date('2020-03-15')) },
118+
],
119+
});
120+
// add ownsOps to your edit batch…
121+
}
122+
```
123+
124+
**Relation JSON example:**
125+
```json
126+
{
127+
"id": "OwnsRelation_ID",
128+
"type": "OWNS_REL_TYPE_ID",
129+
"from_entity": "Teresa_ID",
130+
"to_entity": "Camera_ID",
131+
"entity": "OwnsRelationEntity_ID", // rich relation entity UUID (optional)
132+
"position": "a",
133+
"verified": false
134+
}
135+
```
136+
137+
#### Searching and Idempotency
138+
The SDK generates a new ID for every entity or relation you create—even if an identical relation already exists. To avoid duplicates:
139+
140+
- **Query existing relations** via your GraphQL endpoint with a filter on `from`, `relationType`, and `to`.
141+
- **Use** `getEntityRelations` (from `@graphprotocol/grc-20`) on a local handle to list current relations for an entity:
142+
143+
```ts
144+
import { getEntityRelations } from '@graphprotocol/grc-20';
145+
146+
// Returns all non-deleted owns relations from Teresa
147+
const relations = getEntityRelations(teresaId, PersonSchema, doc).owns;
148+
```
149+
150+
- **Check** if a relation linking the same entities already exists before calling `Graph.createRelation`.
151+
152+
If you call `createRelation` without checking, you'll end up with multiple relation entities of the same type between the same entities. Deduplication is the responsibility of your application or schema governance.
153+
154+
#### Minimal Edit Example
155+
Bundle all operations into an edit:
156+
```ts
157+
const ops = [...cameraOps, ...teresaOps, ...ownsOps];
158+
// Publish ops as an edit (see SDK docs for publishing)
159+
```
160+
161+
Let's bring together everything we've learned above—including our example sentence—into a complete GRC-20–compliant TypeScript example that is fully composable with Hypergraph.
162+
163+
```ts title="example.ts"
164+
// Example: "Teresa, a photographer, owns a Fujifilm camera."
165+
// This script uses the @graphprotocol/grc-20 SDK to:
166+
// 1. Create a Camera entity with a brand property
167+
// 2. Create a Teresa entity with a profession property
168+
// 3. Check for an existing 'owns' relation from Teresa to the Camera
169+
// 4. If none exists, create the 'owns' relation entity
170+
// 5. Bundle all operations into a single edit (ops array)
171+
172+
import { Graph, getEntityRelations } from '@graphprotocol/grc-20';
173+
174+
// Replace these with actual IDs from your schema/space
175+
const PERSON_TYPE_ID = 'PERSON_TYPE_ID';
176+
const DEVICE_TYPE_ID = 'DEVICE_TYPE_ID';
177+
const PROFESSION_ATTR_ID = 'PROFESSION_ATTR_ID';
178+
const BRAND_ATTR_ID = 'BRAND_ATTR_ID';
179+
const OWNS_REL_TYPE_ID = 'OWNS_REL_TYPE_ID';
180+
const DATE_ACQUIRED_ATTR_ID = 'DATE_ACQUIRED_ATTR_ID';
181+
182+
// 1️⃣ Create the Camera entity with a brand property
183+
const { id: cameraId, ops: cameraOps } = Graph.createEntity({
184+
name: 'Fujifilm camera',
185+
types: [DEVICE_TYPE_ID],
186+
values: [
187+
{ property: BRAND_ATTR_ID, value: 'Fujifilm' },
188+
],
189+
});
190+
191+
// 2️⃣ Create the Teresa entity with a profession property
192+
const { id: teresaId, ops: teresaOps } = Graph.createEntity({
193+
name: 'Teresa',
194+
types: [PERSON_TYPE_ID],
195+
values: [
196+
{ property: PROFESSION_ATTR_ID, value: 'photographer' },
197+
],
198+
});
199+
200+
// 3️⃣ Fetch existing 'owns' relations for Teresa
201+
const existingOwns = getEntityRelations(teresaId, PersonSchema, doc).owns;
202+
203+
// 4️⃣ Only create if none exists pointing to this camera
204+
let ownsOps = [];
205+
if (!existingOwns.find(rel => rel.id === cameraId)) {
206+
const { ops } = Graph.createRelation({
207+
fromEntity: teresaId,
208+
toEntity: cameraId,
209+
relationType: OWNS_REL_TYPE_ID,
210+
values: [
211+
{ property: DATE_ACQUIRED_ATTR_ID, value: Graph.serializeDate(new Date('2020-03-15')) },
212+
],
213+
});
214+
ownsOps = ops;
215+
}
216+
217+
// 5️⃣ Combine all ops into a single edit
218+
const ops = [...cameraOps, ...teresaOps, ...ownsOps];
219+
console.log('Ops ready for publishing:', ops);
220+
221+
// (Optional) Publish the edit
222+
// Graph.publishEdit({ ops });
223+
```
224+
---
225+
226+
#### Mental Model Recap
227+
- **Entities** are things.
228+
- **Properties** are facts about things, and define the data type.
229+
- **Relations** connect things (and can have their own properties).
230+
- **Values** are atomic facts (entity, property, value).
231+
- **Edits** are batches of changes.
232+
233+
#### Cheat Sheet Table
234+
| Concept | Example in Sentence | GRC-20 Term | Code Snippet |
235+
|----------|---------------------|-------------|--------------|
236+
| Entity | Teresa, Camera | Entity | `{ id, name }` |
237+
| Type | Person, Device | Type | `types: [PERSON_TYPE_ID]` |
238+
| Property | profession, brand | Property | `{ id: BRAND_ATTR_ID, data_type: 'TEXT' }` |
239+
| Relation | owns | Relation | `{ from_entity, to_entity, type }` |
240+
| Value | `Teresa → profession → photographer` | Value | `{ property: PROFESSION_ATTR_ID, value: 'photographer' }` |
241+
| Edit | batch of all values | Edit | `ops: [...]` |
242+
243+
---
244+
245+
_All of the above is not just theory—Hypergraph puts it to work for you._ **When you call the SDK or its React hooks, Hypergraph turns your mutations into values, bundles them into edits, encrypts them (if the Space is private), and syncs them peer-to-peer or anchors them on-chain if the data is public.** As a developer you think in entities and hooks; behind the scenes Hypergraph speaks pure GRC-20.
246+
247+
---
248+
249+
All of these building blocks are specified by the GRC-20 standard and created in code with the GRC-20 SDK.
250+
251+
### 3. The GRC-20 SDK
252+
The [`@graphprotocol/grc-20`](https://www.npmjs.com/package/@graphprotocol/grc-20) SDK is a toolkit for building, reading, and writing GRC-20-compliant knowledge graphs. It provides APIs for creating entities, types, properties, and relations, and handles serialization, publishing to IPFS, and onchain anchoring—making it easy to implement the GRC-20 standard in your apps.
22253

23254
## Spaces
24255

@@ -63,17 +294,6 @@ Inboxes can be **public** (anyone can read) or **private** (E2EE). Auth policies
63294
type InboxSenderAuthPolicy = 'any' | 'members' | 'admins';
64295
```
65296

66-
## Knowledge Graph
67-
68-
Public data isn't shoved into a siloed SQL DB. Instead, Hypergraph publishes JSON-LD to a decentralized Knowledge Graph (IPFS + Polygon Amoy smart contracts).
69-
70-
Benefits:
71-
72-
* **Composability** — one app's `City` objects can be queried by another app.
73-
* **Network effects** — each new Space or entity enriches the shared graph.
74-
75-
A TypeScript codegen tool (see the _TypeSync app_ in `/apps/typesync`) maps your domain models to on-chain schemas so you can query them like regular React hooks.
76-
77297
## Events & CRDTs
78298

79299
1. A client mutates the Automerge document (`doc.put(…​)`).
@@ -97,4 +317,17 @@ When the event log grows large, a peer may emit `sendCompactedUpdate`—a snapsh
97317

98318
### Edit on GitHub
99319

100-
[✏️ Suggest changes](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/core-concepts.md)
320+
[✏️ Suggest changes](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/core-concepts.md)
321+
322+
:::tip Best Practice
323+
**Always check for an existing relation (by `from`, `to`, and `relationType`) before creating a new one.**
324+
325+
This prevents duplicate relations, keeps your data model clean, and avoids ambiguity in queries and UI. The GRC-20 SDK will create a new relation entity every time unless you check first.
326+
:::
327+
328+
:::info Terminology Update
329+
In the latest GRC-20 spec, what were previously called "triples" are now called "values." The "value type" is now called "data type," and data types are defined on the property, not the value. This change makes the model simpler and validation more robust.
330+
:::
331+
332+
**Note:** The data service validates that each value matches the property's data type.
333+

docs/docs/key-features.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Hypergraph is **more than a database**—it's a complete data layer for building
1313

1414
- [Local-first by design](#local-first-by-design)
1515
- [End-to-end encryption](#end-to-end-encryption)
16+
- [Knowledge Graph SDK](#knowledge-graph-sdk)
1617
- [Graph-based data model](#graph-based-data-model)
1718
- [Conflict-free sync (CRDTs)](#conflict-free-sync-crdts)
1819
- [Spaces & fine-grained auth](#spaces--fine-grained-auth)
@@ -38,9 +39,13 @@ Every update is encrypted **on the client** using XChaCha20-Poly1305. Only membe
3839
* **Automatic key rotation** when members join/leave.
3940
* **Multi-device**: each device holds its own key pair.
4041

42+
## Knowledge Graph SDK
43+
44+
Build, link, and publish knowledge as entities and relations using the [`@graphprotocol/grc-20`](https://www.npmjs.com/package/@graphprotocol/grc-20) Knowledge Graph SDK. It makes it easy to organize data into spaces, anchor edits onchain, and work with The Graph's knowledge graph standard.
45+
4146
## Graph-based data model
4247

43-
Under the hood, Hypergraph stores JSON-LD triples that map nicely to **knowledge graphs**. This makes it trivial to expose public data on-chain or query it with SPARQL later.
48+
Under the hood, Hypergraph stores JSON-LD values that map nicely to **knowledge graphs**. This makes it trivial to expose public data on-chain or query it with SPARQL later.
4449

4550
## Conflict-free sync (CRDTs)
4651

0 commit comments

Comments
 (0)