### Rx Channel Example Source: https://github.com/bearcove/roam/blob/main/docs/content/http-spec/_index.md Illustrates the flow of data for an Rx channel, where the client subscribes to a topic and receives data from the bridge. ```APIDOC ## Rx Channel ### Description This example demonstrates the client-side interaction for an Rx channel, where a client subscribes to a topic and receives data pushed from the bridge. ### Method WebSocket (Implicit) ### Endpoint `/bearcove/roam` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **request** (object) - Required - Represents a request to subscribe to a topic. - **id** (integer) - Required - Unique identifier for the request. - **args** (array) - Required - Arguments for the request, typically including the topic. - **channel** (integer) - Required - The channel identifier for this operation. ### Request Example ```json { "request": {"id":1, "args":["news"], "channel":1} } ``` ### Response #### Success Response (200) - **data** (object) - Represents data received on the channel. - **channel** (integer) - The channel identifier. - **value** (any) - The actual data payload. - **credit** (object) - Represents a credit update for the channel. - **channel** (integer) - The channel identifier. - **bytes** (integer) - The number of bytes available for sending. - **response** (object) - Represents the final response to the initial request. - **id** (integer) - The identifier of the request being responded to. - **result** (null) - The result of the operation (null for subscription). #### Response Example ```json { "data": {"channel":1, "value":{...}} } ``` ```json { "credit": {"channel":1, "bytes":8192} } ``` ```json { "response": {"id":1, "result":null} } ``` ``` -------------------------------- ### Tx Channel Example Source: https://github.com/bearcove/roam/blob/main/docs/content/http-spec/_index.md Illustrates the flow of data for a Tx channel, where the client uploads data in chunks to the bridge. ```APIDOC ## Tx Channel ### Description This example demonstrates the client-side interaction for a Tx channel, where a client uploads data in chunks to the bridge and receives a summary upon completion. ### Method WebSocket (Implicit) ### Endpoint `/bearcove/roam` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **request** (object) - Required - Represents a request to initiate an upload operation. - **id** (integer) - Required - Unique identifier for the request. - **channel** (integer) - Required - The channel identifier for this operation. - **data** (object) - Required - Represents a chunk of data to be uploaded. - **channel** (integer) - Required - The channel identifier. - **value** (object) - Required - The data payload for the chunk. - **close** (object) - Required - Indicates the end of data transmission for the channel. - **channel** (integer) - Required - The channel identifier. ### Request Example ```json { "request": {"id":2, "channel":3} } ``` ```json { "data": {"channel":3, "value":{...}} } ``` ```json { "close": {"channel":3} } ``` ### Response #### Success Response (200) - **credit** (object) - Represents a credit update for the channel. - **channel** (integer) - The channel identifier. - **bytes** (integer) - The number of bytes available for sending. - **response** (object) - Represents the final response to the initial request, containing the upload summary. - **id** (integer) - The identifier of the request being responded to. - **result** (object) - The summary of the upload operation. #### Response Example ```json { "credit": {"channel":3, "bytes":8192} } ``` ```json { "response": {"id":2, "result":{...}} } ``` ``` -------------------------------- ### HTTP Bridge RPC Call Example Source: https://context7.com/bearcove/roam/llms.txt Demonstrates making a simple RPC call to a Roam service exposed via the HTTP Bridge using `curl`. It shows a successful call and an example of a fallible method returning an error. Requires the HTTP Bridge to be running and configured. ```bash # Simple RPC call via HTTP POST curl -X POST http://localhost:8080/Calculator/add \ -H "Content-Type: application/json" \ -H "Roam-Request-Id: abc123" \ -d '[3, 5]' # Response: 8 # Fallible method curl -X POST http://localhost:8080/Calculator/divide \ -H "Content-Type: application/json" \ -d '[10, 0]' # Response: {"error":"user","value":{"DivisionByZero":{}}} ``` -------------------------------- ### TypeScript Development Tasks Source: https://github.com/bearcove/roam/blob/main/DEVELOP.md Instructions for common TypeScript development tasks, including type-checking and regenerating bindings. Uses `pnpm` for local checks and `just` for repository-wide commands. ```bash # Type-check TypeScript cd typescript && pnpm check # Or from repo root just ts-typecheck # Regenerate TypeScript bindings from spec-proto cargo xtask codegen --typescript # Or just ts-codegen ``` -------------------------------- ### Install Roam Tracing Host Collector and Cell Forwarder Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/011-dodeca-flag-day-cutover.md This task focuses on replacing the existing tracing infrastructure with roam's implementation. It involves installing the roam tracing host collector and the cell forwarder. Ensuring failure modes are safe, including lossy buffering and preventing deadlocks, is critical. ```Rust Install roam tracing host collector and cell forwarder. Ensure failure modes are safe (lossy buffering, no deadlocks). ``` -------------------------------- ### Busy-Wait and Sleep Examples (Rust) Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/005-futex-wakeups.md Demonstrates inefficient busy-waiting and sleeping patterns that are to be replaced by futex wakeups. Busy-waiting consumes CPU cycles unnecessarily, while sleeping introduces latency. ```rust while ring_is_full() { std::hint::spin_loop(); } while ring_is_full() { std::thread::sleep(Duration::from_micros(100)); } ``` -------------------------------- ### ReconnectingClient Usage Example Source: https://github.com/bearcove/roam/blob/main/docs/content/reconnecting-client-spec/_index.md Demonstrates how to use the ReconnectingClient with a custom connector. The ReconnectingClient handles connection and reconnection transparently, allowing direct calls to methods. ```rust use std::path::PathBuf; use std::io; use futures_util::StreamExt; use tokio::net::UnixStream; use cobs::CobsFramed; // Assuming these types are defined elsewhere in the project: // use crate::{Connector, ReconnectingClient, Hello, StatusResponse, daemon_method_id}; // Define connector struct DaemonConnector { socket_path: PathBuf, } impl Connector for DaemonConnector { type Transport = CobsFramed; async fn connect(&self) -> io::Result { let stream = UnixStream::connect(&self.socket_path).await?; Ok(CobsFramed::new(stream)) } fn hello(&self) -> Hello { Hello::V3 { max_payload_size: 1024 * 1024, initial_channel_credit: 64 * 1024, } } } // Use it // async fn example_usage(socket_path: PathBuf) -> Result<(), Box> { // let connector = DaemonConnector { socket_path }; // let client = ReconnectingClient::new(connector); // // // Calls reconnect transparently // let status: StatusResponse = client // .call(daemon_method_id::status(), &()) // .await?; // Ok(()) // } ``` -------------------------------- ### TypeScript Wire Type and Schema Generation Example Source: https://github.com/bearcove/roam/blob/main/todo/ts-schema/004-DONE-codegen-wire.md This example demonstrates how to use the generated TypeScript types and schemas for runtime verification. It includes type assertions for generated structures and checks the content of the schema registry. ```typescript // Type structure tests (compile-time verification) import type { Message, Hello, MetadataValue } from "./generated/types.ts"; const hello: Hello = { tag: "V1", maxPayloadSize: 1024, initialChannelCredit: 64 }; const msg: Message = { tag: "Request", requestId: 1n, methodId: 42n, metadata: [], payload: new Uint8Array([]), }; // Schema tests import { MessageSchema, HelloSchema, wireSchemaRegistry } from "./generated/schemas.ts"; expect(MessageSchema.kind).toBe("enum"); expect(MessageSchema.variants.length).toBe(9); // Hello variant uses ref, not inline schema expect(MessageSchema.variants[0]).toEqual({ name: "Hello", discriminant: 0, fields: { kind: "ref", name: "Hello" }, }); // Registry contains all named types expect(wireSchemaRegistry.get("Hello")).toBe(HelloSchema); expect(wireSchemaRegistry.get("Message")).toBe(MessageSchema); expect(wireSchemaRegistry.size).toBe(3); // Hello, MetadataValue, Message ``` -------------------------------- ### Variable-Size Slot Pool Configuration Example Source: https://github.com/bearcove/roam/blob/main/docs/content/shm-spec/_index.md Illustrates a potential configuration for a variable-size slot pool, showing different size classes, their respective slot sizes, counts, total memory usage, and typical use cases. This allows for efficient allocation of memory for payloads of varying sizes. ```text | Class | Slot Size | Count | Total | Use Case | |-------|-----------|-------|-------|----------| | 0 | 1 KB | 1024 | 1 MB | Small RPC args | | 1 | 16 KB | 256 | 4 MB | Typical payloads | | 2 | 256 KB | 32 | 8 MB | Images, CSS | | 3 | 4 MB | 8 | 32 MB | Compressed fonts | | 4 | 16 MB | 4 | 64 MB | Decompressed fonts | ``` -------------------------------- ### Use RPC Client in Rust Source: https://github.com/bearcove/roam/blob/main/README.md Demonstrates how to use a generated roam RPC client in Rust to make calls to a remote service. Includes examples for simple, fallible, and metadata-aware calls. ```rust let client = CalculatorClient::new(connection_handle); // Simple call let result = client.add(2, 3).await?; // Fallible call match client.divide(10, 0).await? { Ok(result) => println!("Result: {result}"), Err(MathError::DivisionByZero) => println!("Cannot divide by zero"), } // With metadata let result = client.add(2, 3) .with_metadata(vec![("request-id".into(), "abc123".into())]) .await?; ``` -------------------------------- ### TypeScript Wire Testing Setup Source: https://github.com/bearcove/roam/blob/main/todo/ts-schema/006-DONE-golden-tests.md Sets up the testing environment for TypeScript wire encoding/decoding tests. It imports necessary testing utilities and functions from the @bearcove/roam-wire package, and includes a helper function to load golden vector fixtures from the file system. ```typescript import { describe, it, expect } from "vitest"; import { readFileSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { encodeMessage, decodeMessage, encodeHello, decodeHello, type Message, type Hello, type MetadataValue, } from "@bearcove/roam-wire"; const __dirname = dirname(fileURLToPath(import.meta.url)); /** Load a golden vector from the test-fixtures directory */ function loadGoldenVector(path: string): Uint8Array { const projectRoot = join(__dirname, "..", "..", "..", ".."); const vectorPath = join(projectRoot, "test-fixtures", "golden-vectors", path); return new Uint8Array(readFileSync(vectorPath)); } ``` -------------------------------- ### Run Tests with Cargo Nextest and Just Source: https://github.com/bearcove/roam/blob/main/DEVELOP.md Commands to execute tests for the project. Supports running all Rust tests using `cargo nextest run`, or spec tests for specific languages (Rust, TypeScript) using `just`. ```bash # Run all Rust tests cargo nextest run # Run spec tests with Rust subject just rust # Run spec tests with TypeScript subject just ts # Run all language subjects just all ``` -------------------------------- ### Rust User Ergonomics for Dispatcher and Middleware Source: https://github.com/bearcove/roam/blob/main/todo/ROAM_NEXT.md Demonstrates how to define a handler, create a dispatcher with middleware, and use it with the connection setup. This showcases the user-facing API for integrating custom logic with the dispatch system. ```rust // Define handler struct MyHandler; impl Testbed for MyHandler { async fn echo(&self, cx: &Context, message: String) -> String { message } } // Create dispatcher with middleware let dispatcher = TestbedDispatcher::new(MyHandler) .with_middleware(AuthMiddleware::new()) .with_middleware(RateLimiter::new()); // Use as before let client = connect(connector, config, dispatcher); ``` -------------------------------- ### Rust Service Definition Example Source: https://github.com/bearcove/roam/blob/main/docs/content/spec/_index.md Defines a service named 'TemplateHost' within a 'proto crate' using Rust async traits. It includes a method 'load_template' with specified parameters and return type, demonstrating the basic structure of a Roam service definition. ```rust // proto.rs #[roam::service] //└────┬────┘ Service definition pub trait TemplateHost { // └────┬─────┘ Service name async fn load_template(&self, context_id: ContextId, name: String) -> LoadTemplateResult; // └─────┬──────┘ └──────────────┬────────────────┘ └────────┬──────────┘ // Method Parameters Return type } // More services can be defined in the same proto crate... ``` -------------------------------- ### Implement RPC Service in Rust Source: https://github.com/bearcove/roam/blob/main/README.md Provides an example of implementing a roam RPC service in Rust. This involves defining the logic for each method declared in the service trait, handling different communication patterns including streaming. ```rust impl Calculator for MyCalculator { async fn add(&self, _cx: &Context, a: i32, b: i32) -> i32 { a + b } async fn divide(&self, _cx: &Context, a: i32, b: i32) -> Result { if b == 0 { Err(MathError::DivisionByZero) } else { Ok(a / b) } } async fn sum(&self, _cx: &Context, mut numbers: Rx) -> i64 { let mut total: i64 = 0; while let Some(n) = numbers.recv().await.ok().flatten() { total += n as i64; } total } // ... } ``` -------------------------------- ### Initialize and Use SpscRing in Rust Source: https://github.com/bearcove/roam/blob/main/rust/shm-primitives/README.md Demonstrates the initialization and usage of an SpscRing (Single-Producer Single-Consumer Ring Buffer) in Rust. It shows how to create a ring buffer, split it into producer and consumer ends, push values, and pop them back. This example utilizes `HeapRegion` for memory management and `SpscRing` for the ring buffer functionality. ```rust use shm_primitives::{SpscRing, HeapRegion, PushResult}; // Create a ring buffer with capacity 16 let region = HeapRegion::new_zeroed(SpscRing::::required_size(16)); let ring = SpscRing::::init(region.region(), 16); // Split into producer and consumer let (mut producer, mut consumer) = ring.split(); // Push some values assert!(matches!(producer.push(42), PushResult::Ok)); assert!(matches!(producer.push(43), PushResult::Ok)); // Pop them back assert_eq!(consumer.pop(), Some(42)); assert_eq!(consumer.pop(), Some(43)); assert_eq!(consumer.pop(), None); ``` -------------------------------- ### Async Send with Zero Buffer in TypeScript Source: https://github.com/bearcove/roam/blob/main/PLAN.md Demonstrates the correct usage of `tx.send()` for channels with zero buffering and backpressure. It highlights that `tx.send()` should only be called after the receiver has been set up (e.g., by calling a client method that accepts the Rx object), otherwise, it will block indefinitely. The example shows the correct sequence of binding the channel, sending data, and awaiting the result. ```typescript const [tx, rx] = channel(); // WRONG - blocks forever, no receiver yet: // await tx.send(1); // client.sum(rx); // CORRECT - start call first, then send: const resultPromise = client.sum(rx); // Binds channels, sets up receiver await tx.send(1); // Now there's a receiver await tx.send(2); tx.close(); const result = await resultPromise; ``` -------------------------------- ### WebSocket Upgrade Request Source: https://github.com/bearcove/roam/blob/main/docs/content/http-spec/_index.md To establish a WebSocket connection for channel communication, clients must include the 'roam-bridge.v1' subprotocol in the upgrade request headers. This example shows a typical HTTP GET request for upgrading to a WebSocket connection. ```http GET /@ws HTTP/1.1 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Protocol: roam-bridge.v1 ``` -------------------------------- ### Accepting Connections with Roam (Rust Server) Source: https://context7.com/bearcove/roam/llms.txt Demonstrates how to set up a server using `accept_framed` to handle incoming connections. It performs the Hello handshake, returns handles for the root connection, a receiver for virtual connections, and a driver that must be spawned to manage the connection lifecycle. Dependencies include `roam_session`, `roam_websocket`, and `tokio`. ```rust use roam_session::{accept_framed, HandshakeConfig, Driver}; use roam_websocket::WsTransport; use tokio::net::TcpListener; async fn run_server() -> Result<(), Box> { let listener = TcpListener::bind("127.0.0.1:9000").await?; let handler = MyCalculator; let dispatcher = CalculatorDispatcher::new(handler); loop { let (stream, _) = listener.accept().await?; let transport = WsTransport::accept(stream).await?; let config = HandshakeConfig::default(); let (handle, incoming_connections, driver) = accept_framed(transport, config, dispatcher.clone()).await?; // Spawn the driver to handle the connection tokio::spawn(async move { if let Err(e) = driver.run().await { eprintln!("Connection error: {e}"); } }); // Optionally handle incoming virtual connections tokio::spawn(async move { while let Some(conn) = incoming_connections.recv().await { let sub_handle = conn.accept(vec![], None).await.unwrap(); // Use sub_handle for this virtual connection } }); } } ``` -------------------------------- ### Initiating Connections with Roam (Rust Client) Source: https://context7.com/bearcove/roam/llms.txt Illustrates how clients can initiate connections using `initiate_framed` for a one-shot connection or `connect_framed` for automatic reconnection with configurable retry policies. It shows establishing a transport layer, performing the handshake, and interacting with a remote service. Dependencies include `roam_session`, `roam_websocket`, and `tokio`. ```rust use roam_session::{initiate_framed, connect_framed, HandshakeConfig, NoDispatcher, RetryPolicy}; use roam_websocket::WsTransport; // One-shot connection async fn connect_once() -> Result<(), Box> { let transport = WsTransport::connect("ws://localhost:9000").await?; let config = HandshakeConfig::default(); let (handle, _incoming, driver) = initiate_framed(transport, config, NoDispatcher).await?; tokio::spawn(driver.run()); let client = CalculatorClient::new(handle); let result = client.add(1, 2).await?; println!("1 + 2 = {result}"); Ok(()) } // Auto-reconnecting client struct WsConnector { url: String, } impl roam_session::MessageConnector for WsConnector { type Transport = WsTransport; async fn connect(&self) -> std::io::Result { WsTransport::connect(&self.url).await .map_err(|e| std::io::Error::other(e)) } } async fn connect_with_retry() { let connector = WsConnector { url: "ws://localhost:9000".into() }; let config = HandshakeConfig::default(); let policy = RetryPolicy { max_attempts: 5, initial_backoff: std::time::Duration::from_millis(100), max_backoff: std::time::Duration::from_secs(10), backoff_multiplier: 2.0, }; let client = connect_framed_with_policy(connector, config, NoDispatcher, policy); let calc = CalculatorClient::new(client); // Calls automatically reconnect on failure let result = calc.add(1, 2).await.unwrap(); } ``` -------------------------------- ### Generated Schema Registry Example (TypeScript) Source: https://github.com/bearcove/roam/blob/main/todo/ts-schema/001-DONE-schema-types.md An example of a generated schema registry in TypeScript, demonstrating how schema definitions, including EnumSchema and RefSchema, are defined and registered. This registry is used for resolving type references within the schema definitions. ```typescript // Generated schema definitions export const HelloSchema: EnumSchema = { ... }; export const MetadataValueSchema: EnumSchema = { ... }; export const MessageSchema: EnumSchema = { kind: "enum", variants: [ { name: "Hello", discriminant: 0, fields: { kind: "ref", name: "Hello" } }, // ... ] }; // Registry for resolving refs export const wireSchemaRegistry: SchemaRegistry = new Map([ ["Hello", HelloSchema], ["MetadataValue", MetadataValueSchema], ["Message", MessageSchema], ]); ``` -------------------------------- ### Initialize Pagefind UI and Search Shortcuts (JavaScript) Source: https://github.com/bearcove/roam/blob/main/docs/templates/base.html Initializes the Pagefind search UI on the DOMContentLoaded event. It also sets up keyboard event listeners for 'Ctrl+K' or 'Meta+K' to focus the search input, and '/' to focus the search input when not in a text field. This enhances user experience for searching within the project documentation. ```javascript document.addEventListener('DOMContentLoaded', () => { new PagefindUI({ element: "#search", showSubResults: true, showImages: false, translations: { placeholder: "Search..." } }); document.addEventListener('keydown', (e) => { const searchInput = document.querySelector('#search input'); if (!searchInput) return; if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); searchInput.focus(); searchInput.select(); } if (e.key === '/' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') { e.preventDefault(); searchInput.focus(); searchInput.select(); } }); }); ``` -------------------------------- ### Unix Doorbell Implementation with socketpair() Source: https://github.com/bearcove/roam/blob/main/docs/content/shm-spec/_index.md On Unix-like systems, doorbells are implemented using `socketpair()` to create a pair of connected sockets. One end is retained by the host, and the other is passed to the guest. This forms the basis for signaling between processes. ```c int fds[2]; socketpair(AF_UNIX, SOCK_STREAM, 0, fds); // fds[0] = host end, fds[1] = guest end ``` -------------------------------- ### ReconnectingClient Example Usage in Rust Source: https://github.com/bearcove/roam/blob/main/specs/reconnecting-client.md Illustrates how to define a custom connector and use it with the ReconnectingClient. The ReconnectingClient transparently handles reconnections when making calls. ```rust use std::path::PathBuf; use tokio::net::UnixStream; use tokio_util::codec::CobsFramed; use std::io; // Assume these types and traits are defined elsewhere in the project // pub trait Connector { ... } // pub struct Hello { ... } // pub struct ReconnectingClient { ... } // pub struct StatusResponse; // mod tracey_daemon_method_id { pub const status: u32 = 1; } // --- Mock implementations for demonstration --- struct DaemonConnector { socket_path: PathBuf, } impl DaemonConnector { async fn connect(&self) -> io::Result> { println!("Attempting to connect to {:?}...", self.socket_path); // In a real scenario, this would establish a UnixStream connection // For this example, we'll simulate success immediately. let stream = UnixStream::connect(&self.socket_path).await?; Ok(CobsFramed::new(stream)) } fn hello(&self) -> Hello { Hello::V3 { max_payload_size: 1024 * 1024, initial_channel_credit: 64 * 1024, } } } enum Hello { V3 { max_payload_size: usize, initial_channel_credit: usize }, } struct ReconnectingClient { connector: C, } impl ReconnectingClient { fn new(connector: C) -> Self { ReconnectingClient { connector } } async fn call(&self, method_id: u32, args: &Req) -> io::Result { println!("ReconnectingClient: Calling method ID {}", method_id); // Simulate a successful call after potential reconnection // In a real implementation, this would involve the reconnection logic // and then executing the call on the established connection. // Dummy response generation - replace with actual logic if method_id == 1 { // Assuming 1 is tracey_daemon_method_id::status // This requires Resp to be constructible. Using a placeholder. // In a real scenario, you'd deserialize the response. let dummy_response: Resp = unsafe { std::mem::zeroed() }; // Unsafe, for demo only Ok(dummy_response) } else { Err(io::Error::new(io::ErrorKind::Other, "Unknown method ID")) } } } struct StatusResponse; mod tracey_daemon_method_id { pub const status: u32 = 1; } // --- Main execution --- #[tokio::main] async fn main() -> io::Result<()> { let socket_path = PathBuf::from("/tmp/roam.sock"); let connector = DaemonConnector { socket_path }; let client = ReconnectingClient::new(connector); println!("Making a call via ReconnectingClient..."); // Calls reconnect transparently let status: StatusResponse = client .call(tracey_daemon_method_id::status, &()) .await?; println!("Call completed successfully."); // Process the status response Ok(()) } ``` -------------------------------- ### Rust Doorbell API for Cross-Process Communication Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/002-doorbells.md This Rust code demonstrates the target API for using doorbells. It covers creating a doorbell pair, signaling the peer, waiting for signals with and without timeouts, polling for events (Signal, Death, Timeout), and obtaining the raw file descriptor for asynchronous integration. Assumes the `Doorbell` and `DoorbellEvent` types are defined elsewhere. ```rust use std::time::Duration; use std::os::unix::io::AsRawFd; // Assume Doorbell and DoorbellEvent are defined elsewhere // struct Doorbell; // enum DoorbellEvent { Signal, Death, Timeout } // impl Doorbell { ... } // Create a doorbell pair (host side) // let (host_bell, guest_bell) = Doorbell::pair()?; // Signal the peer // host_bell.ring()?; // Wait for signal (blocking) // guest_bell.wait()?; // Wait with timeout // guest_bell.wait_timeout(Duration::from_millis(100))?; // Poll for signal or death (non-blocking) // match guest_bell.poll()? { // DoorbellEvent::Signal => { /* peer rang */ } // DoorbellEvent::Death => { /* peer died */ } // DoorbellEvent::Timeout => { /* nothing happened */ } // } // Get raw fd for async integration // let fd = guest_bell.as_raw_fd(); ``` -------------------------------- ### Code Generation for Language Bindings Source: https://github.com/bearcove/roam/blob/main/DEVELOP.md Commands to generate protocol bindings for various programming languages. Uses `cargo xtask codegen` with specific language flags. ```bash # Generate all language bindings cargo xtask codegen # Generate specific language cargo xtask codegen --typescript cargo xtask codegen --swift cargo xtask codegen --go cargo xtask codegen --java cargo xtask codegen --python ``` -------------------------------- ### Encode Vec with Schema Source: https://github.com/bearcove/roam/blob/main/todo/ts-schema/002-DONE-schema-encode.md Shows how `encodeWithSchema` handles vectors, specifying the element type within the schema. The example encodes a vector of unsigned 32-bit integers. ```typescript expect(encodeWithSchema( { kind: "vec", element: { kind: "u32" } }, [1, 2, 3] )).toEqual(encodeVec([1, 2, 3], encodeU32)); ``` -------------------------------- ### Newtype Variant Detection (TypeScript) Source: https://github.com/bearcove/roam/blob/main/todo/ts-schema/001-DONE-schema-types.md Provides examples of how to determine if a variant within an enum schema represents a newtype. A newtype variant is one that contains a single field. ```typescript // isNewtypeVariant({ name: "Hello", fields: HelloSchema }) → true // isNewtypeVariant({ name: "Goodbye", fields: { reason: ... } }) → false // isNewtypeVariant({ name: "Tuple", fields: [a, b] }) → false ``` -------------------------------- ### Rust: Create and Attach to MmapRegion Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/001-mmap-backed-regions.md Demonstrates the creation and attachment to a file-backed memory-mapped region using `MmapRegion`. This includes opening/creating the file, setting permissions, truncating, and memory mapping with `MAP_SHARED`. The `attach` function opens an existing file, retrieves its size, and memory maps it. ```rust use std::fs::{File, OpenOptions}; use std::os::unix::io::AsRawFd; use std::path::Path; /// File-backed memory-mapped region. /// /// shm[impl shm.file.mmap-posix] pub struct MmapRegion { ptr: *mut u8, len: usize, file: File, // Keep file open to maintain mapping } impl MmapRegion { /// Create a new file-backed region. /// /// shm[impl shm.file.create] pub fn create(path: &Path, size: usize) -> io::Result { // 1. Open or create file let file = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(true) .open(path)?; // 2. Set permissions // shm[impl shm.file.permissions] #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; file.set_permissions(std::fs::Permissions::from_mode(0o600))?; } // 3. Truncate to size file.set_len(size as u64)?; // 4. mmap with MAP_SHARED let ptr = unsafe { libc::mmap( std::ptr::null_mut(), size, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, file.as_raw_fd(), 0, ) }; if ptr == libc::MAP_FAILED { return Err(io::Error::last_os_error()); } Ok(Self { ptr: ptr as *mut u8, len: size, file, }) } /// Attach to an existing file-backed region. /// /// shm[impl shm.file.attach] pub fn attach(path: &Path) -> io::Result { let file = OpenOptions::new() .read(true) .write(true) .open(path)?; let metadata = file.metadata()?; let size = metadata.len() as usize; let ptr = unsafe { libc::mmap( std::ptr::null_mut(), size, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, file.as_raw_fd(), 0, ) }; if ptr == libc::MAP_FAILED { return Err(io::Error::last_os_error()); } Ok(Self { ptr: ptr as *mut u8, len: size, file, }) } pub fn region(&self) -> Region { Region::new(self.ptr, self.len) } } impl Drop for MmapRegion { fn drop(&mut self) { unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len); } // File is closed automatically when dropped } } // Safety: The mmap region is valid for the lifetime of MmapRegion unsafe impl Send for MmapRegion {} unsafe impl Sync for MmapRegion {} ``` -------------------------------- ### Rust: Generate Client Method Signature Source: https://github.com/bearcove/roam/blob/main/docs/content/rust-spec/_index.md Demonstrates how an original Rust method signature is transformed into a generated client method signature. The generated signature includes a `RoamError` wrapper to distinguish between application-specific errors and protocol-level errors. ```rust async fn get_user(&self, id: UserId) -> Result; ``` ```rust async fn get_user(&self, id: UserId) -> Result>; ``` -------------------------------- ### Host-Side Roam Tracing API Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/009-roam-tracing.md Demonstrates how a host can install a collector, register peers, and consume tracing records emitted by cells. It initializes the TracingHost and sets up a receiver for incoming records. ```rust let mut tracing = roam_tracing::TracingHost::new(4096); let mut records = tracing.take_receiver().unwrap(); tracing.register_peer(peer_id, Some("cell-name".into()), &handle).await?; // Consume records while let Some(tagged) = records.recv().await { println!("[{}] {:?}", tagged.peer_name.unwrap_or_default(), tagged.record); } ``` -------------------------------- ### Host and Guest Spawn Ticket API Usage (Rust) Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/003-spawn-tickets.md Demonstrates how a host reserves a peer slot and obtains a spawn ticket, and how a guest parses the ticket information to attach. The ticket contains peer ID and a doorbell file descriptor. ```rust use std::sync::Arc; use std::process::Command; // Assuming AddPeerOptions, PeerId, SpawnTicket, SpawnArgs, ShmGuest, Doorbell are defined elsewhere // Host side: reserve a peer slot and get spawn ticket // let (transport, ticket) = host.add_peer(AddPeerOptions { // peer_name: Some("cell-image".into()), // on_death: Some(Arc::new(|peer_id| { /* cleanup */ })), // })?; // ticket contains: // - peer_id: PeerId // - guest doorbell (kept alive by the ticket until spawn) // - fd is inheritable (CLOEXEC cleared) // Spawn child with ticket info // let cell_path = "/path/to/cell"; // Replace with actual path // Command::new(&cell_path) // .args(ticket.to_args()) // --hub-path=... --peer-id=... --doorbell-fd=... // .spawn()?; // Guest side: parse args and attach // let args = SpawnArgs::from_env()?; // or from_args() // let guest = ShmGuest::attach_with_ticket(&args)?; // let doorbell = unsafe { Doorbell::from_raw_fd(args.doorbell_fd) }; ``` -------------------------------- ### CI Checks with Cargo Xtask Source: https://github.com/bearcove/roam/blob/main/DEVELOP.md Commands to perform Continuous Integration checks locally. Includes running all checks, or individual checks like tests, clippy, formatting, and documentation generation. ```bash # Run all CI checks locally cargo xtask ci # Individual checks cargo xtask test cargo xtask clippy cargo xtask fmt cargo xtask doc ``` -------------------------------- ### Circular Type Definition Example (TypeScript) Source: https://github.com/bearcove/roam/blob/main/todo/ts-schema/001-DONE-schema-types.md Shows how to define a circular type, such as a linked list node, using a schema registry. This is possible because schema resolution is deferred until encode/decode time. ```typescript // Circular type example (linked list node) const NodeSchema: StructSchema = { kind: "struct", fields: { value: { kind: "i32" }, next: { kind: "option", inner: { kind: "ref", name: "Node" } }, }, }; const circularRegistry = new Map([["Node", NodeSchema]]); // This works because we only resolve refs at encode/decode time, not at schema definition time ``` -------------------------------- ### Host Spawns Child Process (Rust) Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/overview.md No description ```rust use std::process::Command; // Spawn child process with ticket info Command::new(&cell_path) .arg(format!("--hub-path={}", hub_path)) .arg(format!("--peer-id={}", ticket.peer_id)) .arg(format!("--doorbell-fd={}", ticket.doorbell_fd)) .spawn()?; ``` -------------------------------- ### Update Tokio Imports to Runtime Abstraction (Rust) Source: https://github.com/bearcove/roam/blob/main/todo/01-DONE-move-driver.md This code snippet demonstrates the change in import statements required when moving from direct Tokio dependencies to a generic runtime abstraction. It shows how `tokio::sync::mpsc` and `tokio::spawn` are replaced with `crate::runtime::{channel, spawn, Sender, Receiver}`. ```rust // Before (in roam-stream) use tokio::sync::mpsc; use tokio::spawn; // After (in roam-session) use crate::runtime::{ channel, spawn, Sender, Receiver }; ``` -------------------------------- ### Cell-Side Roam Tracing API Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/009-roam-tracing.md Illustrates how a cell can integrate Roam tracing by installing a layer that forwards tracing events to the host. It initializes the cell tracing service and integrates it with the global tracing subscriber. ```rust let (layer, service) = roam_tracing::init_cell_tracing(1024); tracing_subscriber::registry() .with(layer) .init(); // Register service with dispatcher let tracing_dispatcher = CellTracingDispatcher::new(service); ``` -------------------------------- ### Add Peer Options and Host API in Rust Source: https://github.com/bearcove/roam/blob/main/todo/shm-dodeca/003-spawn-tickets.md Defines structures and methods for managing host-side peer connections. `AddPeerOptions` allows configuring peer-specific settings like names and death callbacks. `ShmHost::add_peer` reserves a peer slot, creates a doorbell pair, and returns a `SpawnTicket` for the guest process. It also handles releasing peer slots if spawning fails. ```rust // roam-shm/src/host.rs use std::sync::Arc; /// Callback invoked when a peer dies. pub type DeathCallback = Arc; /// Options for adding a peer. #[derive(Default)] pub struct AddPeerOptions { /// Human-readable name for debugging pub peer_name: Option, /// Callback when peer dies (doorbell death or heartbeat timeout) pub on_death: Option, } /// Host-side handle for a single peer. pub struct PeerHandle { pub peer_id: PeerId, /// Host's doorbell end doorbell: Doorbell, /// Death callback on_death: Option, } impl ShmHost { /// Add a new peer, returning the spawn ticket. /// /// This reserves a peer slot and creates a doorbell pair. /// The returned ticket should be passed to the spawned process. /// /// shm[impl shm.spawn.ticket] pub fn add_peer( &mut self, options: AddPeerOptions, ) -> io::Result<(PeerHandle, SpawnTicket)> { // Find and reserve an empty slot let peer_id = self.reserve_peer_slot()?; // Create doorbell pair let (host_bell, guest_bell) = Doorbell::pair()?; // Clear CLOEXEC on guest's doorbell // shm[impl shm.spawn.fd-inheritance] guest_bell.clear_cloexec()?; let ticket = SpawnTicket { hub_path: self.path.clone().ok_or_else(|| { io::Error::new(io::ErrorKind::Other, "no path for heap-backed segment") })?, peer_id, guest_doorbell: guest_bell, }; let handle = PeerHandle { peer_id, doorbell: host_bell, on_death: options.on_death, }; self.peer_handles.insert(peer_id, handle.doorbell); Ok((handle, ticket)) } /// Reserve a peer slot, returning its ID. fn reserve_peer_slot(&self) -> io::Result { for i in 1..=self.layout.config.max_guests as u8 { let peer_id = PeerId::from_index(i - 1).unwrap(); let entry = self.peer_entry(peer_id); if entry.try_reserve().is_ok() { return Ok(peer_id); } } Err(io::Error::new(io::ErrorKind::Other, "no available peer slots")) } /// Release a reserved peer slot (if spawn fails). pub fn release_peer(&mut self, peer_id: PeerId) { let entry = self.peer_entry(peer_id); entry.release_reserved(); self.peer_handles.remove(&peer_id); } } ```