Virke OS3

S3-compatible object storage with CDN-cached reads. Virke OS3 wraps Fastly Object Storage and provides automatic CDN caching with surrogate key purge on writes.

Performance

Operation Latency
Read (CDN cache hit, warm POP) 2–12ms
Read (cache miss) 50–200ms
Write 100–500ms
Delete 100–300ms
List objects 50–200ms

CLI Usage

Create a bucket

virke os3 create my-assets

Upload a file

virke os3 put my-assets images/logo.png ./logo.png

Download a file

virke os3 get my-assets images/logo.png -o logo.png

List objects

virke os3 ls my-assets

Delete an object

virke os3 rm my-assets images/logo.png

List and delete buckets

virke os3 buckets
virke os3 delete my-assets

SDK Usage (from Virke Run)

With Hono

import { Hono } from "hono";
import { virke } from "@virke/runtime/hono";

const { fire, middleware, Bindings } = virke({ os3: "my-assets" });
const app = new Hono<{ Bindings: typeof Bindings }>();

app.use("*", middleware);

// Upload a file
app.put("/files/:key", async (c) => {
  const body = await c.req.arrayBuffer();
  const contentType = c.req.header("Content-Type") ?? "application/octet-stream";
  await c.env.os3.put(c.req.param("key"), body, contentType);
  return c.json({ ok: true });
});

// Download a file
app.get("/files/:key", async (c) => {
  const response = await c.env.os3.get(c.req.param("key"));
  if (!response) return c.json({ error: "Not found" }, 404);
  return response;
});

// Get file as JSON
app.get("/data/:key", async (c) => {
  const data = await c.env.os3.getJson(c.req.param("key"));
  if (!data) return c.json({ error: "Not found" }, 404);
  return c.json(data);
});

// Delete a file
app.delete("/files/:key", async (c) => {
  await c.env.os3.delete(c.req.param("key"));
  return c.json({ ok: true });
});

// List files
app.get("/files", async (c) => {
  const prefix = c.req.query("prefix");
  const keys = await c.env.os3.list(prefix);
  return c.json({ keys });
});

fire(app);

OS3 binding API

interface VirkeOS3 {
  get(key: string): Promise<Response | null>;
  getText(key: string): Promise<string | null>;
  getJson<T = unknown>(key: string): Promise<T | null>;
  put(key: string, body: BodyInit, contentType?: string): Promise<void>;
  delete(key: string): Promise<void>;
  list(prefix?: string): Promise<string[]>;
}

CDN Caching

Reads are cached automatically at Fastly's edge POPs worldwide. When you write or delete an object, Virke purges the CDN cache using surrogate keys so readers see the updated content immediately.

  • First read — fetches from origin (~50-200ms), caches at the edge
  • Subsequent reads — served from edge cache (2-12ms)
  • After write — cache is purged, next read fetches from origin

Use Cases

User uploads

app.post("/upload", async (c) => {
  const formData = await c.req.formData();
  const file = formData.get("file") as File;
  const key = `uploads/${crypto.randomUUID()}-${file.name}`;
  await c.env.os3.put(key, await file.arrayBuffer(), file.type);
  return c.json({ key, url: `/files/${key}` });
});

Static assets

// User-customizable theme CSS
await env.os3.put(
  `themes/${userId}/custom.css`,
  cssContent,
  "text/css"
);

Configuration

[project]
name = "my-app"

[storage]
bucket = "my-app-assets"

Limitations

  • Object size — maximum 5 GB per object
  • Region — EU (eu-central-1) by default
  • No partial reads — entire object is fetched (no range requests in V1)
  • S3-compatible — supports GET, PUT, DELETE, ListBucket. Advanced features (multipart, versioning) not yet supported

Further Reading

  • Virke Run — Build compute functions that use Virke OS3
  • CLI Commands — Full virke os3 command reference