# Hypergraph > Framework for building web3 apps This file contains all documentation content in a single document following the llmstxt.org standard. ## in another tab ## Running Connect and Sync Server Locally To run the Connect and Sync Server locally, you need to get the Hypergraph repository: ```bash git clone https://github.com/graphprotocol/hypergraph.git cd hypergraph pnpm install ``` ```bash cd apps/connect pnpm dev # in another tab cd apps/server pnpm dev ``` The Connect app is available at `http://localhost:5173` and the Sync Server is available at `http://localhost:3000`. --- ## AI Usage # AI Usage ## Cursor You can use the Docs feature to add Hypergraph documentation and reference it in your prompts. 1. Type `@docs` and select `Add new doc` 2. Provide the `https://docs.hypergraph.thegraph.com/llms-full.txt` URL 3. Add `Hypergraph` as the name Now you can mention `@Hypergraph` in your prompts whenever you work with Hypergraph. ## Docs for LLMs We support the [llms.txt](https://llmstxt.org/) standard for making documentation available to llms. We offer the following pages: - [`/llms.txt`](/llms.txt) — a listing of the available pages - [`/llms-full.txt`](/llms-full.txt) — complete documentation --- ## API Reference # 📚 API Reference This section documents the main **Space Events** and the matching client-side helpers in `@graphprotocol/hypergraph` and `@graphprotocol/hypergraph-react`. > ℹ️ The SDK abstracts most serialization, encryption and validation. You'll rarely emit raw events yourself—**Hooks** and **helper functions** should cover 95 % of use-cases. Still, understanding their wire format helps with debugging and server integration. ## Table of Contents - [`createSpace`](#createspace) - [`deleteSpace`](#deletespace) - [`createInvite`](#createinvite) - [`acceptInvite`](#acceptinvite) - [`createSpaceInbox`](#createspaceinbox) - [`sendUpdate`](#sendupdate) - [`sendCompactedUpdate`](#sendcompactedupdate) --- ## Event list | Event | Helper | HTTP / WS Path | Auth | Description | | ------------------ | ------------------ | -------------------------------- | ------------------ | ------------------------------------ | | `createSpace` | `createSpace()` | `/spaces` (POST) | **SIWE** + signature | Bootstrap a new Space. | | `deleteSpace` | `deleteSpace()` | `/spaces/:id` (DELETE) | `admin` | Soft-delete a Space. | | `createInvite` | `inviteToSpace()` | `/spaces/:id/invites` (POST) | `admin` | Create an invitation to a Space. | | `acceptInvite` | `acceptInvitation()` | `/invites/:id/accept` (POST) | Invite signature | Accept an invitation & join a Space. | | `createSpaceInbox` | `createInbox()` | `/spaces/:id/inboxes` (POST) | `admin` | Create a new inbox in a Space. | | `sendUpdate` | _internal_ | `/updates` (WS) | `member` | Send a CRDT patch to peers. | | `sendCompactedUpdate` | _internal_ | `/updates` (WS) | `member` | Send a snapshot of the update log. | All payloads are JSON objects transported over either: - **WebSocket** — default for low-latency real-time sync. - **HTTP** — optional fallback for bootstrapping or server-to-server calls. --- ## `createSpace` | | | | --------- | ---------------------------------------------------- | | **Method** | `POST /spaces` (HTTP) **or** WebSocket event | | **Auth** | Signed with the creator's _signature key_ + SIWE cookie | | **Body** | See event schema below. | | **Success** | `201 Created` with `{ "spaceId": "…" }` | | **Errors** | `409 AlreadyExists`, `401 Unauthorized`, `422 InvalidSignature` | ### Event Schema The `CreateSpaceEvent` contains the initial parameters for a new space. ```typescript export const CreateSpaceEvent = Schema.Struct({ transaction: Schema.Struct({ type: Schema.Literal('create-space'), id: Schema.String, creatorAccountId: Schema.String, }), author: EventAuthor, // { accountId: string, signature: { hex: string, recovery: number } } }); ``` ### Request Example ```json title="POST /spaces" { "eventId": "6db7b5f0", "spaceId": "efc45a11", "transaction": { "type": "create-space", "id": "efc45a11", "creatorAccountId": "did:pkh:eip155:1:0x123..." }, "author": { "accountId": "did:pkh:eip155:1:0x123...", "signature": { "hex": "0xabc...", "recovery": 1 } } } ``` ### Response Example ```json title="201 Created" { "spaceId": "efc45a11" } ``` --- ## `deleteSpace` | | | | --------- | ---------------------------------------------------- | | **Method** | `DELETE /spaces/:id` (HTTP) **or** WebSocket event | | **Auth** | `admin` role in the space | | **Success** | `200 OK` | | **Errors** | `401 Unauthorized`, `404 NotFound` | ### Event Schema The `DeleteSpaceEvent` marks a space for soft-deletion. It requires referencing the hash of the previous event to maintain chain integrity. ```typescript export const DeleteSpaceEvent = Schema.Struct({ transaction: Schema.Struct({ type: Schema.Literal('delete-space'), id: Schema.String, // The ID of the space to delete previousEventHash: Schema.String, }), author: EventAuthor, }); ``` --- ## `createInvite` | | | | --------- | ---------------------------------------------------- | | **Method** | `POST /spaces/:id/invites` (HTTP) **or** WebSocket event | | **Auth** | `admin` role in the space | | **Success** | `201 Created` | | **Errors** | `401 Unauthorized`, `404 NotFound`, `422 Unprocessable Entity` | ### Event Schema The `CreateInvitationEvent` creates a new single-use invitation for an account to join the space. ```typescript export const CreateInvitationEvent = Schema.Struct({ transaction: Schema.Struct({ type: Schema.Literal('create-invitation'), id: Schema.String, // The ID of the space inviteeAccountId: Schema.String, // The account ID of the user being invited previousEventHash: Schema.String, }), author: EventAuthor, }); ``` --- ## `acceptInvite` | | | | --------- | ---------------------------------------------------- | | **Method** | `POST /invites/:id/accept` (HTTP) **or** WebSocket event | | **Auth** | Signature from the invited account | | **Success** | `200 OK` | | **Errors** | `401 Unauthorized`, `404 NotFound` | ### Event Schema The `AcceptInvitationEvent` is created when a user accepts an invitation to join a space. This adds them to the member list. ```typescript export const AcceptInvitationEvent = Schema.Struct({ transaction: Schema.Struct({ type: Schema.Literal('accept-invitation'), id: Schema.String, // The ID of the space previousEventHash: Schema.String, }), author: EventAuthor, // The new member }); ``` --- ## `createSpaceInbox` | | | | --------- | ---------------------------------------------------- | | **Method** | `POST /spaces/:id/inboxes` (HTTP) **or** WebSocket event | | **Auth** | `admin` role in the space | | **Success** | `201 Created` | | **Errors** | `401 Unauthorized`, `404 NotFound` | ### Event Schema The `CreateSpaceInboxEvent` creates a new inbox within a space, which can be used for direct or broadcast messaging between members. ```typescript export const CreateSpaceInboxEvent = Schema.Struct({ transaction: Schema.Struct({ type: Schema.Literal('create-space-inbox'), id: Schema.String, // The ID of the new inbox spaceId: Schema.String, // The ID of the space inboxId: Schema.String, encryptionPublicKey: Schema.String, secretKey: Schema.String, // Should be encrypted isPublic: Schema.Boolean, authPolicy: InboxSenderAuthPolicy, // 'all-members' | 'admins-only' | 'self-only' previousEventHash: Schema.String, }), author: EventAuthor, }); ``` --- ## More endpoints The remaining endpoints (`sendUpdate`, `sendCompactedUpdate`) are used internally for state synchronization and are not typically called directly. For a deeper understanding of the entire event-sourcing model, you can refer to the type definitions exported from the SDK: ```ts import { SpaceEvents } from '@graphprotocol/hypergraph'; // e.g., SpaceEvents.CreateSpaceEvent ``` --- ### Edit on GitHub [✏️ Improve this page](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/api-reference.md) --- ## Authentication # Authentication ## Geo Connect The default and recommended way to authenticate is via Geo Connect. Geo Connect is a dedicated application hosted by the GeoBrowser team. Through Geo Connect you can authenticate with your GeoBrowser account and use it to selectively delegate access to your private and public spaces. If you create you application using TypeSync or use the [hypergraph-app-template](https://github.com/geobrowser/hypergraph-app-template) the full authentication flow is already implemented for you. In the connect app you can create spaces. In the near future you will be able to delete private spaces and you also will be able to give an app permissions to create new private and/or public spaces. ## Hypergraph API ```tsx import { useHypergraphAuth } from "@graphprotocol/hypergraph-react"; function RouteComponent() { const { authenticated, identity } = useHypergraphAuth(); } ``` - `authenticated` - a boolean indicating if the user is authenticated - `identity` - the identity of the logged in user ## Authentication Flows with Geo Connect ### Signup coming from an App (without an existing Geo account) 1. User is opening App (Running App) ![Opening App](../static/img/authentication/main_app.png) 2. Clicks on "Sign in/Sign up with Geo" - Redirect to Connect ![Sign in/Sign up with Geo](../static/img/authentication/signup_geo.png) 3. Sign up for Connect -> Email + One-time Code ![Email](../static/img/authentication/login_email.png) ![One time code](../static/img/authentication/login_otp.png) ![Sign message](../static/img/authentication/login_sign_message.png) 4. Connect: "Do you authorize this app (App ID, redirect URL)" - Select spaces - Click "Authorize" -> Redirect to App ![Authorize](../static/img/authentication/authorize_app.png) 5. You are logged into the app with your account ### Signup coming from an App (with an existing Geo account) 1. User is opening App (Running App) ![Opening App](../static/img/authentication/main_app.png) 2. Clicks on "Sign in/Sign up with Geo" - Redirect to Connect ![Sign in/Sign up with Geo](../static/img/authentication/signup_geo.png) 3. Login in the connect App -> Email + One-time Code ![Email](../static/img/authentication/login_email.png) ![One time code](../static/img/authentication/login_otp.png) ![Sign message](../static/img/authentication/login_sign_message.png) 4. Connect: "Do you authorize this app (App ID, redirect URL)" - Select spaces - Click "Authorize" -> Redirect to App ![Authorize](../static/img/authentication/authorize_app.png) 5. You are logged into the app with your account ### Login coming from an App (user is logged out from app and connect) 1. User is opening App (Running App) ![Opening App](../static/img/authentication/main_app.png) 2. Clicks on "Sign in/Sign up with Geo" - Redirect to Connect ![Sign in/Sign up with Geo](../static/img/authentication/signup_geo.png) 3. Login in the connect App -> Email + One-time Code ![Email](../static/img/authentication/login_email.png) ![One time code](../static/img/authentication/login_otp.png) ![Sign message](../static/img/authentication/login_sign_message.png) 4. Do you want to login with this app? (App ID, redirect URL) - Select spaces (optional) - Click "Authorize" -> Redirect to App ![Authorize](../static/img/authentication/authorize_app.png) 5. You are logged into the app with your account ### Login coming from an App (user is logged out from app and logged in to connect) 1. User is opening App (Running App) ![Opening App](../static/img/authentication/main_app.png) 2. Clicks on "Sign in/Sign up with Geo" - Redirect to Connect ![Sign in/Sign up with Geo](../static/img/authentication/signup_geo.png) 3. Do you want to login with this app? (App ID, redirect URL) - Select spaces (optional) - Click "Authorize" -> Redirect to App ![Authorize](../static/img/authentication/authorize_app.png) 4. You are logged into the app with your account ## Geo Connect API ### `redirectToConnect` ```tsx import { useHypergraphApp } from "@graphprotocol/hypergraph-react"; function Login() { const { redirectToConnect } = useHypergraphApp(); return ( { redirectToConnect({ storage: localStorage, connectUrl: "https://connect.geobrowser.io/", successUrl: `${window.location.origin}/authenticate-success`, // your app id (any valid uuid) appId: "93bb8907-085a-4a0e-83dd-62b0dc98e793", redirectFn: (url: URL) => { window.location.href = url.toString(); }, }); }} > Authenticate with Connect ); } ``` ### `processConnectAuthSuccess` ```tsx import { useHypergraphApp } from "@graphprotocol/hypergraph-react"; function RouteComponent() { const { ciphertext, nonce } = Route.useSearch(); // get the ciphertext and nonce from the URL const { processConnectAuthSuccess } = useHypergraphApp(); const navigate = useNavigate(); useEffect(() => { processConnectAuthSuccess({ storage: localStorage, ciphertext, nonce }); console.log("redirecting to /"); navigate({ to: "/", replace: true }); }, [ciphertext, nonce, processConnectAuthSuccess, navigate]); return Authenticating …; } ``` --- ## Core Concepts # 🧠 Core Concepts Hypergraph re-imagines traditional client–server apps as knowledge graphs. Understanding the following building blocks will help you design applications that feel real-time, privacy-preserving, and interoperable by default. ## Table of Contents - [Knowledge Graphs](#knowledge-graphs) - [Spaces](#spaces) - [Identities](#identities) - [Inboxes](#inboxes) - [Events & CRDTs](#events--crdts) - [Security Model](#security-model) - [GRC-20: The Protocol Under the Hood](#grc-20-the-protocol-under-the-hood) --- ## Knowledge Graphs Traditional databases store data in rows and columns. Knowledge graphs store data as **networks of connected information**—think of it like a mind map where every piece of information can link to any other piece. ### Why Knowledge Graphs? Imagine you're building a social app. In a traditional database, you might have separate tables for `users`, `posts`, and `likes`. But what if you want to find "posts by photographers that my friends liked"? That requires complex joins across multiple tables. In a knowledge graph, the relationships *are* the data: ```mermaid graph LR Teresa[👩 Teresa] -->|profession| Photography[📸 Photography] Teresa -->|owns| Camera[📷 Fujifilm X100] Teresa -->|posted| Photo[🖼️ Street Photo] Alex[👨 Alex] -->|friend_of| Teresa Alex -->|liked| Photo ``` This makes complex queries natural and fast. Plus, your data model can evolve organically—just add new types of entities and relationships without schema migrations. ### The Hypergraph Advantage Hypergraph takes knowledge graphs further by making them: - **🔒 Private by default** — Your personal data stays encrypted on your device - **🌐 Peer-to-peer** — No central server required; collaborate directly with friends - **⚡ Real-time** — Changes sync instantly across all your devices - **🔗 Interoperable** — Your data works across different apps that speak the same protocol > **The magic:** Under the hood, Hypergraph serializes everything using the **GRC-20** standard. As a developer, you just work with simple SDK calls—Hypergraph handles the complex cryptography, serialization, and networking. If you're curious about the low-level details, check out the [GRC-20 section](#grc-20-advanced) below. ## Spaces A **Space** is the fundamental unit of collaboration. * Think of it as a **folder**, **Slack channel**, or **Google Doc**—it groups both *people* and *data*. * Each Space maps 1-to-1 with an **Automerge** document for conflict-free offline editing. * Membership & roles (`member`, `editor`, `admin`) are tracked by an append-only _Space Event Log_. ### Lifecycle events | Event | Purpose | |-------|---------| | `createSpace` | Bootstrap a new Space and establish its first encryption key. | | `deleteSpace` | Mark the Space as deleted (soft delete). | | `updateMember` | Promote or demote a member role. | | `removeMember` | Kick a member and rotate keys. | | `createInvite` / `acceptInvite` | Securely invite users—keys are boxed to the invitee's public key. | All events are **signed** by the author and **verified** by the sync server before broadcast. ## Identities Every user controls an **Identity** defined by three asymmetric keypairs: 1. **Signature keys** — Ed25519 keys used to sign Space Events. 2. **Encryption keys** — X25519 keys used to encrypt private Space data. 3. **Account keys** — An EVM account (via wallet) used for SIWE authentication. Identities are encrypted with a **session token** and stored in the browser (`localStorage`, IndexedDB, or the filesystem in React Native). This keeps the SDK _stateless_—you can log in on multiple devices without a backend. ## Inboxes An **Inbox** is a lightweight message queue that delivers updates or DMs. * **Account Inboxes** belong to a single user. * **Space Inboxes** broadcast to all members of a Space. Inboxes can be **public** (anyone can read) or **private** (E2EE). Auth policies decide who may send: ```ts type InboxSenderAuthPolicy = 'any' | 'members' | 'admins'; ``` ## Events & CRDTs 1. A client mutates the Automerge document (`doc.put(…​)`). 2. The SDK encodes the change as **CRDT updates**. 3. Updates are encrypted with the current **spaceKey** and batched into a `sendUpdate` event. 4. The sync server verifies, persists, and broadcasts updates to online peers. 5. Peers apply the updates; conflicts resolve automatically. When the event log grows large, a peer may emit `sendCompactedUpdate`—a snapshot that starts a fresh log segment. ## Security Model | Threat | Mitigation | |--------|-----------| | Server reads private data | **E2EE** — all document updates are encrypted client-side with a per-Space symmetric key. | | Forged events | **Signature verification** for every event using the author's public key. | | Stale clients | Each event carries `lastKnownSpaceEventId`; server rejects out-of-date mutations. | | Key leakage on member removal | **Key rotation** through `removeMember` → generates a new `spaceKey`. | ## How Can I Explore a Space's Data? You can browse a space's data using [Geo Browser's Testnet](https://testnet.geobrowser.io/root). Simply enter the Space ID you want to explore into the search bar to view its contents. If you do not have access to the data due to privacy restrictions, you will not be able to view the data. ## GRC-20: The Protocol Under the Hood > **⚠️ Advanced Section:** You don't need to understand GRC-20 to build with Hypergraph! This is for developers who want to understand the underlying protocol or need low-level access to the knowledge graph. Think of GRC-20 as the "assembly language" of knowledge graphs. While Hypergraph gives you high-level React hooks and intuitive APIs, GRC-20 defines the precise data format that makes everything interoperable. ### Why Does GRC-20 Exist? Imagine if every social app stored data differently—Instagram used JSON, TikTok used XML, Twitter used CSV. Your photos, posts, and connections would be trapped in silos forever. GRC-20 solves this by creating a **universal format** for knowledge. Any app that speaks GRC-20 can read, write, and build upon data created by any other GRC-20 app. ### Edit on GitHub [✏️ Suggest changes](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/core-concepts.md) :::tip Best Practice **Always check for an existing relation (by `from`, `to`, and `relationType`) before creating a new one.** 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. ::: :::info Terminology Update 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. ::: **Note:** The data service validates that each value matches the property's data type. --- ## Frequently Asked Questions # ❓ FAQ ## General ### What is Hypergraph? > Hypergraph is a TypeScript-first framework for building local-first apps that syncs encrypted data to a shared knowledge graph. ### Does it replace my backend? Yes—Hypergraph **is** your data layer. You still host a thin sync server, but your business logic lives entirely on the client. ### Who is Hypergraph for? Developers building collaborative, consumer-facing apps that require real-time data sync, end-to-end encryption, and public knowledge graph interoperability. Ideal for those who want to focus on client-side logic without managing backend infrastructure. ### What problems does Hypergraph solve? - Real-time sync of private data across users and devices with E2EE. - No traditional backend or database required—framework handles storage and sync. - Publishing and consuming public data in an interoperable knowledge graph. - Built-in user authentication and access control. - Enables network effects by reusing existing data across apps. ### What assumptions do we make about developers? We assume you are comfortable writing React applications in TypeScript and familiar with common UI patterns (e.g., inboxes). ### How can I integrate Hypergraph into an existing application? You can add Hypergraph as a collaboration and privacy layer to an existing app, enabling real-time sync and end-to-end encryption while keeping your current stack for other functionality. ### Where can I find more examples or support? Browse our GitHub repository for sample apps and open issues. Join the community through our issue tracker and discussion forums. ### How can I share feedback? Provide feedback via GitHub issues or our upcoming feedback form linked in the docs. ### How do I get started? See our Quickstart guide: [🚀 Quickstart](/docs/quickstart). ### What are Spaces? Spaces are the primary grouping for users and content in Hypergraph; they represent collaboration contexts and topics. Only members of a space can access its private data. ### Where can I find the API reference? Refer to our API documentation: [📚 API Reference](/docs/api-reference). ### How do I troubleshoot common errors? Find solutions in our Troubleshooting guide: [🛠 Troubleshooting](/docs/troubleshooting). --- ## Technical ### Is Hypergraph open-source? 100 %. Apache-2.0 license. Contributions welcome! --- ## Security & Privacy ### Can the server read my private data? No. All private content is encrypted client-side with a per-Space symmetric key. ### What happens if I lose my keys? Today you're out of luck (similar to Signal). A social recovery scheme is on the roadmap—follow [#51](https://github.com/graphprotocol/hypergraph/issues/51). --- ### Edit on GitHub [✏️ Improve this page](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/faq.md) --- ## Geo Testnet Faucet # 🪙 Geo Testnet Faucet Need some ETH testnet tokens to start building with Hypergraph? Use our public faucet to quickly top-up your wallet. [**Open the Geo Faucet**](https://faucet.conduit.xyz/geo-test-zc16z3tcvf) ## How it works 1. Switch your wallet/network to **Geo Testnet**. 2. Navigate to the faucet URL above and connect your wallet. 3. Click **Request tokens**. You should receive GEO in a few seconds. That's it! You can now use the test tokens with any Hypergraph-powered app running on the Geo Testnet. > Tip: If you run out of test tokens, come back to the faucet and request again (rate-limited to prevent abuse). --- ### Edit on GitHub :bust_in_silhouette: [✏️ Improve this page](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/faucet.md) --- ## Filtering Query Results # Filtering Query Results The filter API allows you to filter the results of a query by property values and in the future also by relations. ## Filtering by property values ```tsx export class Event extends Entity.Class("Event")({ name: Type.String, cancelled: Type.Boolean, }) {} // inside the React component const { data } = useQuery(Event, { filter: { cancelled: { is: false }, }, }); ``` The filter API supports different filters for different property types and offers a logical `or` and `not` operator. ```tsx // boolean filter { is: true/false, // exact match exists: true/false, // filter by existence of the property } // string filter { is: "text", // exact match contains: "text", startsWith: "text", endsWith: "text", exists: true/false, // filter by existence of the property } // number filter { is: 42, lessThan: 42, lessThanOrEqual: 42, greaterThan: 42, greaterThanOrEqual: 42, exists: true/false, // filter by existence of the property } // point filter { is: [0, 42], exists: true/false, // filter by existence of the property } // logical `not` for a string { not: { is: "Jane Doe", }, } // logical `or` for a string { or: [ { name: "Jane Doe" }, { name: "John Doe" }, ], } ``` ## Combining logical filters ```tsx { or: [ not: { name: "Jane Doe" }, not: { name: "John Doe" }, ], } ``` ## Full examples ```tsx // ever person except if their name is not Jane Doe or John Doe const { data } = useQuery(Person, { filter: { or: [ { not: { name: { is: 'Jane Doe' } } }, { not: { name: { is: 'John Doe' } } }, ], }, }); // ever person that is 42, but their name is not Jane Doe or John Doe const { data } = useQuery(Person, { filter: { age: { is: 42 }, or: [ { not: { name: { is: 'Jane Doe' } } }, { not: { name: { is: 'John Doe' } } }, ], not: { or: [ { name: { is: "Jane Doe" } }, { name: { is: "John Doe" } }, ], }, }, }); // every person that is not 42 years old const { data } = useQuery(Person, { filter: { age: { not: { is: 42 }, }, }, }); ``` ## Relation filtering ### Filter on values of the to entity ```tsx // schema export class Todo extends Entity.Class('Todo')({ name: Type.String, checked: Type.Boolean, assignees: Type.Relation(User), }) ``` 1 level filtering ```tsx const { data } = useQuery(Person, { filter: { assignees: { name: { is: "John" }, }, }, }); ``` 2 level filtering ```tsx const { data } = useQuery(Person, { filter: { assignees: { name: { is: "John" }, friends: { age: { greaterThan: 60 }, }, }, includes: { name: {}, description: {}, friends: {}, }, }, }); ``` ### Filter on the relation entity ```tsx // schema export class Todo extends Entity.Class('Todo')({ name: Type.String, checked: Type.Boolean, assignees: Type.Relation(User, { entity: { assignedAt: Type.DateTime, } }), }) ``` ```tsx const { data } = useQuery(Person, { filter: { assignees: { _relation: { entity: { assignedAt: { greaterThan: new Date("2025-01-01") } }, }, name: { is: "John" }, }, }, }); ``` Note: To access the relation entity you need to use the `_relation` property. ```tsx { todo.assignees.map((assignee) => ( {assignee._relation.entity.assignedAt} {assignee.name} )); } ``` --- ## Inboxes # Inboxes Inboxes are a way to send and receive messages to and from other users and spaces. They are a way to communicate with other users and spaces. ## Use Cases - Job applications - Contact form submission - Sign up for an event - Direct messages from one account to another - Direct messages from an account to a space More documentation will follow soon. --- ## Key Features # 🌟 Key Features Hypergraph is **more than a database**—it's a complete data layer for building collaborative, local-first apps. Below is a tour of the capabilities you get out of the box. ## Table of Contents - [End-to-end encryption](#end-to-end-encryption) - [Knowledge Graph SDK](#knowledge-graph-sdk) - [Graph-based data model](#graph-based-data-model) - [Conflict-free sync (CRDTs)](#conflict-free-sync-crdts) - [Spaces & fine-grained auth](#spaces--fine-grained-auth) --- ## End-to-end encryption Every update is encrypted **on the client** using XChaCha20-Poly1305. Only members of a Space possess the symmetric key, so neither the sync server nor The Graph can read private data. * **Automatic key rotation**: when members join/leave (not yet implemented). * **Multi-device**: each device holds its own key pair. ## Knowledge Graph SDK 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. ## Graph-based data model 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 a GRC-20 compliant indexer later. ## Conflict-free sync (CRDTs) We use **Automerge** (a JSON CRDT) to merge concurrent edits without conflicts. Snapshots are automatically compacted to keep payloads small. ## Spaces & fine-grained auth A **Space** groups both *people* and *data*. Fine-grained access will come soon. --- Ready to dive deeper? Check out the [Quickstart](/docs/quickstart) or browse the full [API Reference](/docs/api-reference). --- [✏️ Improve this page](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/key-features.md) --- ## Known Design Issues ## Connect App When authenticating with the Connect app, the app will send a callback url to the server. An attacker could intercept this callback url and redirect the user to a malicious site. This could be mitigated by introducing an server‐side “registration” step for with the callback url and nonce directly from the app. Alternatively a full OAuth2 flow would solve the issue. ## Space Info When decrypting the space info (name), there is currently no signature verification. ## Responses All responses in the sync server should be typed and encoded to avoid exposing data that is not intended to be exposed. ## Verifying the app identity in Connect Instead of trusting the server with the app identity address each app-identity should be signed or the address should be stored in the ciphertext containing private keys. ## Sign app identity attached to spaces The information that for an app identity that is attached to a space should be signed and verified instead of trusting the sync-server. ## The Privy App Id should be stored only in .env files Currently the frontend doesn't use the env var. ## Authenticate callback URL design The callback URL should be able to define a `#` instead of `?` for improved security ## Session tokens There should be multiple sessions with different session tokens so the user can logout and invalidate the session token without invalidating the other sessions. ## Disabled Signature Verification Signature verification is currently temporarily disabled when switching to the Connect app authentication flow. --- ## Mapping # Mapping The public knowledge graph is based on property IDs. In order to integrate with the public knowledge graph you need to map your own schema to IDs from the public graph's schema. This is done using an object called a mapping. The mapping has to be provided to the `HypergraphAppProvider` component. A mapping entry defines the type IDs, properties and relations of a type. Here is an example mapping for a schema with an `Event` and a `Company`: ```tsx export const mapping: Mapping = { Event: { typeIds: [Id('407d9e8a-c703-4fb4-830d-98c758c8564e')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'), }, relations: { sponsors: Id('a7ac80a6-d3d9-4b04-9b9f-ead1723af09f'), }, }, Company: { typeIds: [Id('b0220a78-9205-4e5e-9bf1-c03ee0791e23')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'), }, }, ``` The entire mapping structure can be generated using the TypeSync tool. ```bash pnpm install -g @graphprotocol/hypergraph-cli@latest hg typesync --open ``` ## Mapping Examples You can search for dozens of schema/mapping examples on the [Hypergraph Schema Browser](https://schema-browser.vercel.app/). ## Creating and publishing new Properties and Types We created a script to create and publish new properties and types to the public knowledge graph. You can find it at [https://github.com/geobrowser/create-types-and-properties](https://github.com/geobrowser/create-types-and-properties). --- ## Providers # Providers ## HypergraphAppProvider The `HypergraphAppProvider` is the main provider for the Hypergraph app. It is used to provide the app with the necessary context and state. ```tsx import { HypergraphAppProvider } from "@graphprotocol/hypergraph-react"; const App = () => { return ( ); }; ``` It has one mandatory prop: `mapping`. This is the mapping of your schema to the public Knowledge Graph schema. You can find more information about the mapping in the [Mapping](/docs/mapping) section later. Further it has an optional prop: `syncServerUri`. This is the URL of the sync server. By default it is set to `https://sync.geobrowser.io`. ## useHypergraphApp The `useHypergraphApp` is available inside the `HypergraphAppProvider` and manages the sync server connection and provides several useful functions. ```tsx import { useHypergraphApp } from "@graphprotocol/hypergraph-react"; const App = () => { const { isConnecting, logout } = useHypergraphApp(); return {isConnecting ? "Connecting..."}; }; ``` - `isConnecting` is a boolean that indicates that syncing private spaces is not yet possible. You need to wait until it's `false` to query data from private spaces. - `logout` is a function that logs out the user. There are several more that will be explained in the following sections. ## useHypergraphAuth The `useHypergraphAuth` is available inside the `HypergraphAppProvider` and manages the authentication state and provides several useful functions. ```tsx import { useHypergraphAuth } from "@graphprotocol/hypergraph-react"; const App = () => { const { authenticated, identity } = useHypergraphAuth(); return {authenticated ? "Authenticated" : "Not authenticated"}; }; ``` - `authenticated` is a boolean that indicates if the user is authenticated. - `identity` is the identity of the logged in user. ## HypergraphSpaceProvider Whenever you interact with a space you need to provide the space ID. Instead of providing the space ID to every hook e.g. useSpace, useQuery, useCreateEntity, etc. you can use the `HypergraphSpaceProvider` to wrap a section of your app with the space ID. ```tsx import { HypergraphSpaceProvider } from "@graphprotocol/hypergraph-react"; const Space = () => { // the space ID is provided by the HypergraphSpaceProvider const { name, id } = useSpace(); // the space ID is provided by the HypergraphSpaceProvider const { data } = useQuery(Event, { mode: "private" }); return {name}; }; const SpaceDetails = () => { return ( ); }; ``` The `space` prop is the ID of the space. It can be a private or public space. --- ## Publishing Public Data # Publishing Public Data Once you want to share your data with the world you need to publish it. This is done by creating the necessary `Opertations` (Ops) and then publishing them. There are two functions to help you with this: - `preparePublish` - creates the necessary `Operations` to publish the data - `publishOps` - publishes the `Operations` to the public space You can generate the Ops for multiple entities and publish them in one go by concatenating the `ops` arrays. ## Prepare Publish Based on entity Ids, the source space and the target space this function calculates the necessary `Operations` to publish the data. ```tsx import { preparePublish } from "@graphprotocol/hypergraph-react"; const { ops } = preparePublish({ entity: entity, publicSpace: "public-space-id", }); ``` The entity can come from a `useCreateEntity` result or from a `useQuery` result e.g. ## Publish The `publishOps` function is used to publish the changes to the public space. ```tsx import { publishOps } from "@graphprotocol/hypergraph-react"; const { result } = publishOps({ ops, walletClient: smartSessionClient, space: publicSpaceId, name: "Create Event", // description which can be any string }); ``` Additionally, we export a `usePublishToPublishSpace` hook which abstracts the above functionality into a single function call. This function internally uses React Query's useMutate hook, so you have access to the same state machine and callback functions. ```tsx import { usePublishToPublicSpace, useHypergraphApp } from "@graphprotocol/hypergraph-react"; const MyComponent = ({ publicSpaceId }: { publicSpaceId: string }) => { const { getSmartSessionClient } = useHypergraphApp(); const { data: events } = useQuery(Event, { mode: "private" }); const { mutate, isPending } = usePublishToPublicSpace(); if (isPending) { return Publishing... } return ( {events.map((event) => ( mutate({ entity: event, spaceId: publicSpaceId })}> {event.name} ))} ); }; ``` ## Exploring the Knowledge Graph via GeoBrowser In order to explore the knowledge graph you can use GeoBrowser. Visit [https://testnet.geobrowser.io/root](https://testnet.geobrowser.io/root) and explore the knowledge graph. Once you published you can find it via the search by name or by id. By looking up the data you also can verify that it was published correctly. --- ## Query Private Data # Query Private Data Based on your schema, you can query private data that you created using Hypergraph. ## useQuery In order to query private data, you need to pass in the schema type and set the mode to `private`. ```ts import { useQuery } from '@graphprotocol/hypergraph-react'; import { Event } from '../schema'; const { data } = useQuery(Event, { mode: 'private' }); ``` ### Including Relations By default only non-relation properties are included in the query entries. In order to include relations, you can use the `include` parameter. ```ts const { data } = useQuery(Event, { mode: 'private', include: { sponsors: {} }, }); ``` For deeper relations you can use the `include` parameter multiple levels deep. Currently for private data only one level of relations is supported. ### Querying from a specific space You can also query from a specific space by passing in the `spaceId` parameter. ```ts const { data: spaceAData } = useQuery(Event, { mode: 'private', spaceId: 'space-a-id' }); const { data: spaceBData } = useQuery(Event, { mode: 'private', spaceId: 'space-b-id' }); ``` ### Filtering You can filter the data by passing in the `filter` parameter. ```ts const { data } = useQuery(Event, { mode: 'private', filter: { name: 'John' } }); ``` Please learn more about filtering in the [Filtering query results](#filtering-query-results) section. ### Returned data useQuery for private data returns: - data - a list of entities defined in your schema - invalidEntities - a list of entities that are in your space storage with correct type, but can't be parsed to your schema - deleted - a list of entities that are marked as deleted, we keep them around to be able to later be able to publish the deleted information to the public knowledge graph ```ts const { data, invalidEntities, deleted } = useQuery(Event, { mode: 'private' }); ``` --- ## Query Public Data # Query Public Data Based on your schema, you can query public data that you created using Hypergraph. It works very much like [querying private data](/docs/query-private-data). ## useQuery In order to query private data, you need to pass in the schema type and set the mode to `public`. ```ts import { useQuery } from '@graphprotocol/hypergraph-react'; import { Event } from '../schema'; const { data, isPending, isError } = useQuery(Event, { mode: 'public' }); ``` ### Including Relations By default only non-relation properties are included in the query entries. In order to include relations, you can use the `include` parameter. ```ts const { data, isPending, isError } = useQuery(Event, { mode: 'public', include: { sponsors: {} }, }); ``` For deeper relations you can use the `include` parameter multiple levels deep. Currently two levels of relations are supported for public data. ### Querying from a specific space You can also query from a specific space by passing in the `space` parameter. ```ts const { data: spaceAData } = useQuery(Event, { mode: 'public', space: 'space-a-id' }); const { data: spaceBData } = useQuery(Event, { mode: 'public', space: 'space-b-id' }); ``` ### Filtering You can filter the data by passing in the `filter` parameter. ```ts const { data, isPending, isError } = useQuery(Event, { mode: 'public', filter: { name: 'John' } }); ``` Please learn more about filtering in the [Filtering query results](#filtering-query-results) section. ### Returned data useQuery for private data returns: - data - a list of entities defined in your schema - invalidEntities - a list of entities that are in your space storage with correct type, but can't be parsed to your schema - isPending - a boolean indicating if the query is loading - isError - a boolean indicating if the query failed In addition you have access to the full response from `@tanstack/react-query`'s `useQuery` hook, which is used internally to query the public data. ```ts const { data, isPending, isError } = useQuery(Event, { mode: 'public' }); ``` ## Querying Public Data from Geo Testnet using useQuery The Geo testnet contains public data that you can query immediately without any authentication. This section provides examples to quickly explore the available data. **Note**: - **No authentication required** for public data queries. - All examples below use the Geo testnet space ID: `b2565802-3118-47be-91f2-e59170735bac` Each section below includes the relevant `schema.ts`, `mapping.ts`, and a query example. ### Projects Example **Schema Definition:** ```typescript // app/schema.ts import { Entity, Type } from "@graphprotocol/hypergraph"; export class Project extends Entity.Class("Project")({ name: Type.String, description: Type.optional(Type.String), xUrl: Type.optional(Type.String), }) {} ``` **Mapping Definition:** ```typescript // app/mapping.ts import type { Mapping } from '@graphprotocol/hypergraph'; import { Id } from '@graphprotocol/hypergraph'; export const mapping: Mapping.Mapping = { Project: { typeIds: [Id('484a18c5-030a-499c-b0f2-ef588ff16d50')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'), description: Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'), xUrl: Id('0d625978-4b3c-4b57-a86f-de45c997c73c'), }, }, }; ``` **Query Example:** ```tsx "use client"; import { useState } from "react"; import { useQuery } from "@graphprotocol/hypergraph-react"; import { Project } from "../schema"; export default function ProjectsExample() { const [limit, setLimit] = useState(40); const { data: projects, isPending, isError, } = useQuery(Project, { mode: "public", space: "b2565802-3118-47be-91f2-e59170735bac", first: limit, }); if (isPending) return Loading projects...; if (isError) return Error loading projects; return ( Projects {projects.map((project) => ( Name: {project.name} {project.description && ( Description: {project.description} )} {project.xUrl && ( View on X )} ))} {projects.length >= limit && ( setLimit((n) => n + 40)} > Load more )} ); } ``` ### dApps Example **Schema Definition:** ```typescript // app/schema.ts import { Entity, Type } from "@graphprotocol/hypergraph"; export class Dapp extends Entity.Class("Dapp")({ name: Type.String, description: Type.optional(Type.String), xUrl: Type.optional(Type.String), githubUrl: Type.optional(Type.String), }) {} ``` **Mapping Definition:** ```typescript // app/mapping.ts import type { Mapping } from '@graphprotocol/hypergraph'; import { Id } from '@graphprotocol/hypergraph'; export const mapping: Mapping.Mapping = { Dapp: { typeIds: [Id('8ca136d0-698a-4bbf-a76b-8e2741b2dc8c')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'), description: Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'), xUrl: Id('0d625978-4b3c-4b57-a86f-de45c997c73c'), githubUrl: Id('9eedefa8-60ae-4ac1-9a04-805054a4b094'), }, }, }; ``` **Query Example:** ```tsx "use client"; import { useState } from "react"; import { useQuery } from "@graphprotocol/hypergraph-react"; import { Dapp } from "../schema"; export default function DappsExample() { const [limit, setLimit] = useState(40); const { data: dapps, isPending, isError, } = useQuery(Dapp, { mode: "public", space: "b2565802-3118-47be-91f2-e59170735bac", first: limit, }); if (isPending) return Loading dApps...; if (isError) return Error loading dApps; return ( dApps {dapps.map((dapp) => ( Name: {dapp.name} {dapp.description && ( Description: {dapp.description} )} {dapp.xUrl && ( View on X )} {dapp.githubUrl && ( {' '} GitHub )} ))} {dapps.length >= limit && ( setLimit((n) => n + 40)} > Load more )} ); } ``` ### Investment Rounds Example **Schema Definition:** ```typescript // app/schema.ts import { Entity, Type } from "@graphprotocol/hypergraph"; export class Investor extends Entity.Class("Investor")({ name: Type.String, }) {} export class FundingStage extends Entity.Class("FundingStage")({ name: Type.String, }) {} export class InvestmentRound extends Entity.Class( "InvestmentRound" )({ name: Type.String, raisedAmount: Type.optional(Type.Number), investors: Type.Relation(Investor), fundingStages: Type.Relation(FundingStage), }) {} ``` **Mapping Definition:** ```typescript // app/mapping.ts import type { Mapping } from '@graphprotocol/hypergraph'; import { Id } from '@graphprotocol/hypergraph'; export const mapping: Mapping.Mapping = { Investor: { typeIds: [Id('331aea18-973c-4adc-8f53-614f598d262d')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935') }, }, FundingStage: { typeIds: [Id('8d35d217-3fa1-4686-b74f-fcb3e9438067')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935') }, }, InvestmentRound: { typeIds: [Id('8f03f4c9-59e4-44a8-a625-c0a40b1ff330')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'), raisedAmount: Id('16781706-dd9c-48bf-913e-cdf18b56034f'), }, relations: { investors: Id('9b8a610a-fa35-486e-a479-e253dbdabb4f'), fundingStages: Id('e278c3d4-78b9-4222-b272-5a39a8556bd2'), raisedBy: Id('b4878d1a-0609-488d-b8a6-e19862d6b62f'), }, }, }; ``` **Query Example:** ```tsx "use client"; import { useState } from "react"; import { useQuery } from "@graphprotocol/hypergraph-react"; import { InvestmentRound } from "../schema"; export default function InvestmentRoundsExample() { const [limit, setLimit] = useState(40); const { data: investmentRounds, isPending, isError, } = useQuery(InvestmentRound, { mode: "public", space: "b2565802-3118-47be-91f2-e59170735bac", first: limit, include: { investors: {}, fundingStages: {}, }, }); if (isPending) return Loading investment rounds...; if (isError) return Error loading investment rounds; return ( Investment Rounds {investmentRounds.map((round) => ( Name: {round.name} {round.raisedAmount && ( Amount Raised: ${round.raisedAmount?.toLocaleString()} )} {round.fundingStages.length > 0 && ( Stage:{" "} {round.fundingStages.map((stage) => stage.name).join(", ")} )} {round.investors.length > 0 && ( Investors: {round.investors.map((investor) => ( {investor.name} ))} )} ))} {investmentRounds.length >= limit && ( setLimit((n) => n + 40)} > Load more )} ); } ``` ### Assets Example **Schema Definition:** ```typescript // app/schema.ts import { Entity, Type } from "@graphprotocol/hypergraph"; export class Asset extends Entity.Class("Asset")({ name: Type.String, symbol: Type.optional(Type.String), blockchainAddress: Type.optional(Type.String), }) {} ``` **Mapping Definition:** ```typescript // app/mapping.ts import type { Mapping } from '@graphprotocol/hypergraph'; import { Id } from '@graphprotocol/hypergraph'; export const mapping: Mapping.Mapping = { Asset: { typeIds: [Id('f8780a80-c238-4a2a-96cb-567d88b1aa63')], properties: { name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'), symbol: Id('ace1e96c-9b83-47b4-bd33-1d302ec0a0f5'), blockchainAddress: Id('56b5944f-f059-48d1-b0fa-34abe84219da'), }, }, }; ``` **Query Example:** ```tsx "use client"; import { useState } from "react"; import { useQuery } from "@graphprotocol/hypergraph-react"; import { Asset } from "../schema"; export default function AssetMarketExample() { const [limit, setLimit] = useState(40); const { data: assets, isPending, isError, } = useQuery(Asset, { mode: "public", space: "b2565802-3118-47be-91f2-e59170735bac", first: limit, }); if (isPending) return Loading assets...; if (isError) return Error loading assets; return ( Assets {assets.map((asset) => ( Name: {asset.name} {asset.symbol && Symbol: {asset.symbol}} {asset.blockchainAddress && ( Address: {asset.blockchainAddress} )} ))} {assets.length >= limit && ( setLimit((n) => n + 40)} > Load more )} ); } ``` --- ## Quickstart # 🚀 Quickstart The quickest way to create a new Hypergraph application you can use the `create-hypergraph` CLI command. Make sure you have Node.js >= 22 installed. ```bash npx create-hypergraph@latest # or pnpm create hypergraph@latest bunx create-hypergraph@latest yarn create hypergraph@latest ``` During installation, you'll see the following prompts to choose your app name, template, and package manager. 1. What is your app named? … 2. Choose your template … 3. What package manager do you want to use? … After the prompts, you'll have a new Hypergraph application ready to use and can start exploring the app. To run it use the following command: ```bash cd my-hypergraph-app npm run dev # or depending on your package manager yarn run dev pnpm run dev bun run dev ``` Open the browser and navigate to the development server URL shown in your terminal (for example, `http://localhost:5173` for Vite or `http://localhost:3000` for Next.js) ## Example Datasets A few example datasets to use when building your apps are available here: - [Web3 projects](https://testnet.geobrowser.io/space/b2565802-3118-47be-91f2-e59170735bac/0f3e0e21-1636-435a-850f-6f57d616e28e) - [Web3 VCs](https://testnet.geobrowser.io/space/b2565802-3118-47be-91f2-e59170735bac/d8ec3f57-7601-4bef-a648-a64799dfd964) - [Web3 DAPPs](https://testnet.geobrowser.io/space/b2565802-3118-47be-91f2-e59170735bac/09d3188c-8e20-4083-a6ad-e696cc493c7a) - [Token Values](https://testnet.geobrowser.io/space/2df11968-9d1c-489f-91b7-bdc88b472161/f8780a80-c238-4a2a-96cb-567d88b1aa63) --- ## Edit on GitHub :bust_in_silhouette: [✏️ Improve this page](https://github.com/graphprotocol/hypergraph/edit/main/docs/docs/quickstart.md) ``` ``` --- ## Schema # Schema The Hypergraph schema allows you to define the data model for your application. It is based on the GRC-20 specification and allows you to define Types with properties and relations to other Types. ## Hypergraph Schema Browser Building your app using a schema that is already actively used in Hypergraph's knowledge graph unlocks composability between your dataset and other datasets used in Hypergraph's knowledge graph. You can search for schemas used within the Hypergraph knowledge graph using the [Hypergraph Schema Browser](https://schema-browser.vercel.app/). ## Example Here is an example of a schema for an Event entity with the properties `name` and `description`. ```ts import { Entity, Type } from '@graphprotocol/hypergraph'; export class Event extends Entity.Class('Event')({ name: Type.String, description: Type.String, }) {} ``` ## Relations In order to define relations between Types, you can use the `Type.Relation` type. ```ts import { Entity, Type } from '@graphprotocol/hypergraph'; export class Company extends Entity.Class('Company')({ name: Type.String, }) {} export class Event extends Entity.Class('Event')({ name: Type.String, description: Type.String, sponsors: Type.Relation(Company), }) {} ``` ## Available Types - `Type.String` (string) - `Type.Number` (number) - `Type.Date` (date) - `Type.Boolean` (boolean) - `Type.Point` (serialized to a string with a comma separated list of numbers) - `Type.Relation` (relation to another Type) Example: ```ts import { Entity, Type } from '@graphprotocol/hypergraph'; export class Company extends Entity.Class('Company')({ name: Type.String, employees: Type.Number, founded: Type.Date, active: Type.Boolean, location: Type.Point, }) {} ``` ## Optional Fields You can make a field optional by wrapping it in `Type.optional`. ```ts import { Entity, Type } from '@graphprotocol/hypergraph'; export class Company extends Entity.Class('Company')({ name: Type.String, description: Type.optional(Type.String), founded: Type.optional(Type.Date), }) {} ``` --- ## Space Invitations # Space Invitations Space invitations are a way to invite other users to a space. Currently only invitations for private spaces are supported. Public space invitations are possible within GeoBrowser and will be supported in the future. ## Invite to Space ```tsx const { inviteToSpace } = useHypergraphApp(); inviteToSpace({ space: "space-id", invitee: { accountAddress: "0x1234567890123456789012345678901234567890", }, }); ``` ## Listing Invitations ```tsx const { listInvitations } = useHypergraphApp(); listInvitations(); ``` Once the function is called the invitations are requested from and are available in the Hypergraph store. ```tsx import { useSelector } from "@xstate/store/react"; import { store } from "@graphprotocol/hypergraph"; const invitations = useSelector(store, (state) => state.context.invitations); ``` ## Accepting Invitations ```tsx const { acceptInvitation } = useHypergraphApp(); acceptInvitation({ invitation: "invitation-id", }); ``` --- ## Spaces # Spaces Spaces are collections of data managed by a single person or a group of people. Each space is identified by a unique ID and can be public or private. Spaces are owned by a single person or a group of people and not by the app. This ensures data ownership stays with the owner of the space and not with the app. ## Public Spaces Public spaces are spaces that are open to the public. They are visible to anyone who knows the space ID. Public spaces can be found at [Geo Browser](https://www.geobrowser.io/root), where the space ID is in the address bar. ### Indexer API Indexer API that indexes all the public spaces and the content in it! [Railway Graphql API](https://api-testnet.geobrowser.io/graphql) ## Private Spaces Private spaces are spaces that are only accessible to the people who are members of the space. ## Querying Spaces You can query spaces using the `useSpaces` hook. ### Querying Private Spaces List ```tsx const { data, isPending } = useSpaces({ mode: "private" }); ``` The query will return a list of all private spaces that the user is a member of and the information if the spaces list is still loading. ### Querying Public Spaces List The query will return a list of all public spaces that are available to the user. The returned data is the same as the data returned by the `useQuery` hook from `@tanstack/react-query`. ```tsx const { data, isPending, isError } = useSpaces({ mode: "public" }); ``` ### Querying a single private Space ```tsx const { name, isReady, id } = useSpace({ mode: "private" }); ``` The `useSpace` hook returns the name of the space and a boolean if the space is ready. Optionally you can provide a space ID to query a specific space. By default the space ID is the one defined in the `HypergraphSpaceProvider` component. ```tsx const { name, isReady, id } = useSpace({ mode: "private", space: "space-id" }); ``` ### Querying a single public Space ```tsx const { name, isReady, id } = useSpace({ mode: "public", space: "space-id" }); ``` The `useSpace` hook returns the name of the space and a boolean if the space is ready. Optionally you can provide a space ID to query a specific space. By default the space ID is the one defined in the `HypergraphSpaceProvider` component. ```tsx const { name, isReady, id } = useSpace({ mode: "public", space: "space-id" }); ``` ## Creating Spaces Currently spaces can only be created in Geo Connect or GeoBrowser. In the future you will be able to create spaces within an app if the users provides the necessary create space permissions to the app. --- ## Troubleshooting # 🛠️ Troubleshooting Having problems? Below are quick fixes for the issues we hit most often while building with Hypergraph. ## Table of Contents - [Provide Feedback & Report Issues](#provide-feedback--report-issues) - [Join the Community](#join-the-community) --- ## Provide Feedback & Report Issues Please [open an issue](https://github.com/graphprotocol/hypergraph/issues) on GitHub and let us know your ideas, feedback, or report any issues you encounter. ## Join the Community Join the [Hypergraph Discord](https://discord.gg/graphprotocol) to connect with other developers and get help. --- ## TypeSync # 🧬 TypeSync TypeSync is a visual tool that helps you manage your Hypergraph schema and update the schema.ts and mapping.ts for your Hypergraph application. ## Installation TypeSync automatically comes with the `hypergraph` package. Once you have it installed it will be available via: ```bash npx hg typesync --open # or pnpm hg typesync --open bunx hg typesync --open yarn hg typesync --open ``` This will start the TypeSync server. You can now access the TypeSync app in your browser at `http://localhost:3000`. The UI will look like this: ![TypeSync Dashboard](../static/img/typesync/typesync_dashboard.png) The UI is split into two main sections: - The left side is a list of existing types in the Knowledge Graph to pick from. - The right side represents the schema you are currently working on. - At the bottom you can find two buttons: - `Sync with schema.ts` to sync the current schema to your `schema.ts` file. - `Publish Schema` to publish the current schema to the Knowledge Graph. - The top right corner you can find a button to `Sign in to Geo Account` to sign in to your Geo Account. This is a prerequisite to publish your schema to the Knowledge Graph. ## Recommended Flow 1. Design your schema 2. Sync it to your schema.ts file using the `Sync with schema.ts` button 3. Publish your schema to the Knowledge Graph 1. Sign in with Geo Connect. To do so click on the `Sign in to Geo Account` button in the top right corner. 2. In "Connect" select an existing public space or create & select a new public space you want to publish your schema to. Note: Can be any space of your choice and doesn't matter which one. 3. Click the "Publish Schema" button to publish your schema to the Knowledge Graph. ## Best Practices If there is an existing type ideally use this one and adept it to your needs. This will allow for more interoperability with other applications. For properties prefer existing properties, but in case you need a different type best to create a new property. --- ## Writing Private Data # Writing Private Data There are several ways to write private data to the Hypergraph. ## Creating Entities You can create entities using the `useCreateEntity` hook. ```tsx const createEvent = useCreateEntity(Event); createEvent({ name: "Event 1", description: "Event 1 description", }); ``` For relations you can provide a list of IDs of the entities you want to relate to. ```tsx createEvent({ name: "Event 1", description: "Event 1 description", sponsors: ["sponsor-id-1", "sponsor-id-2"], }); ``` A common pattern is to create a new entity and then relate it to an existing entity. ```tsx const createCompany = useCreateEntity(Company); const createEvent = useCreateEntity(Event); const company = createCompany({ name: "Company 1", }); const event = createEvent({ name: "Event 1", description: "Event 1 description", sponsors: [company.id], }); ``` Optionally you can provide a space ID to create an entity in a specific space. ```tsx const createEvent = useCreateEntity(Event, { space: "space-id" }); ``` ## Updating Entities You can update entities using the `useUpdateEntity` hook. ```tsx const updateEvent = useUpdateEntity(Event); updateEvent({ id: "event-id", name: "Event 1", }); ``` Note: You can't update relations using the `useUpdateEntity` hook. It is only possible to update the properties of the entity. Optionally you can provide a space ID to update an entity in a specific space. ```tsx const updateEvent = useUpdateEntity(Event, { space: "space-id" }); ``` ## Deleting Entities You can delete entities using the `useDeleteEntity` hook. ```tsx const deleteEvent = useDeleteEntity(); deleteEvent({ id: "event-id", }); ``` Optionally you can provide a space ID to delete an entity in a specific space. ```tsx const deleteEvent = useDeleteEntity({ space: "space-id" }); ``` ## Adding Relations (not yet supported) TBD ## Updating Relations (not yet supported) TBD ## Removing Relations (not yet supported) TBD