### Install Dependencies and Build Source: https://github.com/inkandswitch/keyhive/blob/main/keyhive_wasm/README.md Installs project dependencies and builds the package. Run this before other commands. ```bash pnpm install && pnpm build ``` -------------------------------- ### Install Dependencies for Testing Source: https://github.com/inkandswitch/keyhive/blob/main/keyhive_wasm/README.md Installs project dependencies required for running tests. This is a prerequisite for test execution. ```bash pnpm install ``` -------------------------------- ### Install Playwright Browser Binaries Source: https://github.com/inkandswitch/keyhive/blob/main/keyhive_wasm/README.md Installs the necessary browser binaries for Playwright to run tests. This is required for test execution. ```bash npx playwright install ``` -------------------------------- ### Mermaid DAG Example Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Illustrates a Directed Acyclic Graph (DAG) structure for commit relationships. ```mermaid graph LR a --> b b --> c a --> d ``` -------------------------------- ### Mermaid Reversed DAG Example 2 (Concurrent Change) Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Illustrates the second DAG with reversed arrows after concurrent changes, showing that the stable ordering is maintained despite modifications. ```mermaid graph LR b --> a d --> c e --> c c --> a ``` -------------------------------- ### Mermaid DAG Example 2 (Concurrent Change) Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Shows a modified DAG structure after concurrent changes, highlighting how a standard depth-first traversal can lead to inconsistent chunk contents between peers. ```mermaid graph LR a --> b a --> c c --> d c --> e ``` -------------------------------- ### Mermaid DAG Example 1 Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Illustrates a simple directed acyclic graph (DAG) structure using Mermaid syntax. This is used to explain the concept of depth-first traversal. ```mermaid graph LR a --> b a --> c c --> e ``` -------------------------------- ### Initialize Keyhive WASM Module Source: https://github.com/inkandswitch/keyhive/blob/main/keyhive_wasm/e2e/server/index.html Use ES module import syntax to import functionality from the compiled WASM module. The `default` import is an initialization function that boots the module. This example shows the basic initialization and makes the module's exports available on the `window` object. ```javascript import init, * as keyhive from './pkg/keyhive_wasm.js'; async function run() { // First up we need to actually load the Wasm file, so we use the // default export to inform it where the Wasm file is located on the // server, and then we wait on the returned promise to wait for the // Wasm to be loaded. // // It may look like this: `await init('./pkg/without_a_bundler_bg.wasm');`, // but there is also a handy default inside `init` function, which uses // `import.meta` to locate the Wasm file relatively to js file. // // Note that instead of a string you can also pass in any of the // following things: // // * `WebAssembly.Module` // // * `ArrayBuffer` // // * `Response` // // * `Promise` which returns any of the above, e.g. `fetch("./path/to/wasm")` // // This gives you complete control over how the module is loaded // and compiled. // // Also note that the promise, when resolved, yields the Wasm module's // exports which is the same as importing the `*_bg` module in other // modes await init(); window.keyhive = keyhive; } run(); ``` -------------------------------- ### Run Playwright Tests Source: https://github.com/inkandswitch/keyhive/blob/main/keyhive_wasm/README.md Executes the automated tests using Playwright. Ensure all dependencies and browser binaries are installed first. ```bash npx playwright test ``` -------------------------------- ### Mermaid Reversed DAG Example 1 Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Depicts the first DAG with arrows reversed, demonstrating how a depth-first traversal from the ends of the graph provides a stable ordering. ```mermaid graph LR b --> a e --> c c --> a ``` -------------------------------- ### Keyhive::generate Source: https://context7.com/inkandswitch/keyhive/llms.txt Initializes a new Keyhive instance with a fresh signing key, prekey pool, and backing stores. This is the entry point for creating a local user identity. ```APIDOC ## Keyhive::generate — Create a new agent instance ### Description Initializes a new `Keyhive` instance with a fresh signing key, prekey pool, and backing stores. This is the entry point for creating a local user identity. ### Method ```rust Keyhive::generate(signer, store, listener, rng) ``` ### Parameters - `signer`: An object implementing the `Signer` trait for cryptographic signing. - `store`: A `CiphertextStore` for storing encrypted data. - `listener`: An object implementing the `Listener` trait for handling events. - `rng`: A random number generator. ### Request Example ```rust use keyhive_core::keyhive::Keyhive; use keyhive_core::listener::no_listener::NoListener; use keyhive_core::store::ciphertext::memory::MemoryCiphertextStore; use keyhive_crypto::signer::memory::MemorySigner; use futures::lock::Mutex; use std::sync::Arc; use future_form::Sendable; #[tokio::main] async fn main() -> Result<(), Box> { let signer = MemorySigner::generate(&mut rand::rngs::OsRng); let store = Arc::new(Mutex::new(MemoryCiphertextStore::<[u8; 32], Vec>::new())); let keyhive: Keyhive, _, NoListener> = Keyhive::generate(signer, store, NoListener, rand::rngs::OsRng).await?; println!("My ID: {:?}", keyhive.id()); Ok(()) } ``` ### Response #### Success Response A new `Keyhive` instance. #### Response Example ```rust // My ID: IndividualId(...) ``` ``` -------------------------------- ### Generate New Agent Instance with Keyhive Source: https://context7.com/inkandswitch/keyhive/llms.txt Initializes a new Keyhive instance with a fresh signing key, prekey pool, and backing stores. This is the entry point for creating a local user identity. Requires `tokio` for async operations. ```rust use keyhive_core::{ keyhive::Keyhive, listener::no_listener::NoListener, store::ciphertext::memory::MemoryCiphertextStore, }; use keyhive_crypto::signer::memory::MemorySigner; use futures::lock::Mutex; use std::sync::Arc; use future_form::Sendable; #[tokio::main] async fn main() -> Result<(), Box> { let signer = MemorySigner::generate(&mut rand::rngs::OsRng); let store = Arc::new(Mutex::new( MemoryCiphertextStore::<[u8; 32], Vec>::new() )); let keyhive: Keyhive, _, NoListener> = Keyhive::generate(signer, store, NoListener, rand::rngs::OsRng).await?; println!("My ID: {:?}", keyhive.id()); // My ID: IndividualId(...) Ok(()) } ``` -------------------------------- ### JsKeyhive::init Source: https://context7.com/inkandswitch/keyhive/llms.txt Initializes the Keyhive instance for use in WebAssembly environments, exposing it to JavaScript/TypeScript via `wasm-bindgen`. ```APIDOC ## JsKeyhive::init ### Description Initializes the `JsKeyhive` for use in WebAssembly (TypeScript). The `keyhive_wasm` crate exposes `JsKeyhive` to JavaScript/TypeScript via `wasm-bindgen`. Initialize with a `JsSigner`, a `JsCiphertextStore`, and an event handler callback. ### Method `init(signer: JsSigner, store: JsCiphertextStore, event_handler: JsEventHandler)` ### Parameters - **signer**: `JsSigner` - An instance of `JsSigner` for cryptographic operations. - **store**: `JsCiphertextStore` - An instance of `JsCiphertextStore` for storing encrypted data. - **event_handler**: `JsEventHandler` - A callback function to handle incoming events. ### Request Example (TypeScript) ```typescript import { Keyhive, Signer, CiphertextStore } from "@keyhive/keyhive"; // Initialize const signer = await Signer.generate(); const store = CiphertextStore.newInMemory(); const keyhive = await Keyhive.init(signer, store, (event) => { console.log("Event received:", event); }); console.log("My ID:", keyhive.idString); // My ID: 0xdeadbeef... // Create a document const doc = await keyhive.generateDocument([], changeId, []); console.log("Document created:", doc); // Encrypt content const contentRef = new ChangeId(new Uint8Array(32).fill(1)); const encrypted = await keyhive.tryEncrypt(doc, contentRef, [], new TextEncoder().encode("hello")); // Decrypt content const decrypted = await keyhive.tryDecrypt(doc, encrypted.encryptedContent()); console.log("Decrypted:", new TextDecoder().decode(decrypted)); // Decrypted: hello ``` ### Response - Returns a `Promise` which resolves to the initialized `JsKeyhive` instance. ``` -------------------------------- ### Mermaid Diagram for Convergent Capabilities Source: https://github.com/inkandswitch/keyhive/blob/main/design/convergent_capabilities.md Illustrates the relationships between documents, capabilities (read/edit), and users (Alice, Bob) using Mermaid syntax. Shows delegation and proof of earlier capabilities. ```mermaid flowchart subgraph Legend doc(("Document")) capRW>"Edit Document"] capRR>"Read from Document"] alice(("Alice")) bob(("Bob")) abRR>Read from Document] end doc --- capRR ---> alice doc --- capRW --> alice abRR -.-o|"Proven by
earlier capability"| capRR alice ---|Alice delegates
Doc Read
to Bob| abRR --> bob ``` -------------------------------- ### Initialize Keyhive in WebAssembly (TypeScript) Source: https://context7.com/inkandswitch/keyhive/llms.txt Initialize `JsKeyhive` in a WebAssembly environment using `Keyhive.init`. This requires a `JsSigner`, a `JsCiphertextStore`, and an event handler callback. The `idString` property provides the agent's unique identifier. You can then generate documents, encrypt, and decrypt content. ```typescript import { Keyhive, Signer, CiphertextStore } from "@keyhive/keyhive"; // Initialize const signer = await Signer.generate(); const store = CiphertextStore.newInMemory(); const keyhive = await Keyhive.init(signer, store, (event) => { console.log("Event received:", event); }); console.log("My ID:", keyhive.idString); // My ID: 0xdeadbeef... // Create a document const doc = await keyhive.generateDocument([], changeId, []); console.log("Document created:", doc); // Encrypt content const contentRef = new ChangeId(new Uint8Array(32).fill(1)); const encrypted = await keyhive.tryEncrypt(doc, contentRef, [], new TextEncoder().encode("hello")); // Decrypt content const decrypted = await keyhive.tryDecrypt(doc, encrypted.encryptedContent()); console.log("Decrypted:", new TextDecoder().decode(decrypted)); // Decrypted: hello ``` -------------------------------- ### Manage Prekey Pool with Keyhive::expand_prekeys / rotate_prekey Source: https://context7.com/inkandswitch/keyhive/llms.txt Manages the prekey pool for asynchronous key exchange. `expand_prekeys` adds a new prekey, while `rotate_prekey` replaces an existing one. Both operations are signed and shareable. ```rust // Add a new prekey to the pool let add_op = alice.expand_prekeys().await?; println!("New prekey added: {:?}", add_op.payload().share_key); // Rotate an existing prekey (replaces old with new) let rotate_op = alice.rotate_prekey(add_op.payload().share_key).await?; println!("Prekey rotated: old={:?} → new={:?}", rotate_op.payload().old, rotate_op.payload().new ); ``` -------------------------------- ### Create New Encrypted Document with Keyhive Source: https://context7.com/inkandswitch/keyhive/llms.txt Creates a new document with an initial set of content-addressed heads and optional co-owners. All listed co-owners receive delegated access. The document is backed by a CGKA tree for group encryption. Requires `nonempty` crate. ```rust use nonempty::nonempty; use keyhive_core::{ keyhive::Keyhive, principal::peer::Peer, }; // alice and bob are Keyhive instances created via Keyhive::generate // bob_individual is an Arc> registered on alice // Create a document with alice as owner and bob as co-owner // Initial content heads are content-addressed identifiers ([u8; 32]) let doc = alice .generate_doc( vec![Peer::Individual(bob_id, bob_individual.clone())], nonempty![[0u8; 32], [1u8; 32]], ) .await?; let doc_id = doc.lock().await.doc_id(); println!("Document created: {:?}", doc_id); // Document created: DocumentId(...) ``` -------------------------------- ### BeeKEM CGKA Core Operations (Rust) Source: https://context7.com/inkandswitch/keyhive/llms.txt Demonstrates how to interact with BeeKEM's Continuous Group Key Agreement (CGKA) operations for group size retrieval and syncing all CGKA operations for a document. These operations are typically managed internally by Keyhive's Document type. ```rust // BeeKEM is used internally by keyhive_core's Document type. // The CGKA is accessed via document methods: // Get CGKA group size let cgka_group_size = doc.lock().await.cgka()?.group_size(); println!("CGKA group has {} members", cgka_group_size); // Retrieve all CGKA operations for a document (for syncing) let epochs = alice.cgka_ops_for_doc(&doc_id).await?.unwrap(); println!("CGKA epoch count: {}", epochs.len()); let total_ops: usize = epochs.iter().map(|e| e.len()).sum(); println!("Total CGKA ops: {}", total_ops); ``` -------------------------------- ### Keyhive::into_archive / Keyhive::try_from_archive Source: https://context7.com/inkandswitch/keyhive/llms.txt Serializes the entire `Keyhive` state into a portable `Archive` for persistence or migration, and restores the state from an `Archive`. ```APIDOC ## Keyhive::into_archive / Keyhive::try_from_archive ### Description Serializes the entire `Keyhive` state (individuals, groups, documents, delegations, prekey pairs) into a portable `Archive` for persistence or migration. Restores from an archive using `try_from_archive`. ### Method - `into_archive()`: Serializes the current state. - `try_from_archive()`: Restores state from a serialized archive. ### Parameters for `try_from_archive` - **archive**: `&Archive` - The archive to restore state from. - **signer**: `impl Signer` - The signer to use for the restored Keyhive instance. - **store**: `impl CiphertextStore` - The ciphertext store for the restored Keyhive instance. - **listener**: `impl Listener` - The event listener for the restored Keyhive instance. - **rng**: `Arc>` - A random number generator. ### Request Example (Serialization) ```rust // Serialize to archive let archive = keyhive.into_archive().await; let bytes = bincode::serialize(&archive)?; println!("Archive size: {} bytes", bytes.len()); println!("Archive contains {} docs", archive.docs.len()); ``` ### Request Example (Restoration) ```rust // Restore from archive (requires the original signing key) let restored: Keyhive, _, NoListener, _> = Keyhive::try_from_archive( &archive, signer, store, NoListener, Arc::new(Mutex::new(rand::rngs::OsRng)), ) .await?; assert_eq!(restored.id(), archive.id()); println!("State restored successfully"); ``` ### Response - `into_archive()`: Returns an `Archive` struct containing the serialized state. - `try_from_archive()`: Returns a `Result, Error>` representing the restored Keyhive instance or an error. ``` -------------------------------- ### Create Membership Group with Keyhive Source: https://context7.com/inkandswitch/keyhive/llms.txt Creates a named group of agents (individuals, other groups, or documents). Groups allow hierarchical membership, granting transitive access to documents. Requires `keyhive_core`. ```rust use keyhive_core::{ principal::{agent::Agent, peer::Peer}, access::Access, principal::membered::Membered, }; // Create an empty group (alice is automatically added as owner) let group = alice.generate_group(vec![]).await?; let group_id = group.lock().await.group_id(); // Add bob to the group alice.add_member( Agent::Individual(bob_id, bob_indie.clone()), &Membered::Group(group_id, group.clone()), Access::Read, &[], ).await?; // Add the group to a document — bob now transitively has Read access alice.add_member( Agent::Group(group_id, group.clone()), &Membered::Document(doc_id, doc.clone()), Access::Read, &[], ).await?; println!("Group {:?} added to document {:?}", group_id, doc_id); ``` -------------------------------- ### Serialize and Restore Keyhive State Source: https://context7.com/inkandswitch/keyhive/llms.txt Use `into_archive` to serialize the entire `Keyhive` state for persistence or migration. Restore the state using `try_from_archive`, which requires the original signing key, store, and other necessary components. The restored `Keyhive` instance should match the original's ID. ```rust // Serialize to archive let archive = keyhive.into_archive().await; let bytes = bincode::serialize(&archive)?; println!("Archive size: {} bytes", bytes.len()); println!("Archive contains {} docs", archive.docs.len()); // Restore from archive (requires the original signing key) let restored: Keyhive, _, NoListener, _> = Keyhive::try_from_archive( &archive, signer, store, NoListener, Arc::new(Mutex::new(rand::rngs::OsRng)), ) .await?; assert_eq!(restored.id(), archive.id()); println!("State restored successfully"); ``` -------------------------------- ### Keyhive::expand_prekeys / rotate_prekey Source: https://context7.com/inkandswitch/keyhive/llms.txt Manages the prekey pool by adding new prekeys or replacing existing ones, enabling asynchronous key exchange. ```APIDOC ## `Keyhive::expand_prekeys` / `rotate_prekey` — Manage prekey pool Prekeys are single-use X25519 public keys published for asynchronous key exchange. `expand_prekeys` adds a new prekey to the pool; `rotate_prekey` replaces an existing one. Both operations are signed and can be shared with peers via `events_for_agent`. ### Method Signatures - `expand_prekeys(&self) -> impl Future>` - `rotate_prekey(&self, old_share_key: &PublicKey) -> impl Future>` ### Parameters - **expand_prekeys**: No parameters. - **rotate_prekey**: `old_share_key`: The public key of the prekey to be replaced. ### Request Example ```rust // Add a new prekey to the pool let add_op = alice.expand_prekeys().await?; println!("New prekey added: {:?}", add_op.payload().share_key); // Rotate an existing prekey (replaces old with new) let rotate_op = alice.rotate_prekey(add_op.payload().share_key).await?; println!("Prekey rotated: old={{:?}} -> new={{:?}}", rotate_op.payload().old, rotate_op.payload().new ); ``` ``` -------------------------------- ### Backup and Restore Prekey Secrets Source: https://context7.com/inkandswitch/keyhive/llms.txt Use `export_prekey_secrets` to back up raw X25519 secret keys as a `bincode`-serialized blob, suitable for device backup or migration. Protect this blob as it contains unencrypted secret material. Use `import_prekey_secrets` on a restored or migrated agent to extend its prekey pool. ```rust // Export prekey secrets from agent A let blob = keyhive.export_prekey_secrets().await?; println!("Exported {} bytes of prekey secrets", blob.len()); // Import on a restored/migrated agent — extends the existing prekey pool keyhive_restored.import_prekey_secrets(&blob).await?; println!("Prekey secrets imported successfully"); ``` -------------------------------- ### Active::export_prekey_secrets / Active::import_prekey_secrets Source: https://context7.com/inkandswitch/keyhive/llms.txt Exports the raw X25519 secret keys for all current prekeys as a `bincode`-serialized blob for backup or migration, and imports such a blob into an existing prekey pool. ```APIDOC ## Active::export_prekey_secrets / Active::import_prekey_secrets ### Description Exports the raw X25519 secret keys for all current prekeys as a `bincode`-serialized blob. Used for device backup or migration. The exported bytes contain unencrypted secret key material and must be protected at rest and in transit. Imports a blob of prekey secrets. ### Method - `export_prekey_secrets()`: Exports the prekey secrets. - `import_prekey_secrets()`: Imports prekey secrets from a blob. ### Parameters for `import_prekey_secrets` - **blob**: `&[u8]` - A bincode-serialized blob of X25519 secret keys. ### Request Example (Export) ```rust // Export prekey secrets from agent A let blob = keyhive.export_prekey_secrets().await?; println!("Exported {} bytes of prekey secrets", blob.len()); ``` ### Request Example (Import) ```rust // Import on a restored/migrated agent — extends the existing prekey pool keyhive_restored.import_prekey_secrets(&blob).await?; println!("Prekey secrets imported successfully"); ``` ### Response - `export_prekey_secrets()`: Returns a `Vec` containing the serialized prekey secrets. - `import_prekey_secrets()`: Returns a `Result<(), Error>` indicating success or failure. ``` -------------------------------- ### Keyhive::events_for_agent Source: https://context7.com/inkandswitch/keyhive/llms.txt Collects all membership, prekey, and CGKA operations that a given agent is authorized to receive. This is used to build synchronization payloads before sending state to a peer. ```APIDOC ## Keyhive::events_for_agent ### Description Collects all membership operations, prekey operations, and CGKA operations that a given agent is authorized to receive. Used to build sync payloads — call this before sending state to a peer. ### Method (Implicitly called via `alice.events_for_agent(&bob_agent).await`) ### Parameters - **agent**: `&Agent` - The agent for which to collect events. ### Request Example ```rust // Collect all events that bob is authorized to see let bob_agent = Agent::Individual(bob_id, bob_indie.clone()); let events = alice.events_for_agent(&bob_agent).await; println!("Events for Bob: {} total", events.len()); // On Bob's side, ingest the received events bob.ingest_event_table(events).await?; println!("Bob now knows about {} docs", bob.documents().lock().await.len()); ``` ### Response - **events**: `Vec` - A vector of events the agent is authorized to receive. ``` -------------------------------- ### Archival Strategy with Decreasing Hash Hardness Source: https://github.com/inkandswitch/keyhive/blob/main/design/stable_chunking.md Visualizes an archival strategy where chunks are merged into larger runs by decreasing hash hardness as chunks are generated. This is a search problem on the causal graph, splitting it into probabilistically smaller regions closer to the latest head. ```mermaid flowchart TD d(u0n7c2) c(f36c09) e(644dn9) j(f34bk0) h(z3fgb8) g(a5j200) i(8ui0n7) a(8vxt00) b(roib8a) f(ig0000) subgraph four["0000"] f c e f --> x(g6t000) --> y(634v03) --> z(xde4g2) --> o(83bd7xa) e --> w(8ubx00) --> z end subgraph two["00"] subgraph B[" "] a b d cB(f36c09) eB(644dn9) b --> cB --> eB d --> eB end subgraph C [" "] g --> dA(u0n7c2) --> eA(644dn9) end end subgraph Heads["None (i.e. heads)"] subgraph D[" "] h j end subgraph E[" "] i end end a --> b c --> e h --> g i ---> a i --> f j --> h b --> d f --> c B ~~~~~~ four eB --> w eA --> w style c fill:red; style cB fill:red; style d fill:red; style dA fill:red; style e fill:red; style eA fill:red; style eB fill:red; ``` -------------------------------- ### Force Post-Compromise Security Update with Keyhive::force_pcs_update Source: https://context7.com/inkandswitch/keyhive/llms.txt Forces a CGKA path update and generates a new root key to ensure post-compromise security. Call this after membership changes to invalidate access for revoked members. ```rust // After revoking a member, rotate the key to restore PCS let signed_op = alice.force_pcs_update(doc.clone()).await?; println!("PCS update operation: {:?}", signed_op.payload()); // The new root key is now inaccessible to revoked members ``` -------------------------------- ### Cross-Group Dependencies in Document Agents Source: https://github.com/inkandswitch/keyhive/blob/main/design/group_membership.md This diagram illustrates how operations within one document agent (e.g., Document A) can be causally dependent on operations or membership changes in another document agent (e.g., Document B). It highlights interdependencies between content and membership operations. ```mermaid flowchart subgraph DocumentAgent[Document A Agent] _docPK["Document A Root
(Public Key)"] subgraph docAGroup[Document A Membership] docRoot docRootAddsAnotherGroup docRootAddsSingleton docRootAddsSingleton singetonRemovesAnotherGroup end subgraph opsA[Document A Content] InitMap addKeyFoo addKeyBar removeKeyFoo end end removeKeyFoo["Carol
---------------------
Remove Key ''foo''"] --> addKeyFoo addKeyFoo["Dan
---------------
foo := 1"] --> InitMap[Document Root
------------------
Initialize Map] addKeyBar["Carol
-----------------------
bar := Document B"] ----> addKeyFoo docRootAddsSingleton["Doc A Root
--------------------
Add Carol PK"] --> docRoot[Document Root
----------------------
Self Certifying Init] docRootAddsAnotherGroup["Doc Root
-----------
Add Dan"] --> docRoot singetonRemovesAnotherGroup[Carol
---------------
Remove Dan] --> docRootAddsSingleton singetonRemovesAnotherGroup --> docRootAddsAnotherGroup singetonRemovesAnotherGroup -.->|lock state after| addKeyFoo singetonRemovesAnotherGroup -.-> docBRootAddsAnotherGroup InitMap -.->|self-certified by| docRoot -.->|self-certified by| _docPK subgraph DocumentAgent2[Document B Agent] _docBPK["Document B Root
(Public Key)"] subgraph docBGroup[Document B Membership] docBRoot docBRootAddsSingleton docBRootAddsAnotherGroup bobAddsDocA docBRootAddsAnotherGroup end subgraph opsB[Document B Content] addCharH["Alice
----------
push('H')"] --> InitStringB[Document Root
------------------
Initialize String] addCharI["Alice
---------
push('i')"] --> addCharH addCharExp["Bob
----------
push('!')"] --> addCharI end end InitStringB-.->|self-certified by| docBRoot -.->|self-certified by| _docBPK addCharH -.-> docBRootAddsAnotherGroup docBRootAddsSingleton -.-> addCharI addCharExp -.-> docBRootAddsSingleton addKeyBar -.-> addCharExp docBRootAddsSingleton["Doc Root
--------------
Add Bob PK"] --> docBRoot[Document Root
----------------------
Self Certifying Init] docBRootAddsAnotherGroup["Doc Root
-----------
Add Alice"] --> docBRoot bobAddsDocA["Bob
-----------
Add Document A"] -..-> docRoot bobAddsDocA --> docBRootAddsAnotherGroup style docBGroup fill:green; style docAGroup fill:green; style opsA fill:blue; style opsB fill:blue; ``` -------------------------------- ### Exchange Identity with Keyhive::contact_card / receive_contact_card Source: https://context7.com/inkandswitch/keyhive/llms.txt Generates and receives signed `ContactCard` operations for exchanging identity information. `contact_card` creates the card, and `receive_contact_card` registers it locally. ```rust // Alice generates a contact card to share out-of-band let card = alice.contact_card().await?; // Bob receives Alice's contact card let alice_individual = bob.receive_contact_card(&card).await?; println!("Registered Alice with ID: {:?}", alice_individual.lock().await.id()); // Registered Alice with ID: IndividualId(...) ``` -------------------------------- ### Encrypt Content with Keyhive::try_encrypt_content Source: https://context7.com/inkandswitch/keyhive/llms.txt Encrypts plaintext content for a document using its group key. Returns ciphertext and potential CGKA update operations. Ensure `content_ref` and `pred_refs` are correctly provided. ```rust let content_ref = [42u8; 32]; let pred_refs = vec![[0u8; 32]]; // previously encrypted content IDs let plaintext = b"Hello, encrypted world!"; let result = alice.try_encrypt_content( doc.clone(), &content_ref, &pred_refs, plaintext, ).await?; println!("Ciphertext length: {}", result.encrypted_content.ciphertext.len()); // Ciphertext length: ... if let Some(cgka_op) = &result.update_op { println!("Key rotation op produced: {:?}", cgka_op.payload()); } ``` -------------------------------- ### Rust Auth State Transition Structures Source: https://github.com/inkandswitch/keyhive/blob/main/design/group_membership.md Defines the data structures for authentication actions and operations, including group and singleton additions, agent removals, and document head locking. ```rust pub struct Attenuation { group_id: Option, ceveats: CeveatDsl } enum AuthAction { // Arguably this could be expressed as AddGroup with group_heads: vec![singleton.id] or possibly vec![] // It's a noop if you give a stateless agent a different head, // since you will never be able to apply the op. AddSingleton { id: PublicKey, attenuation: Attenuation }, // Add Group includes docs, since Doc :< Group // Since Group :< Singleton, you *could* add a group that way, // but it would add at the start of its history // (which may or may not be desirable, depending on the domain) AddGroup { id: PublicKey, attenuation: Attenuation, group_heads: Vec, // REMINDER: this is the group being added's heads (aud), NOT the group being added to (iss) }, RemoveAgent { id: PublicKey }, } struct AuthOp { action: AuthAction, // ⬆️ /// The auth_pred: Vec, /// All heads for all known updated documents. /// In effect, this locks the auth change to occur *after* content updates. doc_heads: BTreeMap>, author: PublicKey, signature: Signature } ``` -------------------------------- ### Keyhive::generate_doc Source: https://context7.com/inkandswitch/keyhive/llms.txt Creates a new document with an initial set of content-addressed heads and optional co-owners. All listed co-owners receive delegated access. The document is backed by a CGKA tree for group encryption. ```APIDOC ## Keyhive::generate_doc — Create an encrypted document ### Description Creates a new document with an initial set of content-addressed heads and optional co-owners. All listed co-owners receive delegated access. The document is backed by a CGKA tree for group encryption. ### Method ```rust Keyhive::generate_doc(co_owners, initial_heads) ``` ### Parameters - `co_owners`: A vector of `Peer` objects representing co-owners of the document. - `initial_heads`: A non-empty vector of content-addressed identifiers (`[u8; 32]`) representing the initial heads of the document. ### Request Example ```rust use nonempty::nonempty; use keyhive_core::keyhive::Keyhive; use keyhive_core::principal::peer::Peer; // alice and bob are Keyhive instances created via Keyhive::generate // bob_individual is an Arc> registered on alice // Create a document with alice as owner and bob as co-owner // Initial content heads are content-addressed identifiers ([u8; 32]) let doc = alice .generate_doc( vec![Peer::Individual(bob_id, bob_individual.clone())], nonempty![[0u8; 32], [1u8; 32]], ) .await?; let doc_id = doc.lock().await.doc_id(); println!("Document created: {:?}", doc_id); ``` ### Response #### Success Response A `Document` object representing the newly created encrypted document. #### Response Example ```rust // Document created: DocumentId(...) ``` ``` -------------------------------- ### Collect Events for Agent Synchronization Source: https://context7.com/inkandswitch/keyhive/llms.txt Use `events_for_agent` to gather all membership, prekey, and CGKA operations an agent is authorized to receive. This is crucial for building synchronization payloads before sending state to a peer. After receiving events, use `ingest_event_table` to process them. ```rust let bob_agent = Agent::Individual(bob_id, bob_indie.clone()); let events = alice.events_for_agent(&bob_agent).await; println!("Events for Bob: {} total", events.len()); // On Bob's side, ingest the received events bob.ingest_event_table(events).await?; println!("Bob now knows about {} docs", bob.documents().lock().await.len()); ``` -------------------------------- ### Delegate Access to an Agent with Keyhive Source: https://context7.com/inkandswitch/keyhive/llms.txt Grants a principal a specified access level on a resource. When adding a reader to a group that is a member of documents, CGKA add operations are automatically propagated. Requires `keyhive_core`. ```rust use keyhive_core::access::Access; // Access levels: Relay < Read < Edit < Admin // Add carol as an editor on a document let update = alice.add_member( Agent::Individual(carol_id, carol_indie.clone()), &Membered::Document(doc_id, doc.clone()), Access::Edit, &[], // other_relevant_docs for cross-document CGKA propagation ).await?; // update.delegation contains the signed delegation // update.cgka_ops contains any CGKA tree operations produced println!("Delegation subject: {:?}", update.delegation.subject_id()); println!("CGKA ops produced: {}", update.cgka_ops.len()); ``` -------------------------------- ### Keyhive::force_pcs_update Source: https://context7.com/inkandswitch/keyhive/llms.txt Forces a CGKA path update for the document, generating a fresh root key to ensure post-compromise security after membership changes. ```APIDOC ## `Keyhive::force_pcs_update` — Trigger post-compromise security key rotation Forces a CGKA path update for the document, generating a fresh root key. This should be called after membership changes to ensure post-compromise security — members who have been removed cannot read future content encrypted after this rotation. ### Method Signature `force_pcs_update(document: Document) -> impl Future>` ### Parameters - **document**: The document for which to force a PCS update. ### Request Example ```rust // After revoking a member, rotate the key to restore PCS let signed_op = alice.force_pcs_update(doc.clone()).await?; println!("PCS update operation: {:?}", signed_op.payload()); // The new root key is now inaccessible to revoked members ``` ``` -------------------------------- ### Commit Graph Structure Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Represents a typical commit graph structure before compression. ```mermaid flowchart LR A --> B B --> C A --> D D --> E E --> F F --> G E --> J G --> H J --> I H --> I ``` -------------------------------- ### Merges with Harder Hash Metric Source: https://github.com/inkandswitch/keyhive/blob/main/design/stable_chunking.md Illustrates merging chunks using a harder hash metric (six '0's). This approach groups content within '00' chunks and deduplicates, only requiring checks on graph heads to determine stopping points. ```mermaid flowchart TD subgraph one["000000 Head(s)"] a b c e i d1(u0n7c2) end subgraph Chunk[00000 Chunk] f c3(f36c09) e3(644dn9) end subgraph Head1["000000 Head(s)"] d g h j e2(644dn9) end j(f34bk0) h(z3fgb8) g(a5j200) d(u0n7c2) e(644dn9) i(8ui0n7) a(8vxt00) b(roib8a) c(f36c09) f(ig0000) a --> b b --> c c --> e d --> e2 g --> d h --> g i --> a i --> f j --> h b --> d1 --> e f --> c3 c3 --> e3 b ~~~ d style c3 fill:red; style e3 fill:red; style d1 fill:red; style e2 fill:red; style c fill:red; style d fill:red; style e fill:red; ``` -------------------------------- ### Commit Graph Encryption Flow Source: https://github.com/inkandswitch/keyhive/blob/main/design/sedimentree.md Illustrates how commit graph data is encrypted, making it inaccessible to sync servers for direct compression. ```mermaid flowchart LR A[A
encrypted chunk] --> B A --> C B[B
encrypted chunk] C[C
encrypted chunk] ``` -------------------------------- ### Sync events to a peer using JsKeyhive::eventsForAgent (TypeScript) Source: https://context7.com/inkandswitch/keyhive/llms.txt Collects events for a specific agent and prepares them for network transmission. On the receiving side, it ingests these events and reports any pending dependencies. ```typescript // On Alice's side: collect events for Bob const bobAgent = await keyhive.getAgent(bobIdentifier); const eventsMap: Map = await keyhive.eventsForAgent(bobAgent); // Transmit events_map values to Bob over the network const eventBytesArray = Array.from(eventsMap.values()); // On Bob's side: ingest received events const pendingEvents = await bobKeyhive.ingestEventsBytes(eventBytesArray); if (pendingEvents.length === 0) { console.log("All events applied"); } else { console.log(`${pendingEvents.length} events pending dependencies`); } ``` -------------------------------- ### Keyhive::ingest_unsorted_static_events Source: https://context7.com/inkandswitch/keyhive/llms.txt Processes a batch of serialized `StaticEvent`s received from a peer, handling dependency resolution automatically. Events that cannot yet be applied are placed in a pending queue. ```APIDOC ## Keyhive::ingest_unsorted_static_events ### Description Processes a batch of serialized `StaticEvent`s received from a peer, handling dependency resolution automatically. Events that cannot yet be applied (missing causal dependencies) are placed in a pending queue and retried when more events arrive. ### Method (Implicitly called via `alice.ingest_unsorted_static_events(events).await`) ### Parameters - **events**: `Vec>` - A vector of serialized static events received from a peer. ### Request Example ```rust use keyhive_core::event::static_event::StaticEvent; // events_bytes is a Vec> received from a peer let pending = alice.ingest_unsorted_static_events(events).await; if pending.is_empty() { println!("All events applied successfully"); } else { println!("{} events are pending (missing dependencies)", pending.len()); } ``` ### Response - **pending**: `Vec>` - A vector of events that are pending due to missing dependencies. ```