Virke Components

Virke supports the WebAssembly Component Model, a portable binary interface standard for WebAssembly. Component Model applications are more portable, composable, and future-proof than traditional Wasm modules.

Quick Start

1. Create a component project

virke init --name my-component --type component
bun add @virke/wasm-component

2. Write your handler

import { defineHandler, json, kv, db, logging } from "@virke/wasm-component";

export default defineHandler(async (request) => {
  logging.log("info", `${request.method} ${request.url}`);

  const counter = kv.get("counter") ?? "0";
  kv.set("counter", String(Number.parseInt(counter) + 1), 3600);

  return json({ visits: Number.parseInt(counter) + 1 });
});

3. Deploy

virke deploy

The CLI detects type = "component" in virke.toml and uses the Component Model build pipeline: bundle with Bun, componentize with componentize-js, upload the .wasm component.

How It Works

Your TypeScript code
       ↓  (bun build)
Bundled JavaScript
       ↓  (componentize-js)
Wasm Component (.wasm)
       ↓  (virke deploy)
Fastly Compute@Edge service

Your component exports a wasi:http/incoming-handler and imports Virke runtime interfaces for KV, DB, OS3, and logging. The Virke runtime on Fastly Compute@Edge provides host implementations of these interfaces.

Virke Bindings

KV Store

import { kv } from "@virke/wasm-component";

// Read
const value = kv.get("my-key");           // string | undefined

// Write with TTL (seconds)
kv.set("my-key", "my-value", 3600);

// Delete
kv.delete("my-key");

// List keys by prefix
const keys = kv.list("user:", 100);

Database

import { db } from "@virke/wasm-component";

// Read query
const result = db.query("SELECT * FROM users WHERE id = ?", ["123"]);

// Write
const affected = db.execute(
  "INSERT INTO users (name, email) VALUES (?, ?)",
  ["Alice", "alice@example.com"]
);

Object Storage

import { os3 } from "@virke/wasm-component";

// Read
const data = os3.get("documents/file.pdf");   // Uint8Array

// Write
os3.put("documents/file.pdf", data, "application/pdf");

// Delete
os3.delete("documents/file.pdf");

// List
const keys = os3.list("documents/", 100);

Logging

import { logging } from "@virke/wasm-component";

logging.log("debug", "Debug message");
logging.log("info", "Request processed");
logging.log("warn", "Rate limit approaching");
logging.log("error", "Database query failed");

Configuration

virke.toml

[project]
name = "my-component-app"
type = "component"

[compute]
entry = "src/handler.ts"

package.json

{
  "dependencies": {
    "@virke/wasm-component": "^0.0.1"
  }
}

WIT Interface Definitions

Virke defines its runtime interfaces using WIT (Wasm Interface Type):

world virke {
  import kv;
  import db;
  import os3;
  import logging;
  export wasi:http/incoming-handler@0.2.0;
}
Full WIT definitions
interface kv {
  get: func(key: string) -> option<string>;
  set: func(key: string, value: string, ttl: option<u32>) -> result<_, string>;
  delete: func(key: string) -> result<_, string>;
  list: func(prefix: string, limit: option<u32>) -> list<string>;
}

interface db {
  type query-result = string;
  query: func(sql: string, params: list<string>) -> result<query-result, string>;
  execute: func(sql: string, params: list<string>) -> result<u64, string>;
}

interface os3 {
  get: func(key: string) -> result<list<u8>, string>;
  put: func(key: string, data: list<u8>, content-type: option<string>) -> result<_, string>;
  delete: func(key: string) -> result<_, string>;
  list: func(prefix: string, limit: option<u32>) -> list<string>;
}

interface logging {
  enum level { debug, info, warn, error }
  log: func(level: level, message: string);
}

Performance

Metric Value
Component size 500 KB – 2 MB
Cold start 60–110ms
KV operations 30–50ms
DB queries (cached) 10–50ms
DB writes 1–2s
OS3 operations 50–200ms
Logging < 1ms

Component Model vs Traditional Compute

Component Model Traditional (Virke Run)
Interface WIT-defined imports/exports Fastly SDK directly
Portability Any Component Model runtime Fastly only
Composability Clean import/export interfaces N/A
Languages TypeScript (more coming) TypeScript, Rust
Bundle size Larger (includes JS runtime) Smaller
Maturity Newer Stable

Choose Component Model when portability and composability matter. Choose Virke Run for the most mature developer experience and smallest bundle sizes.

Security

Components run in a capability-based sandbox:

  • No ambient authority — components can only access explicitly imported capabilities
  • Wasm sandboxing — memory isolation via wasmtime
  • No direct filesystem — all storage goes through host-provided interfaces
  • No raw network access — only via host-provided HTTP handler

Limitations

  • TypeScript only (Rust and other languages via wit-bindgen coming in V2.1)
  • Larger bundle size than native Rust (includes StarlingMonkey JS runtime)
  • Component Model tooling is still maturing
  • No streaming — full request/response bodies (streaming planned for V2.1)

Further Reading

  • Virke Run — Traditional compute with Virke Run
  • Configurationvirke.toml reference for component projects
  • Virke DB — Database access from components
  • Virke KV — KV access from components