Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ body:
id: scope
attributes:
label: Scope
description: Universal `web3:runtime` change, domain extension (e.g. `shepherd:cow`), runtime-only, or SDK-only?
description: Universal `nexum:host` change, domain extension (e.g. `shepherd:cow`), runtime-only, or SDK-only?
- type: textarea
id: extra
attributes:
Expand Down
87 changes: 86 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
members = [
"crates/nxm-engine",
"crates/nexum-engine",
"modules/example",
]
resolver = "2"
Expand All @@ -10,6 +10,91 @@ edition = "2024"
license = "AGPL-3.0"
repository = "https://github.com/nullisLabs/shepherd"

# Shared dependency table. Only deps consumed by 2+ crates across the
# full workspace (nexum-engine + every downstream module crate) are
# hoisted here; single-consumer deps stay per-crate. Crates inherit
# with `dep.workspace = true` and may add features per call site via
# `dep = { workspace = true, features = ["extra"] }`. Version drift
# across crates (the failure mode that prompted hoisting in the first
# place, e.g. cowprotocol on `1.0.0-alpha` vs `1.0.0-alpha.3`) is now
# impossible by construction.
[workspace.dependencies]
# Error + async plumbing.
anyhow = "1"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
futures = "0.3"

# Serde + config.
serde = { version = "1", features = ["derive"] }
serde_json = "1"

# Observability.
tracing = "0.1"
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "env-filter", "ansi", "json"] }

# `strum::IntoStaticStr` on every error / event enum gives a free
# snake_case `&'static str` for every variant, which feeds directly
# into `metrics::counter!(..., "error_kind" => name)` and
# `tracing::warn!(error_kind = name, ...)` recordings without an
# ad-hoc `match err { ... => "connect" ... }` ladder per call site.
strum = { version = "0.26", features = ["derive"] }

# `auto_impl::auto_impl(&, Arc, Box)` forwarding impls for traits
# held through smart pointers. Available workspace-wide so any future
# `Arc<dyn Trait>` boundary can opt in without touching root manifest.
auto_impl = "1"

# `derive_more` newtype boilerplate (`Deref`, `From`, `Display`, ...).
# `default-features = false, features = ["full"]` keeps the proc-macro
# surface predictable; per-derive opt-in via the standard `#[derive(...)]`
# syntax. Available workspace-wide; not pulled in by default.
derive_more = { version = "1", default-features = false, features = ["full"] }

# CLI parser. Used by every binary crate (engine, load-gen,
# orderbook-mock, shepherd-backtest) via the derive macro.
clap = { version = "4", features = ["derive"] }

# alloy stack. Engine uses the full provider/transport surface;
# guest-facing crates use `alloy-primitives` + `alloy-sol-types` for
# typed protocol values. Pinned together so a single workspace bump
# moves every consumer at once.
alloy-primitives = { version = "1.5", default-features = false, features = ["std", "serde"] }
alloy-sol-types = { version = "1.5", default-features = false, features = ["std"] }
alloy-provider = { version = "1.5", default-features = false, features = ["ws", "ipc", "pubsub", "reqwest"] }
alloy-rpc-types-eth = { version = "1.5", default-features = false, features = ["std"] }
alloy-transport-ws = { version = "1.5", default-features = false }

# CoW Protocol bindings. Pinned to one version across the workspace
# (was `1.0.0-alpha` in engine vs `1.0.0-alpha.3` in SDK before
# hoisting).
cowprotocol = { version = "1.0.0-alpha.3", default-features = false }

# HTTP transport for `cow_api::request` REST passthrough and the
# orderbook-mock test surface.
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }

# `wit-bindgen` is consumed by every guest module crate (example +
# every strategy + every fixture). Hoisted so a single bump moves
# them in lock-step.
wit-bindgen = { version = "0.57", default-features = false, features = ["macros", "realloc"] }

# Workspace-standard lint set. New crates inherit via
# `[lints] workspace = true` in their package manifest. `unsafe_code`
# cannot be denied workspace-wide because every wit-bindgen guest
# module emits an `unsafe extern "C"` shim; modules carrying that
# macro keep the default-warn allowance, and unsafe in non-binding
# code still trips review by convention.
[workspace.lints.rust]
unsafe_op_in_unsafe_fn = "warn"

[workspace.lints.clippy]
# Deny the easy footguns. Each crate carries its own narrower
# `#![deny(...)]` where the cost of a violation is high (e.g. the
# binary entrypoints carry `unused_crate_dependencies` warn).
dbg_macro = "deny"
todo = "deny"

[profile.dev]
panic = "abort"

Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

Shepherd is the [CoW Protocol](https://cow.fi) distribution of **Nexum**, a WebAssembly Component Model runtime for secure, sandboxed execution of capability-scoped modules.

A module compiled against the universal `web3:runtime/headless-module` world runs on any Nexum-compatible host. A module compiled against `shepherd:cow/shepherd-module` additionally gains access to CoW Protocol APIs and order submission — and requires a Shepherd host.
A module compiled against the universal `nexum:host/event-module` world runs on any Nexum-compatible host. A module compiled against `shepherd:cow/shepherd` additionally gains access to CoW Protocol APIs and order submission — and requires a Shepherd host.

> **Upgrading from 0.1?** See the [Migration Guide](docs/migration/0.1-to-0.2.md) for the full rename table, the new `host-error` model, and the manifest-driven capability negotiation introduced in 0.2.

## Why

Expand All @@ -20,12 +22,14 @@ A module compiled against the universal `web3:runtime/headless-module` world run

| Path | Purpose |
| --- | --- |
| `crates/nxm-engine/` | Host runtime — wasmtime-based component loader and host implementations. |
| `crates/nexum-engine/` | The **engine** — a wasmtime-based host *implementation* of the `nexum:host` contract. The reference server runtime. |
| `wit/nexum-host/` | The **`nexum:host` WIT package** — the host/guest *contract* (interfaces, types, worlds) that every engine implements and every module imports. |
| `wit/shepherd-cow/` | `shepherd:cow` WIT package — CoW Protocol-specific extensions on top of `nexum:host`. |
| `modules/example/` | Reference guest module demonstrating the module ABI. |
| `wit/web3-runtime/` | Universal `web3:runtime` WIT package (csn, identity, local-store, remote-store, msg, logging). |
| `wit/shepherd-cow/` | `shepherd:cow` WIT package — CoW Protocol-specific extensions. |
| `docs/` | Architecture, design notes, and the universal primitive taxonomy. Start with [`docs/00-overview.md`](docs/00-overview.md). |

> **Engine vs. host.** "Engine" is a concrete implementation that runs WASM components (today: `nexum-engine`, a wasmtime-based daemon). The `nexum:host` WIT package is the *contract* — the host-imports surface a guest sees. Other engines (mobile, browser) can implement the same `nexum:host` contract; modules built against the contract run on any compliant engine.

## Building

Shepherd uses [Nix](https://nixos.org/) flakes to pin the toolchain and [just](https://github.com/casey/just) as a task runner.
Expand All @@ -51,14 +55,15 @@ Without Nix, you need: Rust (edition 2024, see `rust-toolchain.toml` if present)
The `docs/` directory contains the design corpus:

- [`00-overview.md`](docs/00-overview.md) — architecture, primitives, WIT worlds
- [`01-runtime-environment.md`](docs/01-runtime-environment.md) — host runtime
- [`01-runtime-environment.md`](docs/01-runtime-environment.md) — engine internals (wasmtime, fuel, epoch, ResourceLimiter)
- [`02-modules-events-packaging.md`](docs/02-modules-events-packaging.md) — module ABI, events, packaging
- [`03-module-discovery.md`](docs/03-module-discovery.md) — static / ENS / on-chain registry
- [`04-state-store.md`](docs/04-state-store.md) — local + remote state
- [`05-sdk-design.md`](docs/05-sdk-design.md) — guest SDK
- [`06-production-hardening.md`](docs/06-production-hardening.md) — operational concerns
- [`07-rpc-namespace-design.md`](docs/07-rpc-namespace-design.md) — `csn` namespace
- [`07-rpc-namespace-design.md`](docs/07-rpc-namespace-design.md) — `chain` namespace
- [`08-platform-generalisation.md`](docs/08-platform-generalisation.md) — beyond CoW
- [`migration/0.1-to-0.2.md`](docs/migration/0.1-to-0.2.md) — upgrading from Nexum 0.1

## Contributing

Expand Down
15 changes: 11 additions & 4 deletions crates/nxm-engine/Cargo.toml → crates/nexum-engine/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
[package]
name = "nxm-engine"
version = "0.1.0"
name = "nexum-engine"
version = "0.2.0"
edition.workspace = true
license.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
wasmtime = { version = "45", features = ["component-model"] }
wasmtime-wasi = "45"
anyhow = "1"
tokio = { version = "1", features = ["full"] }
anyhow.workspace = true
clap.workspace = true
tokio.workspace = true
getrandom = "0.4"
serde.workspace = true
toml = "1"
Loading