refactor: extract networking ops behind traits, add mosnet OpenRPC backend, drop async_trait #61

Merged
delandtj merged 1 commit from feat/networking-ops-traits into development 2026-03-24 12:49:37 +00:00
Owner

This commit implements ADR-002 and ADR-003, restructuring the entire
networking layer to support pluggable backends — currently local shell
commands and mosnet's JSON-RPC 2.0 API over Unix socket.

== ADR-002: Drop async_trait crate ==

Replace the async_trait proc macro with explicit Pin<Box>
return types on all dyn-dispatched traits (NetworkBackend, StorageBackend).
The macro was hiding heap allocations and producing worse error messages.
On Rust 1.94, native async traits have been stable for 19 releases.

Audit finding: 7 of 8 trait method implementations do zero async work.
The entire networking layer is synchronous shell commands wrapped in
async sugar. Only VirtioFsStorage::prepare() genuinely awaits (a
tokio::time::sleep poll loop). The async signatures are retained because
the mosnet backend (ADR-003) performs real async I/O.

All trait methods now use unified lifetime 'a tying &self and &config
together, required because the boxed future captures both references.

== ADR-003: Extract networking operations behind traits ==

Three new async traits decompose networking into orthogonal concerns:

BridgeOps — bridge lifecycle (ensure_bridge, list_bridge_ports)
TapOps — TAP device lifecycle (create_tap, add_to_bridge, bring_up, delete_tap)
NatOps — port forwarding (add_port_forward, remove_port_forward)

IP allocation is deliberately excluded from TapOps. It is managed by
VmManager::allocate_bridge_ip() which tracks assigned IPs across VMs
via its own state store. mosnet has no visibility into these assignments.

Two backend implementations:

local/ — Current behavior preserved. Shell commands (ip, iptables)
moved from the old monolithic bridge.rs and tap.rs into
trait impls on LocalOps. Zero behavior change.

mosnet/ — JSON-RPC 2.0 client over /var/run/mosnet.sock. Calls
mosnet.bridge.add, mosnet.vm.create_tap (atomic: creates,
attaches, brings up in one call — add_to_bridge/bring_up
are no-ops), mosnet.vm.delete_tap, mosnet.nat.add_forward,
mosnet.nat.remove_forward. Self-contained protocol types
(~50 lines), no cross-project dependency.

TapNetwork is refactored from a unit struct to a composable struct
holding Arc, Arc, Arc.
Constructed via TapNetwork::local() or TapNetwork::new(bridge, tap, nat).
MyceliumNetwork now wraps a TapNetwork instance.

== Module structure ==

network/
traits.rs — NetworkBackend (high-level, unchanged API)
constants.rs — DEFAULT_BRIDGE_IP, SUBNET_PREFIX, GATEWAY, DEFAULT_BRIDGE
ops/mod.rs — BridgeOps, TapOps, NatOps traits + PortForwardRule
local/ — LocalOps implementing all three via shell commands
mosnet/ — MosnetOps implementing all three via JSON-RPC
client.rs — MosnetClient (tokio::sync::Mutex for Arc sharing)
protocol.rs — Request/Response/RpcError types
backends/
tap_backend.rs — TapNetwork (composable, holds dyn ops)
mycelium.rs — MyceliumNetwork (wraps TapNetwork)
namespace.rs — unchanged

== Files removed ==

network/bridge.rs — logic moved to local/bridge.rs + constants.rs
network/tap.rs — logic split into local/tap.rs, local/nat.rs, backends/tap_backend.rs
network/mycelium.rs — moved to backends/mycelium.rs
async-trait dep — removed from workspace and crate Cargo.toml

== Issues ==

== Verification ==

cargo check — clean
cargo clippy — clean
cargo test — 158 passed (1 pre-existing kernel test failure unrelated)

This commit implements ADR-002 and ADR-003, restructuring the entire networking layer to support pluggable backends — currently local shell commands and mosnet's JSON-RPC 2.0 API over Unix socket. == ADR-002: Drop async_trait crate == Replace the async_trait proc macro with explicit Pin<Box<dyn Future>> return types on all dyn-dispatched traits (NetworkBackend, StorageBackend). The macro was hiding heap allocations and producing worse error messages. On Rust 1.94, native async traits have been stable for 19 releases. Audit finding: 7 of 8 trait method implementations do zero async work. The entire networking layer is synchronous shell commands wrapped in async sugar. Only VirtioFsStorage::prepare() genuinely awaits (a tokio::time::sleep poll loop). The async signatures are retained because the mosnet backend (ADR-003) performs real async I/O. All trait methods now use unified lifetime 'a tying &self and &config together, required because the boxed future captures both references. == ADR-003: Extract networking operations behind traits == Three new async traits decompose networking into orthogonal concerns: BridgeOps — bridge lifecycle (ensure_bridge, list_bridge_ports) TapOps — TAP device lifecycle (create_tap, add_to_bridge, bring_up, delete_tap) NatOps — port forwarding (add_port_forward, remove_port_forward) IP allocation is deliberately excluded from TapOps. It is managed by VmManager::allocate_bridge_ip() which tracks assigned IPs across VMs via its own state store. mosnet has no visibility into these assignments. Two backend implementations: local/ — Current behavior preserved. Shell commands (ip, iptables) moved from the old monolithic bridge.rs and tap.rs into trait impls on LocalOps. Zero behavior change. mosnet/ — JSON-RPC 2.0 client over /var/run/mosnet.sock. Calls mosnet.bridge.add, mosnet.vm.create_tap (atomic: creates, attaches, brings up in one call — add_to_bridge/bring_up are no-ops), mosnet.vm.delete_tap, mosnet.nat.add_forward, mosnet.nat.remove_forward. Self-contained protocol types (~50 lines), no cross-project dependency. TapNetwork is refactored from a unit struct to a composable struct holding Arc<dyn BridgeOps>, Arc<dyn TapOps>, Arc<dyn NatOps>. Constructed via TapNetwork::local() or TapNetwork::new(bridge, tap, nat). MyceliumNetwork now wraps a TapNetwork instance. == Module structure == network/ traits.rs — NetworkBackend (high-level, unchanged API) constants.rs — DEFAULT_BRIDGE_IP, SUBNET_PREFIX, GATEWAY, DEFAULT_BRIDGE ops/mod.rs — BridgeOps, TapOps, NatOps traits + PortForwardRule local/ — LocalOps implementing all three via shell commands mosnet/ — MosnetOps implementing all three via JSON-RPC client.rs — MosnetClient (tokio::sync::Mutex for Arc sharing) protocol.rs — Request/Response/RpcError types backends/ tap_backend.rs — TapNetwork (composable, holds dyn ops) mycelium.rs — MyceliumNetwork (wraps TapNetwork) namespace.rs — unchanged == Files removed == network/bridge.rs — logic moved to local/bridge.rs + constants.rs network/tap.rs — logic split into local/tap.rs, local/nat.rs, backends/tap_backend.rs network/mycelium.rs — moved to backends/mycelium.rs async-trait dep — removed from workspace and crate Cargo.toml == Issues == - https://forge.ourworld.tf/geomind_code/my_hypervisor/issues/65 == Verification == cargo check — clean cargo clippy — clean cargo test — 158 passed (1 pre-existing kernel test failure unrelated)
refactor: extract networking ops behind traits, add mosnet OpenRPC backend, drop async_trait
Some checks failed
Unit and Integration Test / test (push) Failing after 15s
cc98a1c6c8
This commit implements ADR-002 and ADR-003, restructuring the entire
networking layer to support pluggable backends — currently local shell
commands and mosnet's JSON-RPC 2.0 API over Unix socket.

== ADR-002: Drop async_trait crate ==

Replace the async_trait proc macro with explicit Pin<Box<dyn Future>>
return types on all dyn-dispatched traits (NetworkBackend, StorageBackend).
The macro was hiding heap allocations and producing worse error messages.
On Rust 1.94, native async traits have been stable for 19 releases.

Audit finding: 7 of 8 trait method implementations do zero async work.
The entire networking layer is synchronous shell commands wrapped in
async sugar. Only VirtioFsStorage::prepare() genuinely awaits (a
tokio::time::sleep poll loop). The async signatures are retained because
the mosnet backend (ADR-003) performs real async I/O.

All trait methods now use unified lifetime 'a tying &self and &config
together, required because the boxed future captures both references.

== ADR-003: Extract networking operations behind traits ==

Three new async traits decompose networking into orthogonal concerns:

  BridgeOps — bridge lifecycle (ensure_bridge, list_bridge_ports)
  TapOps    — TAP device lifecycle (create_tap, add_to_bridge, bring_up, delete_tap)
  NatOps    — port forwarding (add_port_forward, remove_port_forward)

IP allocation is deliberately excluded from TapOps. It is managed by
VmManager::allocate_bridge_ip() which tracks assigned IPs across VMs
via its own state store. mosnet has no visibility into these assignments.

Two backend implementations:

  local/  — Current behavior preserved. Shell commands (ip, iptables)
             moved from the old monolithic bridge.rs and tap.rs into
             trait impls on LocalOps. Zero behavior change.

  mosnet/ — JSON-RPC 2.0 client over /var/run/mosnet.sock. Calls
             mosnet.bridge.add, mosnet.vm.create_tap (atomic: creates,
             attaches, brings up in one call — add_to_bridge/bring_up
             are no-ops), mosnet.vm.delete_tap, mosnet.nat.add_forward,
             mosnet.nat.remove_forward. Self-contained protocol types
             (~50 lines), no cross-project dependency.

TapNetwork is refactored from a unit struct to a composable struct
holding Arc<dyn BridgeOps>, Arc<dyn TapOps>, Arc<dyn NatOps>.
Constructed via TapNetwork::local() or TapNetwork::new(bridge, tap, nat).
MyceliumNetwork now wraps a TapNetwork instance.

== Module structure ==

  network/
    traits.rs         — NetworkBackend (high-level, unchanged API)
    constants.rs      — DEFAULT_BRIDGE_IP, SUBNET_PREFIX, GATEWAY, DEFAULT_BRIDGE
    ops/mod.rs        — BridgeOps, TapOps, NatOps traits + PortForwardRule
    local/            — LocalOps implementing all three via shell commands
    mosnet/           — MosnetOps implementing all three via JSON-RPC
      client.rs       — MosnetClient (tokio::sync::Mutex for Arc sharing)
      protocol.rs     — Request/Response/RpcError types
    backends/
      tap_backend.rs  — TapNetwork (composable, holds dyn ops)
      mycelium.rs     — MyceliumNetwork (wraps TapNetwork)
    namespace.rs      — unchanged

== Files removed ==

  network/bridge.rs   — logic moved to local/bridge.rs + constants.rs
  network/tap.rs      — logic split into local/tap.rs, local/nat.rs, backends/tap_backend.rs
  network/mycelium.rs — moved to backends/mycelium.rs
  async-trait dep     — removed from workspace and crate Cargo.toml

== Verification ==

  cargo check   — clean
  cargo clippy  — clean
  cargo test    — 158 passed (1 pre-existing kernel test failure unrelated)
delandtj force-pushed feat/networking-ops-traits from cc98a1c6c8
Some checks failed
Unit and Integration Test / test (push) Failing after 15s
to 138e06bffd
All checks were successful
Unit and Integration Test / test (push) Successful in 1m55s
2026-03-24 12:12:52 +00:00
Compare
delandtj force-pushed feat/networking-ops-traits from 138e06bffd
All checks were successful
Unit and Integration Test / test (push) Successful in 1m55s
to a13014c8d2
All checks were successful
Unit and Integration Test / test (push) Successful in 1m53s
2026-03-24 12:22:52 +00:00
Compare
delandtj force-pushed feat/networking-ops-traits from a13014c8d2
All checks were successful
Unit and Integration Test / test (push) Successful in 1m53s
to 33bbe4353c
All checks were successful
Unit and Integration Test / test (push) Successful in 1m49s
2026-03-24 12:34:18 +00:00
Compare
@ -0,0 +13,4 @@
- **Hypervisor**: Cloud Hypervisor (external VMM process, controlled via REST API over Unix socket)
- **Guest init**: Static musl binary (x86_64-unknown-linux-musl target)
- **CLI framework**: clap (derive macros)
- **Error handling**: thiserror (ChvmError enum)
Owner

ChvmError is MyHyoervisorError now

`ChvmError` is `MyHyoervisorError` now
rawdaGastan marked this conversation as resolved
delandtj force-pushed feat/networking-ops-traits from 33bbe4353c
All checks were successful
Unit and Integration Test / test (push) Successful in 1m49s
to dcbf7ab534
All checks were successful
Unit and Integration Test / test (push) Successful in 2m29s
2026-03-24 12:46:30 +00:00
Compare
delandtj merged commit c2cd54fcb2 into development 2026-03-24 12:49:37 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
geomind_code/my_hypervisor!61
No description provided.