Skip to content

Latest commit

 

History

History
279 lines (209 loc) · 11.6 KB

File metadata and controls

279 lines (209 loc) · 11.6 KB

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:

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:

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:

{
    "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:

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.

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
Loading

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.
  1. 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.

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
Loading

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.
  1. Execute: You start the bls-runtime with your configuration file.
  2. 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 directory of the SDK repository.