rivet-dev / rivet-dev/dynamic-apps
Make agentOS runtime classifier content-based (match Linux exec semantics), not extension-based
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1k
- Forks
- 51
- Avg merge
- 6m
- Merged PRs (30d)
- 13
Description
Problem
The sidecar's JS-vs-WASM entrypoint classifier (resolve_javascript_command_entrypoint in crates/sidecar/src/execution.rs) decides which embedded runtime (V8 vs WASM) runs an agentOS package entrypoint by file extension: within the /opt/agentos package mount, .wasm → WASM, everything else → JavaScript.
This does not match Linux execve semantics, which classify by content (magic bytes / shebang), never by extension:
#!→ run the named interpreter\0asm(00 61 73 6d) → WebAssembly\x7fELF→ native ELF
The extension heuristic is only correct today because agentOS package bins happen to be exclusively JS or .wasm. It would misclassify an extensionless .wasm binary, or a non-JS script, that content sniffing would get right.
Why it is currently extension-based (context)
This was a deliberate shortcut taken while making /opt/agentos guest-native. The prior content-based path (resolve_javascript_command_entrypoint_inner → load_executable_script_preview) reads the host filesystem, but guest-native tar-mounted package content is never materialized on the host — so the shebang read always failed and fell through to the WASM branch (the original misroute that surfaced as an ENOEXEC/wrong-runtime launch). Reading the bytes guest-native needs vm.kernel.read_file (&mut self), while the classifier chain is &VmState and the sibling child-process path (resolve_javascript_child_process_execution) is &self / &VmState — threading &mut cascades through several signatures. The extension rule avoided that cascade.
Note the kernel layer already IS content-based / Linux-faithful: resolve_spawn_command → parse_shebang_command (crates/kernel/src/kernel.rs) reads the file, parses the #! line, and returns ENOEXEC on a bad one. Only the sidecar's JS-vs-WASM pre-classification is extension-based.
Proposed fix
Classify by content via the kernel VFS instead of by extension:
- leading bytes
\0asm(00 61 73 6d) → WASM - leading
#!→ defer to the shebang interpreter (as the kernel already does) - otherwise → JS
This requires a guest-side read in the classifier — either thread &mut VmState through the classifier chain (and the child-process path), or add an immutable VFS "peek" that reads the first N bytes without requiring &mut.
Location
crates/sidecar/src/execution.rs—resolve_javascript_command_entrypoint(the extension fast-path within the agentOS package mount) and its_innershebang fallback, which currently reads the host path rather than the guest VFS.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in crates/sidecar/src/execution.rs at resolve_javascript_command_entrypoint and its _inner fallback, then read crates/kernel/src/kernel.rs around resolve_spawn_command and parse_shebang_command. Trace the classifier and child-process call signatures, including their VmState references, before choosing how guest VFS bytes can be inspected. Done means agentOS entrypoints classify \0asm, shebang, and other content consistently with the stated Linux semantics without relying on extensions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100