← Files Val TownARCHIVED FILE
skills/blob-storage/SKILL.md
4.12 KB · Oct 3, 2026 · 06:07 UTC
---
name: blob-storage
description: Use when a val needs simple key/value persistence — JSON documents, cached responses, uploaded files, or binary assets. Covers the std/blob API, listing and deleting keys, account-global or val scoping, and storage limits.
---
# Blob Storage
Val Town provides built-in key/value blob storage via the `std/blob` module. Reach for it whenever a val needs to persist simple values — JSON documents, cached API responses, uploaded files, or binary assets — keyed by a string. For relational or structured data you query with SQL, prefer `std/sqlite` instead.
## Scoping: account-global or per-val depending on import
There are two exports of the blob utility: `global.ts`, which is scoped to the user account, and `main.ts`, which is scoped to the val itself. Prefer the `main.ts` interface and val scoping for new vals.
Here is the scoped import:
```ts
/**
* Importing from `main.ts` provides an interface to val-scoped blobs.
*/
import { blob } from "https://esm.town/v/std/blob/main.ts";
```
Here are the global imports:
```ts
/**
* Importing from `global.ts` provides a blob interface that is scoped
* to your account.
*/
import { blob } from "https://esm.town/v/std/blob/global.ts";
/**
* This entrypoint is also available as `v/std/blob`. This is common
* in older vals.
*/
import { blob } from "https://esm.town/v/std/blob";
```
The scoped & global `blob` interfaces have the same methods.
Scoped & global blobs are stored separately: you cannot access global blobs with the scoped interface or vice versa.
## Basic usage (JSON)
```ts
import { blob } from "https://esm.town/v/std/blob/main.ts";
await blob.setJSON("config", { theme: "dark", count: 0 });
const config = await blob.getJSON("config");
// config = { theme: "dark", count: 0 }, or undefined if the key doesn't exist
```
`getJSON` returns `undefined` when the key is missing, so guard before using the result:
```ts
const config = (await blob.getJSON("config")) ?? { theme: "light", count: 0 };
```
## Raw and binary data
Use `set`/`get` for strings, binary, or any `BodyInit`. `get` returns a standard `Response`, so use its body helpers (`.text()`, `.json()`, `.arrayBuffer()`, `.blob()`):
```ts
await blob.set("logo.png", imageBytes); // string | BodyInit (Blob, ArrayBuffer, ReadableStream, …)
const res = await blob.get("logo.png");
const bytes = await res.arrayBuffer();
```
Unlike `getJSON`, `get` **throws** `ValTownBlobNotFoundError` if the key doesn't exist — wrap it in `try/catch` when the key may be absent.
## Listing, deleting, copying
```ts
const entries = await blob.list("user_"); // optional key prefix filter
// entries = [{ key, size, lastModified }, …]
for (const { key } of entries) {
await blob.delete(key);
}
await blob.copy("config", "config.bak"); // duplicate under a new key
await blob.move("draft", "published"); // rename / relocate
```
`list(prefix?)` returns an array of `{ key: string; size: number; lastModified: string }` — objects, not bare key strings.
## Limits
- **Key length:** up to 512 characters.
- **Total storage:** 10 MB on the free plan, 1 GB on Pro — shared across all blobs in the account.
- Store large or structured datasets in `std/sqlite` rather than as one giant blob.
## Reading/writing blobs via tools
When using the `storeBlob`, `readBlob`, `listBlobs`, or `deleteBlob` tools against a val owned by an organization (not your personal account), pass the org handle as the `org` parameter so the call hits that organization's blob storage. Example: `{ key: "myapp:config", org: "some-org" }`. This only matters for the tool calls — code inside the val reads and writes its owning account's storage automatically. Note `storeBlob` accepts UTF-8 text up to 100 KB; write larger or binary blobs from code with `blob.set`.
## Rules
- Treat keys as a flat namespace. Use prefixes (`feature:subkey`) for organization and to scope `list`.
- `getJSON` returns `undefined` for missing keys; `get` throws `ValTownBlobNotFoundError`. Handle the absent case accordingly.
- Don't store secrets in blobs — use environment variables for credentials.
## Reference
Full API docs: https://docs.val.town/std/blob/
SHA-256: e809556517a4234956072e7dc820818ac02b45fdc6ec7aaf3c4868ebb3f77d2e