diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..fb61f0d --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,279 @@ +# Blockless Rust SDK + +The SDK allows your Rust-based WebAssembly (WASM) modules to interact with the `bls-runtime` and `b7s-browser` WASM execution environments/runtimes. +It provides convenient wrappers around the host functions exposed by the runtime, enabling capabilities like HTTP requests, CGI script execution, LLM interactions, and more. + +## Installation + +Add the `blockless-sdk` to your `Cargo.toml`: + +```sh +cargo add blockless-sdk +``` + +### Features + +The SDK has an optional `serde` feature, enabled by default, which derives `Serialize` and `Deserialize` for some of its types. If you don't need this, you can disable default features: +```toml +blockless-sdk = { version = "0.1.10", default-features = false } +``` + +## Core Concepts + +### Permissions + +The `bls-runtime` operates on a permission-based model. +For your WASM module to access resources (like making an HTTP request or accessing a specific CGI script), the runtime must be configured with the appropriate permissions. +If a required permission is missing, SDK calls will likely fail. + +Permissions are typically defined in the runtime's JSON configuration file: + +```json +{ + "permissions": [ + "http://api.example.com/data", // Allow HTTP GET to this specific URL + "file:///scripts/my_script.sh", // Example for CGI if it needs file access + "cgi://my_cgi_alias" // Hypothetical permission for a specific CGI alias + ] +} +``` +Refer to the `bls-runtime` documentation for details on configuring permissions. + +### Compiling Your Rust Code to WASM + +To use this SDK, your Rust code needs to be compiled to the `wasm32-wasip1` target: + +```bash +cargo build --target wasm32-wasip1 --release +``` + +This will typically produce a `.wasm` file in `target/wasm32-wasip1/release/your_crate_name.wasm`. + +## Blockless Rust SDK: Architectural Overview + +The Blockless Rust SDK acts as a bridge between your Rust application code and the wasm runtime. It simplifies interaction with the host functions exposed by the runtime, abstracting away the low-level details of WebAssembly System Interface (WASI) calls and custom Blockless host function ABIs. + +### High-Level System Architecture + +This diagram shows the main components and their relationships when building and running a WASM application with the Blockless Rust SDK on `bls-runtime`. + +```mermaid +graph LR + subgraph "Development Phase" + A["Rust Application Code (.rs)"] -- "Uses" --> B{Blockless Rust SDK Crate}; + B -- "Compiled with App Code" --> C["WASM Module (.wasm)"]; + end + + subgraph "Runtime Execution Phase (bls-runtime)" + D{bls-runtime Engine} -- "Loads & Executes" --> C; + C -- "Calls (via SDK)" --> E["Host Function Interface (ABI)"]; + E -- "Handled by" --> D; + D -- "Routes to" --> F[Blockless Driver Modules]; + F -- "Interact with" --> G[External Resources]; + end + + subgraph "Blockless Driver Modules (Host Plugins)" + direction LR + HTTP_Driver[HTTP Driver] + LLM_Driver[LLM Driver] + CGI_Driver[CGI Driver] + IPFS_Driver[IPFS Driver] + S3_Driver[S3 Driver] + Memory_Driver["Memory Driver (Stdin/Env)"] + TCP_Driver[TCP Driver] + WASI_Support[WASI Support] + F -.-> HTTP_Driver; + F -.-> LLM_Driver; + F -.-> CGI_Driver; + F -.-> IPFS_Driver; + F -.-> S3_Driver; + F -.-> Memory_Driver; + F -.-> TCP_Driver; + F -.-> WASI_Support; + end + + subgraph "External Resources" + direction LR + Internet["Internet Services (APIs, IPFS, S3)"] + LocalFS["Local Filesystem (if permitted)"] + LLM_Services[External LLM Services] + CGI_Exec[CGI Scripts/Executables] + G -.-> Internet; + G -.-> LocalFS; + G -.-> LLM_Services; + G -.-> CGI_Exec; + end + + style A fill:#D2E0FB,stroke:#333,stroke-width:2px + style B fill:#B0C4DE,stroke:#333,stroke-width:2px + style C fill:#98FB98,stroke:#333,stroke-width:2px + style D fill:#87CEFA,stroke:#333,stroke-width:2px + style E fill:#F0E68C,stroke:#333,stroke-width:2px + style F fill:#F4A460,stroke:#333,stroke-width:2px + style G fill:#FFB6C1,stroke:#333,stroke-width:2px +``` + +**Explanation:** + +1. **Development Phase:** +* You write your application logic in Rust (`.rs` files). +* You include and use the `blockless-sdk` crate to access extended functionalities. +* The Rust compiler, targeting `wasm32-wasip1`, bundles your application code and the necessary parts of the SDK into a single `.wasm` module. + +2. **Runtime Execution Phase:** +* The `bls-runtime` (which uses Wasmtime as its core engine) loads your compiled `.wasm` module. +* When your WASM code (through an SDK function call) needs to interact with the host (e.g., make an HTTP request): + * It calls a function defined in the Host Function Interface (ABI). These are the `extern "C"` functions you see in the SDK's source (e.g., `http_open`, `llm_prompt_request`). + * The `bls-runtime` intercepts this call. + * Based on the called function (e.g., from the `blockless_http` or `blockless_llm` Wasm import module), the runtime routes the request to the corresponding **Blockless Driver Module** (plugin). + * The Driver Module (e.g., HTTP Driver, LLM Driver) then performs the actual operation, potentially interacting with **External Resources** like the internet, local filesystem (if permitted by the runtime configuration), or specific LLM services. + * Results are passed back through the same chain to your WASM module. + +### Development and Execution Flow + +This diagram shows the typical lifecycle from writing code to running your Blockless-enabled WASM application. + +```mermaid +graph TD + A["Write Rust Code - using blockless-sdk"] --> B(2. Compile to WASM
`cargo build --target wasm32-wasip1`); + B --> C{app.wasm}; + D["Create/Edit Runtime Config - config.json"] --> E{config.json}; + E --> F["Run with bls-runtime - `bls-runtime config.json`"]; + C --> F; + + subgraph "During Execution (Step 4)" + direction LR + F --> G{WASM App Logic}; + G -- "Calls SDK Function
e.g., BlocklessLlm::new()" --> H{SDK Abstraction}; + H -- "Calls Host ABI
e.g., llm_set_model_request()" --> I[bls-runtime]; + I -- "Checks Permissions
(from config.json)" --> J{Permission Check}; + J -- "Granted" --> K[Invokes LLM Driver]; + J -- "Denied" --> L[Error to WASM]; + K -- "Interacts with
External LLM Service" --> M[LLM Service]; + M -- "Response" --> K; + K -- "Result" --> I; + I -- "Result" --> H; + H -- "Result (e.g., LLM Handle)" --> G; + G -- "Produces Output /
Further Interactions" --> N[Application Output/Effects]; + end + + style A fill:#D2E0FB + style B fill:#B0C4DE + style C fill:#98FB98 + style D fill:#FFE4B5 + style E fill:#FFDAB9 + style F fill:#87CEFA + style G fill:#E6E6FA + style H fill:#D8BFD8 + style I fill:#ADD8E6 + style J fill:#F08080 + style K fill:#F4A460 + style L fill:#CD5C5C + style M fill:#FFB6C1 + style N fill:#90EE90 +``` + +**Explanation:** + +1. **Develop:** You write your application in Rust, leveraging the `blockless-sdk` for functionalities like HTTP, LLM, etc. +2. **Compile:** You compile your Rust project to the `wasm32-wasip1` target. This produces a `.wasm` file containing your application logic and the SDK's glue code. +3. **Configure:** You prepare a JSON configuration file for the `bls-runtime`. This file specifies: +* The path to your `.wasm` file(s). +* **Permissions:** Crucially, you must grant permissions for any host functions your WASM module will use (e.g., allowed HTTP endpoints, CGI script aliases). +* Resource limits (fuel, memory), entry points, etc. +4. **Execute:** You start the `bls-runtime` with your configuration file. +5. **Runtime Interoperation (as shown in the subgraph):** +* Your WASM application logic makes a call to an SDK function (e.g., `BlocklessLlm::new()`). +* The SDK function translates this into a specific low-level host function call (e.g., `llm_set_model_request()`). +* The `bls-runtime` intercepts this call. +* **Permission Check:** The runtime verifies if the operation is allowed based on the `config.json`. If not, an error is returned to the WASM module. +* If permitted, the runtime invokes the appropriate driver module (e.g., LLM Driver). +* The driver performs the action (e.g., initializes an LLM model, potentially downloading it or interacting with an external service). +* The result (e.g., a handle to the LLM session, or data) is passed back through the runtime and SDK to your application code. +* Your application then continues, potentially making more host calls or producing output. + +## SDK Modules + +The SDK provides modules to interact with different host functionalities: + +### 1. HTTP Client (`BlocklessHttp`) + +Make HTTP requests from your WASM module. + +**Host Function Namespace:** `blockless_http` + +### 2. Memory Access (Stdin & Environment Variables) + +Access standard input and environment variables provided by the runtime. + +**Host Function Namespace:** `blockless_memory` + +#### Reading from Stdin + +* `read_stdin(buf: &mut [u8]) -> std::io::Result`: Reads data from stdin into `buf`. Returns the number of bytes read. + +#### Reading Environment Variables + +Environment variables are passed as a single string, typically JSON formatted or newline-separated key-value pairs, as determined by the runtime. + +* `read_env_vars(buf: &mut [u8]) -> std::io::Result`: Reads environment variables string into `buf`. Returns number of bytes read. + +### 3. CGI (Common Gateway Interface) + +Execute external scripts/programs as CGI extensions. + +**Host Function Namespace:** `blockless_cgi` + +Error Type: `CGIErrorKind` + +### 4. Sockets (`blockless_socket`) + +Primarily for creating TCP listener sockets within your WASM module, allowing it to act as a server. + +**Host Function Namespace:** `blockless_socket` + +* `create_tcp_bind_socket(addr: &str) -> Result`: Creates a TCP socket, binds it to the given address (e.g., "0.0.0.0:8080"), and prepares it for listening. Returns a file descriptor. + +Error Type: `SocketErrorKind` + +### 5. LLM (Large Language Model) (`BlocklessLlm`) + +Interact with Large Language Models supported by the runtime. + +**Host Function Namespace:** `blockless_llm` + +#### `Models` Enum + +Specifies which LLM to use. + +Common variants: +* `Llama321BInstruct(Option)` +* `Llama323BInstruct(Option)` +* `Mistral7BInstructV03(Option)` +* `Mixtral8x7BInstructV01(Option)` +* `Gemma22BInstruct(Option)` +* `Gemma27BInstruct(Option)` +* `Gemma29BInstruct(Option)` +* `Custom(String)`: For models identified by a string not covered by specific enum variants. The string inside `Option` usually refers to a quantization format like "Q6_K" or "q4f16_1". + +#### `LlmOptions` + +Configuration options for LLM interactions. + +Fields/Methods: +* `system_message: Option`: A system prompt to guide the LLM's behavior. +* `tools_sse_urls: Option>`: A list of URLs for Model Control Protocol (MCP) tool servers (SSE endpoints). +* `temperature: Option`: Sampling temperature. +* `top_p: Option`: Nucleus sampling probability. +* `.with_system_message(String) -> Self`: Builder method. +* `.with_tools_sse_urls(Vec) -> Self`: Builder method. + +Error Type: `LlmErrorKind` (covers model issues, UTF-8 errors, MCP errors, etc.) + +## Error Handling + +Each module in the SDK has its own `ErrorKind` enum (e.g., `HttpErrorKind`, `LlmErrorKind`). These enums provide specific error details for operations within that module. Always check the `Result` returned by SDK functions. + +## Examples + +You can find more complete, runnable examples in the [`examples`](https://github.com/blocklessnetwork/sdk-rust/tree/main/examples) directory of the SDK repository.