npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@fireproof/encrypted-blockstore

v0.18.0

Published

Content-addressed blockstore with pluggable CRDT storage connectors.

Downloads

163

Readme

Encrypted Blockstore

Multi-writer self-hosted local-first IPFS-compatible blockstore with end-to-end encryption. If you have code that expects a JavaScript blockstore, you can instantiate one of these instead and get superpowers. What kind of superpowers?

By default, encrypted blockstores are backed by the browser or the local filesystem. Each transaction is committed as a new immutable CAR file (content addressed archive), which can be replicated and hosted anywhere. The blockstore has connectors for a range of cloud storage providers, so you can easily sync your data using AWS, Netlify, Cloudflare, web3.storage, or any other cloud storage provider.

Usage

The interface will be similar to other content-addressed blockstores, like the IPFS JS blockstore

const blockstore = new EncryptedBlockstore(requiredConfig)

await blockstore.transaction(async (blocks) => {
  // ... your app logic ...
  await blocks.put(cid1, bytes1)
  
  // ... your app logic ...
  await blocks.put(cid2, bytes2)
  
  // return any small data shape your code needs to read
  // from the blockstore like a CID for the most recent 
  // user data. this is your mutable state pointer.
  return { myCustomHeader : cid2 } 
})

// the blockstore can be read from outside a transaction
const block = await blockstore.get(cid1)

You can see that once you are in a transaction, the blocks look just like an IPFS blockstore. In addition to put get and delete, you can also use putSync and getSync, and list blocks with entries.

This blockstore is excellent for any data structure that you want to persist locally and replicate online. It is especially useful for CRDTs and DAGs, but should work for any IPFS CID storage and retrieval.

Working with CRDTs

For correctness, you'll want to have a data structure that can merge incoming data. There are very many of these. It's important because updates can come from anywhere in any order. The blockstore will make sure that all the blocks are available by CID, but it's up to your code to merge the incoming data. If your merge operation is commutative, then you can merge in any order. Warning, if you try to privilege your local data, or otherwise make a lossy merge, your app will have trouble syncing. If this sounds like something you don't want to worry about, then you should probably be using one of the libraries that depend on Encrypted Blockstore, like Fireproof which gives a MongoDB-like API and handles the merging and sync for you.

Setup

To configure the blockstore, you need to provide a callback and some implementations. The callback is called on every transaction and is where you can do things like update your UI or merge the incoming data with your data structures. The implementations are for the storage and encryption functions, which can vary by platform. The blockstore ships with basic implementations for the browser and the local filesystem, but you can also provide your own to fit your platform.

import { EncryptedBlockstore } from '@fireproof/encrypted-blockstore'
import * as crypto from '@fireproof/encrypted-blockstore/crypto-web'
import * as store from '@fireproof/encrypted-blockstore/store-web'

const blockstoreConfig = {
  name: 'my-app',
  applyMeta: async (meta) => {
    // This is called on multi-user write and local load with your custom header.
    // It's where you can react to remote changes or load a new dataset.
    // You can also use this to merge the incoming data with your local data.
    await myDataStructure.merge(meta.myCustomHeader)
  },
  compact: async (compactionBlocks) => {
    // Traverse your data structure with your existing code, reading from the compaction blockstore
    await myDataStructure.listAll(compactionBlocks, myCustomHeader)
    // return the data-structure header in the same format as a transaction call
    return myCustomHeader
  },
  crypto,
  store
}

const blockstore = new EncryptedBlockstore(blockstoreConfig)

The applyMeta function can call into your data structures to merge the incoming data. The encrypted blockstore takes care of making sure blocks are available by CID, so all your code needs to do is read from the blockstore. The custom header returned from your next transaction will be made available to anyone listening to the blockstore. This is how you can sync your data across multiple devices.

Compaction

Without compaction, your blockstore will grow with each transaction, as each transaction is committed to a new immutable CAR file. This results in thousands of small files, many containing duplicate or out-of-date data. If you don't provide a compaction function, the blockstore can use a default compaction function that deduplicates and preserves all the CIDs in the set, into a single file (coming soon). Your data structure can do better, as it will know which blocks can be safely discarded. Implement compaction as a callback that is passed a blockstore instance which logs any blocks that it reads. Your function should traverse your entire dataset. At the end the blockstore writes out a new unified CAR file, with links to the old CAR files. This speeds up sync and read times because it results in a single CAR file that contains all the deduplicated blocks.

The compact function above is an example of this, or see the Fireproof compaction function in the CRDT library for another example.

The blockstore will take care to update the header and write out a fresh CAR file with all the blocks. This CAR file will be faster to fetch, and new operations will be performed as a diff against this CAR file.

Compaction Cleanup

In shared storage situations, just because compaction has removed references to old files, doesn't mean there aren't other readers that would like to read them. Instead of immediately deleting old files, it deletes the files unlinked from the previous compaction. Additionally, in many connectors, the old files are simply not deleted at all, in favor of syncing well with remote clients. Running a cloud cleanup is possible, but it's not implemented yet.

Encryption

Encrypted Blockstore uses symmetric keys, and each blockstore generates its own key. This means that files are opaque to storage providers, but the key is broadcast on the metadata channel, so it's up to you how secure you want to make it. Read about the encryption model in the Fireproof docs here. The blockstore was extracted from Fireproof, so for the first releases you'll want to read the Fireproof docs for more information.

Connectors

The blockstore layer includes all the Fireproof connectors, so you can leverage all the work that has been done there, and easily sync your data to any cloud storage provider. There are always more connectors, so check here for the main collection.

  • S3 - Verified storage for CAR files, using Lambda and signed URLs
  • Netlify - CAR file storage and shared CRDT access
  • PartyKit - Cloudflare Workers for realtime sync and storage
  • web3.storage - I heard you like IPFS, so I put your IPFS blocks in encrypted CAR files on IPFS

Architecture

Read about the overall architecture in the Fireproof docs.

Contributing

Please join the Fireproof discord to discuss the project and get involved.