Architecture

Understanding how NANO works under the hood

High-Level Architecture

NANO consists of a Rust-based runtime that embeds V8 to execute JavaScript in isolated environments.

  [Client Request]
         |
         v
  [HTTP Router]  -->  [App Registry]
         |                    |
         v                    v
  [Worker Pool]  <--  [Isolate Cache]
         |
         v
  [V8 Isolate]  -->  [JavaScript Execution]

V8 Isolates

Each application runs in its own V8 isolate, providing complete isolation from other applications while sharing the same OS process.

Isolate Properties

  • Memory limit: Configurable heap size per isolate
  • Context reset: ~5ms between requests (not full recreate)
  • Thread-local: Isolates never move between threads

Sliver Snapshots

Slivers enable sub-millisecond cold starts by serializing the pre-initialized V8 isolate state.

Sliver Creation Process
1. Create fresh V8 isolate
2. Load JavaScript bundle into isolate
3. Execute initialization code
4. Serialize isolate state to file
5. Compress and store as sliver

Result: 267ms cold start becomes 267us

Worker Pool

The worker pool manages concurrent request handling with thread-local isolates.

Request Flow

  1. HTTP request arrives at router
  2. Router determines target app from hostname/path
  3. Request is queued for worker pool
  4. Available worker picks up request
  5. Worker resets isolate context (~5ms)
  6. JavaScript handler executes
  7. Response is returned to client

WebSocket Upgrade Path v2.0a

WebSocket connections use a dedicated worker thread per connection. The isolate lifetime is bound to the connection — when the connection closes, the isolate is recycled.

HTTP GET + Upgrade: websocket
        |
        v
  detect_ws_upgrade() — router.rs
        |  axum 101 handshake
        v
  tokio relay task — WsChannels (mpsc)
        |  WsInbound / WsOutbound
        v
  TenantPool::dispatch_ws()
        |  lazy-spawn WS worker thread
        v
  JS fetch() handler (registers addEventListener)
        |
        v
  'ws_messages loop
        |  recv_timeout(idle_timeout_ms) per frame
        |  CpuTimeoutGuard + OOM check per message
        v
  Close/Disconnect → break 'requests → isolate recycled

Key Properties

  • One isolate per connection: Worker thread dedicated to single WS connection
  • Isolate recycled on close: Fresh isolate for next connection — no state leakage
  • Per-frame enforcement: CPU timeout guard and OOM check on every message
  • Idle timeout: Connection closed after ws_idle_timeout_ms with no frames (default 60s)
  • Max message size: 32 MiB default (ws_max_message_bytes)