Skip to main content

Overview

The BlobStore API provides content-addressable storage using FNV-1a 64-bit hashing. It supports both in-memory and IndexedDB-backed implementations for storing arbitrary binary data.

BlobStore Interface

Defines the contract for content-addressable blob storage.
interface BlobStore {
  get(hash: string): Promise<Uint8Array | null>;
  put(data: Uint8Array): Promise<string>;
  delete(hash: string): Promise<void>;
  has(hash: string): Promise<boolean>;
}

Methods

get

Retrieves a blob by its content hash.
async get(hash: string): Promise<Uint8Array | null>
hash
string
required
The 16-character lowercase hex hash of the content
return
Uint8Array | null
The blob data, or null if not found

put

Stores a blob and returns its content hash.
async put(data: Uint8Array): Promise<string>
data
Uint8Array
required
The binary data to store
return
string
The 16-character lowercase hex hash of the content
Duplicate content is automatically deduplicated by hash

delete

Removes a blob from storage.
async delete(hash: string): Promise<void>
hash
string
required
The content hash to delete

has

Checks if a blob exists in storage.
async has(hash: string): Promise<boolean>
hash
string
required
The content hash to check
return
boolean
true if the blob exists, false otherwise

MemoryBlobStore

In-memory implementation using a Map.

Constructor

new MemoryBlobStore()

Example

const store = new MemoryBlobStore();

// Store data
const encoder = new TextEncoder();
const data = encoder.encode('Hello, world!');
const hash = await store.put(data);
console.log('Stored with hash:', hash); // e.g., "d58042e4e4ab7705"

// Retrieve data
const retrieved = await store.get(hash);
const decoder = new TextDecoder();
console.log(decoder.decode(retrieved)); // "Hello, world!"

// Check existence
const exists = await store.has(hash);
console.log(exists); // true

// Delete
await store.delete(hash);
console.log(await store.has(hash)); // false
Data is copied on get/put to prevent callers from mutating internal state. See BlobStore.ts:94-95

IndexedDBBlobStore

Persistent browser storage using IndexedDB.

Constructor

new IndexedDBBlobStore()

Configuration

  • Database name: lifo-blobs (see BlobStore.ts:120)
  • Object store: blobs (see BlobStore.ts:121)

Methods

open

Opens or creates the IndexedDB database. Must be called before other methods.
async open(): Promise<void>
Always call open() before using other methods, or operations will fail silently

Example

const store = new IndexedDBBlobStore();
await store.open();

// Store data
const encoder = new TextEncoder();
const data = encoder.encode('Persistent data');
const hash = await store.put(data);

// Data persists across page reloads
location.reload();

// After reload:
const store2 = new IndexedDBBlobStore();
await store2.open();
const retrieved = await store2.get(hash);
console.log(new TextDecoder().decode(retrieved)); // "Persistent data"
Gracefully degrades if IndexedDB is unavailable (e.g., in private browsing mode)

Hash Function

The hashBytes function computes a 64-bit FNV-1a hash.
function hashBytes(data: Uint8Array): string
data
Uint8Array
required
The bytes to hash
return
string
A 16-character lowercase hexadecimal string representing the 64-bit hash

Example

import { hashBytes } from './BlobStore.js';

const data = new TextEncoder().encode('test');
const hash = hashBytes(data);
console.log(hash); // "f9e6e6ef197c2b25"

Algorithm Details

  • Algorithm: FNV-1a (Fowler-Noll-Vo)
  • Output size: 64 bits
  • Offset basis: 0xcbf29ce484222325
  • Prime: 0x00000100000001b3
The implementation uses 32-bit arithmetic for compatibility and splits the 64-bit state into high and low words. See BlobStore.ts:8-71

Storage Comparison

FeatureMemoryBlobStoreIndexedDBBlobStore
PersistenceNoYes (browser storage)
PerformanceFastestFast (async I/O)
CapacityLimited by RAM~50MB-unlimited*
Use caseTesting, temporaryProduction
*Capacity depends on browser quota policies

Integration with VFS

BlobStore is used by the virtual filesystem for:
  1. Large file storage: Files that exceed chunking thresholds
  2. Content deduplication: Identical content stored once
  3. Lazy loading: Content loaded on-demand from IndexedDB
Example integration:
const blobStore = new IndexedDBBlobStore();
await blobStore.open();

// VFS writes large file
const fileData = new Uint8Array(10 * 1024 * 1024); // 10 MB
const hash = await blobStore.put(fileData);

// Store hash in INode
const fileNode: INode = {
  type: 'file',
  name: 'large-file.bin',
  blobRef: hash,  // Reference to blob store
  data: new Uint8Array(0),  // No inline data
  // ... other fields
};

// Later, retrieve content
const content = await blobStore.get(fileNode.blobRef!);
Use ContentStore for synchronous in-memory caching of chunked content. See ContentStore documentation.

Build docs developers (and LLMs) love