Skip to content

nvx-rs: run the managed agent on the shared NVXS session's guest role - #639

Merged
Enrique Saurez (esaurez) merged 2 commits into
devfrom
esaurez/a1-guest-g6-nvxs-session-20261020
Oct 20, 2026
Merged

Enrique Saurez (esaurez) merged 2 commits into
devfrom
esaurez/a1-guest-g6-nvxs-session-20261020

Conversation

@esaurez

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

Copy link
Copy Markdown
Contributor

The managed agent decoded the control session's NVXS records itself: it checked each record's header, tracked the broker's sequence numbers, the session's identity and the receive credit, and did all of it inline with NVXC decoding. This PR moves the agent onto the guest role of the shared NVXS session from #627, nvx::guest::nvxs::GuestConnection. The agent now keeps only the NVXC frames that DATA records carry. The wire format is unchanged, and so are the agent's records and diagnostics for everything that OpenVMM's broker sends.

Part of #619
Refs #535

What changed

  • guest::managed_agent::session: Session wraps a GuestConnection over the control console's descriptor.
    • next_event returns the broker's RESET or a DATA payload.
    • acknowledge answers a RESET with NVX's receive window.
    • accept_request decodes the NVXC request, then credits the payload back before the agent serves it, as before.
    • send_app sends each frame in one DATA record, and refuses a frame that one record cannot hold.
  • Diagnostics: the session classifies each record that the shared session refuses by the stage at which the agent reported it before, with the same statuses: outer-read, outer-type, reset-ack, data-sequence, data-identity, data-application or data-credit, then data. The diagnostics that the host and the tests read stay the same.
  • dispatch and exec take events from the session instead of decoding records. Their stage reporting is now in report_failure and report_control_failure.
  • protocol::nvxs: SessionError::SequenceOverflow names the type of the record that ran out of sequence numbers, as Unexpected and MissingIdentity do, so that the agent can tell DATA from a RESET at the last sequence number.
  • guest::sys::FileDescriptor implements std::io::Write, with one write system call per call and no buffering, so the connection writes the control console directly.
  • Tests: the dispatch tests' harness can now reset and close the stream. New tests pin the stage and status of each refused record, DATA before the first RESET, a stream that ends inside a record or between records, a RESET between requests, which starts a new epoch, and DATA at the last sequence number. The existing diagnostic tests pass unchanged.
  • Docs: the guest module's conventions mention the std::io::Write implementation, and the crate README's nvx::guest::nvxs row names the managed agent as a user.

Behavior

The shared session checks a few things that the agent didn't, in records that OpenVMM's broker never sends:

  • A RESET without an instance ID, or one that neither names a new instance nor advances the epoch, or one at the last sequence number, now ends the session at reset-ack instead of being acknowledged.
  • DATA at the last sequence number, which the agent took and followed with sequence number 0, now ends the session at data-sequence, because it leaves no sequence number for the broker's next record.
  • DATA before the first RESET reports outer-type 5, instead of a sequence or identity failure.
  • DATA beyond the receive window reports a new stage, data-window. The agent credits each record back before it serves it, so the broker never sends such a record.
  • A record with several faults may report a different one of them than before.

Sizes

I built the guest binaries and the Alpine initramfs natively on Linux, the same way at #629's head, d4ab821, which dev merged as 401c95a with the same guest inputs, and with this change on top of it. The binaries use the guest profile, which strips them.

Artifact Before After Change
nvx-managed-agent 467,424 B 471,520 B +4,096 B
initramfs.cpio.gz (Alpine) 8,197,981 B 8,199,693 B +1,712 B

The other guest binaries keep their sizes.

Not in this PR

#619 also gives G6 the runtime adapter's guest half: the adapter's own framing over the guest role. That follows in #640, stacked on this one.

Validation

  • WHP, Windows 11, with the Alpine initramfs built from this change, the pinned OpenVMM and kernel, and the nvx-rs CLI built from the same tree:
    • python scripts\nvx.py test-microvm --backend whp, the default suite. Every scenario passes. That includes managed-lifecycle, which covers authenticated sessions, reconnects across a reset, refused working directories, the workload identity and stop.
    • The public sandbox scenarios, which run only when named: --scenario managed-exec-config --scenario sandbox-lifecycle. Both pass.
    • On this machine, managed-lifecycle's test sometimes cannot connect to OpenVMM's console port, though the agent's part of the test completes. It fails that way with dev's own initramfs too, in 5 of 8 runs, so where it stopped the suite, I ran the remaining scenarios on their own, and managed-lifecycle again until it passed. One earlier run on this change also stopped when the host's IPv6 egress probe server failed, in a scenario whose guest runs shell probes without the agent; that scenario passes in every other run.
  • 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 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 the public sandbox acceptance and managed-lifecycle on the Ubuntu and Azure Linux guests, built from this change.

Copilot AI balanced review requested due to automatic review settings October 20, 2026 15:53

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

No blocking issues were identified in the session integration, descriptor writes, or updated control paths and regression tests.

No open issues 🎉

What changed in this PR

Moves the managed agent onto the shared NVXS guest session from #627, advancing #619 while retaining NVXC framing and broker-facing diagnostics.

Changes:

  • Replaces inline NVXS handling with GuestConnection.
  • Adapts dispatch and execution to session events and adds unbuffered descriptor writes.
  • Adds regression tests for rejected records, stream closure, and resets; updates documentation.
File Description
nvx-rs/​src/​guest/​sys/​fd.rs Implements unbuffered std::io::Write.
nvx-rs/​src/​guest/​mod.rs Documents descriptor write conventions.
nvx-rs/​src/​guest/​managed_agent/​session.rs Wraps the shared session and maps diagnostics.
nvx-rs/​src/​guest/​managed_agent/​mod.rs Updates the session module comment.
nvx-rs/​src/​guest/​managed_agent/​exec.rs Handles session events during execution.
nvx-rs/​src/​guest/​managed_agent/​dispatch.rs Uses session events and expands regression tests.
nvx-rs/​README.md Documents managed-agent use of GuestConnection.

Review effort: Balanced

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.

🔵 Needs a closer look

The combined privileged fork/exec changes and control-session reset handling warrant final human review.

No open issues 🎉

Review effort: Balanced

The managed agent decoded the control session's NVXS records itself, and
tracked the broker's sequence numbers, the session's identity and the
receive credit inline with NVXC decoding. Move it onto the guest role of
the shared session, nvx::guest::nvxs::GuestConnection, so that the agent
keeps only the NVXC frames that DATA records carry.

Records that the shared session refuses report the stages and statuses
that the agent reported before. The session also refuses records that
OpenVMM's broker never sends: a RESET that neither names a new instance
nor advances the epoch, DATA before the first RESET, and DATA beyond the
receive window.

FileDescriptor implements std::io::Write, without buffering, so that the
connection writes the control console directly.

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 20, 2026 22:46
@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/a1-guest-g6-nvxs-session-20261020 branch from dd171e2 to 6af062f Compare October 20, 2026 22:46

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

The remaining issue affects only sequence-exhaustion diagnostics and does not compromise session termination.

1 open issue

Review effort: Balanced

Comment thread nvx-rs/src/guest/managed_agent/session.rs Outdated
The shared NVXS session refuses a record that takes the last sequence
number of its direction, because none is left for the next record, but
its SequenceOverflow error did not say which record that was. The
managed agent therefore reported DATA at the last sequence number, which
can follow a RESET at the second-to-last one, at the stage of a refused
RESET, reset-ack, and without exec-record while a workload ran.

SequenceOverflow now carries the type of the record that ran out of
sequence numbers, as Unexpected and MissingIdentity do, and the agent
reports DATA at the last sequence number as DATA out of sequence, at
data-sequence with the record's sequence number. The C agent took such a
record and expected sequence number 0 next.

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 20, 2026 23:12

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 unresolved defects, and regression tests cover the integration’s key failure paths; validation was not rerun.

No open issues 🎉

1 resolved since last review

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.

2 participants