@bytecask/core
Browser-native, content-addressed blob storage.
Store binary data in IndexedDB, OPFS, or memory behind one typed API with BLAKE3 addressing, deduplication, lifecycle accounting, and recovery tools.
pnpm add @bytecask/core
Quick Start · Backends · API · Browser Support · Documentation
What It Is
@bytecask/core is the framework-neutral Bytecask storage engine. It accepts
Blob, Uint8Array, or streamed bytes and returns a lowercase BLAKE3 content
hash. Identical content is stored once, while lifecycle metadata determines
whether the object is live, collectible, or missing.
| It provides | It deliberately leaves to the host |
|---|---|
| Content-addressed binary storage | Records and relational data |
| IndexedDB, OPFS, and memory adapters | User authorization and synchronization |
| Deduplication, references, GC, reconciliation | Signing and publisher identity |
| Quota and integrity errors | The only copy of irreplaceable data |
Portable blobs/<hash> packing |
Product-specific attachment workflows |
Highlights
- BLAKE3 content identity: stable hashes support deduplication and portable manifests.
- IndexedDB-first persistence: broad cross-browser storage without a database dependency.
- Optional OPFS streaming: worker-backed large-file I/O is loaded only when selected.
- Explicit lifecycle models: use refcounts or reconcile against a canonical host manifest.
- Failure-aware writes: partial objects are removed after quota, metadata, integrity, or streaming failures.
- ESM and TypeScript: typed public exports with no top-level browser-global access, so importing during SSR is safe.
Installation
Install the core package for IndexedDB, memory, and automatic backend selection:
pnpm add @bytecask/core
Add the worker package only when using OPFS:
pnpm add @bytecask/core @bytecask/worker
Quick Start
import { createBlobStore } from '@bytecask/core'
const store = await createBlobStore({ backend: 'idb' })
const hash = await store.put(new Blob(['hello, Bytecask']))
console.log(hash) // lowercase BLAKE3 hex
const blob = await store.getBlob(hash)
const bytes = await store.get(hash)
await store.close()
Automatic selection tries IndexedDB, then OPFS, then memory and exposes the
result as store.backend. Force a persistent backend for archives that must
never degrade to session memory.
Storage Backends
| Backend | Selection | Persistence | Best fit |
|---|---|---|---|
| IndexedDB | Default persistent tier | Browser-managed, evictable | General files and attachments |
| OPFS | Explicit or automatic fallback | Browser-managed, evictable | Streaming and large binary objects |
| Memory | Explicit or final fallback | Session only | Tests, previews, and ephemeral data |
const store = await createBlobStore({
backend: 'idb',
idb: { databaseName: 'archive-blobs' },
metadata: { databaseName: 'archive-metadata' },
verifyOnRead: true,
})
const persistenceGranted = await store.requestPersistence()
const report = await store.reconcile(liveAttachmentHashes)
A forced backend rejects when unavailable. It never silently changes the requested durability model.
Lifecycle Models
Choose one authority per store:
- Store-managed:
put()creates a reference; balance ownership withref()andunref(), then collect withgc(). - Host-managed: retain hashes in a canonical manifest and call
reconcile(liveHashes)to repair metadata and identify missing or unreferenced bytes.
Do not maintain independent refcount systems for the same archive.
Main API
| Export | Purpose |
|---|---|
createBlobStore(options) |
Select or force a storage backend |
put, get, getBlob, has, delete |
Store and retrieve bytes by hash |
ref, unref, gc |
Store-managed lifecycle accounting |
reconcile(liveHashes) |
Repair state from a host-authoritative manifest |
subscribe(listener) |
Observe local byte-store mutation outcomes |
estimate, requestPersistence |
Observe browser storage and persistence |
packBlobs, unpackBlobs |
Move portable blobs/<hash> entries |
BytecaskError, QuotaError, IntegrityError |
Handle typed storage failures |
const unsubscribe = store.subscribe((event) => {
if (event.type === 'put') {
console.log(event.hash, event.refcount)
}
})
The change feed is store-level and local to the BlobStore instance. It emits
Bytecask byte/lifecycle outcomes, not host record semantics or cross-tab sync
messages.
Browser Support
- IndexedDB behavior is tested in Chromium, Firefox, and Playwright WebKit.
- OPFS worker behavior is tested in Chromium and Firefox.
- Memory storage works in browser and JavaScript test environments.
- The package is ESM-only and targets modern browsers.
Playwright WebKit is an engine proxy, not Apple's shipping Safari binary. Treat browser storage as a rebuildable local copy because persistence requests reduce eviction risk but cannot eliminate it.
Related Packages
| Package | Purpose |
|---|---|
@bytecask/worker |
Optional OPFS worker and streaming I/O |
@bytecask/react |
Optional React provider and hooks |
Documentation
Security
Bytecask verifies content identity, not publisher identity. Verify signed containers or shards before hydrating untrusted bytes. Report vulnerabilities through the project's security policy, not a public issue.
License
AGPL-3.0-only. See LICENSE.