Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WebAssembly Builds

This guide explains how to build and test the WebAssembly packages for abac-wasm and hbac-wasm.

Prerequisites

Rust with rustup

The WASM targets require rustup for toolchain management. If you have Homebrew-installed Rust, you’ll need to switch to rustup:

# Remove Homebrew Rust (if installed)
brew uninstall rust

# Install rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Add WASM target
rustup target add wasm32-unknown-unknown

wasm-pack

Install wasm-pack for building WebAssembly packages:

cargo install wasm-pack

Node.js

Node.js is required for running WASM tests:

# macOS
brew install node

# Or use your preferred package manager

Building

Quick Build

Use the provided script to build both WASM crates:

./scripts/wasm-build.sh

This will build:

  • crates/abac-wasm/pkg/ - Web target
  • crates/abac-wasm/pkg-node/ - Node.js target
  • crates/hbac-wasm/pkg/ - Web target
  • crates/hbac-wasm/pkg-node/ - Node.js target

Manual Build

Build individual packages:

# abac-wasm for web
cd crates/abac-wasm
wasm-pack build --target web --release

# abac-wasm for Node.js
wasm-pack build --target nodejs --release --out-dir pkg-node

# hbac-wasm for web
cd ../hbac-wasm
wasm-pack build --target web --release

# hbac-wasm for Node.js
wasm-pack build --target nodejs --release --out-dir pkg-node

Cargo Aliases

You can also use the cargo aliases defined in .cargo/config.toml:

cargo wasm-build-abac
cargo wasm-build-hbac

Testing

Quick Test

Run all WASM tests:

./scripts/wasm-test.sh

Manual Testing

# Test in Node.js
cd crates/abac-wasm
wasm-pack test --node

# Test in browser (requires Firefox)
wasm-pack test --headless --firefox

Dependency Compatibility

WASM-Compatible Dependencies

The following dependencies are verified to work with WASM:

  • acls-rs (zero dependencies)
  • lru (with default-features = false on wasm32)
  • ipnetwork
  • thiserror
  • log
  • ahash (with no-rng feature on wasm32)
  • serde, serde_json
  • getrandom (with wasm_js feature)
  • js-sys, web-sys
  • console_error_panic_hook

Excluded Features

Cranelift JIT: The jit feature of hbac-rs is NOT WASM-compatible and is explicitly excluded from hbac-wasm builds.

Troubleshooting

Homebrew Rust

If you’re using Homebrew-installed Rust (not rustup), you cannot add WASM targets. The error will be:

error[E0463]: can't find crate for `core`
  = note: the `wasm32-unknown-unknown` target may not be installed
  = help: consider downloading the target with `rustup target add wasm32-unknown-unknown`

Solution: Switch to rustup-based Rust installation as described in Prerequisites.

Bloom Filter on WASM

The bloom feature (backed by fastbloom) is disabled for WASM builds. abac-wasm depends on abac-rs with default-features = false, which excludes the bloom and smallvec dependencies. Bloom filter pre-screening is only available in native builds.

WASM API Features

Structured Errors

Both abac-wasm and hbac-wasm return structured error objects instead of plain strings. Every error thrown by the WASM bindings is a JavaScript object with two fields:

try {
  policy.addRuleFromJson(invalidJson);
} catch (e) {
  console.log(e.type);    // "JsonError", "PolicyError", or "ValidationError"
  console.log(e.message); // human-readable description
}

Custom Matchers (abac-wasm)

AbacPolicyWasm.registerMatcher(dimension, matchFn) installs a JavaScript callback for a named dimension. The ABAC engine calls this function during evaluation instead of its built-in matching logic:

// Threshold matcher: true when request value >= rule threshold
policy.registerMatcher("temperature_high", (ruleValue, requestValue, requestGroups) => {
  if (ruleValue === "all") return true;
  if (!Array.isArray(ruleValue)) return false;
  const actual = requestValue.type === "Float" ? requestValue.value : null;
  if (actual == null) return false;
  return ruleValue.some(t => {
    const threshold = t.type === "Float" ? t.value : null;
    return threshold != null && actual >= threshold;
  });
});

Arguments are serde-serialized: ruleValue is either "all" or an array of {type, value} objects; requestValue and requestGroups follow the same {type, value} shape.

Wildcard Dimensions

Rule dimensions accept the literal string "all" as a wildcard. The custom deserializer rejects any other string value — only "all" (case-insensitive) is valid:

{
  "name": "match-all-locations",
  "dimensions": {
    "sensor_type": [{"type": "String", "value": "temperature"}],
    "location": "all"
  },
  "rule_type": "Allow",
  "enabled": true
}

The AbacRuleBuilderWasm.addDimensionAll(dimension) method provides a type-safe way to set a wildcard dimension from the builder API.

Deterministic JSON Output

getAllRulesJson() and rule serialization use BTreeMap for dimension ordering, so the JSON output is deterministic across calls. This makes snapshot testing and diff-based comparisons reliable.

Bundle Sizes

Target bundle sizes (gzipped):

  • abac-wasm: ~150-200 KB
  • hbac-wasm: ~120-150 KB

Check actual sizes after building:

# After running wasm-build.sh
du -h crates/abac-wasm/pkg/*.wasm
du -h crates/hbac-wasm/pkg/*.wasm

CI/CD

The CI workflow (.github/workflows/ci.yml) runs two WASM jobs:

wasm-build — builds both crates for web and Node.js targets:

  1. Installs wasm-pack@0.13.1 (pinned version)
  2. Verifies hbac-wasm has no cranelift dependency via cargo tree
  3. Builds abac-wasm and hbac-wasm for --target web and --target nodejs
  4. Caches the cargo registry and git checkouts between runs

wasm-test — runs WASM tests after a successful build:

  1. Installs build tooling (gcc, make, pkgconf-pkg-config)
  2. Installs wasm-pack@0.13.1
  3. Runs wasm-pack test --node for both crates
  4. Caches the cargo registry and git checkouts

The cranelift check in wasm-build captures cargo tree output and validates both the command exit code and the absence of cranelift in the dependency tree. This prevents the JIT feature from accidentally leaking into hbac-wasm builds.