Covers: - What antibot_wasm.js is and why it is not in the repo - How wasm-pack compiles wasm/src/ and what wasm/pkg/ contains - Step-by-step build (rustup target, wasm-pack install, build, mv to frontend/pkg) - How heartbeat.js loads and initialises the module via await init() - MIME type requirements for serving .wasm files - .gitignore rationale for wasm/pkg/ and frontend/pkg/ - Troubleshooting (missing target, wasm-opt, 404, empty string returns)
302 lines
8.0 KiB
Markdown
302 lines
8.0 KiB
Markdown
# ChronoSeal — WASM Build Guide
|
||
|
||
## Overview
|
||
|
||
The client-side cryptographic core of ChronoSeal is written in Rust and
|
||
compiled to WebAssembly (WASM). The JavaScript frontend (`heartbeat.js`)
|
||
imports functions from this WASM module to generate keypairs, sign heartbeat
|
||
payloads, compute hash chain links, and execute the stack machine program.
|
||
|
||
The import line in `heartbeat.js`:
|
||
|
||
```js
|
||
import init, { generate_keypair, sign_message, compute_next_hash, run_program }
|
||
from './pkg/antibot_wasm.js';
|
||
```
|
||
|
||
`./pkg/antibot_wasm.js` is a **generated file**. It does not exist in the
|
||
repository and must be produced by building the `wasm/` crate before running
|
||
the server.
|
||
|
||
---
|
||
|
||
## How the WASM Module is Built
|
||
|
||
The tool that compiles Rust to WASM and generates the JavaScript glue is
|
||
[`wasm-pack`](https://rustwasm.github.io/wasm-pack/).
|
||
|
||
When you run:
|
||
|
||
```bash
|
||
wasm-pack build wasm --target web --release
|
||
```
|
||
|
||
wasm-pack does the following in sequence:
|
||
|
||
1. Compiles `wasm/src/lib.rs` (and its submodules) to a `.wasm` binary using
|
||
the `wasm32-unknown-unknown` target.
|
||
2. Runs `wasm-bindgen` to inspect every `#[wasm_bindgen]`-annotated function
|
||
and struct and generate a JavaScript wrapper for each one.
|
||
3. Optionally runs `wasm-opt` (from Binaryen) to size-optimise the binary.
|
||
4. Writes all output to `wasm/pkg/`.
|
||
|
||
---
|
||
|
||
## Output: `wasm/pkg/`
|
||
|
||
After a successful build, `wasm/pkg/` contains:
|
||
|
||
```
|
||
wasm/pkg/
|
||
├── antibot_wasm.js ← ES module; the file heartbeat.js imports
|
||
├── antibot_wasm_bg.wasm ← compiled WASM binary (~300–800 KB release)
|
||
├── antibot_wasm_bg.js ← internal memory bridge (do not import directly)
|
||
├── antibot_wasm.d.ts ← TypeScript type declarations
|
||
├── antibot_wasm_bg.d.ts ← TypeScript declarations for the bg module
|
||
└── package.json
|
||
```
|
||
|
||
### `antibot_wasm.js`
|
||
|
||
This is the public entry point. It contains:
|
||
|
||
- An `init()` function that fetches and instantiates the `.wasm` binary.
|
||
- One JavaScript wrapper function for each `#[wasm_bindgen]` export in
|
||
`wasm/src/`:
|
||
|
||
| Rust export | JS wrapper | Description |
|
||
|---|---|---|
|
||
| `generate_keypair()` | `generate_keypair()` | Generate Ed25519 keypair; return hex public key |
|
||
| `get_public_key()` | `get_public_key()` | Return hex public key, or `""` if not initialised |
|
||
| `sign_message(msg)` | `sign_message(msg)` | Sign string; return hex signature, or `""` if not initialised |
|
||
| `compute_next_hash(prev, ts, entropy, stack, salt)` | `compute_next_hash(...)` | Compute next Blake3 chain hash |
|
||
| `run_program(b64)` | `run_program(b64)` | Execute base64 VM program; return `{ stack, ip }` |
|
||
|
||
### `antibot_wasm_bg.wasm`
|
||
|
||
The compiled binary. The `.bg` suffix means "background" — this is the raw
|
||
WASM that `antibot_wasm.js` loads internally. You should not reference this
|
||
file directly in your HTML.
|
||
|
||
---
|
||
|
||
## Step-by-Step Build
|
||
|
||
### 1. Install the Rust WASM target
|
||
|
||
```bash
|
||
rustup target add wasm32-unknown-unknown
|
||
```
|
||
|
||
This is a one-time step. Without it, the Rust compiler cannot produce WASM
|
||
output.
|
||
|
||
### 2. Install wasm-pack
|
||
|
||
```bash
|
||
cargo install wasm-pack
|
||
```
|
||
|
||
Or via the installer script:
|
||
|
||
```bash
|
||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||
```
|
||
|
||
Verify:
|
||
|
||
```bash
|
||
wasm-pack --version
|
||
# wasm-pack 0.13.x
|
||
```
|
||
|
||
### 3. Build the WASM module
|
||
|
||
From the project root:
|
||
|
||
```bash
|
||
wasm-pack build wasm --target web --release
|
||
```
|
||
|
||
`--target web` produces an ES module (`import`/`export` syntax) suitable for
|
||
use directly in a browser without a bundler. Other targets (`bundler`,
|
||
`nodejs`, `no-modules`) produce different output formats and are not
|
||
compatible with the ChronoSeal frontend as written.
|
||
|
||
`--release` enables Rust's release optimisations (inlining, dead code
|
||
elimination, size reduction). Omit it during development for faster builds
|
||
and better panic messages.
|
||
|
||
### 4. Move the output to the frontend
|
||
|
||
```bash
|
||
rm -rf frontend/pkg
|
||
mv wasm/pkg frontend/pkg
|
||
```
|
||
|
||
The frontend expects the WASM module at `frontend/pkg/antibot_wasm.js`
|
||
because `heartbeat.js` imports from `./pkg/antibot_wasm.js` relative to
|
||
the `frontend/` directory, which is where the server's static file handler
|
||
is rooted.
|
||
|
||
---
|
||
|
||
## Using the Build Script
|
||
|
||
The convenience script at `scripts/build.sh` performs all steps in order:
|
||
|
||
```bash
|
||
bash scripts/build.sh
|
||
```
|
||
|
||
This builds the WASM module, moves it to `frontend/pkg/`, and then builds
|
||
the server binary. Run this for a clean full build before deployment.
|
||
|
||
For development iteration where you are only changing Rust WASM code:
|
||
|
||
```bash
|
||
wasm-pack build wasm --target web # (omit --release for speed)
|
||
rm -rf frontend/pkg && mv wasm/pkg frontend/pkg
|
||
```
|
||
|
||
For development where you are only changing server code:
|
||
|
||
```bash
|
||
cargo build -p server
|
||
```
|
||
|
||
---
|
||
|
||
## How `heartbeat.js` Loads the Module
|
||
|
||
`heartbeat.js` uses a standard ES module dynamic import pattern:
|
||
|
||
```js
|
||
import init, { generate_keypair, sign_message, compute_next_hash, run_program }
|
||
from './pkg/antibot_wasm.js';
|
||
|
||
export async function initHeartbeat() {
|
||
// 1. Fetch and instantiate the .wasm binary
|
||
await init();
|
||
|
||
// 2. Generate keypair — private key stored in WASM memory only
|
||
const pubKeyHex = generate_keypair();
|
||
|
||
// 3. Send public key to server, receive session_id and chain seed
|
||
// ...
|
||
}
|
||
```
|
||
|
||
`init()` is the default export from `antibot_wasm.js`. It fetches
|
||
`antibot_wasm_bg.wasm` (from the same `pkg/` directory) via `fetch()`,
|
||
compiles it in the browser's WASM engine, and links it to the JS glue
|
||
layer. After `await init()` returns, all the named exports
|
||
(`generate_keypair`, `sign_message`, etc.) are ready to call.
|
||
|
||
The `init()` call must complete before any other WASM function is called.
|
||
Calling `sign_message()` or `compute_next_hash()` before `await init()`
|
||
returns will produce an empty string (the module is not yet instantiated).
|
||
|
||
---
|
||
|
||
## Serving the WASM Binary
|
||
|
||
Browsers require WASM files to be served with the correct MIME type:
|
||
|
||
```
|
||
Content-Type: application/wasm
|
||
```
|
||
|
||
Most web servers set this automatically for `.wasm` files. If you see the
|
||
error:
|
||
|
||
```
|
||
WebAssembly.instantiate(): Response has unsupported MIME type
|
||
```
|
||
|
||
Add the MIME type to your server configuration:
|
||
|
||
**nginx:**
|
||
```nginx
|
||
types {
|
||
application/wasm wasm;
|
||
}
|
||
```
|
||
|
||
**Apache `.htaccess`:**
|
||
```apache
|
||
AddType application/wasm .wasm
|
||
```
|
||
|
||
The Axum `ServeDir` handler used by ChronoSeal's built-in static server
|
||
sets the correct MIME type automatically via `tower-http`.
|
||
|
||
---
|
||
|
||
## What Is Not in the Repository
|
||
|
||
| Path | Why excluded |
|
||
|---|---|
|
||
| `wasm/pkg/` | Generated build output — changes on every build |
|
||
| `frontend/pkg/` | Same generated output, moved to serve location |
|
||
| `target/` | Standard Rust build artefacts |
|
||
|
||
Both `wasm/pkg/` and `frontend/pkg/` are listed in `.gitignore`. Committing
|
||
them would bloat the repository (the `.wasm` binary alone is 300–800 KB),
|
||
create noisy diffs on every rebuild, and give a false impression that the
|
||
WASM module is pre-built and ready to use without a build step.
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### `wasm32-unknown-unknown` target not found
|
||
|
||
```
|
||
error[E0463]: can't find crate for `std`
|
||
```
|
||
|
||
Fix:
|
||
|
||
```bash
|
||
rustup target add wasm32-unknown-unknown
|
||
```
|
||
|
||
### `wasm-pack` not found
|
||
|
||
```bash
|
||
cargo install wasm-pack
|
||
```
|
||
|
||
### `wasm-opt` not found (warning, not an error)
|
||
|
||
wasm-pack prints a warning if `wasm-opt` is not installed. The build still
|
||
succeeds; the binary is just not size-optimised.
|
||
|
||
```bash
|
||
# On Debian/Ubuntu/Arch
|
||
sudo apt install binaryen # Debian/Ubuntu
|
||
sudo pacman -S binaryen # Arch
|
||
```
|
||
|
||
### `antibot_wasm_bg.wasm` fetch fails (404)
|
||
|
||
The `.wasm` file is not being served from `frontend/pkg/`. Verify:
|
||
|
||
```bash
|
||
ls /mnt/Programs/ChronoSeal/frontend/pkg/
|
||
# Should list: antibot_wasm.js antibot_wasm_bg.wasm ...
|
||
```
|
||
|
||
If the directory is empty or missing, re-run the build steps above.
|
||
|
||
### MIME type error in browser
|
||
|
||
See the "Serving the WASM Binary" section above.
|
||
|
||
### `sign_message` or `generate_keypair` returns empty string
|
||
|
||
The WASM keypair has not been initialised. Ensure `await init()` and
|
||
`generate_keypair()` are called (and awaited) before any other WASM
|
||
function. Check the browser console for any errors during `init()`.
|