From the chain-seam review (2026-07-22), spun out of the #453 alloy-chains substitution and sibling to the nullislabs/nexum-runtime#52 / nullislabs/nexum-runtime#51 error-typing cleanup.
The permitted JSON-RPC read-surface allowlist ChainMethod is defined twice, byte-identical, and kept in sync by hand:
- guest:
crates/nexum-sdk/src/chain/method.rs
- host:
crates/nexum-runtime/src/host/component/chain.rs
Both carry the same 19 variants in the same order with the same #[strum(serialize = "...")] wire strings and the same as_str(); the only difference is the guest also derives Hash. Nothing enforces that they stay equal. Add or rename a read method on one side and forget the other and they drift silently, with no compile error, because the WIT edge between them is a plain String. This is a security-relevant allowlist (it exists so signing and mutating methods cannot be represented and never cross the seam), so silent drift is exactly the failure we do not want.
Unlike the Chain newtypes retired in #453, this is not an alloy duplication - alloy has no method-name enum, it passes bare &'static str literals - so the fix is de-duplication, not substitution.
Scope
One allowlist, two definitions, three consumer sites:
| role |
definition |
consumers |
| guest |
nexum-sdk/src/chain/method.rs |
nexum-sdk/src/chain/transport.rs (try_from, as_str), re-exported at chain::ChainMethod |
| host |
nexum-runtime/src/host/component/chain.rs |
nexum-runtime/src/host/provider_pool.rs; videre-host tests (zero_leak.rs, platform.rs) via nexum_runtime::host::component::ChainMethod |
Approach
Introduce a shared leaf crate nexum-chain-method (model it on the existing shared guest/host leaf nexum-status-body):
- Single
pub enum ChainMethod with the derive superset Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, IntoStaticStr and the as_str(self) -> &'static str inherent method.
- Sole dependency
strum (already in both consumers' graphs). The enum needs no allocation, so make the crate #![no_std] - future-proof and zero cost.
nexum-sdk and nexum-runtime both depend on it; delete the two hand-rolled copies.
- Preserve the existing public paths so consumers are untouched: guest re-exports it at
chain::ChainMethod (already does), host re-exports it at host::component::ChainMethod so nexum_runtime::host::component::ChainMethod still resolves for the videre-host tests.
- Register the crate in the workspace
members list and pin version = "0.1.0".
Both consumers are L1 (guest SDK and host runtime), so the new crate lives entirely within L1. Gate-safe: nexum-chain-method matches none of videre|intent|venue|cow, so the venue-agnostic crate-graph scan stays green. Must build for wasm32-wasip2 (the guest links it); strum is wasm-safe.
Timing
Independent standalone PR. Because both consumers are L1 they ride the M5 L1 carve together, so the three-repo split is not a forcing function here (contrast nullislabs/nexum-runtime#52 / nullislabs/nexum-runtime#51, which span L1 and L2). Landing pre-split is still simpler - one repo, one Cargo.lock - but this can go in any milestone; parked in M8 as maintainability debt.
Acceptance criteria
ChainMethod is defined once, in nexum-chain-method; no hand-rolled copy remains in nexum-sdk or nexum-runtime.
chain::ChainMethod (guest) and nexum_runtime::host::component::ChainMethod (host) still resolve; no consumer edits beyond the import source.
- The crate builds for
wasm32-wasip2; check-venue-agnostic stays green.
The permitted JSON-RPC read-surface allowlist
ChainMethodis defined twice, byte-identical, and kept in sync by hand:crates/nexum-sdk/src/chain/method.rscrates/nexum-runtime/src/host/component/chain.rsBoth carry the same 19 variants in the same order with the same
#[strum(serialize = "...")]wire strings and the sameas_str(); the only difference is the guest also derivesHash. Nothing enforces that they stay equal. Add or rename a read method on one side and forget the other and they drift silently, with no compile error, because the WIT edge between them is a plainString. This is a security-relevant allowlist (it exists so signing and mutating methods cannot be represented and never cross the seam), so silent drift is exactly the failure we do not want.Unlike the
Chainnewtypes retired in #453, this is not an alloy duplication - alloy has no method-name enum, it passes bare&'static strliterals - so the fix is de-duplication, not substitution.Scope
One allowlist, two definitions, three consumer sites:
nexum-sdk/src/chain/method.rsnexum-sdk/src/chain/transport.rs(try_from,as_str), re-exported atchain::ChainMethodnexum-runtime/src/host/component/chain.rsnexum-runtime/src/host/provider_pool.rs; videre-host tests (zero_leak.rs,platform.rs) vianexum_runtime::host::component::ChainMethodApproach
Introduce a shared leaf crate
nexum-chain-method(model it on the existing shared guest/host leafnexum-status-body):pub enum ChainMethodwith the derive supersetDebug, Clone, Copy, PartialEq, Eq, Hash, EnumString, IntoStaticStrand theas_str(self) -> &'static strinherent method.strum(already in both consumers' graphs). The enum needs no allocation, so make the crate#![no_std]- future-proof and zero cost.nexum-sdkandnexum-runtimeboth depend on it; delete the two hand-rolled copies.chain::ChainMethod(already does), host re-exports it athost::component::ChainMethodsonexum_runtime::host::component::ChainMethodstill resolves for the videre-host tests.memberslist and pinversion = "0.1.0".Both consumers are L1 (guest SDK and host runtime), so the new crate lives entirely within L1. Gate-safe:
nexum-chain-methodmatches none ofvidere|intent|venue|cow, so the venue-agnostic crate-graph scan stays green. Must build forwasm32-wasip2(the guest links it); strum is wasm-safe.Timing
Independent standalone PR. Because both consumers are L1 they ride the M5 L1 carve together, so the three-repo split is not a forcing function here (contrast nullislabs/nexum-runtime#52 / nullislabs/nexum-runtime#51, which span L1 and L2). Landing pre-split is still simpler - one repo, one
Cargo.lock- but this can go in any milestone; parked in M8 as maintainability debt.Acceptance criteria
ChainMethodis defined once, innexum-chain-method; no hand-rolled copy remains innexum-sdkornexum-runtime.chain::ChainMethod(guest) andnexum_runtime::host::component::ChainMethod(host) still resolve; no consumer edits beyond the import source.wasm32-wasip2;check-venue-agnosticstays green.