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 os3command reference