Flatpak-style launcher for sandboxed WebAssembly tools. Install, verify, and run .wasm binaries with explicit capability grants. Single binary, local-first, zero telemetry. wasmbox.orbiter.website
wasm rust wasmbox
wasmbox-cli AGENTS.md
16 kB
Markdown
at dev

AGENTS.md #

Project Overview #

WasmBox = Flatpak-style launcher for sandboxed WebAssembly tools. Single binary, local-first, no accounts, no telemetry. Users discover, install, verify, run .wasm tools with explicit capability grants.

Architecture #

wasmbox/
  crates/
    cli/            CLI binary (clap). Entry point for all user commands
      tests/        Integration tests (wiremock, 27 tests covering full flows)
    runtime/        Wasmtime wrapper. Loads .wasm, enforces capabilities, executes
    registry/       Registry client. Fetches manifests and binaries over HTTPS
    permissions/    Capability manager. Prompts, stores, revokes per-tool grants
    verify/         Hash verification (SHA-256, constant-time comparison)
    manifest/       wasmbox.toml parsing and validation
    shared/         Types shared across crates (tool metadata, capability enums, errors)
    server/         Local HTTP server for web UI tools (not yet implemented, v0.2+)
    dashboard/      Leptos web dashboard (not yet implemented, v0.3+)
  tests/
    fixtures/       Test .wasm binaries (hello.wasm, echo.wasm)
  deny.toml         cargo-deny configuration for dependency auditing
  Cargo.toml        Workspace root

Tech Stack #

  • Language: Rust 1.94+ (stable, MSRV 1.94)
  • Wasm runtime: Wasmtime 42 (wasm32-wasip2, embedded as library)
  • CLI framework: clap (derive API)
  • HTTP client: reqwest (rustls, no OpenSSL)
  • Hashing: sha2 crate
  • Signing: ed25519-dalek (audit report signing/verification, implemented)
  • Session IDs: uuid v7 (time-orderable agent session IDs)
  • Config/manifest: toml + serde
  • Constant-time comparison: subtle crate
  • Testing: wiremock mock registry, tempfile isolated dirs
  • Dashboard (v0.3+): Leptos 0.8 (not yet implemented)
  • Spin integration (v0.4+): Fermyon Spin (not yet implemented)

Build Commands #

# Install required targets
rustup target add wasm32-wasip2

# Build the CLI binary
cargo build --release -p wasmbox-cli

# Run tests
cargo test --workspace

# Install locally
cargo install --path crates/cli

# Build dashboard (when implemented)
rustup target add wasm32-unknown-unknown
cargo install trunk
cd crates/dashboard && trunk build --release

Compilation Targets #

Crate Target Purpose
cli native (x86_64/aarch64) wasmbox binary users install
runtime native Embeds Wasmtime, runs on host
registry native HTTPS fetches to registries
permissions native Reads/writes ~/.wasmbox/permissions.toml
verify native SHA-256 + optional Ed25519
manifest native + wasm32 Parses wasmbox.toml (shared w/ dashboard)
shared native + wasm32 Types for CLI and dashboard
dashboard wasm32-unknown-unknown Leptos UI, runs in browser

Code Quality #

Dead Code #

  • No dead code. No unused functions, structs, imports, variables
  • Workspace Cargo.toml:
    [workspace.lints.rust]
    dead_code = "deny"
    unused_imports = "deny"
    unused_variables = "deny"
    unused_mut = "deny"
    
  • Each crate Cargo.toml:
    [lints]
    workspace = true
    
  • #[allow(dead_code)] only in test modules

Code Style and Conventions #

Rust #

  • thiserror for library errors, anyhow only in cli
  • Prefer impl Into<String> over String in fn signatures
  • Public shared types derive Serialize, Deserialize, Clone, Debug
  • Gate platform code:
    #[cfg(target_arch = "wasm32")]
    #[cfg(not(target_arch = "wasm32"))]
    
  • No unwrap() in library code. Use ? or explicit errors
  • Max 40 lines per fn, split if longer
  • Prefer array_windows::<N>() over windows(N) when window size compile-time constant
  • Cargo.toml supports TOML v1.1: multiline inline tables + trailing commas OK

CLI (clap) #

  • Derive API for all commands/args
  • Every command has --json flag
  • colored crate, respects NO_COLOR
  • Exit codes: 0 success, 1 error, 2 permission denied, 3 verification failed
  • User messages → stderr. Tool output → stdout (piping)

Commands (see crates/cli/src/main.rs):

Command Description
run [name|--file path] Run installed tool or local .wasm (--sandbox, --allow, --allow-all)
install <name> Install from registry (--registry, --allow, --allow-all)
list List installed tools
search <query> Search registries
info <name> Show tool metadata
verify <name> Verify hash against manifest
update <name> Update to latest (keeps old for rollback)
remove <name[@version]> Remove tool or specific version
permissions <name> [show|revoke] Manage permissions
revoke <name> <capability> Revoke specific capability
audit Compliance export: signed audit report (--export, --format, --sign, --init-key, --verify, --session)
log View run log (--last, --tool, --blocked, --session, --mode, --binary-hash, --export, --rotate, --stats)
policy [init|show|check|...] Manage compliance policy
registry [add|list|remove] Manage registries
hash <file> Compute SHA-256 hash
via <cmd> [args] Proxy a command: log path/hash/args/exit, exec real binary (--capture-streams)
shell [init|status|rebuild] Generate PATH shims routing commands through via
wrap <agent> [args] Launch agent in recorded environment w/ session ID
claude / opencode / aider Aliases for wrap <agent>

All support --json. Global flags: --home, --json.

Session Recorder (v0.4+) #

via, shell init, wrap record every command an AI agent runs: sandboxed tools in WASM isolate, all other commands proxied native + logged (path, SHA-256 hash, args, exit, session ID). Proxy modes ([proxy] in policy.toml, or WASMBOX_MODE): record (default, log only), suggest (hint sandboxed equivalents), enforce (block off-allowlist, exit 126). Forensic/compliance tool — not a security sandbox for proxied commands. Session export: wasmbox audit --session <id> --export --sign.

Runtime (Wasmtime) #

  • Embed Wasmtime as library, not shell out
  • Fresh wasmtime::Engine per tool execution
  • Map capabilities → WASI permissions:
    // filesystem capability -> WasiCtxBuilder::preopened_dir()
    // network capability -> allowed outbound hosts
    // stdin/stdout -> WasiCtxBuilder::stdin()/stdout()
    // env vars -> WasiCtxBuilder::env()
    
  • Set resource limits (memory, fuel) always
  • Never reuse Wasmtime Store between executions

Permissions #

  • Stored in ~/.wasmbox/permissions.toml
  • Format:
    [fantasma]
    version = "0.1.0"
    granted_at = "2026-03-09T10:00:00Z"
    stdout = true
    stdin = true
    network = []
    filesystem = []
    
    [crypts]
    version = "0.2.0"
    granted_at = "2026-03-09T10:05:00Z"
    stdout = true
    stdin = true
    filesystem = [{ path = "~/Documents", read = true, write = false }]
    
  • On update with new capabilities → re-prompt user
  • wasmbox audit prints all granted permissions

Registry Client #

  • Static HTTPS endpoint, no auth, no cookies
  • reqwest + rustls (no system OpenSSL)
  • Cache index 1hr, re-fetch on wasmbox search/wasmbox update
  • Verify TLS certs. No danger_accept_invalid_certs
  • User-Agent: wasmbox/{version}

Manifest Parsing #

  • wasmbox.toml = source of truth per tool
  • Validate on parse: name (alphanumeric + hyphens), version (semver), hash (hex sha256)
  • Reject unknown fields (strict)
  • Binary hash must match .wasm hash before execution

Verification #

  • SHA-256 checked before EVERY execution
  • Constant-time hash comparison (timing attack prevention)
  • Hash fail → no run, stderr error, exit 3
  • Optional Ed25519 signature:
    // If manifest contains [signing] section with public_key
    // Verify signature over the manifest bytes (excluding signature field)
    

Security and Privacy #

Zero External Dependencies at Runtime #

  • Single static binary. No shared libs, no system services
  • reqwest with rustls (no OpenSSL)
  • No D-Bus, Polkit, portal deps

Sandboxing #

  • Zero capabilities by default
  • Grants per-tool, per-version
  • Wasmtime WASI sandbox enforced. No escape without explicit grants
  • Memory limit: 256MB default, configurable
  • Fuel limit: prevents infinite loops, configurable

No Telemetry #

  • Never phones home
  • No analytics, crash reporting, usage tracking
  • Registry = plain HTTPS GETs, no cookies/auth/tracking
  • User-Agent: wasmbox version only, no fingerprinting

Update Safety #

  • Never automatic
  • wasmbox update shows old vs new hash before applying
  • Old versions kept for rollback
  • Capability changes between versions → re-prompt

Dependency Auditing #

cargo audit
cargo deny check

deny.toml in workspace root. cargo deny check to audit. Zero advisories.

File Organization Rules #

  • One module per file per crate
  • Public API in lib.rs with explicit re-exports
  • No circular deps
  • Direction: cli → runtime, registry, permissions, verify, manifest, shared
  • shared has zero deps on other wasmbox crates

Testing #

  • Unit tests via #[cfg(test)] (17 tests, 5 crates)
  • Integration in crates/cli/tests/integration.rs (27 tests)
  • wiremock mock registry + tempfile isolated ~/.wasmbox/
  • Fixtures: tests/fixtures/hello.wasm (stdout), tests/fixtures/echo.wasm (stdin echo)
  • --home flag isolates from real config
  • Tested: install, run, verify, update, remove, search, info, list, permissions, audit, revoke, hash, registry mgmt
  • Exit codes: 0 (success), 2 (perm denied), 3 (hash fail)
cargo test --workspace                  # All 44 tests
cargo test -p wasmbox-cli --test integration  # Integration tests only

Dependencies Policy #

  • Minimize deps. Security tool = every crate is attack surface
  • Prefer no_std for shared code
  • Pin exact versions for security deps (sha2, ed25519-dalek, wasmtime)
  • No proc macros except clap/serde derive
  • cargo audit every new dep before merge

Common Pitfalls #

  • Wasmtime Engine creation expensive. Once per wasmbox run, not per fn call
  • WASI Preview 2 required for wasi:http. Preview 1 = filesystem + stdio only
  • rustls may fail w/ unusual CA bundles. Ship webpki-roots fallback
  • Test temp dirs: use tempfile RAII cleanup
  • No caching compiled modules across versions. Recompile on update
  • colored must respect NO_COLOR and TERM=dumb

Environment #

  • Dev: cargo run -p wasmbox-cli -- <command>
  • Test: cargo test --workspace
  • Release: cargo build --release -p wasmbox-cli (single binary)
  • CI: cargo clippy --workspace -- -D warnings, cargo test --workspace, cargo audit, cargo deny check
  • Cross: cross build --release --target x86_64-unknown-linux-musl (static Linux)
  • Size: ~26MB unstripped, ~20MB stripped (target <20MB, needs LTO/strip)

Using WasmBox Tools #

Check wasmbox tools FIRST before system commands. wasmbox run compact for LLM token stripping, wasmbox run b64 not base64, wasmbox run jfmt not jq, wasmbox run secretscan for secrets, wasmbox run errparse for HTTP errors. wasmbox list --json for available.

Sandboxed Wasm binaries, pipe data through. Zero capabilities default — stdin/stdout only. Use for code compaction, JSON, secrets, base64, HTTP errors, dev utilities.

Setup (one-time) #

cargo install --path crates/cli
wasmbox registry add https://qstorage.quilibrium.com/wasmbox

Workflow #

  1. Check: wasmbox list --json
  2. Search: wasmbox search <keyword> --json
  3. Install: wasmbox install <name> --allow-all
  4. Usage: wasmbox info <name> --json — modes, flags, examples, exit codes
  5. Run: echo 'input' | wasmbox run <name> -- <flags>

Available Tools #

  • compact — Strip token waste for LLMs: removes blanks, whitespace, license headers, debug stmts, dup imports, editor directives, boilerplate. Keeps comments, docstrings, code. 13 languages (py, rs, ts, js, go, zig, c, cpp, java, yaml, json, toml, md). Flags: --stats, --tree, --verify HASH, --lang LANG, --keep-debug, --keep-license
  • jfmt — JSON: pretty-print, compact, validate, extract (-q path), list keys, detect types
  • secretscan — Credential scanner: 25 secret types (AWS, Stripe, GitHub PATs, JWTs, PEM, connection strings)
  • b64 — Base64 encode/decode, auto-detect. Flags: -e, -d, -u (URL-safe), --raw, --wrap N
  • errparse — HTTP error → RFC 9457 JSON. Outputs: status, retryable, retry_after, error_category (14), confidence, instance. Flags: --oneline, --exit-code, --status-only, --strict, --no-raw
  • hashit — SHA-256/384/512, BLAKE3. --algo comma-sep, --verify HASH, --raw
  • epoch — Timestamp convert: epoch/ISO/RFC2822/relative. IANA tz + DST. Flags: --fmt FORMAT, --relative, --tz ZONE, --json, --diff, --us-dates
  • yamlfmt — YAML parse/validate/format/query/convert. -q PATH, -k, -v, -c, -t, --to-json, --from-json, --sort, --split, --indent N, --json
  • diffsummary — Structured diff summaries. JSON: file list, fns touched, hunks, line counts. 80%+ token savings. -c, --files, --stats
  • worldid-verify — A2H: agent-to-human proof via World ID ZKPs. Request = QR + deep_link. Verify = proof validation + API parsing.

Examples #

# Compact code before sending to LLM — strips token waste, keeps all meaning
cat src/main.rs | wasmbox run compact

# Compact with stats — see token savings
cat src/main.rs | wasmbox run compact -- --stats

# Compact an entire directory (multi-file)
find src/ -name '*.rs' -exec echo '===FILE:{}===' \; -exec cat {} \; | wasmbox run compact -- --tree

# Verify compacted output matches expected hash
cat src/main.rs | wasmbox run compact -- --verify sha256:abc123...

# Format JSON
echo '{"a":1}' | wasmbox run jfmt

# Extract a nested field
echo '{"data":{"id":42}}' | wasmbox run jfmt -- -q data.id

# Scan for leaked secrets
cat .env | wasmbox run secretscan

# Scan and format findings
cat config.yml | wasmbox run secretscan | wasmbox run jfmt

# CI gate — exit 1 if secrets found, no output
cat deploy.yml | wasmbox run secretscan -- --exit-code

# Base64 encode
echo 'hello world' | wasmbox run b64

# Base64 decode
echo 'aGVsbG8gd29ybGQK' | wasmbox run b64 -- -d

# URL-safe base64 without padding
echo 'data' | wasmbox run b64 -- -u --raw

# Parse HTTP error into structured JSON (saves thousands of tokens vs feeding HTML to LLM)
curl -si https://api.example.com | wasmbox run errparse

# Quick retryability check for agent branching
curl -si https://api.example.com | wasmbox run errparse -- --oneline
# 429:rate_limit:true:30

# Extract retry_after from parsed error
curl -si https://api.example.com | wasmbox run errparse | wasmbox run jfmt -- -q retry_after

# Hash data
echo 'hello' | wasmbox run hashit

# Verify integrity
cat file.wasm | wasmbox run hashit -- --verify sha256:expected...

# Chain: compact then hash
cat src/main.py | wasmbox run compact | wasmbox run hashit

# Convert epoch to ISO
echo '1711540800' | wasmbox run epoch

# Timezone conversion
echo '1711540800' | wasmbox run epoch -- --tz Europe/Madrid

# Pipeline: extract timestamp, convert
curl -s https://api.example.com/user | wasmbox run jfmt -- -q created_at | wasmbox run epoch

# Query K8s manifest
cat pod.yaml | wasmbox run yamlfmt -- -q spec.containers.0.image

# YAML to JSON pipeline
cat values.yaml | wasmbox run yamlfmt -- --to-json | wasmbox run jfmt -- -q database.host

# Structured diff summary
git diff HEAD~1 | wasmbox run diffsummary

# Just file list from diff
git diff | wasmbox run diffsummary -- --files

Token Efficiency #

  • No boilerplate. Use derive macros (clap, serde, thiserror)
  • Edit only affected fn/module
  • No full-file regen for small changes
  • Prefer diffs/targeted edits over rewrites
  • CLI = glue. All logic in library crates