Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
279 changes: 279 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -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 <br/> `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 <br/> e.g., BlocklessLlm::new()" --> H{SDK Abstraction};
H -- "Calls Host ABI <br/> e.g., llm_set_model_request()" --> I[bls-runtime];
I -- "Checks Permissions <br/> (from config.json)" --> J{Permission Check};
J -- "Granted" --> K[Invokes LLM Driver];
J -- "Denied" --> L[Error to WASM];
K -- "Interacts with <br/> 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 / <br/> 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<u32>`: 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<u32>`: 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<u32, SocketErrorKind>`: 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<String>)`
* `Llama323BInstruct(Option<String>)`
* `Mistral7BInstructV03(Option<String>)`
* `Mixtral8x7BInstructV01(Option<String>)`
* `Gemma22BInstruct(Option<String>)`
* `Gemma27BInstruct(Option<String>)`
* `Gemma29BInstruct(Option<String>)`
* `Custom(String)`: For models identified by a string not covered by specific enum variants. The string inside `Option<String>` usually refers to a quantization format like "Q6_K" or "q4f16_1".

#### `LlmOptions`

Configuration options for LLM interactions.

Fields/Methods:
* `system_message: Option<String>`: A system prompt to guide the LLM's behavior.
* `tools_sse_urls: Option<Vec<String>>`: A list of URLs for Model Control Protocol (MCP) tool servers (SSE endpoints).
* `temperature: Option<f32>`: Sampling temperature.
* `top_p: Option<f32>`: Nucleus sampling probability.
* `.with_system_message(String) -> Self`: Builder method.
* `.with_tools_sse_urls(Vec<String>) -> 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.
Loading