Skip to content

nvx-rs: run the control session on the shared NVXS session's host role - #641

Merged
Enrique Saurez (esaurez) merged 1 commit into
devfrom
esaurez/a1-host-h4-nvxs-session-20261020
Oct 21, 2026
Merged

Enrique Saurez (esaurez) merged 1 commit into
devfrom
esaurez/a1-host-h4-nvxs-session-20261020

Conversation

@esaurez

@esaurez Enrique Saurez (esaurez) commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

The host's control session parsed the control endpoint's NVXS records itself: it checked each record's header, tracked the broker's identity and sequence numbers, and did all of it inline with NVXC decoding. This PR makes nvx::host::session::ControlSession the NVXC adapter over the host role of the shared NVXS session from #627, nvx::host::nvxs::HostConnection. Each NVXC frame travels in one DATA record, as before. The wire format is unchanged, and so are the session's failures, and the CLI's messages, for everything that OpenVMM's broker sends.

Part of #620
Refs #535

What changed

  • host::session: ControlSession wraps a HostConnection.
    • attach hands the capability to HostConnection::attach, which waits while the broker sends WAIT.
    • Requests send each frame in one DATA record, and refuse a frame that one record cannot hold, with the message that they gave before.
    • Responses decode the payload of each DATA record as one frame.
  • Failures: each failure of the connection becomes the SessionError that the session reported before: a timeout, a closed stream, a refused capability, a reset or an I/O error. A protocol violation keeps the message of the check that it fails: a malformed or oversized header, a payload in WAIT or READY, an empty DATA record, and otherwise an invalid attach response or data record. A stream that ends inside a record has closed, as before. The CLI's Python-parity messages are unchanged, and so are its golden scenarios.
  • Test brokers: they now send the records that they send as the shared session requires.
    • The fake OpenVMM refuses a capability with an ERROR record that carries the authentication code.
    • The managed agent's loopback broker resets the host with the session's next epoch, at the next sequence number of the host's leg. It no longer discards the records that the host has not read before the RESET.
  • Tests: the session's tests run on a scripted transport. New tests cover WAIT before READY, a coded ERROR, every attach response and data record that the protocol does not allow, a session that a violation ended, and frames that travel in one record each. The CLI's reset test sends a RESET that the protocol allows.
  • Docs: the host module's overview and the crate README's rows for nvx::host::nvxs and nvx::host::session.

Behavior

OpenVMM's broker sends the host WAIT, READY and DATA. It refuses a capability by closing the stream, and never sends the host an ERROR or a RESET. The shared session checks a few things that the control session didn't, in records that the broker never sends:

  • WAIT and READY must carry the sequence numbers of the host's leg, from 0.
  • An ERROR must carry the authentication code. An ERROR without a code used to count as a refused capability, and now fails as an invalid attach response.
  • A RESET must name the session's instance, a later epoch, and the next sequence number. Any RESET used to count as a reset.
  • A failure that leaves the stream unusable now ends the session, and later calls fail. That includes a reset, a record that the protocol does not allow, and a write that times out. A read that times out still keeps the session and any partial record, so Instance can poll with short deadlines.
  • A record with several faults may report a different one of them than before.

Validation

At 38d7b9b, this PR's commit rebased onto dev at b47a9f8. The only conflict was in the host module's overview, next to the new store entry.

  • WHP, Windows 11, with the pinned OpenVMM and kernel, dev's guest artifacts, whose managed agent runs on the shared session's guest role since nvx-rs: run the managed agent on the shared NVXS session's guest role #639, and this PR's nvx-rs CLI as NVX_RS:
    • python scripts\nvx.py test-microvm --backend whp, the default suite. All 42 scenario runs pass. That includes managed-lifecycle, which builds and runs the library's managed_lifecycle test with this change.
    • The public sandbox scenarios, which run only when named: --scenario managed-exec-config --scenario sandbox-lifecycle. Both pass.
  • Windows 11 and Linux (Ubuntu, x86-64), in nvx-rs with NVX_BUNDLE=skip:
    • cargo fmt --all --check
    • cargo clippy --locked --all-targets --all-features -- -D warnings
    • cargo clippy --locked --lib --no-default-features -- -D warnings
    • cargo test --locked --all-features, then cargo test --locked
    • the synthetic-release bundled test
    • cargo doc --locked --no-deps --all-features, with RUSTDOCFLAGS=-D warnings
    • cargo +1.89 check --locked --all-targets --all-features
  • Linux only:
    • cargo check --locked --all-targets --all-features --target aarch64-apple-darwin
    • python3 scripts/nvx.py check-guest-binaries --toolchain 1.95.0
    • build-guest-binaries, then test-guest-binaries
  • The runtime adapter's configurations, as check-nvx-rs runs them: protocol-only and host-only on both OSes, and guest-only on Linux.
  • aci_edge_sandboxes, on both OSes: its fmt, Clippy, test, doc and MSRV steps, and test-agent-bundling. On Linux, its macOS check too.
  • python scripts/nvx.py verify and python scripts/check_markdown_links.py.

Everything passes except one environmental failure on Linux, where my shell runs as root. The CLI golden test's caller-owned-share scenario, with 20 differences, refuses its temporary share because UID 0 owns it, as --mount-owner caller requires. dev fails the same way there, and CI runs the test unprivileged.

I didn't run KVM or MSHV myself: this machine's nested KVM can't boot OpenVMM's built-in CPU profile. CI runs the KVM, MSHV and WHP matrix, which adds managed-lifecycle on the Ubuntu and Azure Linux guests.

Copilot AI balanced review requested due to automatic review settings October 20, 2026 16:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

Static review identified no blocking issues in the delegation, error translation, or test-broker updates; tests were not rerun.

No open issues 🎉

What changed in this PR

Moves the host control session onto the shared NVXS HostConnection, keeping NVXC framing in the adapter and preserving broker-compatible behavior.

Changes:

  • Delegates transport and session validation to HostConnection, translating failures into existing control-session errors.
  • Updates test brokers and expands protocol, timeout, and framing coverage.
  • Documents the shared-session architecture.
File Description
nvx-rs/​src/​testing/​fake_openvmm.rs Sends authentication ERROR records with the required code.
nvx-rs/​src/​host/​session.rs Adapts NVXC over HostConnection and expands tests.
nvx-rs/​src/​host/​mod.rs Documents the host session layering.
nvx-rs/​src/​host/​cli/​session/​tests.rs Uses a protocol-valid RESET record.
nvx-rs/​src/​host/​cli/​session.rs Clarifies the Python-parity comment.
nvx-rs/​src/​guest/​managed_agent/​loopback.rs Preserves queued records and sequences RESET correctly.
nvx-rs/​README.md Updates the host module descriptions.

Review effort: Balanced

ControlSession parsed the control endpoint's NVXS records itself, and
tracked the broker's identity and sequence numbers inline with NVXC
decoding. Make it the NVXC adapter over the host role of the shared
session, nvx::host::nvxs::HostConnection: each NVXC frame travels in one
DATA record, and a failure keeps its variant and, for a record with one
fault, its message, so that the CLI reports what it reported before.

The shared session checks more than the control session did: the
sequence numbers of WAIT and READY, the code that an ERROR carries, and
the identity and sequence number of a RESET. A failure that leaves the
stream unusable, such as a reset, a record that the protocol does not
allow, or a write that times out, now ends the session, and later calls
fail.

OpenVMM's broker refuses a capability by closing the stream, and never
sends the host a RESET. The fake OpenVMM refuses one with an ERROR
record, and the managed agent's loopback broker resets the host; both
now send these records as the shared session requires: an ERROR carries
its code, and a RESET to the host carries the session's next epoch at
the host leg's next sequence number.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 086f45a6-c39c-4704-85ef-1fa7f8b8b672
Copilot AI balanced review requested due to automatic review settings October 21, 2026 00:34
@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/a1-host-h4-nvxs-session-20261020 branch from 144fc51 to 38d7b9b Compare October 21, 2026 00:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

Static review found no blocking issues in the delegation, error translation, or test updates; tests were not run here.

No open issues 🎉

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants