Runtime Architecture
Mapping how high-level workflow primitives compile down to deterministic VM execution. Honest architectural audit of the current alpha prototype.
The Layered Stack
Understanding how high-level workflow DSLs map to low-level VM execution. Each layer builds on the one below it.
W3 Workflow Stack (Draft)
High-level workflows → Low-level VM execution
1 Workflow DSL
✓YAML spec with triggers, Smart Actions, verification policy
2 Compiler
✗Parse, validate, optimize → compile to bytecode
3 VM / ISA
✗Instruction set, interpreter loop, gas metering
4 Host Environment
~Syscalls for external ops (HTTP, storage, Smart Actions)
5 Runtime State
✓Block-based state machine, mempool, consensus
₿ Bitcoin
◆ Ethereum
◎ Solana
Current Implementation Reality
Honest assessment of the alpha prototype's architecture. Understanding where we are vs. where we need to be.
Layer 1: Workflow DSL
Status: Great! GitHub Actions YAML syntax with W3-specific extensions.
name: "DeFi Risk Monitor"
on:
schedule:
cron: "*/5 * * * *"
verification: onchain
jobs:
monitor:
runs-on: w3/ubuntu-latest
steps: - name: Fetch price
uses: w3/oracle-action@v1
verification: tee
- name: Alert if risky
run: ./check_and_alert.sh
verification: shadowLayer 2: Compiler
Status: Missing! Template compilation happens at runtime, not deploy time.
// ❌ Compilation work done DURING execution!
let parsed_command = parse_template(run_command)?;
let evaluated_command = evaluate_value(&parsed_command, context)?;
for (key, value) in step.env() {
let evaluated = evaluate_value(value, context)?;
env_vars.insert(key.clone(), value_str);
}
// ❌ No bytecode, no optimization, no static analysis✗
Layer 3: VM / ISA
Status: Doesn't exist! Just Rust enum matching.
match step.kind() {
StepKind::Run { run } =>
run::execute_run_step(...),
StepKind::Uses { uses, with } =>
uses::execute_uses_step(...),
}
// ❌ No VM, no bytecode, no gas metering~
Layer 4: Host Environment
Status: Implicit! Docker provides isolation but no explicit syscall boundary.
docker run --rm --user 1001:1001 \
-v $workspace:/workspace \
-v $temp:/_temp \
w3-runner-ubuntu:22.04 \
bash -c "$evaluated_command"
// ✅ Process isolation (good!)
// ❌ No explicit host call interfaceLayer 5: Runtime State
Status: Solid! Block-based state machine with deterministic replay.
for block in &blocks {
for message in &block.messages {
replay_message_into_context(message)?;
}
}
// ✅ State derived from blocks
// ✅ Deterministic replayThe Layering Violations
Critical architectural issues where layers mix responsibilities, making the system fragile and non-deterministic.
1 Template Parsing During Execution (Layer 2 work in Layer 3)
The prototype evaluates templates and environment variables at runtime, not compile time. This means compilation happens during every single execution, making determinism fragile.
// Layer 2 work: Parsing + evaluation (COMPILATION!)
let parsed_command = parse_template(run_command)?;
let evaluated_command = evaluate_value(&parsed_command, context)?;
// Build environment variables (also Layer 2 work!)
for (key, value) in step.env() {
let evaluated = evaluate_value(value, context)?;
env_vars.insert(key.clone(), value_str);
}
// Then immediately jump to Layer 4: Docker execution
let result = container::execute_in_container_with_shell(
"w3-runner-ubuntu:22.04",
&evaluated_command,
&runner_env,
workspace_dir.path(),
temp_dir.path(),
None,
shell,
).await?;
// ❌ Problem: No Layer 3 (VM) at all!
// ❌ Layer 2 work (compilation) done at runtime
// ❌ Direct jump from high-level DSL to host executionShould be: Compiler produces bytecode at deploy time. VM executes bytecode. Host environment receives explicit syscalls.
2 No VM Layer (Pattern Matching ≠ Virtual Machine)
There's no actual VM. Execution is just a Rust match statement dispatching to native functions. No bytecode, no instruction set, no gas metering.
// "VM" is just pattern matching!
match step.kind() {
StepKind::Run { run } => {
run::execute_run_step_with_defaults(
step, run, context, job_defaults
).await?
}
StepKind::Uses { uses, with } => {
uses::execute_uses_step(
step, uses, with, action_cache, context
).await?
}
}
// ❌ No instruction set
// ❌ No bytecode interpreter
// ❌ No gas metering
// ❌ Just Rust calling RustShould be: True VM with
instruction set (WASM/eBPF/EVM), bytecode interpreter, and gas metering. VM
executes OP_LOAD_CONST,
OP_HOST_CALL, etc.
3 Implicit Host Environment (No Explicit Syscall Interface)
Host functions are implicit. Docker provides isolation, but there's no explicit HostCall enum defining what operations the VM can request from the runtime.
// Host environment is implicit: Docker + shell
cmd.arg("run")
.arg("--rm")
.args(runner_env.docker_args(...));
cmd.arg(image);
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
let output = cmd.spawn()?.wait_with_output().await?;
// ❌ No explicit HostCall enum
// ❌ No permission model
// ❌ No gas cost for operationsenum HostCall {
ShellCommand { cmd: String, shell: String },
HttpRequest { url: String, method: String },
StorageRead { key: String },
EmitEvent { data: Value },
}
trait HostEnvironment {
async fn invoke(
&self,
call: HostCall,
gas_limit: u64
) -> Result<(HostResult, u64)>;
}
// ✅ Explicit interface
// ✅ Can add permission checks
// ✅ Can meter gas per callWhat's Actually Good
Despite architectural issues, several components are solid and provide a strong foundation.
✓ Consensus Hooks
- Block production/verification separation
- Mempool with priority fees
- BFT consensus ready (Tendermint-style)
- Clean validator interface
✓ State Management
- Deterministic replay from blocks
- Context-based execution model
- Message-driven state transitions
- Clean separation from consensus
✓ Mempool Design
- Gas pricing + priority fees
- Transaction validation before inclusion
- Account nonce tracking
- Clean interface for proposers
✓ Process Isolation
- Docker containers isolate execution
- Filesystem sandboxing
- User-level permissions
- Resource limits (CPU, memory)
Path Forward
Concrete steps to fix layering violations and build proper VM infrastructure.
⚙️ Build Compiler (Layer 2)
- Parse YAML at deploy time
- Validate refs, types, schemas
- Emit bytecode + manifest
- Static analysis (gas estimation)
- Deterministic builds
⚡ Implement VM (Layer 3)
- Choose ISA (WASM recommended)
- Integrate runtime (wasmer/wasmtime)
- Gas metering per instruction
- Stack/memory limits
- Deterministic execution
🔌 Define Host Interface (Layer 4)
- Explicit HostCall enum
- Permission model per call
- Gas costs for operations
- Async syscall handling
- Receipt-based verification
Migration Path
- Week 1-2: Build compiler prototype (YAML → bytecode)
- Week 3-4: Integrate WASM runtime, replace execution engine
- Week 5-6: Define and implement HostCall interface
- Week 7-8: End-to-end testing, gas tuning, benchmarks