Source commit: 309831e1e021f962c118452336776fd9a94025f9 Public tree identity: sha256:6fa95c1745579bd6256dbeb3d476db0b07c2f24aa9213b0f7783ce1adfc8aca5
31 KiB
Disasmer Specification
Status: draft design spec
Name: Disasmer, short for distributed Wasm. The name intentionally sounds a bit like Wasmer and a bit like “disaster.”
Primary goal: make distributed execution feel like normal multithreaded programming and normal source-level debugging.
Disasmer is a distributed WebAssembly runtime that lets multiple real machines behave like one logical machine. A program runs as one Virtual Process with many Virtual Threads. Each virtual thread runs on exactly one real machine at a time, but different virtual threads, memory spaces, filesystems, and build tasks may live on different machines.
The core promise:
Developers debug Disasmer programs like ordinary multithreaded programs.
The runtime handles the distributed reality underneath.
Disasmer is also intended to support a build-system use case: one build is a virtual process, and build steps are virtual threads/tasks that may run in containers or Nix-defined environments on different operating systems and architectures.
1. Goals
Disasmer should provide:
distributed Wasm execution
normal VS Code debugging
virtual threads across machines
distributed/sharded memory spaces
cluster-wide suspend/resume
per-thread virtual filesystems
container/Nix-backed execution environments
single-source-file distributed builds
explicit artifact publication
low-latency reliable control-plane coordination
The debugger should see:
one process
many threads
normal stacks
normal locals
normal source files
normal breakpoints
normal memory inspection
The optional Disasmer inspector may expose distributed internals, but the default debugging experience should remain normal.
2. Non-goals for the MVP
The MVP should not attempt:
transparent raw remote pointers
live hot-swapping arbitrary Wasm/Rust stack frames
checkpointing live sockets
checkpointing arbitrary external OS state
consensus on every memory access
eagerly copying every byte of cluster memory on each breakpoint
transparent survival of lost non-replicated nodes
full Kubernetes/CI-platform replacement semantics
The MVP should favor simple, explicit, debuggable semantics over maximum transparency.
3. Core terminology
Cluster
A set of real (or virtual) machines participating in one Disasmer runtime.
Coordinator
The control-plane entry point for the cluster.
Usually reachable through DNS, e.g. cluster.example.com.
Node
A real machine participating in the cluster.
Node Runtime
The Disasmer runtime process running on a node.
It hosts virtual threads/tasks, memory shards, filesystem data, debug services,
environment caches, and node-to-node communication.
Virtual Process
The logical process seen by the program and debugger.
Internally, it may span many nodes.
Virtual Thread
A debugger-visible logical execution unit.
For ordinary Disasmer programs, this is usually a Wasm execution context.
For build-system use, it may be backed by a containerized process, sandboxed task,
or platform-specific build command.
Virtual Task
Synonym for Virtual Thread when the unit of work is task-like rather than
thread-like. The debugger still sees it as a thread.
Memory Space
A logical addressable memory region.
It may be owned by any node and accessed remotely by other threads.
Filesystem Overlay
A per-thread writable virtual filesystem layer.
Environment Resource
A Containerfile, Dockerfile, Nix flake, or equivalent project resource that
defines where and how a virtual thread/task should run.
Disasmer Bundle / Virtual Binary
The packaged executable artifact containing Wasm modules, manifests, debug
metadata, environment definitions, and selected input files.
Checkpoint
A saved execution image containing enough state to resume a virtual thread.
Debug Epoch
A globally coordinated stopped state used for debugging.
4. High-level architecture
VS Code / Debugger
-> DAP / LLDB / custom Disasmer debug adapter
-> Disasmer debug endpoint
-> Coordinator
-> Node Runtimes
Cluster connectivity:
new node starts
-> connects to coordinator through DNS
-> authenticates
-> receives membership/configuration
-> establishes peer links to other nodes
Node-to-node communication should prefer:
QUIC direct link
-> NAT punching where possible
-> relay fallback if direct connectivity fails
The coordinator is not a hypervisor. It is the cluster control plane. It manages membership, identity, placement, debug epochs, metadata, and failure decisions. The data plane should be mostly direct node-to-node traffic.
5. Project layout and resource discovery
Disasmer should make environment resources and input files first-class project resources.
Example project:
my-project/
src/
main.rs
Cargo.toml
Cargo.lock
envs/
linux/Containerfile
windows/Containerfile
macos/flake.nix
macos/flake.lock
inputs/
config.json
schema.sql
test-data/
example-a.txt
example-b.txt
disasmer.toml # optional; used only for overrides/disambiguation
Default discovery rules:
envs/<name>/Containerfile
-> defines environment <name> of kind container
envs/<name>/Dockerfile
-> defines environment <name> of kind container
envs/<name>/flake.nix
-> defines environment <name> of kind nix-flake
inputs/**
-> included in the bundle's default VFS seed
src/**, Cargo.toml, Cargo.lock
-> included for reproducible debugging/building when configured
The source code should be able to reference discovered environments by name:
env!("linux")
env!("windows")
env!("macos")
Adding this file:
envs/linux/Containerfile
should make this possible in source:
spawn::task(compile).env(env!("linux"))
No image name, host VM, registry, CI runner, or YAML platform configuration should be required in the source code.
6. Environment resources
An Environment Resource defines the execution environment for a virtual thread/task.
Supported MVP environment kinds:
container
Built from Containerfile or Dockerfile.
Produces or references an OCI-compatible image or equivalent runtime image.
nix-flake
Resolved from flake.nix and flake.lock.
Produces a reproducible command environment, dev shell, package closure, or
runtime root depending on project configuration.
Containerfiles and Nix flakes are not identical concepts. Disasmer treats both as ways to produce a normalized Execution Environment:
Execution Environment
name
kind
digest
platform constraints
runtime capabilities
build/resolution recipe
cached materialized form on capable nodes
Example normalized environments:
environment linux
kind: container
recipe: envs/linux/Containerfile
context: envs/linux
digest: sha256:...
requires: os=linux, arch=x86_64
environment macos
kind: nix-flake
recipe: envs/macos/flake.nix
lock: envs/macos/flake.lock
digest: sha256:...
requires: os=macos, arch=arm64
Environment materialization should be cached on nodes:
environment digest -> local image / Nix store path / sandbox root
The virtual binary should not embed full container images by default. It should embed recipes, manifests, hashes, lock files, and selected context files. Full vendoring should be an explicit mode:
disasmer build --vendor-envs
Possible vendored data:
OCI layers
Nix closure
prebuilt environment archive
7. Disasmer bundle / virtual binary
A Disasmer bundle is the packaged executable artifact.
It contains:
Wasm module(s)
Wasm component metadata
source maps / DWARF / debug metadata
Disasmer manifest
environment resource manifests
selected environment recipe files
selected environment context files
selected VFS seed files
source snapshot when enabled
content hashes
capability declarations
Conceptual bundle layout:
app.dis
/modules/main.wasm
/debug/main.dwarf
/manifest/disasmer.json
/envs/linux/Containerfile
/envs/windows/Containerfile
/envs/macos/flake.nix
/envs/macos/flake.lock
/inputs/config.json
/inputs/schema.sql
/inputs/test-data/**
/source/src/main.rs
/source/Cargo.toml
/source/Cargo.lock
The bundle is content-addressed where possible. Every resource that affects execution should contribute to the bundle digest.
The coordinator schedules bundles, not loose files.
8. Execution model
A Disasmer application runs as one Virtual Process.
A virtual process contains:
virtual threads/tasks
memory spaces
filesystem overlays
runtime metadata
debug metadata
checkpoint metadata
environment bindings
Each virtual thread runs on exactly one node at a time:
thread T1 -> node A
thread T2 -> node B
thread T3 -> node C
A virtual thread may be implemented as:
Wasm execution context
containerized process
sandboxed native process
platform-specific build command
The debugger sees all of these as threads in one virtual process.
Thread placement is based on environment requirements:
spawn task with env!("linux")
-> coordinator finds a node that can materialize/run environment linux
-> node runtime starts the task
The source code selects logical environments, not specific machines:
env!("linux") // logical environment
Not:
node("builder-17") // real machine, generally not wanted in portable source
9. Virtual filesystem model
Each virtual thread/task receives a virtual filesystem.
Default mount model:
/vfs/project
Read-only source/input snapshot from the bundle.
/vfs/work
Per-thread writable copy-on-write overlay.
/vfs/artifacts
Published artifact namespace.
/vfs/shared
Optional shared namespace with explicit synchronization rules.
A thread can create and modify files in its writable overlay.
File creation:
Thread T creates file F
-> T owns the writable version of F
-> bytes are physically stored on T's current node by default
Flush:
fs::flush()
-> closes the current write epoch
-> publishes filesystem changes
-> makes changes visible according to VFS consistency rules
Sync:
fs::sync()
-> makes selected published changes durable/replicated
Important distinction:
flush = visibility / synchronization point
sync = durability / replication point
Suspend implicitly performs:
flush()
checkpoint_thread()
optionally sync()
Git-like analogy:
working tree = thread-local filesystem overlay
commit = flush point
object store = chunk/blob storage
branch/head = latest visible version
Disasmer does not need to literally use Git. A content-addressed chunk store is a better internal primitive.
10. Memory model
Disasmer memory is divided into Memory Spaces.
A memory space has:
stable virtual identity
address range or handle
current owner node
version/epoch metadata
optional cached pages
optional replicas
Allocation policy:
Memory allocated by a virtual thread is placed on that thread's current node by default.
Example:
thread T1 running on node A allocates memory M1
-> M1 initially owned by node A
thread T2 running on node B allocates memory M2
-> M2 initially owned by node B
Remote access is allowed, but mediated by Disasmer.
The MVP should not make ordinary raw pointers transparently remote. Instead, distributed state should use explicit Disasmer handles/types:
DBox<T>
DArc<T>
DMutex<T>
DVec<T>
DAtomicU64
Local state remains normal language state:
Vec<T> // local to one Wasm context/thread
DVec<T> // distributed memory object
std::sync::Mutex<T> // local/node/thread concept
disasmer::sync::DMutex<T> // distributed synchronization primitive
The debugger sees a coherent virtual memory model. Internally, Disasmer maps:
virtual memory reference
-> memory space
-> owner node
-> page/chunk
-> epoch
11. Synchronization and consensus
The programming model should feel close to normal thread safety:
locks
atomics
happens-before
shared mutable memory
race conditions
linearizable operations
The distributed runtime still needs distributed-systems machinery underneath.
Consensus/control-plane coordination is used for:
cluster membership
node identity
debug epochs
memory ownership metadata
thread placement metadata
checkpoint ownership
filesystem version metadata
failure decisions
Consensus should not be on the hot path for every load/store.
Preferred split:
Control plane:
consensus-backed metadata
Data plane:
direct QUIC/RPC between node runtimes
owner-based memory protocol
leases
fencing tokens
copy-on-write pages/chunks
Every sensitive operation should carry an epoch or fencing token:
remote_load(addr, epoch)
remote_store(addr, epoch)
transfer_memory_owner(space, node, epoch)
migrate_thread(thread, node, epoch)
debug_freeze(epoch)
resume(epoch)
12. Suspend / resume / checkpoint
Disasmer supports suspending a virtual thread.
User-facing operation:
suspend().await?
Semantics:
1. Reach a safepoint.
2. Stop the virtual thread.
3. Flush the thread filesystem overlay.
4. Snapshot Wasm execution state.
5. Snapshot or reference stack/heap/memory pages.
6. Store checkpoint metadata.
7. Mark thread as PARKED.
Resume:
thread.resume().await?
Semantics:
1. Select a target node.
2. Fetch checkpoint metadata.
3. Restore Wasm execution state.
4. Restore/mount filesystem overlay.
5. Reconnect memory spaces.
6. Continue execution from a safepoint.
The coordinator should store checkpoint metadata, not necessarily all bytes.
Actual checkpoint data should live in:
node-local storage
replicated storage
content-addressed blob storage
object storage
peer-to-peer transferred chunks
This enables:
thread migration
hibernation
debug pause/resume
load balancing
manual relocation
fault recovery when state is replicated
13. Debugging model
Debuggability is a primary design requirement.
Recommended external interface:
VS Code
-> DAP
-> LLDB / lldb-dap or custom Disasmer adapter
-> Disasmer debug endpoint
-> Coordinator
-> Node Runtimes
Disasmer should present the cluster as one virtual debug target.
When a breakpoint is hit:
1. A virtual thread hits a breakpoint or trap.
2. Coordinator creates a new Debug Epoch.
3. All node runtimes stop guest execution at safepoints.
4. Memory ownership/migration is frozen.
5. Thread states are collected.
6. Memory pages are pinned copy-on-write.
7. Filesystem overlay versions are pinned.
8. Debugger is told that all threads are stopped.
Debugger memory access should be served from a coherent snapshot.
Collect eagerly:
thread states
stacks
locals
globals
memory maps
filesystem manifests
Collect lazily:
heap pages
large memory regions
filesystem chunks
container logs beyond recent tail
large artifact data
The debugger illusion:
Debugger thinks:
process P is stopped
Reality:
cluster is stopped at Debug Epoch E
Optional Disasmer inspector:
thread -> node
thread -> environment
memory space -> owner node
file -> overlay version
artifact -> producer task
environment -> digest/cache state
remote calls
page migrations
lock wait graph
checkpoint state
consensus/control-plane state
The default debugger view should remain normal.
14. VS Code UX
Disasmer should provide a VS Code extension.
The extension should:
watch envs/**/Containerfile
watch envs/**/Dockerfile
watch envs/**/flake.nix
watch envs/**/flake.lock
watch inputs/**
watch src/**
run disasmer build before debugging
start or attach to a virtual process
bind breakpoints to Wasm/source debug info
show virtual threads in the normal debugger thread list
show stdout/stderr per virtual thread/task
show VFS/artifact state in an optional panel
show environment resources as first-class project items
provide autocomplete/diagnostics for env!("...") references
The intended UX:
1. Add envs/linux/Containerfile.
2. VS Code detects environment "linux".
3. Source autocomplete now accepts env!("linux").
4. Press F5.
5. Disasmer builds the virtual binary.
6. Disasmer starts the virtual process.
7. VS Code attaches to it as one normal debug target.
8. Breakpoints work across virtual threads/tasks.
The extension may generate editor metadata:
target/disasmer/generated/envs.rs
target/disasmer/generated/resources.json
But this generated metadata should not be hand-edited.
The project should not require YAML for ordinary usage.
Optional disasmer.toml may exist for overrides:
[envs.linux]
path = "envs/linux/Containerfile"
requires = { os = "linux", arch = "x86_64" }
[vfs]
include = ["inputs/**", "src/**", "Cargo.toml", "Cargo.lock"]
exclude = ["target/**", ".git/**"]
15. Editing source while debugging
Disasmer should support a fast edit/restart/debug loop.
MVP behavior:
source edit
-> rebuild bundle
-> restart selected virtual task/thread from a clean boundary
-> reattach/rebind breakpoints
The MVP should not hot-swap arbitrary live code into existing stack frames.
Supported restart boundaries:
task entrypoint
last explicit task checkpoint
last published filesystem state
last clean build input snapshot
Useful VS Code commands:
Disasmer: Restart Selected Virtual Thread
Disasmer: Restart Failed Task
Disasmer: Restart From Last Flush
Disasmer: Restart Whole Virtual Process
Disasmer: Rebuild Bundle And Rebind Breakpoints
Semantics for restarting a selected task:
1. Save source changes.
2. Rebuild the Disasmer bundle.
3. Stop the selected virtual thread/task.
4. Discard unflushed task-local filesystem changes unless configured otherwise.
5. Start the task again using the new bundle version.
6. Mount the same declared environment.
7. Mount the same input/VFS seed or chosen checkpoint boundary.
8. Rebind breakpoints using new debug metadata.
The runtime may keep the rest of the virtual process alive only if compatibility checks pass:
same public task signatures
same serialized argument schema
compatible resource manifest
compatible memory/handle ABI
If compatibility checks fail, the extension should restart the whole virtual process.
Design rule:
Task restart is an MVP feature.
Live code hot-swap is not.
16. Rust as the primary source language
Rust should be the primary guest/source language for Disasmer.
Reasons:
explicit async boundaries map well to suspend/checkpoint
strong types map well to distributed handles
macros can register tasks and environments
source-level debugging is good
one source file can express real build logic
C/C++ should be supported later or in parallel as lower-level guest languages, but Rust should define the first-class ergonomic API.
Design rule:
Local things use normal Rust.
Distributed things use Disasmer types.
Examples:
Vec<T> // local
DVec<T> // distributed
std::sync::Mutex<T> // local
disasmer::sync::DMutex<T> // distributed
std::fs // local/WASI-style filesystem, where supported
disasmer::fs // virtual filesystem with flush/sync semantics
17. Rust API shape
The MVP Rust API should be explicit.
Example concepts:
#[disasmer::main]
async fn main() -> Result<()> { ... }
#[disasmer::task]
async fn compile(target: Target) -> Result<Artifact> { ... }
spawn::task(compile)
.arg(Target::LinuxX64)
.env(env!("linux"))
.start()
.await?;
The env!("linux") macro validates at build time that an environment resource named linux exists.
The #[disasmer::task] macro registers a task entrypoint so it can be started remotely by function identity plus serialized arguments.
Task arguments and results should be serializable:
arguments -> serialized into task start message
results -> serialized into join result
handles -> serialized as Disasmer object references
18. Rust example: multiplatform build in one source file
Project layout:
example-build/
src/main.rs
Cargo.toml
Cargo.lock
envs/
linux/Containerfile
windows/Containerfile
macos/flake.nix
macos/flake.lock
inputs/
build-config.json
Source file:
use disasmer::prelude::*;
#[derive(Serialize, Deserialize, Clone, Debug)]
enum Target {
LinuxX64,
WindowsX64,
MacosArm64,
}
impl Target {
fn name(&self) -> &'static str {
match self {
Target::LinuxX64 => "linux-x64",
Target::WindowsX64 => "windows-x64",
Target::MacosArm64 => "macos-arm64",
}
}
fn triple(&self) -> &'static str {
match self {
Target::LinuxX64 => "x86_64-unknown-linux-gnu",
Target::WindowsX64 => "x86_64-pc-windows-msvc",
Target::MacosArm64 => "aarch64-apple-darwin",
}
}
fn output_name(&self) -> &'static str {
match self {
Target::WindowsX64 => "app.exe",
_ => "app",
}
}
}
#[disasmer::main]
async fn main() -> Result<()> {
let completed = DAtomicU32::new(0).await?;
let linux = spawn::task(compile)
.arg(Target::LinuxX64)
.arg(completed.clone())
.env(env!("linux"))
.start()
.await?;
let windows = spawn::task(compile)
.arg(Target::WindowsX64)
.arg(completed.clone())
.env(env!("windows"))
.start()
.await?;
let macos = spawn::task(compile)
.arg(Target::MacosArm64)
.arg(completed.clone())
.env(env!("macos"))
.start()
.await?;
let linux_artifact = linux.join().await?;
let windows_artifact = windows.join().await?;
let macos_artifact = macos.join().await?;
package(vec![linux_artifact, windows_artifact, macos_artifact]).await?;
assert_eq!(completed.load(Ordering::SeqCst).await?, 3);
fs::sync().await?;
Ok(())
}
#[disasmer::task]
async fn compile(target: Target, completed: DAtomicU32) -> Result<Artifact> {
let target_name = target.name();
let triple = target.triple();
let output_name = target.output_name();
cmd!("cargo", "build", "--release", "--target", triple)
.cwd("/vfs/project")
.run()
.await?;
let built_file = format!(
"/vfs/project/target/{triple}/release/{output_name}"
);
let artifact_file = format!(
"/vfs/artifacts/{target_name}/{output_name}"
);
fs::copy(&built_file, &artifact_file).await?;
// Publish this task's filesystem changes so other tasks can see the artifact.
fs::flush().await?;
completed.fetch_add(1, Ordering::SeqCst).await?;
Ok(Artifact::from_path(artifact_file).await?)
}
#[disasmer::task]
async fn package(outputs: Vec<Artifact>) -> Result<()> {
fs::create_dir_all("/vfs/artifacts/dist").await?;
for output in outputs {
fs::copy(output.path(), "/vfs/artifacts/dist/").await?;
}
fs::flush().await?;
Ok(())
}
19. What the Rust example does
The example defines a complete multiplatform build as one Rust program.
It does this:
1. Starts one Disasmer Virtual Process.
2. Creates one distributed atomic counter.
3. Spawns three virtual tasks:
- Linux x64 build task
- Windows x64 build task
- macOS ARM64 build task
4. Places each task into an environment discovered from envs/<name>/.
5. Runs each build command in that environment.
6. Copies each produced binary into /vfs/artifacts.
7. Calls fs::flush() to publish each task's artifact.
8. Joins all tasks.
9. Packages all artifacts into /vfs/artifacts/dist.
10. Calls fs::sync() to make final outputs durable.
Runtime behavior:
env!("linux")
-> resolved from envs/linux/Containerfile
env!("windows")
-> resolved from envs/windows/Containerfile
env!("macos")
-> resolved from envs/macos/flake.nix + flake.lock
spawn compile Linux
-> coordinator finds a node capable of running the linux environment
-> node runtime materializes/caches the environment
-> task runs with its own VFS overlay
spawn compile Windows
-> coordinator finds a node capable of running the windows environment
-> task runs separately but in the same virtual process
spawn compile macOS
-> coordinator finds a node capable of running the macos environment
-> task runs separately but in the same virtual process
Debugger behavior:
VS Code sees one process:
Thread 1: main
Thread 2: compile LinuxX64
Thread 3: compile WindowsX64
Thread 4: compile MacosArm64
Thread 5: package
The user can set breakpoints in:
main()
compile()
package()
When a breakpoint hits, Disasmer enters a Debug Epoch and stops the whole virtual process.
20. What the Rust example proves
The example proves the core Disasmer thesis:
A distributed, multiplatform build can be expressed as normal source code,
run across different machines/environments,
and debugged as one normal multithreaded program.
It proves these specific capabilities:
one source file can define the build graph
Containerfiles/Nix flakes can be project resources, not CI YAML
source code can reference environments by logical name
virtual tasks can run on different operating systems/architectures
artifacts can be published through flush()
final outputs can be made durable through sync()
shared distributed state can use explicit Disasmer types
the debugger can show all tasks as threads of one process
It also demonstrates the intended mental model:
Build = Virtual Process
Build Step = Virtual Thread / Virtual Task
Containerfile/Nix flake = Environment Resource
Build output = VFS artifact
flush() = publish/synchronization point
sync() = durability point
21. What this makes possible
Disasmer makes these workflows possible:
multiplatform builds without proprietary CI YAML
source-level debugging of build logic
breakpoints inside distributed build orchestration
restart one failed build task after editing source
inspect all build tasks in one debugger session
share artifacts through a versioned virtual filesystem
run Linux/Windows/macOS tasks from one source file
mix Wasm orchestration with containerized native commands
make distributed execution feel like threaded programming
Example VS Code loop:
1. Press F5.
2. Build starts as one virtual process.
3. Windows task fails.
4. Breakpoint/trap stops the virtual process.
5. User edits src/main.rs or project source.
6. User runs "Disasmer: Restart Failed Task".
7. Disasmer rebuilds the bundle.
8. Only the failed task restarts if compatible.
9. Breakpoints are rebound.
10. User continues debugging.
This is intentionally different from typical CI systems:
not a pile of YAML
not tied to one proprietary platform
not opaque remote job logs only
not separate debuggers per machine
22. C/C++ support
C/C++ should be possible but should not define the primary user experience.
C-style API shape:
dis_task_t linux = dis_spawn_env("linux", compile_task, &args, sizeof(args));
dis_join(linux);
dis_fs_flush();
dis_fs_sync();
Distributed memory in C should use handles:
dis_ref_t buffer = dis_mem_alloc(1024);
dis_mem_read(buffer, 0, tmp, sizeof(tmp));
dis_mem_write(buffer, 0, data, len);
The MVP should not make raw C pointers transparently remote.
Rust remains the preferred source language because it gives better type safety, async ergonomics, task registration, and IDE UX.
23. Failure semantics
MVP failure behavior should be simple.
If a node with non-replicated guest state is lost:
the virtual process is considered crashed
If a task is checkpointed and its checkpoint data is durable:
the task may be resumed elsewhere
If a task has only unflushed local filesystem changes and its node is lost:
those unflushed changes are lost
If artifacts have been flushed but not synced:
they may be visible but not durable
If artifacts have been synced:
they should survive according to the configured replication policy
Do not pretend missing non-replicated state can be debugged.
24. Security and identity
Disasmer should treat cluster identity and bundle identity as core primitives.
MVP requirements:
nodes authenticate to the coordinator
coordinator authorizes nodes for environments/capabilities
bundles are content-addressed
bundle manifests are signed or integrity-checked
environment resources are resolved by digest
tasks run with explicit capabilities
filesystem access is scoped to the virtual filesystem unless granted
network access is explicit per environment/task
The source should not casually select real machines by name. It should select logical environments and capabilities.
25. MVP feature set
The MVP should include:
Rust SDK
#[disasmer::main]
#[disasmer::task]
env!("name") environment references
auto-discovery of envs/<name>/Containerfile
auto-discovery of envs/<name>/Dockerfile
auto-discovery of envs/<name>/flake.nix
bundle manifest generation
selected input file bundling
per-task VFS overlay
fs::flush()
fs::sync()
spawn task into environment
join task
basic distributed handles
all-stop debugging
Debug Epochs
lazy memory/filesystem snapshotting
VS Code launch/debug extension
restart selected task from clean boundary after rebuild
QUIC node-to-node transport
coordinator-based node discovery
MVP restrictions:
all-stop debugging only
cooperative safepoints only
task restart, not live hot-swap
explicit distributed types only
no transparent raw remote pointers
no checkpointing live sockets
no automatic survival of non-replicated node loss
no eager full-cluster memory copy by default
no global consensus on every data operation
26. Later extensions
Possible later extensions:
non-stop debugging
live code replacement for compatible functions
state-schema-aware checkpoint migration
replicated memory spaces
recoverable distributed locks
watchpoints across distributed memory
record/replay debugging
artifact cache sharing across clusters
remote execution marketplace
Kubernetes backend
cloud worker backend
local laptop cluster mode
full offline vendored bundles
language SDKs for C/C++, Go, Zig, Python/Wasm
These are not MVP requirements.
27. Design summary
Disasmer is:
a distributed Wasm runtime
that presents a cluster as one virtual process
with virtual threads/tasks, sharded memory,
per-thread virtual filesystems, checkpoint/suspend/resume,
container/Nix-backed execution environments,
and normal debugger support.
For build systems, Disasmer means:
Build = Virtual Process
Build Step = Virtual Thread / Virtual Task
Environment = Containerfile / Dockerfile / Nix flake resource
Artifact = VFS-published file
Debugger = normal VS Code session
The core UX promise:
Add a Containerfile or flake.nix to the project.
Reference it from Rust with env!("name").
Press F5.
Debug the distributed build/program like one normal process.
The internal truth:
normal debugger illusion
backed by
cluster-wide debug epochs
copy-on-write snapshots
node runtimes
QUIC peer links
distributed memory spaces
filesystem overlays
checkpoint manifests
environment caches
and a consensus-backed control plane