Skip to content

feat(venue-sdk): add nexum-venue-sdk crate#251

Open
mfw78 wants to merge 2 commits into
feat/m1-pool-routerfrom
feat/m1-venue-sdk
Open

feat(venue-sdk): add nexum-venue-sdk crate#251
mfw78 wants to merge 2 commits into
feat/m1-pool-routerfrom
feat/m1-venue-sdk

Conversation

@mfw78

@mfw78 mfw78 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Ships crates/nexum-venue-sdk (single commit 34077c1), the venue-side SDK
that generates the nexum:adapter/venue-adapter world bindings once
in-crate and layers the VenueAdapter trait, the borsh IntentBody
codec, and the typed intent client core on top.

Closes #230

Changes

  • Add crates/nexum-venue-sdk (new workspace member): generates the
    nexum:adapter/venue-adapter world bindings once, in bindings.rs, via
    wit_bindgen::generate! with pub_export_macro: true and an
    additional PartialEq derive.
  • Add the VenueAdapter trait (adapter.rs) mirroring the world's export
    face (init, derive_header, submit, status, cancel), plus
    export_venue_adapter! which turns an impl into the component's export
    glue for the adapter's cdylib.
  • Add the borsh IntentBody codec: the IntentBody trait and typed
    BodyError in body.rs (Empty, UnknownVersion { version },
    Malformed { version, detail }), with the wire form being the borsh
    enum layout (one-byte tag = declaration index, then the borsh payload).
  • Add #[derive(IntentBody)] in crates/nexum-macros (intent_body.rs,
    wired into lib.rs), re-exported at the SDK root, generating the
    tag/version handling so an unknown tag decodes as the typed
    BodyError::UnknownVersion rather than a stringly borsh error.
  • Add the typed intent client core (client.rs): IntentPool as the
    byte-level seam and IntentClient<P> binding one venue, encoding
    request/response bodies through IntentBody.
  • Add typed transport wrappers and fault conversions (transport.rs,
    faults.rs): From<host::Fault> for Fault, From<host::Fault> for VenueError, and From<nexum_sdk::http::FetchError> for VenueError.
  • Add crate-level integration tests (tests/adapter.rs).
  • Root Cargo.toml: add the crates/nexum-venue-sdk member and hoist
    borsh to a workspace dependency; Cargo.lock updated accordingly.

Test plan

Independent verification of feat/m1-venue-sdk (single commit 34077c1,
stacked on origin/feat/m1-pool-router), all gates run inside nix develop (rustc 1.94.0), warm target, --locked. No wit/ changes in
range, so the wasmtime bindgen smoke-compile requirement does not apply.

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace

Notes for reviewer

Adversarial red-team of this PR found no blocking or fixable defects:
every generated type matches the WIT contracts field-for-field, fold arms
are exhaustive where claimed (with non_exhaustive wildcards only where
required), and the derive's tag/version handling is sound, including the
256-variant boundary.

Stacked on feat/m1-pool-router; merges bottom-up after the fault train
lands; retarget to develop then.

@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from be70b63 to 0a3398a Compare July 7, 2026 23:12
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 34077c1 to 503ea26 Compare July 7, 2026 23:12
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 0a3398a to 6f2bdbb Compare July 8, 2026 00:42
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 503ea26 to 9b37071 Compare July 8, 2026 00:42
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 6f2bdbb to 6806824 Compare July 8, 2026 01:19
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 9b37071 to 51eb32c Compare July 8, 2026 01:19
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 6806824 to 0e4c79b Compare July 8, 2026 01:44
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 51eb32c to e519e54 Compare July 8, 2026 01:44
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 0e4c79b to 233e9fc Compare July 8, 2026 02:11
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from e519e54 to 3fbb66b Compare July 8, 2026 02:11
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 233e9fc to 7593566 Compare July 8, 2026 02:20
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 3fbb66b to 277be91 Compare July 8, 2026 02:20
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 7593566 to ce4648a Compare July 8, 2026 02:23
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 277be91 to 6235e9a Compare July 8, 2026 02:23
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from ce4648a to d074cf6 Compare July 8, 2026 03:03
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 6235e9a to ac6cecf Compare July 8, 2026 03:03
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from d074cf6 to de679c3 Compare July 8, 2026 04:59
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from ac6cecf to 7b9e72d Compare July 8, 2026 04:59
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from de679c3 to 9de7450 Compare July 8, 2026 05:31
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 7b9e72d to c2ecccb Compare July 8, 2026 05:31
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 9de7450 to eaebfaf Compare July 8, 2026 06:21
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from c2ecccb to a13db1d Compare July 8, 2026 06:21
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from eaebfaf to aa15304 Compare July 8, 2026 06:40
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from a13db1d to b272899 Compare July 8, 2026 06:40
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from aa15304 to 9b7a402 Compare July 8, 2026 07:07
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from b272899 to 5725d00 Compare July 8, 2026 07:07
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 9b7a402 to f1f8871 Compare July 8, 2026 07:21
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 5725d00 to 9176d60 Compare July 8, 2026 07:21
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from f1f8871 to 349d31b Compare July 8, 2026 07:36
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 9176d60 to 23e6e3c Compare July 8, 2026 07:36
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 560688d to e19251f Compare July 8, 2026 08:55
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 59a0937 to bde0251 Compare July 8, 2026 08:55
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from e19251f to d2fd2b1 Compare July 8, 2026 09:02
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from bde0251 to 28da180 Compare July 8, 2026 09:02
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from d2fd2b1 to be53811 Compare July 8, 2026 09:19
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 28da180 to 44a33a6 Compare July 8, 2026 09:19
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from be53811 to 2cf2cf9 Compare July 8, 2026 12:10
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 44a33a6 to 6a63dad Compare July 8, 2026 12:10
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 2cf2cf9 to 24bc04f Compare July 8, 2026 12:20
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 6a63dad to 26e4eac Compare July 8, 2026 12:20
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 24bc04f to 77688fc Compare July 8, 2026 12:50
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 26e4eac to 6da73e3 Compare July 8, 2026 12:50
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 77688fc to aa0304e Compare July 8, 2026 13:41
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from 6da73e3 to b1204a5 Compare July 8, 2026 13:41
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from aa0304e to 6db2785 Compare July 8, 2026 13:59
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from f76b9dc to b272be9 Compare July 8, 2026 13:59
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 6db2785 to f757151 Compare July 8, 2026 14:09
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from b272be9 to b9e0061 Compare July 8, 2026 14:09
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from f757151 to ca3b167 Compare July 8, 2026 22:33
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch 3 times, most recently from e7a8560 to b75198e Compare July 8, 2026 23:24
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 2ef07d6 to 52775b4 Compare July 8, 2026 23:24
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from b75198e to ac9227f Compare July 8, 2026 23:29
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 52775b4 to 1d9b27a Compare July 8, 2026 23:29

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean SDK design and good test coverage overall. Three items below.

Comment on lines +24 to +26
/// Project an opaque intent body onto the stable header guard
/// policy runs on. Must be a pure derivation: no transport, no side
/// effects, so the host can inspect a header before deciding to

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same purity-claim gap as PR #249 — this doc says "Must be a pure derivation: no transport, no side effects" but the venue-adapter world unconditionally imports nexum:host/chain and nexum:host/messaging, so nothing in the WIT or the generated export shim prevents derive_header from calling them. The host router calls derive_header before running the guard, so any side effects happen before policy runs.

Suggested change
/// Project an opaque intent body onto the stable header guard
/// policy runs on. Must be a pure derivation: no transport, no side
/// effects, so the host can inspect a header before deciding to
/// Project an opaque intent body onto the stable header guard
/// policy runs on. By convention a pure derivation — no transport, no
/// side effects — so the host can inspect a header before deciding to
/// submit; the WIT cannot enforce this, as chain and messaging imports
/// remain available inside `derive-header`.
fn derive_header(body: Vec<u8>) -> Result<IntentHeader, VenueError>;

Comment on lines +88 to +90
/// The pool or the venue behind it failed the call. The payload is
/// the wire `venue-error`, which carries no `Display`; format via
/// `Debug`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"carries no Display" is inaccurate. VenueError appears in the error slot of all four nexum:intent/adapter results, so wit-bindgen 0.58 sets TypeInfo::error = true and generates impl Display for VenueError (delegates to Debug). The {0:?} format is still correct — the generated Display and Debug produce the same output — but the stated reason is wrong.

Suggested change
/// The pool or the venue behind it failed the call. The payload is
/// the wire `venue-error`, which carries no `Display`; format via
/// `Debug`.
/// The pool or the venue behind it failed the call. The wire
/// `venue-error`'s generated `Display` delegates to `Debug`, so
/// `{0:?}` gives the richer output.
#[error("venue error: {0:?}")]
Venue(VenueError),

Comment on lines +117 to +130
fn transport_fault_folds_to_venue_error_by_shape() {
assert_eq!(
VenueError::from(host::Fault::Denied("nope".into())),
VenueError::Denied("nope".into()),
);
assert!(matches!(
VenueError::from(host::Fault::Timeout),
VenueError::Unavailable(_)
));
assert!(matches!(
VenueError::from(host::Fault::InvalidInput("bug".into())),
VenueError::InternalError(_)
));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RateLimited arm in From<host::Fault> for VenueError silently drops retry_after_ms — the caller loses the backoff hint and gets a plain string. That's the only arm where structured data is destroyed rather than threaded through, making it the most likely regression target. Adding it to this test would catch a future refactor that accidentally routes it elsewhere:

Suggested change
fn transport_fault_folds_to_venue_error_by_shape() {
assert_eq!(
VenueError::from(host::Fault::Denied("nope".into())),
VenueError::Denied("nope".into()),
);
assert!(matches!(
VenueError::from(host::Fault::Timeout),
VenueError::Unavailable(_)
));
assert!(matches!(
VenueError::from(host::Fault::InvalidInput("bug".into())),
VenueError::InternalError(_)
));
}
#[test]
fn transport_fault_folds_to_venue_error_by_shape() {
assert_eq!(
VenueError::from(host::Fault::Denied("nope".into())),
VenueError::Denied("nope".into()),
);
assert!(matches!(
VenueError::from(host::Fault::Timeout),
VenueError::Unavailable(_)
));
assert!(matches!(
VenueError::from(host::Fault::RateLimited(host::RateLimit { retry_after_ms: Some(500) })),
VenueError::Unavailable(_) // retry_after_ms is intentionally dropped here
));
assert!(matches!(
VenueError::from(host::Fault::InvalidInput("bug".into())),
VenueError::InternalError(_)
));
}

mfw78 and others added 2 commits July 9, 2026 01:55
Introduce the venue-author persona crate: the VenueAdapter trait and
export macro over an in-crate bindgen of the adapter world, the borsh
IntentBody derive whose outer version enum fails unknown tags typedly,
the typed intent client core over the byte-level pool seam, and typed
wrappers over the scoped chain, messaging, and wasi:http imports. The
derive expansion lands in nexum-macros and re-exports from the SDK;
borsh enters the workspace dependency table.
The SDK RpcError.data is Bytes; the wit-bindgen chain-error carries
Vec<u8>, so wrap it (Vec -> Bytes is O(1)) when reprojecting.
@mfw78 mfw78 force-pushed the feat/m1-pool-router branch from 1d9b27a to aaaa9e1 Compare July 9, 2026 02:09
@mfw78 mfw78 force-pushed the feat/m1-venue-sdk branch from ac9227f to 959404f Compare July 9, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-train Part of the current stacked PR train; lands in sequence, do not merge out of order.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants