OutLayer Documentation
Building OutLayer App
What is WASI?#
WASI (WebAssembly System Interface) is a standardized API that allows WebAssembly modules to interact with the outside world - read files, access environment variables, make network requests, and generate random numbers.
Think of WASI as a "syscall interface for WebAssembly" - it provides the basic building blocks your WASM code needs to do real work, like reading input data or calling external APIs, while maintaining security through sandboxing.
Supported Languages#
Any language that compiles to WASM with WASI support: Rust, C/C++, Go, AssemblyScript, and more. Rust is recommended for best tooling and ecosystem support.
WASI Preview 1 vs Preview 2#
OutLayer supports both WASI P1 and P2 standards. Choose based on your requirements:
- Target:
wasm32-wasip1 - Use for: Simple computations, random numbers, basic I/O
- Binary size: Smaller (~100-200KB)
- Compilation: Faster
- Stability: Mature and stable
- Target:
wasm32-wasip2 - Use for: HTTP requests, complex I/O, modern features
- Binary size: Larger (~500KB-1MB)
- Features: HTTP client, advanced filesystem, sockets
- Requires: wasmtime 28+
Rule of thumb: Use P1 unless you need HTTP or advanced I/O.
WASI Interface#
OutLayer provides a minimal WASI environment with support for:
- stdin/stdout: Read JSON input data, write JSON output results
- Environment variables: Access encrypted secrets via
std::env::var() - Random numbers: Cryptographically secure random generation (WASI P1 & P2)
- HTTP requests: Make external API calls (WASI P2 only, via
wasi-http-client) - File I/O (limited): Basic file operations in sandboxed environment
- NEAR context: Access execution metadata via env vars (
NEAR_SENDER_ID,NEAR_BLOCK_HEIGHT, etc.)
Host Functions (Advanced)#
OutLayer provides advanced host functions for direct NEAR RPC access from WASM. These functions enable your code to interact with the NEAR blockchain without relying on external HTTP APIs.
What are Host Functions?
Host functions are native functions provided by the worker runtime that WASM code can call directly. They bypass HTTP and give your code privileged access to private NEAR RPC endpoints (powered by Fastnear), enabling operations like sending transactions and querying blockchain state.
Available Functions
call() - Execute NEAR contract call
Send function calls to NEAR contracts with attached deposit and gas. Your WASM provides the signer credentials via secrets.
call(signer_id, signer_key, receiver_id, method_name, args_json, deposit_yocto, gas) → (tx_hash, status)
transfer() - Send NEAR tokens
Transfer NEAR tokens from one account to another.
transfer(signer_id, signer_key, receiver_id, amount_yocto) → (tx_hash, status)
view() - Query contract state
Read-only view calls to query contract state without sending transactions.
view(contract_id, method_name, args_json) → (result, status)
Key Security Features
- WASM provides signer: Your code passes
signer_keyfrom secrets - worker never uses its own keys - Private RPC access: Fastnear-powered endpoints with higher rate limits and reliability
- Transaction tracking: All transactions are logged and can be verified on-chain
- TEE isolation: Signing keys remain inside TEE and never leave the secure enclave
Example: botfather-ark
The botfather-ark example demonstrates host functions in action:
- Creates multiple NEAR accounts programmatically using
call() - Distributes NEAR tokens across accounts using
transfer() - Executes batch contract calls (e.g., token purchases, staking delegation)
- Queries account balances via
view()
WIT Interface Definition
Host functions are defined in worker/wit/world.wit:
package near:[email protected]; interface api { view: func( contract-id: string, method-name: string, args-json: string ) -> tuple<string, string>; call: func( signer-id: string, signer-key: string, receiver-id: string, method-name: string, args-json: string, deposit-yocto: string, gas: string ) -> tuple<string, string>; transfer: func( signer-id: string, signer-key: string, receiver-id: string, amount-yocto: string ) -> tuple<string, string>; } world rpc-host { import api; }
Requirements
- WASI Preview 2: Host functions require
wasm32-wasip2target - Signer credentials: Must provide
NEAR_SENDER_PRIVATE_KEYvia secrets - NEAR tokens: Signer account must have sufficient balance for gas and deposits
API Versioning
Host functions are versioned using semantic versioning (@0.1.0). This ensures backward compatibility when the API evolves.
- Current version:
near:[email protected] - Multiple versions: Workers can run WASM compiled with different API versions simultaneously
- No breaking changes: Your WASM will continue working even when new versions are released
Critical Requirements#
- Binary format: Must use
[[bin]]in Cargo.toml, NOT[lib] - Entry point: Must have
fn main()function - Input: Always read from stdin (not command-line arguments)
- Output: Always write to stdout (not stderr)
- Format: JSON only (UTF-8 encoded)
- Size limit: Output must be ≤900 bytes (NEAR Protocol limit)
- Flush: Call
stdout().flush()after writing
Working Examples#
We provide 9 complete, open-source examples demonstrating different WASI patterns:
random-arkP1
Random number generation (starter example)
echo-arkP1
NEAR context & environment variables
ai-arkP2
OpenAI API integration (HTTPS requests)
weather-arkP2
OpenWeather API oracle (specialized price oracle)
oracle-arkP2
Multi-source price oracle with aggregation
ethereum-apiP2
Ethereum blockchain data access via RPC
botfather-arkP2Host Functions
Account factory with AI names & batch operations. Demonstrates call() and transfer() host functions for NEAR RPC access.
intents-arkP2
DEX swaps via NEAR Intents (paused FT transfer)
private-dao-arkP1Advanced
Anonymous DAO voting with cryptographic privacy (ECIES, HKDF, Merkle trees)
captcha-arkP2Full Stack
Token launchpad with CAPTCHA verification
Detailed Documentation Available
Each example includes complete source code, input/output examples, build instructions, and deployment guides.
View All Examples →Resource Considerations#
Be mindful of resource limits: instruction counts, memory usage, and execution time. Optimize your code to stay within requested limits to avoid failures and minimize costs.
- Max Instructions: 100 billion per execution (fuel metering enforced)
- Max Execution Time: 60 seconds per execution
- Max Memory: Configurable up to platform limits
- Output Size: Must be ≤900 bytes (NEAR limit)
Testing Locally#
Option 1: Test Compiler Script (Quick Compilation Test)
Use test_compiler.sh to test compilation of your GitHub repository locally without running the full worker infrastructure. This script uses the same Docker image (zavodil/wasmedge-compiler:latest with Rust 1.85) and compilation logic as the production worker.
Perfect for: Testing if your repository compiles correctly before deploying to OutLayer.
# Test compilation for WASI Preview 1 ./scripts/test_compiler.sh \ https://github.com/zavodil/random-ark main wasm32-wasip1 # Test compilation for WASI Preview 2 ./scripts/test_compiler.sh \ https://github.com/zavodil/ai-ark main wasm32-wasip2 # Custom output file ./scripts/test_compiler.sh \ https://github.com/user/myproject main wasm32-wasip1 myapp.wasm # The script will: # 1. Pull zavodil/wasmedge-compiler:latest Docker image (instant if already up to date) # 2. Clone your repository and checkout the commit # 3. Run cargo build with the exact same flags as worker # 4. Optimize WASM (wasm-opt for P1, wasm-tools for P2) # 5. Output compiled WASM with SHA256 checksum
Key features:
- Uses official Docker image from Docker Hub (no local builds needed)
- Exactly mirrors worker compiler behavior
- Supports all three targets:
wasm32-wasip1,wasm32-wasip2,wasm32-wasi - Shows compilation time, file size, and SHA256 checksum
- Configurable memory/CPU limits via environment variables
Troubleshooting: If you get cabi_realloc error with wasm32-wasip2, it means the project is not configured as a WASI P2 component. WASI Preview 2 requires projects to be built as components (using cargo-component) and export special memory management functions. Most existing projects are written for WASI P1. Solution: Use wasm32-wasip1 instead.
Note on Rust version: The Docker image uses Rust 1.85 for maximum compatibility. While newer Rust versions (1.88+) exist, they may have breaking changes with certain dependencies. The production worker uses 1.85 to ensure broad compatibility.
Option 2: WASI Test Runner (Full Validation)
We provide wasi-test-runner - a universal test tool that validates your WASM modules for OutLayer compatibility. It tests binary format, fuel metering, I/O handling, resource limits, JSON validation, and output size.
GitHub Repository: wasi-examples/wasi-test-runner
# Install test runner
cd wasi-examples/wasi-test-runner
cargo build --release
# Test your WASM module
./target/release/wasi-test \
--wasm path/to/your-app.wasm \
--input '{"test":"data"}' \
--verbose
# Example output:
# ✓ Detected: WASI Preview 1 Module
# ✅ Execution successful!
# 📊 Fuel consumed: 456789 instructions
# 📤 Output: {"result":"success"}
# ✅ All checks passed!What it validates:
- Binary format (WASI P1 or P2)
- Fuel metering (instruction counting)
- Input/output handling (stdin → stdout)
- Resource limits enforcement
- JSON validation
- Output size limits (<900 bytes)
Option 3: Manual Testing with wasmtime
Test directly using wasmtime:
# Install wasmtime
curl https://wasmtime.dev/install.sh -sSf | bash
# Test WASI P1 binary
echo '{"value":21}' | wasmtime your-app.wasm
# Test WASI P2 component
echo '{"prompt":"test"}' | wasmtime your-app.wasm
# Test with environment variables
echo '{"message":"test"}' | wasmtime --env SECRET=my-key your-app.wasmCommon Pitfalls#
Using [lib] instead of [[bin]] in Cargo.toml
Forgot to call io::stdout().flush()? after writing
Using WASI P1 instead of P2 - HTTP requires wasm32-wasip2 target
Output exceeds 900 bytes - truncate before returning
Next Steps#
- Explore working examples with complete source code and deployment guides
- Read the complete WASI tutorial
- Clone examples:
git clone https://github.com/fastnear/near-outlayer.git - Test your WASM locally with wasmtime before deploying
- Start with random-ark or echo-ark for simple use cases
- Use ai-ark or oracle-ark for HTTPS-based applications
- Study private-dao-ark for advanced cryptography (ECIES, HKDF, Merkle trees) and privacy patterns
- Deploy captcha-ark for full-stack async human verification