Repository navigation
nvx-rs: a shared credit and stdio engine for executions #623
Description
Parent: #535. Phase: A5. Needs A1-guest's exec, status and cgroup interfaces. Delivers: #537's engine.
Goal. One framing-free credit and stdio engine gives bounded in-flight memory with no total output cap. Independently framed adapters drive it, and so will NVXC v2. NVX's cutover to v2, its admission queue, the guest build identity and #581's decoder moved to #816.
Today (dev ff95973):
- Both NVX host paths use NVXC v1 (
protocol/app.rs:13). - The guest fixes stdin to
/dev/null, kills a workload whose combined output exceeds 1 MiB, and answers every request except the matching CANCEL with BUSY during an exec, Stop included (managed_agent/exec.rs:387-397). - The CLI collects output in memory (
cli/session.rs:407-449);host::instanceforwards it through synchronous sinks under the same cap. - Start probes FEATURES.
- EXEC's fixed header (nvx-rs: decode the EXEC header once for both payload decoders #603) and the MAPS entries (nvx-rs: decode the MAPS entries once for both payload decoders #616) are each decoded once; EXEC body parsing is still duplicated.
Scope:
- One shared engine: a std-only module (for example
nvx::exec) that builds without default features or Tokio. A1's guest driver supplies the Linux process and descriptor mechanics; hosts may be synchronous or asynchronous.- Types: execution and generation key, stream, credit pool, byte or frame limits, maximum chunk, termination cause, caller-reaped status, settlement result and terminal outcome.
- Inputs: credit grant, data, EOF, consumed or written acknowledgements, launch result, observed exit, cancel, drop, disconnect, reset, a monotonic deadline tick and the settlement result.
- Outputs: started; bounded read and write allowances; output, data, EOF and credit; termination and settlement requests; exactly one terminal outcome; and whether the execution is reusable or fenced.
- Driving: non-blocking transitions, bounded action retrieval and the next deadline. No wire types, global admission or hidden reaper.
- Credit pools: NVXC uses one bounded window per stream. Another adapter may charge frames and share one pool between stdout and stderr. The engine alone does the credit arithmetic: initial grants, partial consumption, over-credit refusal, late messages and an aggregate memory bound.
- EOF, order and completion:
- Stdin EOF follows every accepted input byte and doesn't close the control direction. A workload that closes stdin early never leaves the producer waiting.
- Each stream keeps its order. A normal EXIT follows both streams' final data and EOF; there is no order across streams.
- Cancel, timeout, reset, drop, disconnect and Stop progress whatever the streams are doing. Pending output drains for a bounded grace, then is discarded with a truncation flag in the terminal record. Normal completion is always lossless.
- The logical outcome, delivery completion and settlement are separate. Admission reopens only after the descendants and inherited pipe writers have settled. A failed cleanup fences the execution. A caller may request forced teardown of the verified VM; the CLI's Stop never does. Leader exit, a process-group signal or an outer ACK never counts as settlement.
Moved to #816: #581's shared EXEC body decoder (formerly scope 1); NVXC v2, Ready and its validation, and the CLI's streaming and the other callers (scopes 4, 5 and 7, #821); the guest build ID and packaging (scopes 5 and 8, #820); the CLI's admission (scope 6, #819); and the bundled re-pin (#624).
Not in scope: #513 (fixed by merged PR #585), restore, TTY and resizing, arbitrary signals, exec concurrency in NVX's adapter, other adapters' admission policy, Python-parity helpers, and a redesign of aci_edge_sandboxes.
Acceptance:
- Deterministic engine tests cover window edges, tiny and maximal frames, invalid grants, partial consumption, EOF, early stdin close, late or stale messages and terminal races.
- A slow or blocked stdin, stdout or stderr never stops timers, cancel, reset or Stop, including under transport backpressure and an exhausted outer window.
- Normal streams are byte-exact beyond 1 MiB, stdin exceeds one window, and memory stays bounded without collecting the whole test stream.
- Descendant, reparenting,
setsid, inherited-pipe and cleanup-failure tests prove settlement before readmission; unrelated children and other adapters' concurrent executions are untouched. - The test adapter uses its own framing and a frame-counted shared output pool, and runs several executions at once without enabling concurrency in NVX's adapter.
- The engine's cases of consumer-contract scenario C2 pass through the test adapter; NVX's adapter covers C1 and C2 with nvx-rs: move NVX's adapter to NVXC v2 on the shared engine, with a validated Ready #821.
PR stack:
- The shared engine on A1's driver seam, with stdio and control that stay live under blocked streams, and the test adapter's tests (nvx-rs: add the shared execution engine and its guest binding #748, tests: run executions in the runtime adapter through the shared engine #784).
The earlier PRs 1 and 3–5 moved to #816: #581, #819, #820 and #821.
Carries over: #537's engine, reusing #358 and #359's regression tests. #581 and #537's NVX stdin and output-cap part moved to #816.
Meta A: #535. Phases: A1-guest #619, A1-host #620, A3 #621, A4 #622, A5 #623. NVX's binaries and tools, and the final acceptance: #816.
Activity
[-]nvx-rs: exec v2 with credit-based streaming[/-][+]nvx-rs: a shared credit and stdio engine for executions[/+]esaurez commentedon Oct 23, 2026
The shared engine merged in #748, and the test adapter runs executions through it (#784). #581, NVXC v2, the CLI's admission and the guest build identity moved to #816.