You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The pipeline: **WebAssembly → Rust source → Safe binary**
8
8
9
-
See the `docs/` directory for the full draft specification.
9
+
## Documentation
10
+
11
+
| Document | Purpose |
12
+
|----------|---------|
13
+
|`docs/REQUIREMENTS.md`| What the system must do — formal requirements with REQ_* IDs |
14
+
|`docs/SPECIFICATION.md`| How it works — module representation, architecture, transpilation rules, integration, performance. Also includes getting started guide. |
15
+
|`docs/FUTURE.md`| Planned but unimplemented features — verified/hybrid backends, temporal isolation, contract-based verification |
10
16
11
17
## Repository structure
12
18
13
19
The project is a Rust workspace with three crates:
Wasm linear memory is represented as a page-level const generic:
50
-
51
-
```rust
52
-
structIsolatedMemory<constMAX_PAGES:usize> {
53
-
pages: [[u8; PAGE_SIZE]; MAX_PAGES], // Fully allocated at compile time, contiguous
54
-
active_pages:usize, // Current live size (starts at initial_pages)
55
-
}
56
-
```
83
+
Wasm linear memory is `IsolatedMemory<const MAX_PAGES: usize>` — a 2D array `[[u8; PAGE_SIZE]; MAX_PAGES]` with `active_pages` tracking. Fully allocated at compile time, no heap. See `crates/herkos-runtime/src/memory.rs` and SPECIFICATION.md §2.1.
57
84
58
-
**Design decisions**:
59
-
-**`MAX_PAGES` const generic**: Derived from the Wasm module's declared maximum. Prevents any heap allocation (`no_std` compatible). Enables monomorphization for zero-cost dispatch.
60
-
-**`active_pages` tracking**: Starts at the module's initial page count. Incremented by `memory.grow`. Accesses beyond `active_pages * PAGE_SIZE` trap.
No `MemoryView` wrappers — the API is flat and fast. See SPECIFICATION.md §3 for complete details.
91
+
Each has a **Globals struct**`G` (one typed field per mutable Wasm global) and a **Table** for indirect calls. See `crates/herkos-runtime/src/module.rs` and SPECIFICATION.md §2.2.
68
92
69
-
### Module representation
70
-
71
-
Two kinds of modules:
93
+
### Capability-based security via traits
72
94
73
-
1.**`Module<G, MAX_PAGES, TABLE_SIZE>`** — Owns its own memory (like a process)
74
-
2.**`LibraryModule<G, TABLE_SIZE>`** — Borrows memory from caller (like a shared library)
95
+
-**Imports** → trait bounds on generic host parameter `H`
96
+
-**Exports** → trait implementations on the module struct
97
+
-**Zero-cost**: monomorphization, no vtables, no trait objects in hot paths
98
+
-**WASI**: standard traits (`WasiFd`, `WasiPath`, `WasiClock`, `WasiRandom`) shipped with runtime
75
99
76
-
Each has its own:
77
-
-**Globals struct**`G`: One typed field per mutable Wasm global; immutable globals are `const`
78
-
-**Table**: Indirect call table with function references (for `call_indirect`)
100
+
See SPECIFICATION.md §2.4–2.6.
79
101
80
-
Modules can call each other via trait-bounded functions. Memory ownership enforces spatial isolation — one module's memory is inaccessible to others.
102
+
### Function calls
81
103
82
-
### Capability-based security via traits
104
+
-**Direct** (`call`): regular Rust function calls with state threaded through
105
+
-**Indirect** (`call_indirect`): safe static match dispatch over `func_index`, no function pointers
106
+
-**Structural type equivalence**: canonical type index mapping at transpile time
83
107
84
-
**Imports** (what a module needs) become Rust trait bounds on generic host parameter `H`.
85
-
**Exports** (what a module provides) become trait implementations.
-`WasmResult<T> = Result<T, WasmTrap>` — no panics, no unwinding
114
+
-`ConstructionError` for programming errors during module instantiation
95
115
96
-
**Zero-cost**: All dispatch via monomorphization (no vtables, no trait objects). If a module doesn't import a capability, the trait bound doesn't exist — no code path to call it.
116
+
### Current status
97
117
98
-
WASI support is built-in via standard traits (`WasiFd`, `WasiPath`, `WasiClock`, `WasiRandom`, etc.) shipped with `herkos-runtime`. See SPECIFICATION.md §5 for complete details.
118
+
-**Implemented**: Safe backend only (runtime bounds checking, no unsafe in output)
`herkos-runtime` and all transpiled output **must be `#![no_std]`**. No heap allocation without the optional `alloc` feature gate. No panics, no `format!`, no `String` in the runtime or generated code. Errors are `Result<T, WasmTrap>` only. The `herkos` CLI crate is standard `std` binary, this constraint applies only to the runtime and generated output.
124
+
`herkos-runtime` and all transpiled output **must be `#![no_std]`**. No heap allocation without the optional `alloc` feature gate. No panics, no `format!`, no `String`. Errors are `Result<T, WasmTrap>` only. The `herkos` CLI crate is a standard `std` binary.
103
125
104
126
## Coding conventions
105
127
106
128
-**Rust edition**: 2021
107
129
-**MSRV**: latest stable
108
-
-**Error handling**: use `thiserror` for library errors, `anyhow` in CLI binaries. Wasm execution errors use the `WasmTrap` / `WasmResult<T>` types from the runtime crate (no panics, no unwinding).
109
-
-**Naming**: follow Rust API guidelines. Wasm spec terminology (e.g., `i32.load`, `br_table`) maps to snake_case Rust (`i32_load`, `br_table`).
110
-
-**Unsafe**: avoid `unsafe` in the runtime crate as much as possible — the whole point is compile-time safety. Any `unsafe` block requires a `// SAFETY:` comment explaining the invariant. In the verified backend output, `unsafe` blocks carry `// PROOF:` references to verification metadata instead.
111
-
-**Tests**: unit tests live in `#[cfg(test)] mod tests` inside each module. Integration tests go in `tests/` directories per crate. End-to-end tests (Wasm → Rust → run) go in `tests/e2e/` at the workspace root.
112
-
-**Dependencies**: keep the dependency tree small. Prefer `wasmparser` for Wasm parsing. Avoid pulling in a full Wasm runtime — we are the runtime. `herkos-runtime` must have zero dependencies in the default (no `alloc`) configuration.
113
-
114
-
## Function calls and indirect dispatch
115
-
116
-
### Direct calls (`call`)
117
-
Transpile to regular Rust function calls with shared state (memory, globals, table) threaded through:
118
-
```rust
119
-
v5=func_3(&mutmemory, &mutglobals, v3, v4)?;
120
-
```
121
-
122
-
### Indirect calls (`call_indirect`)
123
-
Implement safe dispatch via match statements. The transpiler:
124
-
1. Looks up the table entry and validates its type
125
-
2. Enumerates all functions matching the expected signature
126
-
3. Emits a static match statement (becomes jump table in machine code)
127
-
4.**No function pointers, no vtables, no dynamic dispatch** — 100% safe
128
-
129
-
Example:
130
-
```rust
131
-
let__entry=table.get(idx)?;
132
-
if__entry.type_index !=canonical_type {
133
-
returnErr(WasmTrap::IndirectCallTypeMismatch);
134
-
}
135
-
v4=match__entry.func_index {
136
-
0=>func_0(v0, v1),
137
-
1=>func_1(v0, v1),
138
-
_=>returnErr(WasmTrap::UndefinedElement),
139
-
};
140
-
```
141
-
142
-
See SPECIFICATION.md §8.5 for structural type equivalence and table initialization details.
143
-
144
-
## Integration patterns
145
-
146
-
### Primary: Trait-based (Recommended)
147
-
Host instantiates modules and provides capabilities through trait implementations:
See docs/SPECIFICATION.md §13.3 for complete strategies.
184
-
185
-
### Parallelization
186
-
The transpilation pipeline can be parallelized. IR building and code generation are embarrassingly parallel (each function is independent). The transpiler should use `rayon` for parallel iteration when processing modules with 20+ functions. See SPECIFICATION.md §13.5 for implementation details and performance expectations.
187
-
188
-
## Wasm parsing and code generation
189
-
190
-
### Parsing
191
-
Use the `wasmparser` crate for reading `.wasm` binaries. Do NOT use `wasm-tools` or `walrus` unless there is a clear justification. The transpiler should operate on the structured output of `wasmparser` (types, functions, memory sections, etc.) and emit Rust source via string building or a small codegen IR — not via `syn`/`quote` procedural macro machinery.
192
-
193
-
### Generated Rust output
194
-
Transpiled code should be:
195
142
- Self-contained (only depends on `herkos-runtime`)
196
143
- Formatted (run through `rustfmt`)
197
-
- Readable and auditable — prefer clarity over compactness
198
-
- No panics, no unwinding — use `Result<T, WasmTrap>` for error handling
144
+
- Readable and auditable
145
+
- No panics, no unwinding — `Result<T, WasmTrap>` only
146
+
147
+
### Performance considerations
148
+
149
+
-**Outline pattern** (mandatory for runtime): non-generic inner functions, generic wrapper is thin shell
150
+
-**MAX_PAGES normalization**: standard sizes (16, 64, 256, 1024)
151
+
-**Parallelization**: IR building and codegen are embarrassingly parallel (each function independent). Use `rayon` for 20+ functions.
199
152
200
153
## PR and commit guidelines
201
154
202
155
- Keep commits focused: one logical change per commit
203
156
- Commit messages: imperative mood, short summary line, body if needed
204
157
- All PRs must pass `cargo test`, `cargo clippy`, and `cargo fmt --check`
205
-
- See docs/SPECIFICATION.md for the full technical specification of all components
0 commit comments