├── .gitignore ├── FAQ.code-workspace ├── FAQ.md ├── LICENSE ├── README.md ├── img └── Tradle-MyCloud-aws-architecture.png ├── package-lock.json ├── package.json ├── settings.json ├── test ├── hyperbeeUnion.test.js ├── mountableHypertrie.test.js ├── multi-hyperdrive.js └── ras3.test.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | .vscode -------------------------------------------------------------------------------- /FAQ.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Hypercore universe FAQ 2 | 3 | Many of the answers below are taken from the Hypercore protocol discussion forums (currently on [Discord](https://discord.com/invite/ga5hxGf) and the older forum on [Gitter](https://gitter.im/datproject/discussions)). All interpretations are ours, and so are the possible mistakes and misunderstandings. Please send corrections as pull requests, or request commit rights. Or just reach out to us over the email posted on github or on [@tradles](https://twitter.com/tradles/). Questions with partial answers are marked with **Need help with this**. Hypercore needs your help, please help us make it fully documented so that all P2P projects start benefiting from it. 4 | 5 | - [General](#general) 6 | - [What are the main components / modules / packages?](#what-are-the-main-components--modules--packages) 7 | - [What is the USP (Unique Selling Proposition) of Hypercore?](#what-is-the-usp-unique-selling-proposition-of-hypercore) 8 | - [Streaming](#streaming) 9 | - [Lego blocks for constructing distributed apps](#lego-blocks-for-constructing-distributed-apps) 10 | - [Real-time UI updates](#real-time-ui-updates) 11 | - [Large file handling](#large-file-handling) 12 | - [Bottom line on USP](#bottom-line-on-usp) 13 | - [What is offline-first local-first software?](#what-is-offline-first-local-first-software) 14 | - [What is a streaming DB?](#what-is-a-streaming-db) 15 | - [How is Hypercore different from BitTorrent, WebTorrent?](#how-is-hypercore-different-from-bittorrent-webtorrent) 16 | - [How is Hypercore different from ScuttleButt (SSB)?](#how-is-hypercore-different-from-scuttlebutt-ssb) 17 | - [How is Hypercore different from IPFS?](#how-is-hypercore-different-from-ipfs) 18 | - [more work needed to compare IPFS and Hypercore](#more-work-needed-to-compare-ipfs-and-hypercore) 19 | - [Is there a synergy between blockchain and Hypercore?](#is-there-a-synergy-between-blockchain-and-hypercore) 20 | - [Does Hypercore have a community?](#does-hypercore-have-a-community) 21 | - [Why Hypercore is not yet mainstream?](#why-hypercore-is-not-yet-mainstream) 22 | - [What is the state of P2P and its evolution?](#what-is-the-state-of-p2p-and-its-evolution) 23 | - [Who is using Hypercore P2P framework today?](#who-is-using-hypercore-p2p-framework-today) 24 | - [How integrity of the data is assured?](#how-integrity-of-the-data-is-assured) 25 | - [Can Hypercore's author change history?](#can-hypercores-author-change-history) 26 | - [Is there support for key recovery?](#is-there-support-for-key-recovery) 27 | - [Is there a regular key rotation and key replacement mechanism?](#is-there-a-regular-key-rotation-and-key-replacement-mechanism) 28 | - [Is there an authentication system?](#is-there-an-authentication-system) 29 | - [Is there an authorization system?](#is-there-an-authorization-system) 30 | - [Is there a discovery system to learn what feeds the other peer shares?](#is-there-a-discovery-system-to-learn-what-feeds-the-other-peer-shares) 31 | - [If Hypercore is a P2P Web, what is its URL format?](#if-hypercore-is-a-p2p-web-what-is-its-url-format) 32 | - [What is the biggest gotcha with Hypercore?](#what-is-the-biggest-gotcha-with-hypercore) 33 | - [Can Hypercore be backed up?](#can-hypercore-be-backed-up) 34 | - [Backup to S3](#backup-to-s3) 35 | - [Network](#network) 36 | - [What are the reliability guarantees of Hypercore protocol?](#what-are-the-reliability-guarantees-of-hypercore-protocol) 37 | - [Is network traffic encrypted end-to-end?](#is-network-traffic-encrypted-end-to-end) 38 | - [Is Hypercore a push or pull system?](#is-hypercore-a-push-or-pull-system) 39 | - [How to discover all feeds that a peer can give us?](#how-to-discover-all-feeds-that-a-peer-can-give-us) 40 | - [Does Hypercore work in the browser, on mobiles?](#does-hypercore-work-in-the-browser-on-mobiles) 41 | - [Does Hyperswarm work in browsers, on mobile?](#does-hyperswarm-work-in-browsers-on-mobile) 42 | - [Can Hypercore network protocol be extended?](#can-hypercore-network-protocol-be-extended) 43 | - [Storage](#storage) 44 | - [Can Hypercore storage be encrypted at-rest?](#can-hypercore-storage-be-encrypted-at-rest) 45 | - [Is Hypercore quantum-safe?](#is-hypercore-quantum-safe) 46 | - [Does Hypercore support zero-knowledge store / blind storage?](#does-hypercore-support-zero-knowledge-store--blind-storage) 47 | - [Does Hypercore support erasure-coding?](#does-hypercore-support-erasure-coding) 48 | - [Can data be deleted?](#can-data-be-deleted) 49 | - [Hypercore components / modules](#hypercore-components--modules) 50 | - [Hyperbee](#hyperbee) 51 | - [Help me picture use cases for Hyperbee?](#help-me-picture-use-cases-for-hyperbee) 52 | - [Personal database](#personal-database) 53 | - [Marketplace of data feeds](#marketplace-of-data-feeds) 54 | - [Lightweight Blockchain client](#lightweight-blockchain-client) 55 | - [How does Hyperbee relate to Hypercore?](#how-does-hyperbee-relate-to-hypercore) 56 | - [What are the consistency guarantees?](#what-are-the-consistency-guarantees) 57 | - [Can it serve as LevelDB replacement?](#can-it-serve-as-leveldb-replacement) 58 | - [What proves the scalability of Hyperbee?](#what-proves-the-scalability-of-hyperbee) 59 | - [Hyperswarm](#hyperswarm) 60 | - [Help me picture use cases for Hyperswarm?](#help-me-picture-use-cases-for-hyperswarm) 61 | - [Can we distinguish between peers before connecting to them?](#can-we-distinguish-between-peers-before-connecting-to-them) 62 | - [Is Hyperswarm anonymous?](#is-hyperswarm-anonymous) 63 | - [Is Hyperswarm susceptible to DDOS?](#is-hyperswarm-susceptible-to-ddos) 64 | - [Hyperdrive](#hyperdrive) 65 | - [Can you help me picture use cases for Hyperdrive?](#can-you-help-me-picture-use-cases-for-hyperdrive) 66 | - [How can Hyperdrive be shared?](#how-can-hyperdrive-be-shared) 67 | - [What are the limits on file sizes?](#what-are-the-limits-on-file-sizes) 68 | - [What proves the scalability of Hyperdrive?](#what-proves-the-scalability-of-hyperdrive) 69 | - [What is missing in Hypercore?](#what-is-missing-in-hypercore) 70 | - [Distributed Time / Clocks](#distributed-time--clocks) 71 | - [Multi-device editing with conflict resolution (CRDT)](#multi-device-editing-with-conflict-resolution-crdt) 72 | - [Collaborative (team) editing with conflict resolution](#collaborative-team-editing-with-conflict-resolution) 73 | - [Topology management](#topology-management) 74 | - [Schema / data model / data dictionary](#schema--data-model--data-dictionary) 75 | - [Identity](#identity) 76 | - [Multi-writer](#multi-writer) 77 | - [Multi-hyperdrive](#multi-hyperdrive) 78 | - [co-hyperdrive](#co-hyperdrive) 79 | - [Multi-hyperbee](#multi-hyperbee) 80 | - [Union](#union) 81 | - [Consensus / converging state](#consensus--converging-state) 82 | - [Databases](#databases) 83 | - [Collaborative editing](#collaborative-editing) 84 | - [CRDT and cloud peers](#crdt-and-cloud-peers) 85 | - [Is Hypercore multi-process-safe?](#is-hypercore-multi-process-safe) 86 | - [Which additional modules, outside of core modules, are notable?](#which-additional-modules-outside-of-core-modules-are-notable) 87 | - [Where can I learn more about Hypercore universe?](#where-can-i-learn-more-about-hypercore-universe) 88 | 89 | ## General 90 | 91 | This section is for general questions. See other sections for questions specific to individual Hypercore modules. 92 | 93 | ### What are the main components / modules / packages? 94 | 95 | - **[Hypercore protocol](https://www.datprotocol.com/deps/0010-wire-protocol/)**, secure network protocol, providing reliable peer data exchanges. 96 | - **[Hypercore](https://github.com/hypercore-protocol/hypercore)**, append-only log that can be used as-is, and is also used as a building block for other data structures. 97 | - **[Hypertrie](https://github.com/hypercore-protocol/hypertrie)**, a key-value store, which uses the underlying Hypercore and is used by other higher level services, like Hyperdrive, for files directories and metadata. You can view key-value store as a simplest possible database and it is a [common component in modern applications](https://www.consul.io/docs/intro/vs/zookeeper), and especially as embedded store in P2P applications. E.g. Ethereum uses KV store for [smart-contract storage](https://medium.com/hackernoon/getting-deep-into-ethereum-how-data-is-stored-in-ethereum-e3f669d96033). 98 | - **[Hyperbee](https://github.com/mafintosh/hyperbee)** goes much higher than Hypertrie by providing sorted range queries, and can therefore can be used to build indexed databases. It is compatible with the popular LevelUP interface, a [standard building block](https://github.com/Level/awesome#awesome-level) for databases, query engines, and even several SQL engines. 99 | - **Hyperdrive**, a component for a P2P alternative to Google Drive and Dropbox, providing team file sharing, including great support for large media files. Hyperdrive can also be mounted as a normal OS folder, with the help of FUSE interface. 100 | - **[Corestore](https://github.com/andrewosh/corestore)**, to managing many Hypercores you typically end up authoring or replicating from others. 101 | - **[Hyperspace](https://github.com/hypercore-protocol/hyperspace)**, a server that uses Corestore and Hyperdrive to provide availability and persistence to hypercore data structures. It offers a network API for using hypercore from any language. Hyperspace notches another step towards the P2P alternative for Dropbox and Google Drive. Key missing components are a permission system and collaborative editing, which is work in progress by the hypercore community. 102 | - **[Hyperswarm](https://github.com/hyperswarm/hyperswarm)**, a discovery mechanism for connecting to other peers (sort of a decentralized DNS), direct P2P connections (NAT traversal), and P2P connection management. 103 | - **[Beaker Browser](https://beakerbrowser.com/)**, a full-blown browser that also supports the Web without servers (P2P Web). 104 | 105 | ### What is the USP (Unique Selling Proposition) of Hypercore? 106 | 107 | Hypercore is an open source P2P technology, but we saw other P2P technologies, BitTorrent and Bitcoin. What is unique about Hypercore? 108 | 109 | #### Streaming 110 | 111 | Hypercore's key USP is **streaming**. You can think of it as video streaming, but now for videos and also filesystems, databases, messages, IoT signals, and any other structured data constructs. With streaming, you get: 112 | 113 | - **Almost immediate access**, even though the data is not yet fully downloaded or may never need to be fully downloaded 114 | - **Higher security**, as it provides the verifiable integrity and authentication for each data element on the wire. This is huge! A typical database, file and messaging servers stake their security on the initial connection establishment and protecting communications on the wire. But normally they do not guard against a compromised or fraudulent peer, while Hypercore does. Our evolution in this area of security needs to always examine data from remote peers in view of the potential risk and the probability of fraud. 115 | - **Higher scalability**, as you can shove your streaming database in AWS S3 and let billion people use it. 116 | 117 | This streaming point needs to be repeated again and again, as streaming data, just by itself, without any other wonderful Hypercore capabilities, may create a new class of applications, much like Netflix re-invented the movie watching. This paradigm shift is one reason why Hypercore is hard to grok for app developers, it just requires full rethinking of our current architectures. 118 | 119 | Note, when reading Hypercore docs you will find many references to sparse replication. This is the capability used for streaming, allowing a peer to efficiently request individual blocks from remote peers, instead of loading the whole remote dataset, be it a video file or a database. 120 | 121 | #### Lego blocks for constructing distributed apps 122 | 123 | Another USP of Hypercore is that it implements essential patterns of distributed systems in a reusable way, so that systems and applications are not forced to re-invent the wheel. 124 | 125 | **WAL**. All distributed systems need a [Write Ahead Log (WAL)](https://martinfowler.com/articles/patterns-of-distributed-systems/wal.html), be it databases, orchestration engines, like Zookeeper or etcd, or event streaming systems like Kafka. Every system implements its own WAL today. Hypercore generalized this pattern as an append-only-log and consistently uses it in its higher-level data structures such as Hypertrie, Hyperbee, Hyperdrive. 126 | 127 | **Time travel**. Hypercore provides universal history for all data structures which work on top of it, which gives them a universal undo-redo, rewind-replay. This capability is common in editors, but now it can be used in any application including retracing actions in a filesystem and a database. Use cases are plentiful: 128 | - [time-travel debugging](https://elm-lang.org/news/time-travel-made-easy) 129 | - for reactive applications (React, [Reflux](https://github.com/reflux/refluxjs#more-on-stores)) 130 | - customer support (to replay user actions) 131 | - building a test suite that starts from different checkpoints playing out many scenarios 132 | - forensic investigations 133 | - playing out what-if business scenarios 134 | - to support blockchain reorg in a full node 135 | - for performing risky operations, knowing you can come back to a starting point, such as installing a new version of the app and rolling back if it does not work well. Same for changing configuration of the system and rolling back 136 | - a million other use cases, all with one simple abstraction, that all users already know and love 137 | 138 | **Recovery** Same history capability can be used for classic database point-in-time recovery, DB and filesystem snapshots, and versioning for data integrity assurance, VM snapshots, filesystem and DB snapshots, container image layers, etc. etc. 139 | 140 | #### Real-time UI updates 141 | 142 | We have gotten used to how Gmail automatically refreshes UI when new email arrives or when you view email on mobile, and it is marked as read now. This capability is pervasively used by Google in Docs, Slides, and other apps. Distributed apps need to match that and Hypercore's real-time updates, including on changes on other devices or by our teammates, provides us with this capability. 143 | 144 | #### Large file handling 145 | 146 | Many applications (like chat, group chat, photo apps, social media apps and collaborative apps) need an ability to handle file sync, especially for very large media files. Those providers hold large files in the central holding area, and we never really know of they delete them afterwards, potentially violating our privacy. They also have significant limits on file sizes. Hypercore is geared really well to enable these scenarios without a central server / provider and without limits. And on top of this it adds partial sync or streaming, so data do not need to be loaded fully to be viewed / explored. 147 | 148 | #### Bottom line on USP 149 | 150 | Distributed apps need these and therefore apps using hypercore become simpler to write. 151 | 152 | Need help with this: 153 | How would one implement in Hypercore forwarding a message with a large video from one chat to another (both in a one-on-one and a group chat). 154 | 155 | ### What is offline-first local-first software? 156 | 157 | Take a look at [Jared Forsyth's criteria](https://jaredforsyth.com/posts/in-search-of-a-local-first-database/) for the above and the various products he reviewed using these criteria. The framework for this type of software is very hard to create, but it is a fresh direction away from massive aggregation of our personal data, and it is close to become a reality. Isn't it why you are here? 158 | 159 | Hypercore is built to give you full control of your data. This means, it continues to work even when you have no connectivity, when your other peers are offline, and when hosting / cloud provider closes your account. It also allows portability to other machines or hosting providers. 160 | 161 | Note for example, that Google Docs only has support for offline work in Chrome, but not in other browsers. This is how these services tie you up. The new phenomenon is de-platforming, when app provider, like Twitter closes your account, or App Store blocks your app downloads. Facebook and Twitter famously killed rich ecosystems of apps on their platforms. 162 | 163 | By relying on providers without a mobility we give up on self-sovereignty and core freedoms and become slaves of the platforms, morphing our behavior to their demands, and facilitating creation of mass surveillance systems, like happened with WeChat and other platforms in China. 164 | 165 | ### What is a streaming DB? 166 | 167 | There is a new hot area in Big Data world for querying static databases. In AWS it is Athena, based on Apache Presto engine, and SQL SELECT. CSV Files (or files in JSON, Parquet, ORC, Avro formats) are shoved into S3 and then queried by Serverless applications and by Business Intelligence packages like Tableau. 168 | 169 | This requires no databases servers and shows where Hyperbee can be very useful. 170 | 171 | Database with data stored in S3 (or the like) is very tricky to make performant. AWS Athena uses above specific formats, helps split large files into smaller chunks, has tools to prepare columnar indexes for each S3 object to enable search queries, adds extra metadata to help decide which S3 object contains the right subset of data for the query and provides a farm of servers that queue and execute your queries. S3 Select provides a simpler alternative to above, avoiding the use of server farm, but also with decreased functionality. 172 | 173 | Why is this important to understand in view of Hyperbee streaming? Because Hyperbee provides a similar capability implemented in a completely different way. It not only does not require servers, like S3 Select, its underlying protocol is optimized to load byte-ranges from the [S3 object] offset, and efficiently traverses the btree (in Hyperbee) of the trie (in Hypertrie) to avoid extra round trips when executing queries for remote data (S3 in this case). This is a so called sparse mode in Hypercore. 174 | 175 | What other applications that can we think of that can be enabled by such a server-less DB, a DB that redefines how querying is done (via sparse data propagation), a DB that embeds a replication mechanism? 176 | 177 | Some pointers to possible answers can be found when we compare a P2P source control system Git that replaced SVN and CVS which used central server. Those entrepreneurs that think "Is it possible to make a big business on this?", please note that Microsoft bought Github for $7.5B. 178 | 179 | ### How is Hypercore different from BitTorrent, WebTorrent? 180 | 181 | **BitTorrent**. Hypercore can do what BitTorrent does and more. Hypercore can do discovery and accelerated file download with bandwidth-sharing like BitTorrent, that is, the more viewers watch, the better it works. 182 | 183 | But Hypercore can do more - it is built as a data and communications framework for modern decentralized applications. 184 | 185 | 1. Applications need data structures. Hypercore provides append-only log, Hypertrie key-value store, Hyperbee database and Hyperdrive filesystem. 186 | 2. Hypercore supports data editing as a design principle. [BitTorrent enhancement BEP 46](https://news.ycombinator.com/item?id=17306106) exists for updating files, but it is limited, e.g. it does not allow updating a record in a database, so is not suitable for applications. 187 | 3. It provides full data add/edit version history which allows auditing, point-in-time-recovery, and state snapshots. 188 | 4. It provides sparse download like BitTorrent, but extends it to databases. 189 | 5. Hypercore has data access authentication. 190 | 6. Hypercore has network encryption. 191 | 192 | **WebTorrent**. WebTorrent is awesome, it pioneered BitTorrent in browsers and its is a great success, but its mission statement was just that, a BitTorrent for the Web. 193 | 194 | Note that WebTorrent's tech can be helpful to Hypercore, as it perfected peer discovery (via DHT) on the Web and it allowed a number of innovative streaming clients to emerge, which could be helpful for Hypercore applications, like Beaker Browser. 195 | 196 | ### How is Hypercore different from ScuttleButt (SSB)? 197 | 198 | [Secure Scuttlebutt (SSB)](https://en.wikipedia.org/wiki/Secure_Scuttlebutt) is a peer-to peer communication protocol, mesh network, and self-hosted social media ecosystem. Hypercore has been growing towards a more generic model of structured data (file systems, databases) synchronized over many devices. 199 | 200 | - **Durability**. Each user hosts their own content and the content of the peers they follow, which provides fault tolerance. Same as with Hypercore. 201 | - **Availability**. It suffers from less than 100% availability, since peers are not always online. This is similar to Hypercore. Always-on seeding nodes are needed. 202 | - **Integrity**. Messages are digitally signed and added to an append-only list of messages published by an author, like Hypercore. It ensures content is not tempered with as it is propagated through the network, like Hypercore. 203 | - **Consistency**. Data is distributed with a simple read-only eventual consistency, like Hypercore. 204 | - **Streaming**. SSB is not well suited for streaming, as it does not have a sparse data structure (enabled in Hypercore by Merkle trees, while SSB uses linked lists). 205 | - SSB uses a gossip protocol to sync data between peers. It does not automate peer discovery for a given dataset (e.g. via DHT), so topology must be manually managed. Hypercore uses a DHT to automate discovery of peers for exchanging of data. 206 | 207 | ### How is Hypercore different from IPFS? 208 | 209 | Both are cool open source P2P data projects that have existed for roughly the same 5-7 years. 210 | 211 | You can review Reddit discussion that makes [some good points](https://www.reddit.com/r/ipfs/comments/glnra9/hypercore_protocol/). 212 | 213 | Some key differences, [described here](https://www.datprotocol.com/deps/0002-hypercore/), are: 214 | 215 | | Feature | IPFS | Hypercore | 216 | | -- | -- | -- | 217 | |Addressing | Content-based addressing | Public-key based addressing | 218 | |Addressing stability | Dynamic address, changing as block changes | Static stable address | 219 | |Addressing granularity| each block has unique address (block's hash) | Address (pub key) corresponds to a collection of objects / files, each consisting of many blocks | 220 | |Filesystem|Low. Files are composed of lists of linked blocks, no notion of file directories, metadata, |Great. Files are managed as full-blown filesystem, with stable API and a daemon that provides REST API and [POSIX-compliant](https://en.wikipedia.org/wiki/POSIX) OS extension, so it appears to users as a regular folder| 221 | |Storage size efficiency | Great. Same block can be reused on your machine, even between files. This is usually called deduplication, or dedup. But when block changes, old block remains in storage.| Low. No block-level dedup. Change in one byte, creates a new version of the file. File-level dedup can be achieved with additional management level, called corestore. | 222 | |Mutability| Low. Originally developed for static content, but is being re-designed now. Specifically, the IPNS component attempts to provide stable hash-based address for the file (the tip of the list of blocks), but it needs to be refreshed periodically. New project IPLD is in development and aims for structured data, such as adding primitive data types, maps, lists |In Hypercore editable content was a prime design objective, supported by the internal data structures, its protocol, Change Data Capture system, APIs, etc. Structured data are supported by Hypertrie and Hyperbee | 223 | |Hyper-linking | Yes. Formal specification, called CID. |Partial. URLs work in Beaker, [Agregore](https://github.com/AgregoreWeb/agregore-browser) and [Gateway](https://gitlab.com/gateway-browser/gateway/) browsers, but formal definition for links for structured data is still in works | 224 | |Human-friendly naming| No. [IPNS](https://docs.ipfs.io/concepts/ipns/#example-ipns-setup) is not human friendly. | No. Hypercore's hyper:// URLs includes hash and is not human-friendly | 225 | |Databases|TBD. [OrbitDB](https://github.com/orbitdb/orbit-db), [ThreadDB](https://docs.textile.io/threads/), [AvionDB](https://github.com/dappkit/aviondb)|Two variants: Key-value store (Hypertrie) and LevelUP-compatible DB (Hyperbee). Embedded DB, does not require DB server. Provides unique streaming capability to greatly improve storage size and startup time. Using Hypercore data structures community produced replicated databases [KappaDB](https://github.com/kappa-db) and [multi-hyperbee](https://github.com/tradle/multi-hyperbee)| 226 | |Availability|Fairly high. IPFS community sponsors hosting of content, there are also commercial providers like [Infura](https://infura.io/) and [Textile](https://textile.io/), and Filecoin protocol to reward hosting | Low. See for example [Our Networks](https://ournetworks.ca/) page referring to both IPFS and Dat URLs and Dat URL does not open. Same [here](https://2019.ournetworks.ca/). [Homebase](https://github.com/beakerbrowser/homebase) attempted to provide hosting. [Datdot](https://github.com/playproject-io/datdot-service) project is developing blockchain-based reward system. Perhaps we need a breakthrough hosting model here, could VPS-made-simple be it?| 227 | |Startup speed| Slow | Great. Hypercore provides sparse replication for any type of data, that allows immediate streaming of both structured data and media content.| 228 | | Discovery | Uses DHT. Avoids dependency on a more centralized DNS system. IPFS uses a DHT for every single data chunk globally, which works great for dedup. However, IPFS architecture creates an enormous overhead of DHT traffic compared to the other protocols. It also fails to benefit from the assumed knowledge that peers who have one chunk of the repository you’re interested in, are likely to also have more chunks you’re interested in. | Uses DHT (via Hyperswarm service). To avoid overloading DHT, topic in DHT is usually the whole Hyperdrive with potentially millions of files. To find a file via the DHT, URL becomes drive/file. In addition, Hyperswarm provides a flexible mechanism to design your own DHT-based discovery system, e.g. discover communities, teams, people, etc.| 229 | |Directory structures and file metadata| TBD. Somewhat. IPFS simulates directories by creating files with links to other files. | Full. Hypercore does full POSIX-compliant file system emulation and therefore can be mounted natively (via FUSE) to be viewed in File Explorer, Finder and to be used from the command line as a normal file system.| 230 | 231 | **Some notes on IPFS goodies:** 232 | 233 | - [IPNS has has captured imagination of Ethereum community](https://blog.infura.io/an-introduction-to-ipfs/) to build fully decentralized apps, as most blockchain apps today still keep data and processing centralized. 234 | - Smart contracts can refer to a hash of IPFS file and since it is address is content-based and therefore immutable, it provides the deterministic behavior for the smart contract. IPFS sponsors the storage to provide the guarantee for the data to be available for the dApp that uses this contract to be operational. Very cool! 235 | - IPFS project has produced solid core libraries, like libp2p, solving many of the same issues as Hypercore's Hyperswarm. 236 | - IPFS has implementations in a number of languages, while Hypercore is only in JavaScript. Rust implementation of Hypercore was recently started and hopefully will lead to overall health of Hypercore, forcing better documented specs and more test-suits. 237 | - IPFS team runs a number of public servers that help make the network more usable. 238 | - IPLD is IPFS's sub-system to define structured interlinked data. IPLD structures implemented on top are ipld-bitcoin or ipld-ethereum. 239 | 240 | #### more work needed to compare IPFS and Hypercore 241 | 242 | Review these and structure its content for summary below: 243 | https://medium.com/decentralized-web/comparing-ipfs-and-dat-8f3891d3a603 244 | https://blog.cloudflare.com/e2e-integrity/ 245 | https://docs.ipfs.io/concepts/usage-ideas-examples/#usage-ideas-and-examples 246 | 247 | Rough outline: 248 | 249 | - Support for streaming 1) live streaming, 2) recorded content 3) or just sharing a video on a messenger app. 250 | - Integrity - See section on one Hypercore integrity. But how is the integrity of the multiple feeds achieved, e.g. metadata and content feeds in Hyperdrive, or composite feeds like multi-hyperdrive? 251 | How does IPFS support data integrity? 252 | - Granularity, not just files, e.g. with Hypercore you can do live updates in the UI in Hypercore, like Gmail does it. 253 | - Does IPFS support connection Multiplexing? 254 | - Secure Session management. Hypercore has sessions with forward secrecy. 255 | - how is NAT traversal different? 256 | - HTTPS and other gateways to provide access when other things do not work. For example, see discussion on Hypercore not working on [campus network](https://github.com/datproject/discussions/issues/87). 257 | - Download progress and health of seeded data. See [discussion here](https://github.com/datproject/discussions/issues/81). How does IPFS handle it? 258 | - Docs availability and depth 259 | - Bandwidth sharing. How does IPFS support it? 260 | - Pinning of files and dirs in IPFS. How management of local cache compares to Hypercore 261 | - URL to individual data elements: File, Resource in the database. 262 | - Change Data Capture - does not exist in IPFS, attempts are being made to create something for 4 years 263 | - PubSub 264 | - S3: https://docs.ipfs.io/concepts/usage-ideas-examples/#aws-s3-integration 265 | - Hosting - https://docs.ipfs.io/concepts/usage-ideas-examples/#ipfs-hosting-with-textile 266 | - Mobile support https://twitter.com/jarredsumner/status/1223633060551225344 267 | - How to build chat and other apps on IPFS and Hypercore 268 | - Git on IPFS or Hypercore. How P2P supports Git: https://www.ctrl.blog/entry/git-p2p-compared.html 269 | 270 | ### Is there a synergy between blockchain and Hypercore? 271 | 272 | Both blockchain and Hypercore provide verifiable data structures. But blockchain is limited to very small storage and Hypercore is limited by absence of pubic timestamping and data history, and the absence of verifiable computations. See further on this in section *"Can Hypercore's author change history"*. 273 | 274 | Here are the cases where blockchain and Hypercore already help each other: 275 | 276 | - [Ara.one](https://ara.one/): crypto rewards for content 277 | - [Datdot](https://github.com/playproject-io/datdot-substrate): hosting with crypto incentives 278 | - Bitfinex: streaming hyperbee as trading signals for crypto exchange) 279 | 280 | This use case is theoretical but is plausible: 281 | 282 | Light client to blockchain full node, using streaming hyperbee, to avoid dependence on centralized infrastructure, like Infura. Most blockchain full nodes support a very limited amount of queries by default. Many apps need a lot more, and for that infrastructure players, like Infura, have created a SaaS infrastructure. But this re-centralized the decentralized P2P setup and re-inserts a trust into a specific company. 283 | 284 | ### Does Hypercore have a community? 285 | 286 | Yes. Community is very active and helps newcomers and developers building on Hypercore. [Join it on Discord](https://discord.com/invite/ga5hxGf), open [issues on Github](https://github.com/mafintosh/hypercore), and follow core developers on Twitter [@mafintosh](https://twitter.com/mafintosh), @pfrazee, and @andrewosh. 287 | 288 | ### Why Hypercore is not yet mainstream? 289 | 290 | It is a fact that Hypercore is 7 years old and still has no runaway apps built on it. So what gives, if it is so amazing, and it is! Here is my take, aside from a general statement that making a P2P framework work smoothly is super-hard: 291 | 292 | Many P2P apps struggle as they lack availability, durability and work in the unforgiving networking environments. 293 | 294 | - **Availability**. For example, in a P2P collaborative editing app competing with Google Docs, once you close your laptop, your collaborators can't get your latest content, unless they were online when you made edits. With Google Docs, if you had a connection at the time of the last edit, the changes are available to others, even if you went offline right after. This is especially important for team work across the timezones. So some master nodes that "seed" the content are always needed in P2P applications (e.g. Hashbase), but these so called super-nodes often re-centralize things and introduce challenges for permissioning, data sovereignty, and data privacy. Availability problem remains unsolved. 295 | 296 | - **Durability**. We are spoiled with Google (and others) taking care of preserving our content. We pay a steep price of giving them everything on us, but this convenience is very hard to achieve in P2P world. Your peers may be good friends but there is no guarantee they will not lose your precious content. Many solutions are being tried, including those with Cryptocurrencies incentivizing users to keep content, but they all have technical and convenience frictions. Besides, who wants to be responsible for disseminating a potentially illegal content? Durability problem remains unsolved. 297 | 298 | - **Networking**. Current Internet, with its routing and firewalling system is just hostile to P2P connections. Although Hypercore's Hyperswarm offers an ingenious NAT hole punching, there are too many edge cases, when it does not work on mobiles, it needs workarounds in browsers and is often blocked by VPNs and corporate firewalls. This does not mean it can't be used, we just need a fallback to a trusted server acting as a proxy. But this comes at the same price of decentralization. Besides, if the user wants to send data to someone else, both devices need to be online simultaneously. 299 | 300 | Reliable P2P networking **remains unsolved**. 301 | 302 | Is there an answer to those perpetual problems of P2P? We believe there is. In crypto world the answer was found with the notion of miners. This is why some P2P projects are attempting to repeat this approach introducing their own blockchains. IPFS team's Filecoin, Storj, Theta.tv and a number of others are examples. But they are all focused on data storage. 303 | 304 | Hypercore is so much more. It is a foundation for apps, it is made for storage, content distribution, messaging, decentralized databases, etc. And it feels like it is a good match for analytics and AI as well (more on that later). 305 | 306 | Perhaps the answer to perpetual P2P reliability problems is not in copying the blockchain's mining model or just offering the crypto-incentives to host files. Maybe the answer is orthogonal, instead of looking to incentivize third-parties to keep our files, we could do it ourselves, with an always-available cloud peer, a companion to the sometimes-available personal devices we own already. 307 | 308 | Viewed this way, cloud peer is not a hosting provider, it is just a different type of a personal device. It does does not have a screen, but it is capable in a different way, it complements our other personal devices with its 100% availability, a durable storage and elastic / expandable compute and data store. 309 | 310 | Taking it a step further, the cloud peer could be a place to run many Hypercore apps that can't run on personal devices. Think cloud app store, unencumbered by the domineering Apple and Google app stores. 311 | 312 | This will make Hypercore shine! 313 | 314 | ### What is the state of P2P and its evolution? 315 | 316 | Both classes of P2P systems, blockchains and P2P data are nudging towards mass market adoption. Cryptocurrencies made huge strides in creating a new foundation for global financial system, especially evidenced by the rise of DeFi in 2020. 317 | 318 | In addition, many new projects are employing tokens as incentives mechanisms to avoid points of centralization that exist today. Examples are VPN (Orchid), Routing (PKT + CJDNS), social media (Steam), live streaming (Theta.tv), Web Browsing (Brave), Storage (StorJ, IPFS FileCoin). Several high-profile projects were shut down (Telegram TON) or have hit high resistance from governments (Facebook Libra). 319 | 320 | Crypto-currency P2P projects are a huge step ahead of data-centric P2P projects like Hypercore and IPFS, as they have found their native hosting model, in the form of miners. This makes them independent of the Cloud providers, which is essential for their survivability in the face of regulatory scrutiny. 321 | 322 | P2P Data projects do not experience such a resistance, but they have not invented their own sustainable infrastructure. IPFS is looking to Filecoin to incentivize storage providers, while meanwhile subsidizing hosting via ipfs.io. 323 | 324 | Hypercore community has produced Hashbase, Homebase, DatDot and other hosting solutions, but they have not reached significant maturity and adoption. Personal cloud peer could be an alternative to the aggregated hosting model. 325 | 326 | Almost every P2P project is still largely held back by numerous overlapping infrastructure needs for this novel tech to hit a wider market. E.g. one common problem is the management of the ownership keys. 327 | 328 | The difference between Hypercore and IPFS is that it has moved relatively slowly with its marketing. The core team chose to patiently and somewhat stealthily build the foundational technology and avoid starting the "hype cycle", that is until it is ready for prime time. Initial releases of Hypercore (then called "Dat") in 2016-2018 had scaling issues, which have been addressed by subsequent releases. 329 | 330 | Specifically, Hypercore team focused on the performance of its unique "streaming database" design (see below). Team prepares for marketing push at the end of this year (2020), starting with the Beaker ecosystem (Beaker is the Web and P2P Browser and authoring platform built on Hypercore) 331 | 332 | ### Who is using Hypercore P2P framework today? 333 | 334 | Each project building on Hypercore is stretching Hypercore's flexibility and contributes back solutions that are not yet available in the core. Then Hypercore team generalizes them and makes available for everyone. See some of the projects and their notable contributions: 335 | 336 | - Bitfinex, major crypto exchange, uses: 337 | - **Hyperswarm** in their microservices framework [Grenache](https://github.com/bitfinexcom/grenache). Bitfinex helped [extend](https://github.com/bitfinexcom/grenache-grape/pull/73) Hyperswarm DHT to improve peer discovery. Bitfinex also pushed the envelope with Hypercore team on creating the first payments framework for Hypercore. 338 | - **Hyperbee** to deliver [streaming data and signals](https://blog.bitfinex.com/dazaar/backtest-your-trading-strategies-with-bitfinex-terminal-honey-framework/) for backtesting trading strategies. Hyperbee allows Bitfinex to create a community of free and paid providers of trading signals that seed structured data like BitTorrent seeds files. 339 | - [Pushpin](https://github.com/automerge/pushpin). A local-first collaborative corkboard app. PushPin supports taking notes, and can archive web content, images, PDFs, audio, video, and any other files you might want to hang out. It can synchronize across all your devices, and doesn't require any infrastructure to operate. Pushpin's collaborative simultaneous editing is achieved with the help of [Automerge CRDT](https://github.com/automerge/automerge). 340 | - [Cobox community](https://ledger-git.dyne.org/CoBox/cobox-resources/src/branch/master/ledger-deliverables/2_work-plan/mvp/mvp-design.md), focused on enabling teams. Cobox community created a KappaDB and collaborative editing. 341 | - [Peermaps](https://peermaps.org/), building P2P alternative to Google Maps based on OpenStreetMap 342 | - [Datdot](https://github.com/playproject-io/datdot-substrate), a P2P hosting for Dat / Hypercore data structures, with blockchain-based incentives. Similar to Filecoin for IPFS. Note a command-line (CLI) [publisher tool](https://github.com/RangerMauve/hyperdrive-publisher) to publish hyperdrive to some hosting site like Datdot, and later, restore it from hosting to another location. Restored hyperdrive is writeable (make sure the original is stopped and not writing anymore or you will get a corrupt hyperdrive - see Gotchas section) 343 | - [Sonar](https://arso-project.github.io/sonar-book), distributed media archives on Hypercore. Note an interesting [bulk update](https://discordapp.com/channels/709519409932140575/727886901100675083/755723909709561856) feature discussion re:Sonar, which sounds like addressing a pain similar to serverless apps. 344 | - [Beaker browser](https://beakerbrowser.com/). Beaker is likely the most complex app on top of the hypercore today. It supports both normal web browsing (https:// protocol) and hyper:// protocol. It also aims to become a P2P social media with a way for people to post blogs, comments, likes, etc. without central entity like Facebook. It achieves that with mutual hosting, where your Beaker browser will hosts sites for people you follow. 345 | - [Agregore](https://github.com/AgregoreWeb/agregore-browser) browser, takes a different approach, aiming to be very simple and lean, moving all additional capabilities into downloadable extensions. It also expands beyond http and hyper protocols, aiming to support any P2P protocol, including IPFS. 346 | Currently: http://, dat://, hyper://, ipfs://, ipns://, gemini:// 347 | Planned: earthstar:// and eth:// 348 | - [Gateway](https://gitlab.com/gateway-browser/gateway/) mobile browser 349 | - See at the bottom of [Hypercore protocol page](https://hypercore-protocol.org/) 350 | - [Consento](https://consento.org/). A mobile digital identity app that you use to keep your data protected. Supports co-ownership of data. Protects keys to data by dividing a key into pieces, each given to some friend. To reconstruct the key, you ask some of those friends to give you their piece (let's say 2 out of 3 is enough - you choose the ratio, e.g. 3/4). 351 | - [Ara.one](https://ara.one/). ARA aims to help creators maximize the value of their work, removing third-parties, reducing hosting costs and, most importantly, attracting content consumers with rewards (via blockchain-based tokens). 352 | - See discussion forum where people [showcase their Hyper projects](https://discordapp.com/channels/709519409932140575/712037351244955809/712037741126221924). 353 | - And finally, see what [apps are built on IPFS](https://github.com/ipfs/awesome-ipfs) and new ideas [listed by IPFS community](https://docs.ipfs.io/concepts/usage-ideas-examples/). 354 | 355 | ### How integrity of the data is assured? 356 | 357 | Hypercore goes into great length to provide data integrity. For that it uses a Merkle tree hashing into it each block that is added to the append-only log. On every change the root of the Merkle tree is signed by the private key of the of this Hypercore (note that this also creates a limitation of a single writer, see later how it is overcome). When Hypercore is shared to another peer, with the help of Merkle branches it is possible to to prove authenticity and integrity of a subset of blocks, without sharing the whole Hypercore. This allows to accept partial data from the untrusted peers (as they can't fudge the data). This capability supports a number of potential use cases, like distributed caching and CDNs, bandwidth sharing, distributed files systems, streaming databases, audit trails and supervision protocols, etc. See an [interesting discussion](https://github.com/AljoschaMeyer/bamboo/issues/2) in which Hypercore integrity guarantees were challenged and defended. 358 | 359 | Append-only log also allows to recover the state of Hypercore at any prior a point-in-time, a highly desirable function in databases. It allows to preserve Hypercore backup snapshots at a particular point in time. 360 | 361 | In addition, Hypercore supports versioning of data elements, a capability highly sought after in enterprise systems. Versioning allows protect data from accidental overwrite by a human being or a broken or malicious program. It also provides auditability and regulatory compliance. 362 | 363 | #### Can Hypercore's author change history? 364 | 365 | An actor could decide to revert Hypercore to a previous state, and share this fork. This could also be used in the attack where attacker aims for the initial data gets to either get deleted or destroyed by backups. Another possibility is for the author to rewind and serve different version of the history to different peers. See a [community discussion on this subject](https://gitter.im/datproject/discussions?at=5d9d962e973587467320b241). 366 | 367 | The required protection can be achieved by sealing Hypercore's root on public blockchain, utilizing its immutability and secure timestamping properties. Hypercore also does not guarantee long-term write-once storage. See explanation how audit trails benefit from [such services added on top](https://techblog.bozho.net/audit-trail-overview/). 368 | 369 | As Hypercore signing key is rotated with [multi-key](https://github.com/mafintosh/hypercore-multi-key), we need a proof that the new key is a valid successor from the old one. Different applications might use different algorithms for such a transition, and recipients of hypercore need a way to verify the code for this algorithm was not altered and was executed properly, but without running the code themselves. Smart contracts is one way of doing this, and Zero Knowledge provable computation is an [emerging new option](https://medium.com/starkware/hello-cairo-3cb43b13b209). 370 | 371 | Why can't recipients run the code themselves, like they do when verifying Merkle tree and signature in Hypercore today? Because the key rotation algorithm may involve processes that recipient can't repeat, like contacting a 3rd party for key recovery, or not having access to some private data that was used by the algorithm's code, but can't be shared. For reference on Zero Knowledge see this [question on StackExchange](https://crypto.stackexchange.com/questions/37581/why-are-zk-snarks-possible-in-laymans-terms). 372 | 373 | Need help with this. 374 | 375 | ### Is there support for key recovery? 376 | 377 | No. But a community solution and other open source projects exist that can possibly be adapted. 378 | 379 | Note that corestore makes this easier as it introduces Master key (and generates deterministically the keypairs for Hypercores it manages). It is much easier to manage one key than many, one per Hypercore. 380 | 381 | Key recovery is essential need for any P2P applications, and the same need for Bitcoin, as the user may only rely only on themselves for key management. 382 | 383 | - Community solution: [secret into N parts and allows restore with M of N replicas](https://github.com/jwerle/hyper-secret-sharing) 384 | 385 | - Full blown framework for this exists, called [Dark Crystal](https://darkcrystal.pw/) 386 | 387 | - A number of implementations of [Shamir secret sharing in JS](https://github.com/topics/shamir-secret-sharing?l=javascript&o=desc&s=stars) exist 388 | 389 | For reference, see how open source app [Consento](https://consento.org/) does it. 390 | 391 | ### Is there a regular key rotation and key replacement mechanism? 392 | 393 | Yes, for ephemeral session encryption keys. 394 | No, for Hypercore log, but can be added on top with the help of [Hypercore-multi-key](https://github.com/mafintosh/hypercore-multi-key) module which allows to switch to a new keypair. It is your responsibility to sign the new key with the old to establish the secure continuity, and to verify this signature on receiving nodes to prove the legality of key rotation. Perhaps this can be added as a [hypercore extension](https://github.com/mafintosh/hypercore-extension-rpc)? 395 | 396 | ### Is there an authentication system? 397 | 398 | Yes. Each Hypercore feed has a corresponding public / private key pair. 399 | 400 | 1. The receiving feed must prove it knows sending feed's publicKey. 401 | 2. There is an extension that allows to [prove you own feed's publicKey](https://github.com/substack/hypercore-authenticate-session-extension). 402 | 3. There is a hook to registered a custom feed authenticator. 403 | 404 | ### Is there an authorization system? 405 | 406 | Yes, but it has limitations: 407 | 408 | - **Level of granularity** is a hypercore. For example, if you like to give file access to one person and not to the other, you need to put this file in a separate hypercore to be able to achieve that. This works for small number of files, and is an approach used by Hypermerge for [Pushpin](https://github.com/automerge/pushpin). Note that you start hitting limits on performance with too many hypercores file handles used and limits on replication of many hypercores between nodes. 409 | 410 | - **Flexibility**. Access control is based on revealing the Public key for your hypercore to a peer who needs access. Since Public key is baked directly into the hypercore data structure, once it is revealed, there is no taking back the access. 411 | 412 | See a [community discussion on this subject](https://discordapp.com/channels/709519409932140575/727886901100675083/762787502456963101) with one idea being to use **per-file encryption**. You can replicate all of the hypercore but have separate keys for individual records or files. This fits the project management apps, small team collaboration with light-weight documents, but is not suitable for large file sizes. 413 | 414 | Need help on this. 415 | 416 | ### Is there a discovery system to learn what feeds the other peer shares? 417 | 418 | Yes. Managed by Corestore, or by the community-provided multifeed. 419 | 420 | Need help with this. 421 | 422 | ### If Hypercore is a P2P Web, what is its URL format? 423 | 424 | URL looks like this `hyper://[+]/[][?][#]` where `public-key` is the address of the hypercore feed, `version` is an optional numeric identifier of a specific revision of the feed (also called index or seq, a block number in the append only log), and `path` `query` `fragment` are akin to HTTP URLs (though `query` has no defined interpretation). Formal schema is defined by [this specification](https://github.com/hypercore-protocol/hyp/blob/master/proposals/0002-hyper-url.md). Beaker browser is the primary way such URLs are used. 425 | 426 | There is a proposal for [Strong linking](https://github.com/datproject/dat/issues/976) which would add a `hash` to URL. This is a hash of the hypercore at a specified `version`. This would lock down the history of the hypercore at that `version`. 427 | 428 | To understand why this link is strong, you need to know that every time a new block is added to hypercore feed, a new root hash corresponding to all the data in hypercore, up to current position, is calculated and saved in hypercore (this hash is also referred to as the Merkle tree root hash or just tree-hash). Now, should the author, by mistake or intentionally, rewind their hypercore to `version - 1` or earlier, and fill it with different data, the hash of the hypercore at `version` will be different and it will now not match the hash given in the URL. 429 | 430 | Note that the hypercore can and will continue to be appended to after the point referred to in the URL. This is normal and fine, and URL will continue to work, referring to a particular point-in-time in data history. But it does not mean that the strong URL creates an obligation for the author not to rewind the hypercore. It only means that such change can be discovered by whoever has received strong URL. 431 | 432 | Applications can use [this module](https://github.com/mafintosh/hypercore-strong-link) to construct and verify strong links. 433 | 434 | Use cases for strong links include listing files in a [module's manifest](https://gist.github.com/pfrazee/c13b86f84485aced69a1509a00b12e66), cross-linking between JSON attributes in Hyperbee object and a file in Hyperdrive (e.g. for a file attached to an email). 435 | 436 | ### What is the biggest gotcha with Hypercore? 437 | 438 | - You can create conflicting forks of a hypercore log by first copying the hypercore feed directory to another machine along with its private key and then writing into hypercore copy while making updates in the original. There is an idea how to address this with [self-healing hypercores](https://gist.github.com/martinheidegger/82dbf775e3ff071d897819d7550cb3d7). 439 | 440 | - If you rewind the feed that is replicated, [replicas will stop syncing](https://discord.com/channels/709519409932140575/709522092424429742/765662438103842866). 441 | 442 | ### Can Hypercore be backed up? 443 | 444 | Yes. see [Hypercore archiver](https://awesome.datproject.org/hypercore-archiver) as a starting point, but more work is needed: 445 | 446 | #### Backup to S3 447 | 448 | Backup to s3 is not supported yet. This [underlying module's](https://github.com/random-access-storage/random-access-s3) does not have the write method implemented yet. This is work in progress, tracked by [this issue](https://github.com/tradle/why-hypercore/issues/1). 449 | 450 | ## Network 451 | 452 | ### What are the reliability guarantees of Hypercore protocol? 453 | 454 | Hypercore [requires the underlying transport](https://www.datprotocol.com/deps/0010-wire-protocol/) to provide the following guarantees: 455 | 456 | - reliable delivery (no dropped or partial messages) 457 | - in-order delivery of messages 458 | 459 | Those are satisfied by TCP, WebSockets, WebRTC, QUIC and uTP 460 | 461 | Hypercore itself adds: 462 | 463 | - no missed messages, no duplicates (note that TCP and others above provide this only on a single connection, but not across interrupted connections) 464 | - efficiency, a single connection is used for multiple channels 465 | - persistence of messages 466 | 467 | ### Is network traffic encrypted end-to-end? 468 | 469 | 1) Yes for Hypercore and 2) no for Hyperswarm. 470 | 471 | Hyperswarm uses uTP over UDP to connect to DHT nodes and for NAT traversal (hole-punching). It can use [Noise protocol](https://github.com/mafintosh/noise-network), but doesn't today [as it will cause extra round trips (RTT)](https://discordapp.com/channels/709519409932140575/727886901100675083/757704436289372225). Hyperswarm also does not authenticate peers. Note that TLS 1.3 achieves 0-RTT on re-connections, and QUIC achieves 0-RTT on the first connection. See [analysis and mitigation](https://noise.getoto.net/tag/0-rtt/) of the replay-attacks for these 0-RTT protocols by CloudFlare. Cloudflare open sourced [QUIC / HTTP/3 implementation in Rust](https://github.com/cloudflare/quiche) (so it may be able to run in WebAssembly in Node and browser). See alternative [0-RTT in Noise](https://noisesocket.org/post/1/), which removes dependency on SSL certificates. 472 | 473 | Hypercore uses Noise protocol for authentication and encryption. Noise is the protocol designed as part of Signal Messenger and is now used by WhatsApp, WireGuard, Lightning, I2P, etc. 474 | 475 | A new channel is open for each Hypercore and multiple channels use the same connection, which is great. What is cool is that with the help of Noise, each channel gets its own encryption and keys are rotated to achieve forward secrecy (attacker who cracked this session's key will have to crack it again for the next session). 476 | 477 | Note, as always with end-to-end encryption, you need to watch out for the cases when you introduce a proxy in the middle, for example to deal with overly restrictive firewalls. The best approach is for the Proxy to be blind, just passing encrypted streams between peers. 478 | 479 | ### Is Hypercore a push or pull system? 480 | 481 | Normally updates are pulled by the peers. Protocol supports a Push-ing data as well but it is [not exposed in the API today](https://discordapp.com/channels/709519409932140575/709519410557222964/755797065879257178) 482 | 483 | ### How to discover all feeds that a peer can give us? 484 | 485 | Hypercore is not like Kafka, which is one big log. With Hypercore you usually have many Hypercore logs. So you need a way to manage them and discover what hypercores other people have shared with you. 486 | 487 | The bootstrapping mechanism for this is to find peers, a Hyperswarm. But it is not enough, thus several discovery systems were designed, and the main one is corestore. Simpler one, is multifeed created by community, but it assumes all feeds are public. 488 | 489 | ### Does Hypercore work in the browser, on mobiles? 490 | 491 | Yes. Hypercore is transport-independent. One can use TCP/IP, WebRTC to peers, WebSockets to server. 492 | 493 | ### Does Hyperswarm work in browsers, on mobile? 494 | 495 | Not directly, but community solutions exist. 496 | See the [issue for this](https://github.com/hyperswarm/hyperswarm/issues/62). 497 | 498 | It is a hard problem, note that [WebTorrent works in the browser](https://webtorrent.io/docs), but ["DHT in browser"](https://github.com/webtorrent/webtorrent/issues/288), even after 7 years of discussions, is still not realized. 499 | 500 | Current solution, [advised by Hypercore team, uses 2 servers for signaling](https://github.com/RangerMauve/hyperswarm-web). 501 | 502 | Summary of a problem and an alternative solution: 503 | 504 | 1. **No UDP in browsers**. Other transport protocols create connection establishment delays (extra round-trips), which make DHT too slow to be practical. Delegation to signaling servers challenges privacy. 505 | 506 | 2. **Corporate firewalls may block UDP**. Although the hope arises with QUIC / HTTP/3 gaining traction as it is using UDP on TLS port 443. 507 | 508 | 3. **No peer discovery on Cell Phone networks**. Cellphone networks employ symmetric firewalls that block direct P2P connections (although UDP works, NAT hole punching does not). This affects mobile apps and PCs on HotSpots. With 5G proliferation, more applications operate on cell networks, making progress for direct P2P connections unlikely. 509 | 510 | 4. **DHT state needs stability**. Peers that come and go (browser tabs) lose DHT state and need to recreate it (although this can be overcome with caching state in browser's database). Peers that change their IP address too often, destabilize DHT. This is the case of cell networks. 511 | 512 | 5. **Porting Web and mobile environments**. See a number of issues still pending resolution to make Hyperswarm and Hypercore [work in react-native](https://dat.discourse.group/t/dat-and-react-native/184). These problems can be solved. 513 | 514 | ### Can Hypercore network protocol be extended? 515 | 516 | Yes. The [protocol](https://github.com/hypercore-protocol/hypercore-protocol) is formalized with protobuf and supports [defining extensions](https://github.com/hypercore-protocol/hypercore-protocol/#stream-message-extensions). 517 | 518 | See community video that explains the [Extensions system](https://youtu.be/HyHk4aImd_I?list=PL7sG5SCUNyeYx8wnfMOUpsh7rM_g0w_cu&t=4379). Community projects like Cobox and others are using it already. 519 | 520 | Possibly useful are [abstract-extension](https://github.com/mafintosh/abstract-extension) and [hypercore-extension-rpc](https://github.com/mafintosh/hypercore-extension-rpc). 521 | 522 | ## Storage 523 | 524 | ### Can Hypercore storage be encrypted at-rest? 525 | 526 | Yes, offered by community solutions. You will need explore their limitations. See some below: 527 | 528 | - [Block-level encryption](https://github.com/little-core-labs/hypercore-xsalsa20-onwrite-hook) 529 | 530 | - [Cobox Hypercore Encryption](https://ledger-git.dyne.org/CoBox/cobox-resources/src/branch/master/ledger-deliverables/2_work-plan/mvp/mvp-design.md). 531 | 532 | - [hypercore-encrypted](https://www.npmjs.com/package/hypercore-encrypted), a wrapper around hypercore. 533 | 534 | ### Is Hypercore quantum-safe? 535 | 536 | Need help with this. 537 | 538 | Hypercore uses [Noise-protocol](https://github.com/emilbayes/noise-protocol) which implements the Noise_*_25519_ChaChaPoly_BLAKE2b handshake, meaning Curve25519 for DH, ChaCha20Poly1305 for AEAD and BLAKE2b for hashing. 539 | 540 | Handshake and transport encryption are documented by [@Frando](https://github.com/Frando) as part of his implementation of Hypercore in Rust. 541 | 542 | - ED25519 is used for signatures, and is not quantum-safe. 543 | - BLAKE2b with 512-bit hashes that is considered [quantum-safe](https://cryptobook.nakov.com/quantum-safe-cryptography). 544 | - XSalsa20 is used as transport cypher and [seems to be quantum-safe](https://crypto.stackexchange.com/questions/70492/how-resistant-are-stream-ciphers-like-salsa20-or-chacha-in-a-post-quantum-world). 545 | 546 | ### Does Hypercore support zero-knowledge store / blind storage? 547 | 548 | Yes, offered by community solutions. 549 | the above terms refer to an encrypted replica kept by a friend or a services provider, like [SpiderOak](https://spideroak.com/one/), but can't be read by them. 550 | 551 | Current solutions are provided by the community: 552 | 553 | - [ciphercore](https://github.com/telamon/ciphercore) 554 | 555 | - [Cobox community](https://ledger-git.dyne.org/CoBox/cobox-resources/src/branch/master/ledger-deliverables/3_mock-up/technology/architecture.md) 556 | 557 | ### Does Hypercore support erasure-coding? 558 | 559 | No. 560 | Erasure coding is used to recover data from a subset of overall amount of replicas, and is especially important in hosting across many providers, where none of them has a full version of data, so even if the encryption is cracked, they are not able to read the data. 561 | 562 | Open Source S3-compatible object storage, e.g. provided by [Min.io](https://docs.min.io/docs/minio-erasure-code-quickstart-guide.html) has erasure coding. 563 | Cloud providers sometimes offer a virtualized file system over multiple replicas. Open Source Ceph offers it and so does AWS with EFS (note that Ceph is not easy to manage). 564 | 565 | ### Can data be deleted? 566 | 567 | [Somewhat](https://discordapp.com/channels/709519409932140575/709519410557222964/755404488415772746) - you can [clear() your content locally](https://github.com/hypercore-protocol/hypercore#feedclearstart-end-callback), but if someone replicated it already, you can’t force them to clear. Also, internal data integrity records are still kept, but they do not leak any data (Merkle tree hashes are kept, so you can keep appending data to your log even if you clear the contents). Use cases: 568 | 569 | - Chat. You can delete a chat message locally. To delete the chat message at recipient(s) need to send a custom some message “please delete this”. 570 | - Mobile. You can delete photos from mobile to save space, but keep them on a replica (your other PC or a Personal Cloud). 571 | 572 | ## Hypercore components / modules 573 | 574 | ### Hyperbee 575 | 576 | #### Help me picture use cases for Hyperbee? 577 | 578 | Use cases for embedded replicated streaming DB are plentiful. 579 | 580 | ##### Personal database 581 | 582 | A database that is automatically syncing between all personal devices, but without the help of Apple, Google or any other central provider. For Cloud this could be a serverless personal-use replacement for AWS DynamoDB (Azure Cosmos, etc.), while providing **complete isolation** of data in a multi-tenant execution environment. Some use cases: 583 | 584 | - Multi-device Contact list. 585 | - Multi-device Calendar. 586 | - Multi-device Bookmarks. 587 | - Multi-device list of installed apps and their respective settings. 588 | - Multi-device chat and group chat. See [Cabal](https://cabal.chat/), an attempt to do just that. 589 | - Multi-device email front-end? It is a tall order, but we do need to stop giving Google all our mail. We need to find a backend that takes care of retrieval and sending, spam, virus scanning, and then loads data into Hyperbee + Hyperdrive + [Tantivy search engine](https://github.com/tantivy-search/tantivy). 590 | 591 | ##### Marketplace of data feeds 592 | 593 | Note what Bitfinex is doing with trading data and signals (see above), and extrapolate it to other types of structured data. 594 | 595 | ##### Lightweight Blockchain client 596 | 597 | Hyperbee could very well be a killer app for accessing blockchains. 598 | 599 | One of the persistent problems with the blockchains is that mobile and web applications have to rely on full-node servers as trusted gateways, a contradiction to blockchains' trustless value proposition. SPV wallets were designed to solve this problem, but they are (https://www.reddit.com/r/ethereum/comments/avk7ew/is_spv_of_eth_value_transfers_possible/ehg5wud/), are not so lightweight and are anemic as they can't answer all the questions client apps have. 600 | 601 | This led to the emergence of services like [Infura](https://infura.io/customers/compound). Hyperbee could be more lightweight and more flexible than SPV. SPV protocol is usually confided to specific proofs, e.g. that a transaction was included in the blockchain. But it can't answer queries like show me 'all transactions involving a specific blockchain address'. Hyperbee can run arbitrary queries against the blockchain node (providing indexes were added). To avoid trusting one Hyperbee, imagine a number of independent Hyperbee providers that all return chunks of data for the same query, a core capability of Hyperbee. With this We have restored a trustless access to blockchains. 602 | 603 | #### How does Hyperbee relate to Hypercore? 604 | 605 | Hyperbee uses Hypercore as an underlying storage and a replication mechanism. The cool thing is that one replication stream [can carry many Hypercores](https://discordapp.com/channels/709519409932140575/709519410557222964/755415844808556594), which can carry Hyperbees, Hypertries, and Hyperdrives. 606 | 607 | To manage multiple hypercore feeds, with permissions, use the corestore. 608 | 609 | #### What are the consistency guarantees? 610 | 611 | Hyperbee, like any other Hypercore-based data structure is single-writer. That means when it is replicated, it is replicated as-is, and eventually reaches the same state. See multi-hyperbee that builds on top of hyperbee and offers consistency in a multi-master scenario (each node / peer making changes to objects in the same multi-hyperbee, even the simultaneous changes to the same object). 612 | 613 | #### Can it serve as LevelDB replacement? 614 | 615 | Yep. Needs to be wrapped into [Hyperbeedown](https://github.com/andrewosh/hyperbeedown) and fed into [LevelUP](https://github.com/Level/levelup). 616 | 617 | This is awesome as there are many databases that work on top of the LevelUP API exposed by LevelDB. One example is AWS DynamoDB emulation on top of LevelDB. See its [replacement with Hyperbee](https://github.com/tradle/dynalite/). Some tests are still failing, but it is getting there. 618 | 619 | #### What proves the scalability of Hyperbee? 620 | 621 | Hyperbee is still in Alpha, but perhaps we can stress-test it on loading the whole of the Ethereum blockchain and indexing it in different ways. This Hyperbee could provide a valuable service to the community. We could even put its snapshots in S3, or IPFS for that matter, and let it be streamed. Note that Google BigTable [provides this service](https://cloud.google.com/blog/products/data-analytics/introducing-six-new-cryptocurrencies-in-bigquery-public-datasets-and-how-to-analyze-them). 622 | 623 | There is also a number of benchmarks for LevelDB (e.g. [here](https://github.com/maxogden/leveldb-benchmarks)) that community can help running with Hyperbee, since Hyperbee is LevelUP compatible. 624 | 625 | We need your help!! 626 | 627 | ### Hyperswarm 628 | 629 | Hyperswarm is a key element of Hypercore system that allows to discover network addresses of the peers by topic names using DHT. 630 | 631 | Hyperswarm also allows peer's network address discovery on local network (LAN) via mDNS broadcasts. [nDNS](https://en.wikipedia.org/wiki/Multicast_DNS) is a protocol used by Apple [Bonjour](https://en.wikipedia.org/wiki/Bonjour_(software)) for [AirDrop](https://apple.stackexchange.com/questions/24885/use-the-airdrop-network-to-access-a-computer) and is standardized by RFC 6762. 632 | 633 | #### Help me picture use cases for Hyperswarm? 634 | 635 | Ideas that fit Hyperswarm's mission to help discover peers and connect to them without using any servers: 636 | 637 | - **Decentralized DNS**. This allows to find and establish direct connections to peers. In that way it serves as a P2P variant of DNS. Like DNS it provides network location independence. Like DNS it allows to store small data in location records. Unlike DNS it does not require any configuration. So it is well suited for non-technical users. Unlike DNS it does not depend on a service provider - it is fully decentralized. Actually there is still a dependency on a list of bootstrap servers, but they are not fixed, like DNS root servers, and any Hyperswarm peer can bootstrap from the bootstrap servers it trusts (see [bootstrap](https://github.com/hyperswarm/hyperswarm#swarm--hyperswarmoptions)). 638 | 639 | To be precise, DNS system has another function which Hyperswarm does not replace. DNS is providing a friendly recognizable name for the IP address. We register today this so called domain name via some domain registrar, which is a commercial entity that is working with the root registrar (.com, .io) to rent domain names. This part is [very hard to decentralize](https://en.wikipedia.org/wiki/Zooko%27s_triangle). DHT does not help here. Namecoin was the first to solve Zooko triangle puzzle, and [Ethereum ENS smart contract](https://docs.ens.domains/) is now well on the way to be adopted as a decentralized solution for this problem. 640 | 641 | - **Avoid central signaling servers**. For example, a [video chat over WebRTC](https://twitter.com/pfrazee/status/1248744869419458561) requires a [STUN server](https://www.callstats.io/blog/2017/10/26/webrtc-product-turn-server) but with Hyperswarm it is avoided, increasing privacy and avoiding dependency on service providers. 642 | 643 | - **Connect to peers sitting behind a firewall**, such as home routers, which otherwise can't otherwise connect directly to each other. This can be used for video chats or any other P2P traffic (Hyperswarm's huge value here is so called NAT hole punching, the algorithm is in [DHT-RPC package](https://github.com/mafintosh/dht-rpc/blob/master/lib/io.js#L47-L68)). Keep in mind this does not work on mobiles (and behind some corporate firewalls), and requires a fallback to a relaying proxy (e.g. This post says [30% of P2P connections need TURN proxy](https://www.callstats.io/blog/2017/10/26/webrtc-product-turn-server)). 644 | 645 | Relaying proxy is a potential loss of privacy point. What if we could use a **personal** cloud peer, not a 3rd party service as such a proxy? 646 | 647 | - Server-less Contact Tracing on DHT. See this idea described in detail [in this paper](https://eprint.iacr.org/2020/398.pdf). 648 | 649 | - Hyperswarm is also a Publish Subscribe system, in a way. 650 | Need help on this. 651 | 652 | #### Can we distinguish between peers before connecting to them? 653 | 654 | Need help with this. 655 | 656 | No. 657 | Once you write your first Hyperswarm and print all the new peers joining it, you will likely notice all kinds of peers that have nothing to do with you. Who are they? For a website and public media like Twitter or YouTube-style application, it is totally fine. But in a security-focused application you might get concerned. 658 | 659 | Any hypercore-savvy person will argue that this is ok, as you will not be sharing any data. To access the Hypercore feeds you still need to know their publicKey. But the fact is, you still need to connect to all peers to figure out if you even want them. This is not efficient and can present some surveillance challenges. 660 | 661 | This can be very useful: 662 | 663 | - To know if peers are readers or writers, or filter them out with some cryptography-based primitives, and avoid connecting to those that you do not trust. 664 | 665 | - Load balancing between peers. It would ridiculous for the Router design to expect the Router to connect to peers to determine who to forward request to. 666 | 667 | - Can Sybil attacks and DDOS on DHT, mentioned in [Hyperswarm blog](https://pfrazee.hashbase.io/blog/hyperswarm), be prevented if DHT itself could be selective about the peers? 668 | 669 | #### Is Hyperswarm anonymous? 670 | 671 | No. Let's explore what is revealed. 672 | Hyperswarm announces IP and Port of the peer to allow other peers in P2P network to connect with them. Hyperswarm's DHT holds that data, so any observer could simply collect this information. The observer will also learn the topic this peer is advertizing. Aside from that no other information is leaked. Is it worse than DNS? In DNS servers also announce their name and address to the world. But clients do not, while in Hyperswarm they do. On the other hand, topic name is more private than in DNS, it is just some hash, not a human-readable name. 673 | 674 | So what can be done to protect IP addresses in DHT? 675 | 676 | - Hyperswarm can be improved to encrypt data in DHT, and this way only the peers that know some shared secret could find each other. 677 | 678 | - Potentially [I2P](https://dat.discourse.group/t/feature-support-i2p/62/6) can be used in the future. 679 | 680 | #### Is Hyperswarm susceptible to DDOS? 681 | 682 | [Yes](https://pfrazee.hashbase.io/blog/hyperswarm). One potential approach is to have Hyperswarm peers sign data in DHT, and refuse to accept unsigned data. Other measures could include the approach used by Bitcoin, to prove that you have spent some CPU time (e.g. 3-5) when announcing a topic in DHT (crypto-puzzle). This is the area of active research. 683 | 684 | A resilience to DDOS could be enhanced by creating a large network of provably legitimate DHT nodes. 685 | 686 | ### Hyperdrive 687 | 688 | Hyperdrive provides many of the hard to create components to replicate the functionality of Dropbox and Google Drive. Beaker Browser adds the UI to it. 689 | 690 | Hyperdrive is a library and can also [run as a service](https://github.com/hypercore-protocol/hyperdrive-daemon), that is accessible via an API and can show up as a [normal directory on your disk](https://github.com/hypercore-protocol/hyperdrive-daemon#fuse) (this part works on MacOS and Linux, with Windows in works). 691 | 692 | #### Can you help me picture use cases for Hyperdrive? 693 | 694 | - Dropbox, Google Drive, etc. alternative without a central server. These systems are used by millions of teams and everyone's privacy is compromised. In addition, Hyperdrive adds magic powers of media streaming and bandwidth sharing with peers (Hyperdrive is helped by a companion Hyperspace service (daemon), which runs like a Dropbox service in the background). 695 | 696 | - Distributed, replicated file system, an alternative to NFS, Samba/cifs or sshfs. Distributed file system is essential component of Cloud services, e.g. many serverless applications [can't be built without it](https://lumigo.io/blog/unlocking-more-serverless-use-cases-with-efs-and-lambda/). Hyperdrive could provide a better isolation of personal data in a multi-tenant Cloud environment. 697 | 698 | - A building block to create a real alternative to Object Storage (S3). 699 | 700 | - Distribution of software and large datasets to / from / between Data Centers, as described in [older eBay paper](https://tech.ebayinc.com/engineering/bittorrent-for-package-distribution-in-the-enterprise/), and any case of Big Data fan-out. 701 | 702 | #### How can Hyperdrive be shared? 703 | 704 | 1. Underlying mechanism is built into Hypercore, and works for all data structures that use it: Hyperdrive, Hypertrie and Hyperbee. You can share the read-only version of your whole hypercore with others, by giving them the public key of the Hypercore. 705 | 706 | 2. Hyperdrive itself is actually 2 hypercores for directory structure and metadata, and for file content. So to share it you use the above URL (need confirmation for that). 707 | 708 | 3. Hyperdrive also support mounts. It allows to include other people's drives under your your own Hyperdrive as a folder. Mounts are still read-only though, But this allows people to continue editing files on their Hyperdrives and all people that mounted it will see updates in real-time. 709 | 710 | 4. Hypertrie also supports mounts, which allows a Key-Value store supported by the whole team. [Mountable Hypertrie](https://github.com/andrewosh/mountable-hypertrie) is actually what Hyperdrive uses underneath for mounts. 711 | 712 | #### What are the limits on file sizes? 713 | 714 | There is no inherent size limits. As a demo Hypercore team put a complete Wikipedia mirror with tens of millions of files on Hyperdrive and it reads very fast. 715 | 716 | #### What proves the scalability of Hyperdrive? 717 | 718 | The whole of Wikipedia was loaded into Hyperdrive and it provides a decent speed for finding articles. This also stressed Hypertrie, as Hyperdrive used Hypertries for managing file system directory structure and file metadata. 719 | 720 | Need help with this: what other public datasets would be good to load into Hyperdrive? How about the whole of the Web? 721 | 722 | ## What is missing in Hypercore? 723 | 724 | Hyperdrive provides many key primitives (lego blocks) needed in distributed systems. But it lacks certain others that you will need to build yourself for a full P2P application, and to avoid frustration it is better to be aware of them upfront. 725 | 726 | ### Distributed Time / Clocks 727 | 728 | To reach the same state peers in distributed systems need to synchronize clocks in view of computer time drift and network disconnects from other peers. Typical solutions include [generation / causal clocks](https://martinfowler.com/articles/patterns-of-distributed-systems/generation.html), and newer hybrid clocks like [HLC](https://jaredforsyth.com/posts/.hybrid-logical-clocks/)). 729 | 730 | - Every message, including the heartbeat message needs to carry this time 731 | - Point-in-time recovery and snapshots could utilize this stronger time 732 | - Secure timestamping is necessary for legal documents, copyright, for many forms of compliance, dashboard cameras, gig economy contracts (e.g. state of rental property at entrance), and today can be achieved with [IETF RFC 3161 and 5816](https://www.linkedin.com/pulse/why-secure-electronic-signature-requires-timestamp-ott-sarv/). But it relies on a trusted provider of time. [eIDAS law in Europe](https://blog.eid.as/tag/time-stamping-authority/) requires such Trusted Provider for strong digital signatures to be accepted by the court. In the P2P applications we are always looking to decentralize infrastructure that is currently centralized, and Blockchain is the first and the only known currently [decentralized secure time keeping system](https://grisha.org/blog/2018/01/23/explaining-proof-of-work/). Hypercore community could benefit from it greatly. For example a public service that [proves the time](https://www.jamieweb.net/blog/proof-of-timestamp/) with a 1-day precision could be quite cheap to build, and could itself be built on a Hypercore. 733 | 734 | Any volunteers to help us build it? 735 | 736 | ### Multi-device editing with conflict resolution (CRDT) 737 | 738 | - CRDT are a new way for achieving consensus. It is super cool as it allows distributed actors to arrive at the same state, like blockchain nodes, but with transactions that are applied in a different order. 739 | - CRDT must use the above clocks for structured data and for document editing (docs, slides). 740 | - With multiple devices comes a need for per-device key management, key coordination and revocation. Key management in general is missing in Hypercore. 741 | 742 | ### Collaborative (team) editing with conflict resolution 743 | 744 | - Structured data and document editing need different approaches. 745 | - With teams we also need access control. 746 | 747 | ### Topology management 748 | 749 | Devices have different storage capacity (cloud vs mobile, storage durability (e.g. browser vs desktop app vs cloud), and different networks (fast, metered, capped, etc). CPU and RAM capacity might also be factors. Replication and storage management algorithms might take all above into account. For example, for sharing media from mobile, replication algorithm should only upload each block once, to the peers with a better connection. 750 | 751 | ### Schema / data model / data dictionary 752 | 753 | As apps have a need to understand each other. Data modeling emerges as a necessity as automation needs arise, as AI needs to know what data it is trained on, as searching in database needs a guiding UI. If that does not happen, then data models get buried inside the apps. Data models become a top priority in systems that allow users to interact with the data directly. Hypercore leaves this area to what it calls a "userland". 754 | 755 | ### Identity 756 | 757 | Full apps will need some form of identity management. Hypercore provides the basic elements, a keypair per each core (and in corestore master key for corestore and generated keys per core), but identity of a peer is much more than the identity of the core. 758 | 759 | ### Multi-writer 760 | 761 | Hypercore is engineered as a set of small single-purpose primitives (lego blocks) to be highly composable. This is a methodology used by Linux community and it allows to have simple mental model about the building blocks, and to create purpose-made systems. This is opposite to systems that attempt to serve many use cases upfront and become over time very hard to manage and secure. The example is OpenVPN which recently is being replaced with Wireguard and a family of single-purpose modules that are built for it. In Hypercore this approach is especially evident in the case of multi-writer. 762 | 763 | Hypercore, and data structures on top (Hypertrie, Hyperbee and Hyperdrive) are single-writer primitives. This means only one private key can have access to write into each. 764 | 765 | But when hypercore is replicated to personal devices (phone, tablet, PC, cloud peers) each device needs to have its own private key, which means now multiple writers need to write into hypercore data structures. Same need arises when you want to collaborate with peers, with shared documents, files and databases, as you want peers to **edit** same objects and **search** across them. 766 | 767 | To support such use cases multi-writer modules can be composed on top. 768 | 769 | *Note that supporting multi-writer in core modules [has been requested many times](https://github.com/hypercore-protocol/hyperdrive/issues/230) but it turns out one size does not fit all. [HyperDB](https://github.com/mafintosh/hyperdb) is an abandoned multi-writer database that became too complex as it tried to provide discovery, networking, authorization, conflict resolution, etc. in one package, serving many masters and satisfying none.** 770 | 771 | So simple compositions, that are themselves composable is a better approach, see below: 772 | 773 | #### [Multi-hyperdrive](https://github.com/RangerMauve/multi-hyperdrive/) 774 | 775 | Allows several nodes share and write to the same drive. This is useful for multi-device support or in a team. It supports not just one, but a set of shared drives. At the moment it provides a simple last-write-wins (LWW) conflict resolution. It scales well on writes, sames as the hyperdrive and adds a fairly small performance penalty on reads (which grows O(n) with the number of drives). Drives are added to the shared set via an API at start. Multi-hyperdrive is network-agnostic. No authorization mechanism for individual files is provided. 776 | 777 | Note the difference with hyperdrive mounts. Mounts allow read-only access to peer's drives, while multi-hyperdrive allows both sides to write. With Mounts, the path to a files changes, with a mount point added to it, e.g. drive with path `/parlor` mounted at `/fred` will require need to be accessed via `/fred/parlor`. Multi-hyperdrive will keep the path the same, which is more natural, but it has a downside. If you are sharing between your own devices, this is perfect. But if you are sharing in a team, directory path `fred` may already be used by someone. 778 | 779 | #### [co-hyperdrive](https://github.com/RangerMauve/co-hyperdrive) 780 | 781 | Builds on top of multi-hyperdrive and allows to add / remove shared drives (writeable bi-directionally) to the set. 782 | 783 | #### [Multi-hyperbee](https://github.com/tradle/multi-hyperbee/) 784 | 785 | A single replicated hyperbee, not a set, no discovery, networking agnostic, no authorization. Provides convergence to the same state with automatic conflict resolution (CRDT), effectively creating a leaderless multi-master. Scales well on reads (same as hyperbee). Simple, one replicated hyperbee, not a set, no discovery. Network-agnostic, no authorization mechanism. 786 | 787 | #### Union 788 | 789 | A union of Hyperbees can be [easily constructed](https://github.com/tradle/why-hypercore/blob/master/test/hyperbeeUnion.test.js) utilizing another lego block, [a streaming sort-merge](http://github.com/mafintosh/sorted-union-stream). 790 | 791 | Need help with this: 792 | 793 | Cobox community has created a number of compositions: 794 | 795 | - **[local indexes for remote feeds](https://discordapp.com/channels/709519409932140575/709519410557222964/756414542669676573)**. This approach works well for groups of up to 50 members. 796 | - **KappaDB**. [Cobox community](https://ledger-git.dyne.org/CoBox/cobox-resources/src/branch/master/ledger-deliverables/3_mock-up/technology/architecture.md) produced a multi-writer DB [KappaDB](https://github.com/kappa-db). 797 | 798 | ### Consensus / converging state 799 | 800 | Normally a single person will not be using 2 devices simultaneously. Yet because of the loss of connectivity changes made on each device may need to be merged. This includes documents, filesystems and databases. It becomes much more difficult in multi-user scenarios. 801 | 802 | In distributed systems, of which P2P is a subclass, reaching the same state is a hard problem with a long history. The reason it is hard was only recently formally described as a [CAP theorem](https://en.wikipedia.org/wiki/CAP_theorem). The holy grail of distributed systems is to reach the [ACID](https://en.wikipedia.org/wiki/ACID) guarantees of SQL databases - Atomicity, Consistency, Isolation and Durability. But SQL databases mostly operated on a single machine or on a closely managed cluster. Over the Internet the connectivity can be spotty and malicious actors abound. 803 | 804 | Handling bad actors became a specialty of blockchains, and it was a huge win for the P2P movement. Yet, since blockchains serve as shared databases for the whole world, they come with limitations. They have high transaction costs, low throughput, can store only the miniscule amounts of data, and can't hold or process private data. To overcome these limitations some applications re-centralize, adding web servers, application servers and DB servers. Others try to remain pure P2P by using IPFS or Hypercore. 805 | 806 | Algorithms, that tackle bad connections and reliability issues with compute and storage, **but not bad actors** have evolved from the highly complex Paxos to a simpler RAFT, to PBFT, and finally, in the last 5-7 years, to CRDT. CRDT is very lightweight and allows to operate leaderless multi-master, allowing each master to merging edits on the edge without any central coordination. This means no operators to run central service (Zookeeper, etcd, etc.) and handle complex cluster failure modes. CRDT, combined with HLC clocks, increases throughput with wait-free transaction ordering by avoiding any coordination between masters. 807 | 808 | Note that CRDT is quietly being used by AWS DynamoDB and Azure Cosmos - and if it is good enough for those web-scale databases, it is good enough for P2P. 809 | 810 | Here is a [great introductory talk](https://www.youtube.com/watch?v=M8-WFTjZoA0) on CRDT (here is [another one](https://www.youtube.com/watch?v=B5NULPSiOGw)) and an [advanced one](https://www.youtube.com/watch?v=PMVBuMK_pJY). 811 | 812 | For NodeJS the prime candidate is [Automerge](https://github.com/automerge/automerge), but there are others like [YJS](https://github.com/yjs/yjs) and [Delta-CRDT](https://github.com/peer-base/js-delta-crdts) (please share if you know a better one). [CRDT is implemented](https://github.com/orbitdb/crdts) and used by OrbitDB that runs on top of IPFS. 813 | 814 | CRDT matches perfectly multi-device and collaborative editing use cases of P2P: 815 | 816 | #### Databases 817 | 818 | CRDT provides new data types with **magic properties** that allow automatic merging of independent edits. Anyone who sent Word documents by email to their teammates or lawyers knows the "joys" of redlining. Any developer knows the chores of merging conflicts that Git could not auto-merge. Good news for the humankind, no more conflicts with CRDT. But it also means that we need to adapt our databases to keep history of changes, sequence them properly, use stable IDs of our peers and coordinate clocks. Hypercore multi-writer modules will incorporate CRDTs for this by the end of 2020. 819 | 820 | #### Collaborative editing 821 | 822 | P2P needs a mechanism to match Google Docs (and Slides, Sheets, Diagrams, etc.) that allow multiple people edit the same document simultaneously. Google Docs uses an older Operational Transforms algorithm that is highly complex and allows only 2 concurrent edits (which Google overcomes by having a central server quietly merging in the background). A special branch of CRDT for sequences (LSEQ is one of them) was developed recently. Hypercore multi-writer modules will incorporate CRDTs for this by Q1 2021. 823 | 824 | #### CRDT and cloud peers 825 | 826 | Personal cloud "device" is always on. This resolves a common P2P issue when you edited a document, closes your laptop or an app on the phone. Cloud peer can make your changes available for others. But consensus still need to be reached and without a Google in the middle. 827 | 828 | - CRDT allows multiple cloud peers to sync with other devices and sync between themselves 829 | - An always-on cloud peer allows CRDT to merge edits from multiple devices in real-time, so that conflicting edits don't accumulate, matching Google experience but without Google reading all our documents. 830 | 831 | Note that CRDT resolution works smoother when clocks between machines are well synchronized. NTP existed for years, and now there is a new iteration [NTS, published by Cloudflare](https://blog.cloudflare.com/announcing-cfnts/). Normal clocks are not enough though. Need causal clocks too. See more on that later. 832 | 833 | ### Is Hypercore multi-process-safe? 834 | 835 | We know it is single-writer. But can the same writer accidentally screw up the Hypercore while being executed from a second processes on the same machine, like from a Nodejs cluster process or a Nodejs worker thread? If so, it will present a significant design challenge in Serverless environment. 836 | 837 | For reference, note that LevelDB is not multi-process safe, but [LMDB is](https://dev.doctorevidence.com/lmdb-in-node-29af907aad6e). 838 | 839 | Need help with this. 840 | 841 | ## Which additional modules, outside of core modules, are notable? 842 | 843 | | Module | Author | Description | 844 | | -- | -- | -- | 845 | | **[Hypercore-peer-auth](https://github.com/Frando/hypercore-peer-auth)** | [Franz Heinzmann (@Frando)](https://github.com/Frando) | Verifies that remote hypercore is an original author (is in the possession its secretKey). Also tells you which remote hypercore has just connected to you. This comes useful when you join a hyperswarm topic and get connections from many peers. Hyperswarm itself does not tell you the identity of the peer. Note, that since each hypercore has its own identity (keypair), you can designate one of the hypercores of the peer to represent the identity of the peer. Hypercore-peer-auth module implements an extension to hypercore protocol, and is an example of how you can create your own extensions. Note that there are channel extensions and [stream extensions](https://github.com/hypercore-protocol/hypercore-protocol#stream-message-extensions). Channel is encrypted, so its extensions are secure, not so with stream extensions. 846 | | **[Multi-key](https://github.com/mafintosh/hypercore-multi-key)**| [Mathias Buus (@mafintosh)](https://github.com/mafintosh) | Allows to rotate a keypair for hypercore | 847 | | **[Streaming sort-merge](http://github.com/mafintosh/sorted-union-stream)** | [Mathias Buus (@mafintosh)](https://github.com/mafintosh) | This works great with several hyperbees. It is composable, so you can use more than 2 hyperbees | 848 | 849 | ## Where can I learn more about Hypercore universe? 850 | 851 | 1. Visit the [Hypercore Protocol site](https://hypercore-protocol.org/). 852 | 853 | 2. In the summer of 2020 there was a [Dat Conference](https://www.youtube.com/playlist?list=PL7sG5SCUNyeYx8wnfMOUpsh7rM_g0w_cu) (Dat was renamed to Hypercore this year). You can see the breadth of discussions that took place, both on tech and the opportunities. 854 | 855 | 3. [Workshop at the 2020 summer Hypercore / Dat Conference](https://github.com/RangerMauve/dat-workshop) with sources and video. 856 | 857 | 4. [Hyperbee 'P2P indexing and search' workshop at the 2020 fall Nodeconf conference](https://github.com/hypercore-protocol/p2p-indexing-and-search) 858 | 859 | 5. [Kappa workshop](https://github.com/kappa-db/workshop) is a great basic intro, we [forked it](https://github.com/tradle/hypercore-workshop) to update to new materials and shift focus to core Hypercore modules. 860 | 861 | 6. Read [old FAQ](https://docs.dat.foundation/docs/faq) (before project was renamed from Dat to Hypercore). 862 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tradle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | We believe that Personal Computing is entering the new major disruption cycle, which will be based on [software defined Networks](https://github.com/tradle/simplecloud/blob/main/README.md), software-defined Compute and software-defined Storage for personal use. 2 | 3 | According to this vision, Tradle is evolving its MyCloud platform, designed for banks to manage their own data without giving this data away to SaaS companies. MyCloud will continue to serve banks, and for their clients we are building a personal cloud offering, under the working title, CloudPal. See the primer on [CloudPal here](https://github.com/tradle/simplecloud/blob/main/cloudpal.md). 4 | 5 | This article is covering the design for personal Data using our chosen framework, Hypercore. Hypercore allows us to think of the CloudPal as one of the personal devices in the multi-device world, and help manage data across all of them. You can find a lot more information on Hypercore in this [rapidly growing FAQ](FAQ.md). 6 | 7 | To dive deeper, take a look at the [development plan](https://github.com/tradle/why-hypercore/issues). 8 | 9 | - [What is Hypercore?](#what-is-hypercore) 10 | - [Monopolies are not good](#monopolies-are-not-good) 11 | - [P2P tech is hard to create, but it is finally entering mainstream](#p2p-tech-is-hard-to-create-but-it-is-finally-entering-mainstream) 12 | - [Why Tradle on Hypercore?](#why-tradle-on-hypercore) 13 | - [We need a flexible foundation for Personal and SME Applications](#we-need-a-flexible-foundation-for-personal-and-sme-applications) 14 | - [What was the Initial Impulse?](#what-was-the-initial-impulse) 15 | - [Local Data Centers have lost to AWS and other Hyperscalers, so what is the Point?](#local-data-centers-have-lost-to-aws-and-other-hyperscalers-so-what-is-the-point) 16 | - [Tradle on AWS can be adapted to move to Hypercore](#tradle-on-aws-can-be-adapted-to-move-to-hypercore) 17 | - [Assembling a modern Cloud stack on Hypercore](#assembling-a-modern-cloud-stack-on-hypercore) 18 | - [What do we gain from Hypercore, at the high level?](#what-do-we-gain-from-hypercore-at-the-high-level) 19 | - [Data Sovereignty](#data-sovereignty) 20 | - [Data Continuum](#data-continuum) 21 | - [Rethinking AWS from P2P first-principles](#rethinking-aws-from-p2p-first-principles) 22 | - [Simplifying Cloud for individuals](#simplifying-cloud-for-individuals) 23 | - [Undo and redo](#undo-and-redo) 24 | - [Forego the Object store](#forego-the-object-store) 25 | - [Database](#database) 26 | - [Identity](#identity) 27 | - [What makes Hypercore suitable for Tradle?](#what-makes-hypercore-suitable-for-tradle) 28 | - [Data Durability, Load balancing, and Mobility](#data-durability-load-balancing-and-mobility) 29 | - [Data Durability](#data-durability) 30 | - [Load Balancing](#load-balancing) 31 | - [Mobility](#mobility) 32 | - [Durability, Load Balancing and Mobility for Databases](#durability-load-balancing-and-mobility-for-databases) 33 | - [Data Integrity, Digital Signatures and Compliance](#data-integrity-digital-signatures-and-compliance) 34 | - [Uniformity](#uniformity) 35 | - [Recovery](#recovery) 36 | - [Shared files and folders (like Dropbox or Google Drive)](#shared-files-and-folders-like-dropbox-or-google-drive) 37 | - [Shared document editing like Google Docs and Google Slides](#shared-document-editing-like-google-docs-and-google-slides) 38 | - [Offline-first, local-first](#offline-first-local-first) 39 | - [Use cases and potential apps](#use-cases-and-potential-apps) 40 | - [Group messaging](#group-messaging) 41 | - [Direct media sharing](#direct-media-sharing) 42 | - [Local file sharing, like Apple Airdrop](#local-file-sharing-like-apple-airdrop) 43 | - [Research data and DB sharing](#research-data-and-db-sharing) 44 | - [Live streaming](#live-streaming) 45 | - [CDN](#cdn) 46 | - [Headless CMS](#headless-cms) 47 | - [Continuous Backup](#continuous-backup) 48 | 49 | ## CloudPal Reasoning 50 | 51 | ### Monopolies are not good 52 | 53 | The web's hub and spoke technical architecture is the reason we now have monopolies with billions of users, the size of which was never seen before. 54 | Billion dollar fines for them seem to be issued every year now, but this is not a solution. 55 | We need a new P2P architecture and Hypercore is it. 56 | 57 | ### P2P tech is hard to create, but it is finally entering mainstream 58 | 59 | P2P architectures and applications are much harder to create than centralized, they are often less efficient (BitTorrent's download super-speeds are an exception) and investors are less interested, as they do not produce monopolies that create outsized returns. 60 | 61 | Yet, we live in the age of the rise of decentralized and P2P tech renaissance. Notable is the whole crypto space, which helped launch decentralized VPNs, like [Orchid](https://www.orchid.com/), decentralized live streaming like [Theta.tv](https://theta.tv), etc. 62 | 63 | Some P2P technology is inherently very slow. Luckily Hypercore is designed for real-time, and works in time-sensitive video streaming and decentralized filesystems and database scenarios. 64 | 65 | ## Why CloudPal on Hypercore? 66 | 67 | It has been our long held belief that data not services should be the first class citizens on the Web. Enterprises understand that, and Data Governance is at the core of well run businesses. Yet on the Web and mobiles we still build applications that hoard the data. From this, the notion of data sharing between applications arises. Tim Berners-Lee and W3C failed to change that with Semantic Web. May be they failed due to obscure and complex formats they invented, or may be we collectively did not yet then get scared enough of a rapidly centralizing Web. 68 | 69 | So what if we built apps the other way around, in a way where all data would belong not to the app / website owner, but to the user. App would visit the data, help the user with some insights and either go away, or go to sleep. Data would remain user's and never get exfiltrated, or possessed by the app. Later a different app can visit the same data and draw new insights. No need for sharing data between apps, as when data leaves you, you lose control over it. This does not mean we never share data, we do, with friends and teams, but not with apps and their owners. 70 | 71 | Hypercore, with its data-first design fits this paradigm. 72 | 73 | ### We need a flexible foundation for Personal and SME Applications 74 | 75 | We explored a plethora of systems for privacy-focused first-person data management. Most systems are focused on chasing AWS with Enterprise-centric offerings. Existing solutions for Personal Cloud are focused just on data (mimicking Dropbox), not applications. Others, like NextCloud, are full blown packages of applications not cloud-native and not designed as a foundation for building new apps. 76 | 77 | Our requirements include Personal Cloud that will then evolve to SME Cloud. We have already built an Enterprise Cloud system for the banks (we call it MyCloud), so we know the higher end requirements quite well. Hypercore will give us many things we need, such as reliable messaging for our Transactional Messenger, data sharing for teams, will provide the Data Integrity we need for compliance, and Change Data Management for building search indexes, Data Lakes and to run ETL jobs. 78 | 79 | What we have found in Hypercore is a highly modularized and composable framework, with many standard APIs, and reusable abstractions that allow developers to plug in alternative implementations. Unlike any competing P2P framework, Hypercore's design allows for a much wider class of privacy-first applications, which is on our roadmap. 80 | 81 | Hypercore is also like a puzzle of a hundred pieces. It is its strength but also its complexity. It is not easy to build on top of today, and this limits its potential. At Tradle we would hope to help higher-level developers with tools to build P2P apps really fast. 82 | 83 | ### What was the Initial Impulse? 84 | 85 | Initial impulse for the design exploration of Hypercore has arrived from the Data Sovereignty demands we got from the banks and the governments for our open source Digital Identity product. With Tradle server, called MyCloud, we had decentralization figured out, or so we thought. 86 | 87 | Tradle MyCloud is installed by its users under their own AWS account, so they end up with their own Tradle installation. It is serviced by Tradle support staff, but from the outside, without any access to their data or operations. We call it Private SaaS. 88 | 89 | We started Tradle with the notion that Credit Bureaus, and any other identity aggregators, are a systemic risk for our economy and personal safety. With the Equifax disaster, in which Equifax lost detailed financial information of all working Americans, we now know for sure that data aggregators are evil. 90 | 91 | So with MyCloud we solved data ownership issue. But Data Sovereignty became a new and powerful phenomena for us as AWS data centers are present in only 15 countries at the moment (similar with other Cloud Hyperscalers). Besides, in many countries storing sensitive data in the data center owned by a foreign operator is not permitted. And, interestingly, in Europe it has recently [become a sore point](https://www.bbc.com/news/technology-53418898) and a new policy for [Data Sovereignty is forming](https://ec.europa.eu/digital-single-market/en/policies/building-european-data-economy). It is not surprising, as Snowden's revelations showed, that whole population data, centralized in a small number of corporate hands, are a powerful magnet for governments. The temptation is just too great. And now there is also a [US Cloud Act](https://en.wikipedia.org/wiki/CLOUD_Act). 92 | 93 | ### Local Data Centers have lost to AWS and other Hyperscalers, so what is the Point? 94 | 95 | In the past 10 years there were many failed attempts to create Open Source AWS alternatives. 96 | 97 | The most massive effort was OpenStack. But AWS is a very fast moving target and simulating all that it does (160+ services and counting), became impossible. OpenStack was designed by Rackspace initially, but later adopted a multi-stakeholders committee process, which turned out to be very complex to manage and just plain too slow to chase AWS. 98 | 99 | Luckily, in the past 10 years major components of Cloud's virtualization and networking have become part of the standard Linux kernel or have emerged as Open Source projects. This is KVM and WireGuard, FireCracker and Containers, S3-compatible storage like Min.io and scalable low-management databases like Facebook’s RocksDB and CockroachDB, built on top of it, by former Google Spanner engineers. 100 | 101 | Many other innovations are happening on the Edge of the network, outside of the AWS stronghold. This is Fly.io serverless offering with FireCracker and Redis, CloudFlare serverless with Node V8 Isolates (like Workers Threads) and Key-Value store, Fastly.io serverless with blazingly fast WebAssembly. 102 | 103 | **And finally, there is an under-appreciated gem of Hypercore.** 104 | 105 | ### Tradle on AWS can be adapted to move to Hypercore 106 | 107 | Luckily Tradle uses only a small subset of AWS services, and this makes the task manageable. See our current AWS architecture: 108 | 109 | ![AWS architecture](/img/Tradle-MyCloud-aws-architecture.png) 110 | 111 | What makes this task even more practical is that [Tradle MyCloud](https://github.com/tradle/mycloud) already uses an emulation layer for AWS, called [Localstack](https://github.com/localstack/localstack). Localstack is sufficient to run and debug Tradle MyCloud, but does not robust underlying components to run in production environment. 112 | 113 | We need a solid base that can be deployed in any Local Data Center, removing our dependency on AWS. This document outlines the steps we can take by replacing one by one its components with the production-ready components for **singe-tenant** use cases, with replication, reliability, security and performance guarantees. 114 | 115 | ## Assembling a modern Cloud stack on Hypercore 116 | 117 | Matching a vast array of AWS services is a daunting task, and it is not our goal. We choose to focus on a Simple Cloud foundation, sufficient to run a Personal Cloud. Communications, virtualization, networking and other aspects of the Cloud will be described in [Simple Cloud](SimpleCloud.md) document, while here we focus on Hypercore, that can help address many aspects of data, sharing and communications. 118 | 119 | ### What do we gain from Hypercore, at the high level? 120 | 121 | #### Data Sovereignty 122 | 123 | Ability to offer Data Locality / Residency / Sovereignty for private-first offline-first P2P applications, with functionality and convenience similar to and surpassing the centralized, Google-style apps. 124 | 125 | #### Data Continuum 126 | 127 | Ability to build apps that work in the Cloud **and** on local PCs and mobiles. Many cloud-native systems only work in the Cloud, which limits their scope of use, and makes them extremely difficult for developers to debug and test, leading to a huge loss in productivity. AWS is notoriously difficult in that respect. 128 | 129 | ### Rethinking AWS from P2P first-principles 130 | 131 | The core idea of Personal Cloud is to give individuals access to Infrastructure and Platform level of services (IaaS and PaaS), which are sold only to organizations. Direct infrastructure ownership provides the level of isolation, security and privacy that most closely resembles PCs at home. This Personal Computing platform would then allow to build truly personal apps, which we abandoned with SaaS, giving SaaS vendors access to all our data. 132 | 133 | To make infrastructure acceptable to people, it needs to be simple. 134 | 135 | ### Simplifying Cloud for individuals 136 | 137 | We must use abstractions users already know. 138 | 139 | #### Undo and redo 140 | 141 | With the help of Hypercore any type of data, document, database, files can support undo / redo right off the bat. Versioning of files like undo / redo. 142 | 143 | #### Forego the Object store 144 | 145 | S3 Object Store today is the staple of cloud services. But it is very different from the file system that people already understand. Users will get lost in storage classes, and different ways of working with them. Storage classes have capabilities, guarantees and costs, defined by durability, access time, ability for random access, immutability, searches, replication to other regions, etc. 146 | 147 | We should implement these capabilities without asking users to sign up for some additional services, and understand upfront their different levels of service and costs involved. 148 | 149 | For example, the following policies could apply: 150 | 151 | - Least used files should move to cold storage automatically, and should be available from cold-storage immediately (albeit with slower access). Sparse replication will come very handy here. 152 | - Photo albums are much less sensitive to the loss of one of thousands of pictures, than personal databases. Videos could be moved to cold storage more aggressively. Hypercore's streaming from cold storage comes handy here. 153 | - IoT signals could go to cold storage directly. IoT signals never need to be edited, so can use cheaper non-editable storage (S3 for example. only allows to replace objects, which helps it to be cheaper). 154 | - Delete protection. Hypercore offers versioning that allows to undo deletes. Deleted files could be moved to cold storage, and auto-removed after 30 days. 155 | 156 | With that said, storage classes and other capabilities above should be offered to app developers on the granular level with the help of file / folder metadata, while preserving file system abstraction. 157 | 158 | #### Database 159 | 160 | Personal database, that people would use for simple planning and tracking should behave in a robust and most predictable way. It should support multiple devices sync out-of-the-box, seamlessly, behind the scenes (using Hypercore's native replication capabilities). 161 | 162 | Underlying syncing should use protective measures, such as conditional writes (update only if version read is not different) to avoid overwriting data, incremental adds so that operations are commutative, and with CRDT and causal clocks to facilitate conflict-free merges when a person made conflicting changes. 163 | 164 | Users interact directly with data, database is just a file, like xls. The viewer and editor for database are built in. 165 | 166 | #### Identity 167 | 168 | Identity is a big subject and Tradle has spent years getting deep understanding of it. Cloud services offer a [multitude of identity services](https://aws.amazon.com/identity/) which are complex and not suitable for personal use. Yet identity is critical for building apps and controlling access to data. 169 | 170 | The way mobile apps and web apps ask us to create an app-specific identity, with a new login and password, is insane. Reuse of passwords and their general weakness is one of the reasons we have many data breaches. 171 | 172 | Identity should be intrinsic to our actions in the Cloud, and we should not need to manage it so much. With Personal Cloud Tradle is redefining person's relationship with the apps, making data always personal, it does anymore belong to the apps. Apps are visitors, helpers, service providers, they come to our home, Personal Cloud, do what we ask them to do, and go away, taking nothing of ours with them. In this setup, identity changes its meaning to the app. 173 | 174 | More on this later, but note that WeChat's vast apps ecosystems is greatly enabled by identity. 175 | 176 | ### What makes Hypercore suitable for Tradle? 177 | 178 | ### Data Durability, Load balancing, and Mobility 179 | 180 | #### Data Durability 181 | 182 | Hypercore has built-in capabilities for data replication. This can be used to replicate files and databases in-Data Center (DC) and inter-DC. This provides durability of data, with the resilience to hardware failures. 183 | 184 | #### Load Balancing 185 | 186 | We can build load balancing, routing requests to another machine to perform computation on the desired data replica. It also adds to durability, as it allows scheduled and emergency hardware maintenance by forwarding traffic to other machines and Data Centers. 187 | 188 | #### Mobility 189 | 190 | It can help migration of data to allow users to move to another Data Center at will. 191 | 192 | ### Durability, Load Balancing and Mobility for Databases 193 | 194 | It is not enough to have just file replication. Replicating databases is much harder. Replication of databases is provided by Hypercore and it can be used for durability, load balancing and mobility. It also can be used for analytics applications that are performed on a replica, offloading the operational DB. 195 | 196 | Hyperbee is compliant to Level API, which allows it to become a drop-in replacement for AWS DynamoDB, with a [Dynalite adapter](https://github.com/mhart/dynalite). This is simpler for personal use, while for teams, and for e-commerce we will need to explore additional Hypercore tech for streaming sort-merge, see below. 197 | 198 | ### Data Integrity, Digital Signatures and Compliance 199 | 200 | Protects every data item from unauthorized modifications and assures that the old data is intact (Thus is similar to blockchain, where all transactions are added to a Merkle tree and new blocks linked to old ones). 201 | 202 | In Hypercore, the writer digitally signs any addition or change. Previous versions of the data are kept and can be retrieved later. This creates a very nice audit trail. 203 | 204 | Note that it still lacks secure timestamping and allows the author to roll back to a previous version and start again, without the peers that arrived after the fact noticing it. Both can be prevented with a blockchain-based binding (e.g. such as used by Tradle). 205 | 206 | This can further benefit from [qualified digital signatures](https://en.wikipedia.org/wiki/Qualified_digital_certificate) compliant to eIDAS in EU, equivalent US laws [E-Sign Act](http://www.gpo.gov/fdsys/pkg/PLAW-106publ229/content-detail.html) and [UETA](https://www.uniformlaws.org/committees/community-home?communitykey=2c04b76c-2b7d-4399-977e-d5876ba7e034&tab=groupdetails)), and other jurisdictions that accept digital signatures in the court of law. This would also be critical for forensic investigations in regulated industries and for criminal investigations (This lack can be alleviated by Tradle with its finance-grade identity management and associated digital signatures to alleviate this). 207 | 208 | ### Uniformity 209 | 210 | Hypercore allows to discover peers in the network and connect to them to consistently and reliably exchange all types of data. Hypercore uniquely works for messaging, files, huge media, IoT streams and databases. Uniquely, it allows to stream all those data, a notion that existed for media, but was not attempted before for databases. 211 | 212 | ### Recovery 213 | 214 | Hypercore uniquely works for both files and databases. Provides point-in-time restore from any past versions of the data state. This simulates both the DynamoDB point-in-time backup / restore and S3 object versioning. This capability can also be used as snapshots, as it allows to checkout the store, tagged at a particular version. This may be used for devops and for development. It can also be used in forensic investigations for regulated industries and in criminal investigations. 215 | 216 | ### Shared files and folders (like Dropbox or Google Drive) 217 | 218 | Hyperdrive, one of Hypercore components provides the core capabilities. It allows to share a drive with the family, friends and teams. To compete with Dropbox and Drive, it needs durability, typically associated with the Cloud. Inherent replication capability of Hypercore comes handy as any sharing with peers increases the durability, but it is not enough. What is missing also is mobile and browser implementations and easy UI, permission system, notifications, etc. 219 | 220 | ### Shared document editing like Google Docs and Google Slides 221 | 222 | Because Hypercore is designed for immediacy of real time data exchanges, it can be used for simultaneous editing. Its P2P nature allows it to work without a central site. What is also needed is intelligent document merge. There are two community projects that provide the missing capability. [YJS](https://github.com/yjs/yjs) and [Hypermerge](https://github.com/automerge/hypermerge). 223 | 224 | Like with the Hyperdrive, the remaining issue to be solved is the always-online nature of competing non-P2P services. 225 | 226 | ### Offline-first, local-first 227 | 228 | All data is available when offline. Messages and media (of any size) are delivered from mobiles to server and back with full reliability, in the presence of intermittent or rare connectivity. 229 | 230 | ## Use cases and potential apps 231 | 232 | ### Group messaging 233 | 234 | Using publish / subscribe capability of Hypercore (TBD - need to describe further). 235 | 236 | ### Direct media sharing 237 | 238 | Supports large-size media. E.g. 150mb file can’t be shared on WhatsApp or Skype. Sharing between two users over the Internet, while bypassing the server (in most cases), makes it faster, cheaper, more secure, protecting other essential freedoms and more resilient against surveillance. 239 | 240 | There is still work to be done for making Hypercore more private, e.g. improve Hyperswarm to not reveal IP addresses when not on VPN, perhaps with the help of [I2P](https://geti2p.net/en/). 241 | 242 | ### Local file sharing, like Apple Airdrop 243 | 244 | This is supported by local peer discovery (with the help of MDNS). Allows sharing on the local network, without going to the internet. This is another privacy and security enhancing capability. 245 | 246 | ### Research data and DB sharing 247 | 248 | Support for super-large file sizes is important for scientific data sets. Support for BitTorrent-stile download acceleration by downloading from multiple universities simultaneously is also a critical capability. Allows verifying integrity of chunks of data loaded in parallel from untrusted peers. 249 | 250 | But let's not forget Hypercore's totally unique capability for Database streaming, which allows Petabyte-scale DB served remotely, without a database server. 251 | 252 | What new killer apps will this novel database approach create? Here is one possible example, a Database CDN. We are used to static files served on the edge, but a Hyperbee data structure can be [piped onto AWS S3](https://github.com/mafintosh/hypercore-archiver), and streamed from it without a server, with the help of this [module](https://github.com/random-access-storage/random-access-s3). 253 | 254 | Any Database Server can be accessed remotely and many can serve huge databases. But a server needs a machine, and human resources associated with its operational management. Such costs are detrimental for pay-per-use model of the cloud and especially to a new popular with developers class of Serverless applications. 255 | 256 | Let's repeat this point again: Hypercore's database structure gives us an ability to access the database remotely, without a database server, and without a database client, too. This point needs to sync in so that engineers can pause and ask themselves the question - how will this reshape [multi-tier applications](https://en.wikipedia.org/wiki/Multitier_architecture)? What applications will benefit from this the most? 257 | 258 | ### Live streaming 259 | 260 | Hypercore enables live streaming as allows to upload video and for viewers to start downloading media, before the whole file is uploaded. It also allows to pause, rewind, fast-forward and view from any point. 261 | 262 | 1. Concerts and podcasting. Note that although critical, Hypercore is not a complete set of capabilities needed for live streaming apps, e.g. they need video transcoding, micro-payments. Note [Dazaar](https://blog.bitfinex.com/dazaar/dazaar-how-the-internet-should-be-open-free-and-hyper-scalable/), a Hypercore team's recent collaboration with Bitfinex on micropayments for streaming apps based on Hypercore. 263 | 2. Security cameras: at the door or inside (nanny cam). Such non-P2P apps are a gross privacy concern. 264 | 3. Other Home sensors. 265 | 4. Car signals and cameras. Cars are expected to generate the majority of data on the Internet, at the rate of terabytes per hour. 266 | 5. Wearables signals (fitness, health, location for child / elderly monitoring) 267 | 6. Building and Equipment signals streaming. 268 | 7. Drone cameras. 269 | 8. Other **IoT** signals 270 | 271 | Note that live streaming also needs always-online capability for content sharing, like any other P2P apps. 272 | 273 | ### CDN 274 | 275 | Hypercore can be used to build a CDN for distributing files to the edge, and distributing load between the replicas. It is static storage friendly. Files and databases can be served from a static storage, as in CDNs. Additionally, due to BitTorrent-like functionality, it can help CDN in the following ways: 276 | 277 | 1. Saves CDN bandwidth costs by bandwidth sharing, turning media watchers into uploaders 278 | 279 | 2. Accelerates download as it allows to load chunks from multiple peers simultaneously. This is especially important for 4K, VR and 3D printing content. 280 | 281 | 3. Real-time incremental CDN updates. Many CDNs take significant time to replace old files. And many require the full flush of current files. Hypercore can help optimize both with immediate updates and Change Management events. 282 | 283 | Note that Hypercore team still has work to do on efficient editing of large files. Editing existing files is inefficient, it currently results in file duplication. This is not good for videos and for FUSE-mounted Hyperdrive that has a big database file or a log file that needs to be appended to. 284 | 285 | ### Headless CMS 286 | 287 | [Headless Content Management System](https://www.storyblok.com/tp/headless-cms-explained) separates a backend and a front-end. 288 | 289 | - CMS Backend can be built on Hypercore 290 | - CMS Front-end needs collaborative editing, that is one of the Hypercore use cases 291 | 292 | ### Continuous Backup 293 | 294 | Personal and team data backup. Usable on mobiles, desktops, and servers. Hypercore is archive-able to S3. 295 | 296 | -------------------------------------------------------------------------------- /img/Tradle-MyCloud-aws-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tradle/why-hypercore/3330b382c53ef1b3feb0911bb8c9213f58c56174/img/Tradle-MyCloud-aws-architecture.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "why-hypercore", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@geut/hyperdrive-promise": { 8 | "version": "3.1.3", 9 | "resolved": "https://registry.npmjs.org/@geut/hyperdrive-promise/-/hyperdrive-promise-3.1.3.tgz", 10 | "integrity": "sha512-eE3W1qD6BUVVi+dP/JBbQAhEcwWrf2Bb7eUB5rBfSshVGIVgljLxipXEPZNMiTjBZG6KTqAECpKxZvCsfscdQQ==", 11 | "requires": { 12 | "hyper-typings": "^1.0.0", 13 | "hyperdrive": "^10.17.0" 14 | } 15 | }, 16 | "abstract-extension": { 17 | "version": "3.1.1", 18 | "resolved": "https://registry.npmjs.org/abstract-extension/-/abstract-extension-3.1.1.tgz", 19 | "integrity": "sha512-qmUIqQEh6ZZBKN6JfysKgCEBqI4qVexE6/N/MUJjqtQhhuGR8a16GKnK6SGFKv/n1cAlbYxLDXbtyhkxSnVYRQ==", 20 | "requires": { 21 | "codecs": "^2.0.0" 22 | } 23 | }, 24 | "array-lru": { 25 | "version": "1.1.1", 26 | "resolved": "https://registry.npmjs.org/array-lru/-/array-lru-1.1.1.tgz", 27 | "integrity": "sha1-DH4bTgIq4Wb/HoRIxZXzGB/NMzc=" 28 | }, 29 | "array.prototype.every": { 30 | "version": "1.1.3", 31 | "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.3.tgz", 32 | "integrity": "sha512-vWnriJI//SOMOWtXbU/VXhJ/InfnNHPF6BLKn5WfY8xXy+NWql0fUy20GO3sdqBhCAO+qw8S/E5nJiZX+QFdCA==", 33 | "dev": true, 34 | "requires": { 35 | "call-bind": "^1.0.2", 36 | "define-properties": "^1.1.3", 37 | "es-abstract": "^1.19.0", 38 | "is-string": "^1.0.7" 39 | } 40 | }, 41 | "atomic-batcher": { 42 | "version": "1.0.2", 43 | "resolved": "https://registry.npmjs.org/atomic-batcher/-/atomic-batcher-1.0.2.tgz", 44 | "integrity": "sha1-0WkB0QzOxZUWwZe5zNiTBom4E7Q=" 45 | }, 46 | "available-typed-arrays": { 47 | "version": "1.0.5", 48 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", 49 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", 50 | "dev": true 51 | }, 52 | "aws-sdk": { 53 | "version": "2.1131.0", 54 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1131.0.tgz", 55 | "integrity": "sha512-Ic3f2fSgVhYDQ0OBGxE6ODtSwaKyxBPGrI2RGrYt2Oj0Z8f227P7dB90o9X7y2MtnuJ4WoAzkf3Vc1c1UnnZlA==", 56 | "requires": { 57 | "buffer": "4.9.2", 58 | "events": "1.1.1", 59 | "ieee754": "1.1.13", 60 | "jmespath": "0.16.0", 61 | "querystring": "0.2.0", 62 | "sax": "1.2.1", 63 | "url": "0.10.3", 64 | "uuid": "3.3.2", 65 | "xml2js": "0.4.19" 66 | } 67 | }, 68 | "balanced-match": { 69 | "version": "1.0.2", 70 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 71 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 72 | "dev": true 73 | }, 74 | "base64-js": { 75 | "version": "1.5.1", 76 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 77 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 78 | }, 79 | "bitfield-rle": { 80 | "version": "2.2.1", 81 | "resolved": "https://registry.npmjs.org/bitfield-rle/-/bitfield-rle-2.2.1.tgz", 82 | "integrity": "sha512-wrDhHe7LUkqaytxgbsFXoemzHRv6e8FrVNWWsQCgUfmuVYW6ke44hoGc9VdpjgfIsJ/ejmCFA8wDtDqACNAvyw==", 83 | "requires": { 84 | "buffer-alloc-unsafe": "^1.1.0", 85 | "varint": "^4.0.0" 86 | }, 87 | "dependencies": { 88 | "varint": { 89 | "version": "4.0.1", 90 | "resolved": "https://registry.npmjs.org/varint/-/varint-4.0.1.tgz", 91 | "integrity": "sha1-SQgpuULSSEY7KzUJeZXDv3NxmOk=" 92 | } 93 | } 94 | }, 95 | "blake2b": { 96 | "version": "2.1.3", 97 | "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz", 98 | "integrity": "sha512-pkDss4xFVbMb4270aCyGD3qLv92314Et+FsKzilCLxDz5DuZ2/1g3w4nmBbu6nKApPspnjG7JcwTjGZnduB1yg==", 99 | "requires": { 100 | "blake2b-wasm": "^1.1.0", 101 | "nanoassert": "^1.0.0" 102 | } 103 | }, 104 | "blake2b-universal": { 105 | "version": "1.0.1", 106 | "resolved": "https://registry.npmjs.org/blake2b-universal/-/blake2b-universal-1.0.1.tgz", 107 | "integrity": "sha512-vmMqpF8E9RCde8/+Su/s2rZRxOBwAYQsTyCgok4kLWhWrzMrX0CzID6pVheNJSESY0S0FNTOG4QfRJqnSLOjFA==", 108 | "requires": { 109 | "blake2b": "^2.1.3", 110 | "sodium-native": "^3.0.1" 111 | } 112 | }, 113 | "blake2b-wasm": { 114 | "version": "1.1.7", 115 | "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz", 116 | "integrity": "sha512-oFIHvXhlz/DUgF0kq5B1CqxIDjIJwh9iDeUUGQUcvgiGz7Wdw03McEO7CfLBy7QKGdsydcMCgO9jFNBAFCtFcA==", 117 | "requires": { 118 | "nanoassert": "^1.0.0" 119 | } 120 | }, 121 | "brace-expansion": { 122 | "version": "1.1.11", 123 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 124 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 125 | "dev": true, 126 | "requires": { 127 | "balanced-match": "^1.0.0", 128 | "concat-map": "0.0.1" 129 | } 130 | }, 131 | "buffer": { 132 | "version": "4.9.2", 133 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 134 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 135 | "requires": { 136 | "base64-js": "^1.0.2", 137 | "ieee754": "^1.1.4", 138 | "isarray": "^1.0.0" 139 | } 140 | }, 141 | "buffer-alloc": { 142 | "version": "1.2.0", 143 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 144 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 145 | "requires": { 146 | "buffer-alloc-unsafe": "^1.1.0", 147 | "buffer-fill": "^1.0.0" 148 | } 149 | }, 150 | "buffer-alloc-unsafe": { 151 | "version": "1.1.0", 152 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 153 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 154 | }, 155 | "buffer-fill": { 156 | "version": "1.0.0", 157 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 158 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 159 | }, 160 | "buffer-from": { 161 | "version": "1.1.1", 162 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 163 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 164 | }, 165 | "bulk-write-stream": { 166 | "version": "1.1.4", 167 | "resolved": "https://registry.npmjs.org/bulk-write-stream/-/bulk-write-stream-1.1.4.tgz", 168 | "integrity": "sha512-GtKwd/4etuk1hNeprXoESBO1RSeRYJMXKf+O0qHmWdUomLT8ysNEfX/4bZFXr3BK6eukpHiEnhY2uMtEHDM2ng==", 169 | "requires": { 170 | "buffer-from": "^1.0.0", 171 | "inherits": "^2.0.1", 172 | "readable-stream": "^2.1.4" 173 | } 174 | }, 175 | "byte-stream": { 176 | "version": "2.1.0", 177 | "resolved": "https://registry.npmjs.org/byte-stream/-/byte-stream-2.1.0.tgz", 178 | "integrity": "sha1-Mu7LpiU4IdaVELnPNLMVzj5Vsxo=", 179 | "requires": { 180 | "debug": "^1.0.4", 181 | "readable-stream": "~1.1.10" 182 | }, 183 | "dependencies": { 184 | "debug": { 185 | "version": "1.0.5", 186 | "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.5.tgz", 187 | "integrity": "sha1-9yQSF0MPmd7EwrRz6rkiKOh0wqw=", 188 | "requires": { 189 | "ms": "2.0.0" 190 | } 191 | }, 192 | "isarray": { 193 | "version": "0.0.1", 194 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 195 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 196 | }, 197 | "ms": { 198 | "version": "2.0.0", 199 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 200 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 201 | }, 202 | "readable-stream": { 203 | "version": "1.1.14", 204 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 205 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 206 | "requires": { 207 | "core-util-is": "~1.0.0", 208 | "inherits": "~2.0.1", 209 | "isarray": "0.0.1", 210 | "string_decoder": "~0.10.x" 211 | } 212 | }, 213 | "string_decoder": { 214 | "version": "0.10.31", 215 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 216 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 217 | } 218 | } 219 | }, 220 | "call-bind": { 221 | "version": "1.0.2", 222 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 223 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 224 | "dev": true, 225 | "requires": { 226 | "function-bind": "^1.1.1", 227 | "get-intrinsic": "^1.0.2" 228 | } 229 | }, 230 | "call-me-maybe": { 231 | "version": "1.0.1", 232 | "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", 233 | "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" 234 | }, 235 | "chacha20-universal": { 236 | "version": "1.0.4", 237 | "resolved": "https://registry.npmjs.org/chacha20-universal/-/chacha20-universal-1.0.4.tgz", 238 | "integrity": "sha512-/IOxdWWNa7nRabfe7+oF+jVkGjlr2xUL4J8l/OvzZhj+c9RpMqoo3Dq+5nU1j/BflRV4BKnaQ4+4oH1yBpQG1Q==", 239 | "requires": { 240 | "nanoassert": "^2.0.0" 241 | }, 242 | "dependencies": { 243 | "nanoassert": { 244 | "version": "2.0.0", 245 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 246 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 247 | } 248 | } 249 | }, 250 | "clone": { 251 | "version": "2.1.2", 252 | "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", 253 | "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" 254 | }, 255 | "codecs": { 256 | "version": "2.1.0", 257 | "resolved": "https://registry.npmjs.org/codecs/-/codecs-2.1.0.tgz", 258 | "integrity": "sha512-nSWYToViFEpZXOxhtMQ6IDs76TN9xKIkHOu1KCr/iFiBcgzKuY1AFPZktuXN8r82FbZ/TXP9fwITszLgcp3eQg==" 259 | }, 260 | "concat-map": { 261 | "version": "0.0.1", 262 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 263 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 264 | "dev": true 265 | }, 266 | "core-util-is": { 267 | "version": "1.0.2", 268 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 269 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 270 | }, 271 | "corestore": { 272 | "version": "5.8.2", 273 | "resolved": "https://registry.npmjs.org/corestore/-/corestore-5.8.2.tgz", 274 | "integrity": "sha512-8OJyqGo1m3PyVvQtUoVcKYz4m4QoBAJE/2rNMJ8SD/U03NGD1z1cYep/n/32I0f3IbujYP5+4lIfBMIDkasB2w==", 275 | "requires": { 276 | "call-me-maybe": "^1.0.1", 277 | "dat-encoding": "^5.0.1", 278 | "derive-key": "^1.0.0", 279 | "derived-key-storage": "^2.1.0", 280 | "fd-lock": "^1.0.2", 281 | "hypercore": "^9.0.0", 282 | "hypercore-crypto": "^2.0.0", 283 | "hypercore-protocol": "^8.0.0", 284 | "nanoresource": "^1.3.0", 285 | "refpool": "^1.2.0" 286 | } 287 | }, 288 | "count-trailing-zeros": { 289 | "version": "1.0.1", 290 | "resolved": "https://registry.npmjs.org/count-trailing-zeros/-/count-trailing-zeros-1.0.1.tgz", 291 | "integrity": "sha1-q6bFgzvkENRbHso+bVg4RM5oLHc=" 292 | }, 293 | "custom-error-class": { 294 | "version": "1.0.0", 295 | "resolved": "https://registry.npmjs.org/custom-error-class/-/custom-error-class-1.0.0.tgz", 296 | "integrity": "sha512-bHT5BAycUbsHYexiPuoIEM/o770u48yWBrZHw/f+BRVkERn19xTgNwcHt79A/AYMxUcOfPjb9OrKoj6rVvPJuA==" 297 | }, 298 | "dat-encoding": { 299 | "version": "5.0.2", 300 | "resolved": "https://registry.npmjs.org/dat-encoding/-/dat-encoding-5.0.2.tgz", 301 | "integrity": "sha512-XgP3jZOanmnAeBgmbnPW2h/eLM9afGsuDF5di/PSQRhS09E+zRQmw0bLzmVFPIcHSbOwp384odXr6WdOhp7hBQ==", 302 | "requires": { 303 | "safe-buffer": "^5.0.1" 304 | } 305 | }, 306 | "debug": { 307 | "version": "4.2.0", 308 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 309 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 310 | "requires": { 311 | "ms": "2.1.2" 312 | } 313 | }, 314 | "deep-equal": { 315 | "version": "2.0.5", 316 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", 317 | "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", 318 | "dev": true, 319 | "requires": { 320 | "call-bind": "^1.0.0", 321 | "es-get-iterator": "^1.1.1", 322 | "get-intrinsic": "^1.0.1", 323 | "is-arguments": "^1.0.4", 324 | "is-date-object": "^1.0.2", 325 | "is-regex": "^1.1.1", 326 | "isarray": "^2.0.5", 327 | "object-is": "^1.1.4", 328 | "object-keys": "^1.1.1", 329 | "object.assign": "^4.1.2", 330 | "regexp.prototype.flags": "^1.3.0", 331 | "side-channel": "^1.0.3", 332 | "which-boxed-primitive": "^1.0.1", 333 | "which-collection": "^1.0.1", 334 | "which-typed-array": "^1.1.2" 335 | }, 336 | "dependencies": { 337 | "isarray": { 338 | "version": "2.0.5", 339 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 340 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 341 | "dev": true 342 | } 343 | } 344 | }, 345 | "define-properties": { 346 | "version": "1.1.4", 347 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 348 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 349 | "dev": true, 350 | "requires": { 351 | "has-property-descriptors": "^1.0.0", 352 | "object-keys": "^1.1.1" 353 | } 354 | }, 355 | "defined": { 356 | "version": "1.0.0", 357 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 358 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", 359 | "dev": true 360 | }, 361 | "derive-key": { 362 | "version": "1.0.1", 363 | "resolved": "https://registry.npmjs.org/derive-key/-/derive-key-1.0.1.tgz", 364 | "integrity": "sha512-7DHGLGvxFF8umw8NEGH3n9KKgEN8duk4Fiy4WmN3QgNKEogDhaNIsTDd5JVN7ilB8xw4ike1Q08z8UJSJ7hebA==", 365 | "requires": { 366 | "blake2b-universal": "^1.0.0" 367 | } 368 | }, 369 | "derived-key-storage": { 370 | "version": "2.1.0", 371 | "resolved": "https://registry.npmjs.org/derived-key-storage/-/derived-key-storage-2.1.0.tgz", 372 | "integrity": "sha512-4RKKrpf2YouCASaRHqUvyxtHABGLH7UJWNXPjsJxMvzCj4tettUvuyGsmP2/mpGYhSda7caZkS2oP4rqWjgkZg==", 373 | "requires": { 374 | "random-access-storage": "^1.4.0", 375 | "thunky": "^1.0.3", 376 | "varint": "^5.0.0" 377 | } 378 | }, 379 | "dotignore": { 380 | "version": "0.1.2", 381 | "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", 382 | "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", 383 | "dev": true, 384 | "requires": { 385 | "minimatch": "^3.0.4" 386 | } 387 | }, 388 | "duplexify": { 389 | "version": "3.7.1", 390 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", 391 | "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", 392 | "requires": { 393 | "end-of-stream": "^1.0.0", 394 | "inherits": "^2.0.1", 395 | "readable-stream": "^2.0.0", 396 | "stream-shift": "^1.0.0" 397 | } 398 | }, 399 | "end-of-stream": { 400 | "version": "1.4.4", 401 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 402 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 403 | "requires": { 404 | "once": "^1.4.0" 405 | } 406 | }, 407 | "es-abstract": { 408 | "version": "1.20.0", 409 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", 410 | "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", 411 | "dev": true, 412 | "requires": { 413 | "call-bind": "^1.0.2", 414 | "es-to-primitive": "^1.2.1", 415 | "function-bind": "^1.1.1", 416 | "function.prototype.name": "^1.1.5", 417 | "get-intrinsic": "^1.1.1", 418 | "get-symbol-description": "^1.0.0", 419 | "has": "^1.0.3", 420 | "has-property-descriptors": "^1.0.0", 421 | "has-symbols": "^1.0.3", 422 | "internal-slot": "^1.0.3", 423 | "is-callable": "^1.2.4", 424 | "is-negative-zero": "^2.0.2", 425 | "is-regex": "^1.1.4", 426 | "is-shared-array-buffer": "^1.0.2", 427 | "is-string": "^1.0.7", 428 | "is-weakref": "^1.0.2", 429 | "object-inspect": "^1.12.0", 430 | "object-keys": "^1.1.1", 431 | "object.assign": "^4.1.2", 432 | "regexp.prototype.flags": "^1.4.1", 433 | "string.prototype.trimend": "^1.0.5", 434 | "string.prototype.trimstart": "^1.0.5", 435 | "unbox-primitive": "^1.0.2" 436 | } 437 | }, 438 | "es-get-iterator": { 439 | "version": "1.1.2", 440 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", 441 | "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", 442 | "dev": true, 443 | "requires": { 444 | "call-bind": "^1.0.2", 445 | "get-intrinsic": "^1.1.0", 446 | "has-symbols": "^1.0.1", 447 | "is-arguments": "^1.1.0", 448 | "is-map": "^2.0.2", 449 | "is-set": "^2.0.2", 450 | "is-string": "^1.0.5", 451 | "isarray": "^2.0.5" 452 | }, 453 | "dependencies": { 454 | "isarray": { 455 | "version": "2.0.5", 456 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 457 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 458 | "dev": true 459 | } 460 | } 461 | }, 462 | "es-to-primitive": { 463 | "version": "1.2.1", 464 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 465 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 466 | "dev": true, 467 | "requires": { 468 | "is-callable": "^1.1.4", 469 | "is-date-object": "^1.0.1", 470 | "is-symbol": "^1.0.2" 471 | } 472 | }, 473 | "events": { 474 | "version": "1.1.1", 475 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 476 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 477 | }, 478 | "fast-bitfield": { 479 | "version": "1.2.2", 480 | "resolved": "https://registry.npmjs.org/fast-bitfield/-/fast-bitfield-1.2.2.tgz", 481 | "integrity": "sha512-t8HYqkuE3YEqNcyWlAfh55479aTxO+GpYwvQvJppYqyBfSmRdNIhzY2m09FKN/MENTzq4wH6heHOIvsPyMAwvQ==", 482 | "requires": { 483 | "count-trailing-zeros": "^1.0.1" 484 | } 485 | }, 486 | "fast-fifo": { 487 | "version": "1.0.0", 488 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.0.0.tgz", 489 | "integrity": "sha512-4VEXmjxLj7sbs8J//cn2qhRap50dGzF5n8fjay8mau+Jn4hxSeR3xPFwxMaQq/pDaq7+KQk0PAbC2+nWDkJrmQ==" 490 | }, 491 | "fd-lock": { 492 | "version": "1.2.0", 493 | "resolved": "https://registry.npmjs.org/fd-lock/-/fd-lock-1.2.0.tgz", 494 | "integrity": "sha512-Lk/pKH2DldLpG4Yh/sOOY84k5VqNzxHPffGwf1+yYI+/qMXzTPp9KJMX+Wh6n4xqGSA1Mu7JPmaDArfJGw2O/A==", 495 | "requires": { 496 | "napi-macros": "^2.0.0", 497 | "node-gyp-build": "^4.2.2" 498 | } 499 | }, 500 | "filesystem-constants": { 501 | "version": "1.0.0", 502 | "resolved": "https://registry.npmjs.org/filesystem-constants/-/filesystem-constants-1.0.0.tgz", 503 | "integrity": "sha512-/ue62eYa8Mk53dc1XXxT1nhwat3ygWMepjrFON8tBVjtjCTVUzM8JTEAQquNoZnmimM4dbxfV9tZeEav1KUccg==" 504 | }, 505 | "flat-tree": { 506 | "version": "1.9.0", 507 | "resolved": "https://registry.npmjs.org/flat-tree/-/flat-tree-1.9.0.tgz", 508 | "integrity": "sha512-WRu5/q9fcdL9f7L6Ahq8fR153e5Zr5aU6plPHupN6JDWuvEJbMjrEQprge3/I7ytndHC6/GUNg5Rg8XEisuv5w==" 509 | }, 510 | "for-each": { 511 | "version": "0.3.3", 512 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 513 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 514 | "dev": true, 515 | "requires": { 516 | "is-callable": "^1.1.3" 517 | } 518 | }, 519 | "foreach": { 520 | "version": "2.0.5", 521 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 522 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", 523 | "dev": true 524 | }, 525 | "fs.realpath": { 526 | "version": "1.0.0", 527 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 528 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 529 | "dev": true 530 | }, 531 | "fsctl": { 532 | "version": "1.0.0", 533 | "resolved": "https://registry.npmjs.org/fsctl/-/fsctl-1.0.0.tgz", 534 | "integrity": "sha512-uNHlfhyUJiVO2kHA6ZBnDkBhG1wM8fII+xGfCi5aBoWR7DLh9Q3nOAWe4fEETQzkptahITI1xbe5r23HuA5ECA==", 535 | "optional": true, 536 | "requires": { 537 | "napi-macros": "^2.0.0", 538 | "node-gyp-build": "^4.2.3" 539 | } 540 | }, 541 | "function-bind": { 542 | "version": "1.1.1", 543 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 544 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 545 | "dev": true 546 | }, 547 | "function.prototype.name": { 548 | "version": "1.1.5", 549 | "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", 550 | "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", 551 | "dev": true, 552 | "requires": { 553 | "call-bind": "^1.0.2", 554 | "define-properties": "^1.1.3", 555 | "es-abstract": "^1.19.0", 556 | "functions-have-names": "^1.2.2" 557 | } 558 | }, 559 | "functions-have-names": { 560 | "version": "1.2.3", 561 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 562 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 563 | "dev": true 564 | }, 565 | "generate-function": { 566 | "version": "2.3.1", 567 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", 568 | "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", 569 | "requires": { 570 | "is-property": "^1.0.2" 571 | } 572 | }, 573 | "generate-object-property": { 574 | "version": "1.2.0", 575 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", 576 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", 577 | "requires": { 578 | "is-property": "^1.0.0" 579 | } 580 | }, 581 | "get-intrinsic": { 582 | "version": "1.1.1", 583 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 584 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 585 | "dev": true, 586 | "requires": { 587 | "function-bind": "^1.1.1", 588 | "has": "^1.0.3", 589 | "has-symbols": "^1.0.1" 590 | } 591 | }, 592 | "get-package-type": { 593 | "version": "0.1.0", 594 | "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", 595 | "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", 596 | "dev": true 597 | }, 598 | "get-symbol-description": { 599 | "version": "1.0.0", 600 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 601 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 602 | "dev": true, 603 | "requires": { 604 | "call-bind": "^1.0.2", 605 | "get-intrinsic": "^1.1.1" 606 | } 607 | }, 608 | "glob": { 609 | "version": "7.2.0", 610 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 611 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 612 | "dev": true, 613 | "requires": { 614 | "fs.realpath": "^1.0.0", 615 | "inflight": "^1.0.4", 616 | "inherits": "2", 617 | "minimatch": "^3.0.4", 618 | "once": "^1.3.0", 619 | "path-is-absolute": "^1.0.0" 620 | } 621 | }, 622 | "has": { 623 | "version": "1.0.3", 624 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 625 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 626 | "dev": true, 627 | "requires": { 628 | "function-bind": "^1.1.1" 629 | } 630 | }, 631 | "has-bigints": { 632 | "version": "1.0.2", 633 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 634 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", 635 | "dev": true 636 | }, 637 | "has-dynamic-import": { 638 | "version": "2.0.1", 639 | "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", 640 | "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", 641 | "dev": true, 642 | "requires": { 643 | "call-bind": "^1.0.2", 644 | "get-intrinsic": "^1.1.1" 645 | } 646 | }, 647 | "has-property-descriptors": { 648 | "version": "1.0.0", 649 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 650 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 651 | "dev": true, 652 | "requires": { 653 | "get-intrinsic": "^1.1.1" 654 | } 655 | }, 656 | "has-symbols": { 657 | "version": "1.0.3", 658 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 659 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 660 | "dev": true 661 | }, 662 | "has-tostringtag": { 663 | "version": "1.0.0", 664 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 665 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 666 | "dev": true, 667 | "requires": { 668 | "has-symbols": "^1.0.2" 669 | } 670 | }, 671 | "hmac-blake2b": { 672 | "version": "2.0.0", 673 | "resolved": "https://registry.npmjs.org/hmac-blake2b/-/hmac-blake2b-2.0.0.tgz", 674 | "integrity": "sha512-JbGNtM1YRd8EQH/2vNTAP1oy5lJVPlBFYZfCJTu3k8sqOUm0rRIf/3+MCd5noVykETwTbun6jEOc+4Tu78ubHA==", 675 | "requires": { 676 | "nanoassert": "^1.1.0", 677 | "sodium-native": "^3.1.1", 678 | "sodium-universal": "^3.0.0" 679 | } 680 | }, 681 | "hyper-typings": { 682 | "version": "1.0.0", 683 | "resolved": "https://registry.npmjs.org/hyper-typings/-/hyper-typings-1.0.0.tgz", 684 | "integrity": "sha512-2JCNu+Fx4erLRxp4BaulkmOQ1fOAqUQe/WGgbUrAzY5964DLI5NBeTRMgkv/WMxW2Yz+qQx4XvrA/C7H9JrEow==" 685 | }, 686 | "hyperbee": { 687 | "version": "0.0.18", 688 | "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-0.0.18.tgz", 689 | "integrity": "sha512-o3AjkxvR9oQjIsUO7cpBRH8nAG35FMb083MVDGvFCcUWXqadm/YGYM9HdEoVen/NSC2VEov+EGELm+15AC0mIQ==", 690 | "requires": { 691 | "codecs": "^2.1.0", 692 | "mutexify": "^1.3.1", 693 | "protocol-buffers-encodings": "^1.1.0", 694 | "streamx": "^2.6.6" 695 | } 696 | }, 697 | "hypercore": { 698 | "version": "9.12.0", 699 | "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-9.12.0.tgz", 700 | "integrity": "sha512-wIlM4Iwl618NcmsY+1O/X08Cx/bY6ti3LXmQhRnPZFGQPqAeX0x53hASckW082IIAVWzXD3zcmREw5yEINzb4w==", 701 | "requires": { 702 | "abstract-extension": "^3.0.1", 703 | "atomic-batcher": "^1.0.2", 704 | "bitfield-rle": "^2.2.1", 705 | "codecs": "^2.0.0", 706 | "fast-bitfield": "^1.2.2", 707 | "flat-tree": "^1.6.0", 708 | "hypercore-cache": "^1.0.1", 709 | "hypercore-crypto": "^2.0.0", 710 | "hypercore-default-storage": "^1.1.1", 711 | "hypercore-protocol": "^8.0.5", 712 | "hypercore-streams": "^1.0.1", 713 | "inherits": "^2.0.3", 714 | "inspect-custom-symbol": "^1.1.0", 715 | "last-one-wins": "^1.0.4", 716 | "memory-pager": "^1.0.2", 717 | "merkle-tree-stream": "^4.0.0", 718 | "nanoguard": "^1.2.0", 719 | "nanoresource": "^1.3.0", 720 | "pretty-hash": "^1.0.1", 721 | "sparse-bitfield": "^3.0.0", 722 | "timeout-refresh": "^1.0.3", 723 | "uint64be": "^2.0.1", 724 | "unordered-array-remove": "^1.0.2", 725 | "unordered-set": "^2.0.0" 726 | }, 727 | "dependencies": { 728 | "hypercore-protocol": { 729 | "version": "8.0.7", 730 | "resolved": "https://registry.npmjs.org/hypercore-protocol/-/hypercore-protocol-8.0.7.tgz", 731 | "integrity": "sha512-b5TXhqXUZ+Z7M/5/PlCTgElfufDRa3EzACd7y7BA7owLkxQreaUQ58wUO7nzJppDP1bnC2Hz6Hg7nlRPc75bKw==", 732 | "requires": { 733 | "abstract-extension": "^3.0.1", 734 | "debug": "^4.1.1", 735 | "hypercore-crypto": "^2.0.0", 736 | "inspect-custom-symbol": "^1.1.0", 737 | "nanoguard": "^1.2.1", 738 | "pretty-hash": "^1.0.1", 739 | "simple-hypercore-protocol": "^2.0.0", 740 | "streamx": "^2.1.0", 741 | "timeout-refresh": "^1.0.0" 742 | } 743 | }, 744 | "timeout-refresh": { 745 | "version": "1.0.3", 746 | "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-1.0.3.tgz", 747 | "integrity": "sha512-Mz0CX4vBGM5lj8ttbIFt7o4ZMxk/9rgudJRh76EvB7xXZMur7T/cjRiH2w4Fmkq0zxf2QpM8IFvOSRn8FEu3gA==" 748 | } 749 | } 750 | }, 751 | "hypercore-byte-stream": { 752 | "version": "1.0.12", 753 | "resolved": "https://registry.npmjs.org/hypercore-byte-stream/-/hypercore-byte-stream-1.0.12.tgz", 754 | "integrity": "sha512-JnpLfCkvH9EPRZ8JXLBUAXo+L2wRQ504yWTwtveH+cSwwx0E8I2dbxXvNIsYGDeghOlX3hka0Ng3GiYI0risZw==", 755 | "requires": { 756 | "readable-stream": "^3.1.1" 757 | }, 758 | "dependencies": { 759 | "readable-stream": { 760 | "version": "3.6.0", 761 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 762 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 763 | "requires": { 764 | "inherits": "^2.0.3", 765 | "string_decoder": "^1.1.1", 766 | "util-deprecate": "^1.0.1" 767 | } 768 | } 769 | } 770 | }, 771 | "hypercore-cache": { 772 | "version": "1.0.2", 773 | "resolved": "https://registry.npmjs.org/hypercore-cache/-/hypercore-cache-1.0.2.tgz", 774 | "integrity": "sha512-AJ/q7y6EOrXnOH/4+DVcfDygsh1ZXMRGvNc67GBNqwCt22oSCOWhRI6EJ+3HEJciM9M2oSm1WX3qg6kgRhT/Gw==" 775 | }, 776 | "hypercore-crypto": { 777 | "version": "2.1.1", 778 | "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-2.1.1.tgz", 779 | "integrity": "sha512-KIA25GiFJXieB7Oa0HSDNtXrPH41VJY5j2xWmIjBqREt3quKOFxL7ceOLz7zBMoWkoYeF2m67RhtDE1C4XfoNA==", 780 | "requires": { 781 | "sodium-universal": "^3.0.0", 782 | "uint64be": "^3.0.0" 783 | }, 784 | "dependencies": { 785 | "uint64be": { 786 | "version": "3.0.0", 787 | "resolved": "https://registry.npmjs.org/uint64be/-/uint64be-3.0.0.tgz", 788 | "integrity": "sha512-mliiCSrsE29aNBI7O9W5gGv6WmA9kBR8PtTt6Apaxns076IRdYrrtFhXHEWMj5CSum3U7cv7/pi4xmi4XsIOqg==" 789 | } 790 | } 791 | }, 792 | "hypercore-default-storage": { 793 | "version": "1.1.1", 794 | "resolved": "https://registry.npmjs.org/hypercore-default-storage/-/hypercore-default-storage-1.1.1.tgz", 795 | "integrity": "sha512-y7dSX3VUT4I/X5Cj0h6hcKN2R+/QQIi1HnElnCqY3tQYbVaWYljdbVe3aBQIvkRCfOgWMfe2RbCLX4N78D5syg==", 796 | "requires": { 797 | "fsctl": "^1.0.0", 798 | "random-access-file": "^2.1.5" 799 | } 800 | }, 801 | "hypercore-protocol": { 802 | "version": "8.0.4", 803 | "resolved": "https://registry.npmjs.org/hypercore-protocol/-/hypercore-protocol-8.0.4.tgz", 804 | "integrity": "sha512-QhjpbO8OVSd03U8bI9/bGprmv0TO+sAMdzoptYT4UE4AfMQ+YEMv9wjJwiEwqnsgwXBhU5DyA8AoUY6bePyosw==", 805 | "requires": { 806 | "abstract-extension": "^3.0.1", 807 | "debug": "^4.1.1", 808 | "hypercore-crypto": "^2.0.0", 809 | "inspect-custom-symbol": "^1.1.0", 810 | "nanoguard": "^1.2.1", 811 | "pretty-hash": "^1.0.1", 812 | "simple-hypercore-protocol": "^2.0.0", 813 | "streamx": "^2.1.0", 814 | "timeout-refresh": "^1.0.0" 815 | } 816 | }, 817 | "hypercore-streams": { 818 | "version": "1.0.1", 819 | "resolved": "https://registry.npmjs.org/hypercore-streams/-/hypercore-streams-1.0.1.tgz", 820 | "integrity": "sha512-OcN2zq9DEoArC84q9VCSrf9Hx1QUkR6ineCOwyOwhE4v/8aUTOx87mAk1nyjMOf76DQmF+tl2vnS2FssLx5N+Q==", 821 | "requires": { 822 | "streamx": "^2.6.0" 823 | } 824 | }, 825 | "hyperdrive": { 826 | "version": "10.21.0", 827 | "resolved": "https://registry.npmjs.org/hyperdrive/-/hyperdrive-10.21.0.tgz", 828 | "integrity": "sha512-kvq7aGODR8mcV+c1vTGrqVLhdJKl604/aNYXiOEg6pH6qb3t9AGTm/DKvQrbR+DA5Qq8X9TbQZWeZw4wgJRlXw==", 829 | "requires": { 830 | "byte-stream": "^2.1.0", 831 | "corestore": "^5.3.2", 832 | "custom-error-class": "^1.0.0", 833 | "duplexify": "^3.7.1", 834 | "filesystem-constants": "^1.0.0", 835 | "hypercore-byte-stream": "^1.0.2", 836 | "hypercore-protocol": "^8.0.0", 837 | "hyperdrive-schemas": "^2.0.0", 838 | "mountable-hypertrie": "^2.6.0", 839 | "mutexify": "^1.2.0", 840 | "nanoiterator": "^1.2.0", 841 | "nanoresource": "^1.3.0", 842 | "pump": "^3.0.0", 843 | "pumpify": "^2.0.1", 844 | "stream-collector": "^1.0.1", 845 | "streamx": "^2.6.3", 846 | "thunky": "^1.0.3", 847 | "thunky-map": "^1.0.0", 848 | "unixify": "^1.0.0", 849 | "varint": "^5.0.0" 850 | } 851 | }, 852 | "hyperdrive-schemas": { 853 | "version": "2.0.0", 854 | "resolved": "https://registry.npmjs.org/hyperdrive-schemas/-/hyperdrive-schemas-2.0.0.tgz", 855 | "integrity": "sha512-mzD741NjsSt3ttIaavbh3zyNdR3zy0X55HRweNRsw/JEduWjaoOZa6EXz7ly2JxuD7MvAbJxsuNPlnVl9saL6w==", 856 | "requires": { 857 | "protocol-buffers-encodings": "^1.1.0" 858 | } 859 | }, 860 | "hypertrie": { 861 | "version": "5.1.1", 862 | "resolved": "https://registry.npmjs.org/hypertrie/-/hypertrie-5.1.1.tgz", 863 | "integrity": "sha512-6PjBRPsTH+hqhMpjt1QmxXMFW6XaHNXkjxH1KrL1bp8Fpz7SPOvBNSaQVttvAP6GRzDKkeLraG4q3yJiSL4IhQ==", 864 | "requires": { 865 | "array-lru": "^1.1.1", 866 | "bulk-write-stream": "^1.1.4", 867 | "codecs": "^2.0.0", 868 | "hypercore": "^9.4.1", 869 | "hypercore-protocol": "^8.0.0", 870 | "inherits": "^2.0.3", 871 | "inspect-custom-symbol": "^1.1.0", 872 | "is-options": "^1.0.1", 873 | "mutexify": "^1.2.0", 874 | "nanoiterator": "^1.2.0", 875 | "protocol-buffers-encodings": "^1.1.0", 876 | "siphash24-universal": "^1.0.0", 877 | "thunky": "^1.0.2", 878 | "unordered-set": "^2.0.1", 879 | "varint": "^5.0.0" 880 | } 881 | }, 882 | "ieee754": { 883 | "version": "1.1.13", 884 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 885 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 886 | }, 887 | "inflight": { 888 | "version": "1.0.6", 889 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 890 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 891 | "dev": true, 892 | "requires": { 893 | "once": "^1.3.0", 894 | "wrappy": "1" 895 | } 896 | }, 897 | "inherits": { 898 | "version": "2.0.4", 899 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 900 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 901 | }, 902 | "ini": { 903 | "version": "1.3.8", 904 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 905 | "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" 906 | }, 907 | "inspect-custom-symbol": { 908 | "version": "1.1.1", 909 | "resolved": "https://registry.npmjs.org/inspect-custom-symbol/-/inspect-custom-symbol-1.1.1.tgz", 910 | "integrity": "sha512-GOucsp9EcdlLdhPUyOTvQDnbFJtp2WBWZV1Jqe+mVnkJQBL3w96+fB84C+JL+EKXOspMdB0eMDQPDp5w9fkfZA==" 911 | }, 912 | "internal-slot": { 913 | "version": "1.0.3", 914 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 915 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 916 | "dev": true, 917 | "requires": { 918 | "get-intrinsic": "^1.1.0", 919 | "has": "^1.0.3", 920 | "side-channel": "^1.0.4" 921 | } 922 | }, 923 | "is-arguments": { 924 | "version": "1.1.1", 925 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 926 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 927 | "dev": true, 928 | "requires": { 929 | "call-bind": "^1.0.2", 930 | "has-tostringtag": "^1.0.0" 931 | } 932 | }, 933 | "is-bigint": { 934 | "version": "1.0.4", 935 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 936 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 937 | "dev": true, 938 | "requires": { 939 | "has-bigints": "^1.0.1" 940 | } 941 | }, 942 | "is-boolean-object": { 943 | "version": "1.1.2", 944 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 945 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 946 | "dev": true, 947 | "requires": { 948 | "call-bind": "^1.0.2", 949 | "has-tostringtag": "^1.0.0" 950 | } 951 | }, 952 | "is-callable": { 953 | "version": "1.2.4", 954 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", 955 | "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", 956 | "dev": true 957 | }, 958 | "is-core-module": { 959 | "version": "2.9.0", 960 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 961 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 962 | "dev": true, 963 | "requires": { 964 | "has": "^1.0.3" 965 | } 966 | }, 967 | "is-date-object": { 968 | "version": "1.0.5", 969 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 970 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 971 | "dev": true, 972 | "requires": { 973 | "has-tostringtag": "^1.0.0" 974 | } 975 | }, 976 | "is-map": { 977 | "version": "2.0.2", 978 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", 979 | "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", 980 | "dev": true 981 | }, 982 | "is-negative-zero": { 983 | "version": "2.0.2", 984 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 985 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", 986 | "dev": true 987 | }, 988 | "is-number-object": { 989 | "version": "1.0.7", 990 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 991 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 992 | "dev": true, 993 | "requires": { 994 | "has-tostringtag": "^1.0.0" 995 | } 996 | }, 997 | "is-options": { 998 | "version": "1.0.1", 999 | "resolved": "https://registry.npmjs.org/is-options/-/is-options-1.0.1.tgz", 1000 | "integrity": "sha512-2Xj8sA0zDrAcaoWfBiNmc6VPWAgKDpim0T3J9Djq7vbm1UjwbUWzeuLu/FwC46g3cBbAn0E5R0xwVtOobM6Xxg==" 1001 | }, 1002 | "is-property": { 1003 | "version": "1.0.2", 1004 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 1005 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" 1006 | }, 1007 | "is-regex": { 1008 | "version": "1.1.4", 1009 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 1010 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 1011 | "dev": true, 1012 | "requires": { 1013 | "call-bind": "^1.0.2", 1014 | "has-tostringtag": "^1.0.0" 1015 | } 1016 | }, 1017 | "is-set": { 1018 | "version": "2.0.2", 1019 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", 1020 | "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", 1021 | "dev": true 1022 | }, 1023 | "is-shared-array-buffer": { 1024 | "version": "1.0.2", 1025 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 1026 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 1027 | "dev": true, 1028 | "requires": { 1029 | "call-bind": "^1.0.2" 1030 | } 1031 | }, 1032 | "is-string": { 1033 | "version": "1.0.7", 1034 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 1035 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 1036 | "dev": true, 1037 | "requires": { 1038 | "has-tostringtag": "^1.0.0" 1039 | } 1040 | }, 1041 | "is-symbol": { 1042 | "version": "1.0.4", 1043 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 1044 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 1045 | "dev": true, 1046 | "requires": { 1047 | "has-symbols": "^1.0.2" 1048 | } 1049 | }, 1050 | "is-typed-array": { 1051 | "version": "1.1.8", 1052 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", 1053 | "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", 1054 | "dev": true, 1055 | "requires": { 1056 | "available-typed-arrays": "^1.0.5", 1057 | "call-bind": "^1.0.2", 1058 | "es-abstract": "^1.18.5", 1059 | "foreach": "^2.0.5", 1060 | "has-tostringtag": "^1.0.0" 1061 | } 1062 | }, 1063 | "is-weakmap": { 1064 | "version": "2.0.1", 1065 | "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", 1066 | "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", 1067 | "dev": true 1068 | }, 1069 | "is-weakref": { 1070 | "version": "1.0.2", 1071 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 1072 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 1073 | "dev": true, 1074 | "requires": { 1075 | "call-bind": "^1.0.2" 1076 | } 1077 | }, 1078 | "is-weakset": { 1079 | "version": "2.0.2", 1080 | "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", 1081 | "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", 1082 | "dev": true, 1083 | "requires": { 1084 | "call-bind": "^1.0.2", 1085 | "get-intrinsic": "^1.1.1" 1086 | } 1087 | }, 1088 | "isarray": { 1089 | "version": "1.0.0", 1090 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1091 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1092 | }, 1093 | "jmespath": { 1094 | "version": "0.16.0", 1095 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", 1096 | "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" 1097 | }, 1098 | "last-one-wins": { 1099 | "version": "1.0.4", 1100 | "resolved": "https://registry.npmjs.org/last-one-wins/-/last-one-wins-1.0.4.tgz", 1101 | "integrity": "sha1-wb/Qy8tGeQ7JFWuNGu6Py4bNoio=" 1102 | }, 1103 | "memory-pager": { 1104 | "version": "1.5.0", 1105 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1106 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" 1107 | }, 1108 | "merkle-tree-stream": { 1109 | "version": "4.0.0", 1110 | "resolved": "https://registry.npmjs.org/merkle-tree-stream/-/merkle-tree-stream-4.0.0.tgz", 1111 | "integrity": "sha512-TIurLf/ustQNMXi5foClGTcEsRvH6DCvxeAKu68OrwHMOSM/M1pgPXb7qe52Svk1ClvmZuAVpLtP5FWKzPr/sw==", 1112 | "requires": { 1113 | "flat-tree": "^1.3.0", 1114 | "streamx": "^2.7.1" 1115 | }, 1116 | "dependencies": { 1117 | "streamx": { 1118 | "version": "2.12.4", 1119 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.12.4.tgz", 1120 | "integrity": "sha512-K3xdIp8YSkvbdI0PrCcP0JkniN8cPCyeKlcZgRFSl1o1xKINCYM93FryvTSOY57x73pz5/AjO5B8b9BYf21wWw==", 1121 | "requires": { 1122 | "fast-fifo": "^1.0.0", 1123 | "queue-tick": "^1.0.0" 1124 | } 1125 | } 1126 | } 1127 | }, 1128 | "minimatch": { 1129 | "version": "3.1.2", 1130 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1131 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1132 | "dev": true, 1133 | "requires": { 1134 | "brace-expansion": "^1.1.7" 1135 | } 1136 | }, 1137 | "minimist": { 1138 | "version": "1.2.6", 1139 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 1140 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", 1141 | "dev": true 1142 | }, 1143 | "mkdirp-classic": { 1144 | "version": "0.5.3", 1145 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 1146 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" 1147 | }, 1148 | "mountable-hypertrie": { 1149 | "version": "2.8.0", 1150 | "resolved": "https://registry.npmjs.org/mountable-hypertrie/-/mountable-hypertrie-2.8.0.tgz", 1151 | "integrity": "sha512-UYwewr82cZvrhJRQLWJtVJRWvJv+zQnp+2SnG051yO7c4rd3auUgwWJ71LyQKfVGq7OPYG1CUtXJKqbo+bVyPw==", 1152 | "requires": { 1153 | "hypercore-crypto": "^2.0.1", 1154 | "hypercore-protocol": "^8.0.0", 1155 | "hypertrie": "^5.0.0", 1156 | "is-options": "^1.0.1", 1157 | "nanoiterator": "^1.2.0", 1158 | "nanoresource": "^1.3.0", 1159 | "protocol-buffers": "^4.1.0", 1160 | "protocol-buffers-encodings": "^1.1.0", 1161 | "thunky": "^1.1.0", 1162 | "unixify": "^1.0.0" 1163 | } 1164 | }, 1165 | "ms": { 1166 | "version": "2.1.2", 1167 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1168 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1169 | }, 1170 | "multi-hyperdrive": { 1171 | "version": "1.1.4", 1172 | "resolved": "https://registry.npmjs.org/multi-hyperdrive/-/multi-hyperdrive-1.1.4.tgz", 1173 | "integrity": "sha512-gSOnJFiRrdLLD3Dr8V8OweBuuFnZMCXM6eJiN8zTdr1gwG929f6UJa3HPjrzFXDtKrtxeD0xQJtvClEXvixAlQ==", 1174 | "requires": { 1175 | "protocol-buffers": "^4.2.0", 1176 | "protocol-buffers-encodings": "^1.1.0" 1177 | } 1178 | }, 1179 | "mutexify": { 1180 | "version": "1.3.1", 1181 | "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.3.1.tgz", 1182 | "integrity": "sha512-nU7mOEuaXiQIB/EgTIjYZJ7g8KqMm2D8l4qp+DqA4jxWOb/tnb1KEoqp+tlbdQIDIAiC1i7j7X/3yHDFXLxr9g==" 1183 | }, 1184 | "nanoassert": { 1185 | "version": "1.1.0", 1186 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", 1187 | "integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=" 1188 | }, 1189 | "nanoguard": { 1190 | "version": "1.3.0", 1191 | "resolved": "https://registry.npmjs.org/nanoguard/-/nanoguard-1.3.0.tgz", 1192 | "integrity": "sha512-K/ON5wyflyPyZskdeT3m7Y2gJVkm3QLdKykMCquAbK8A2erstyMpZUc3NG8Nz5jKdfatiYndONrlmLF8+pGl+A==" 1193 | }, 1194 | "nanoiterator": { 1195 | "version": "1.2.1", 1196 | "resolved": "https://registry.npmjs.org/nanoiterator/-/nanoiterator-1.2.1.tgz", 1197 | "integrity": "sha512-M7V9cvfDErMg/H3j90zIGY7Fq3vIGjnnNXwcZ/EXO4plZT3dGNwvykfslHgtbJ8prOGuu3khmc87pND0jdmkcA==", 1198 | "requires": { 1199 | "inherits": "^2.0.3", 1200 | "readable-stream": "^2.3.3" 1201 | } 1202 | }, 1203 | "nanoresource": { 1204 | "version": "1.3.0", 1205 | "resolved": "https://registry.npmjs.org/nanoresource/-/nanoresource-1.3.0.tgz", 1206 | "integrity": "sha512-OI5dswqipmlYfyL3k/YMm7mbERlh4Bd1KuKdMHpeoVD1iVxqxaTMKleB4qaA2mbQZ6/zMNSxCXv9M9P/YbqTuQ==", 1207 | "requires": { 1208 | "inherits": "^2.0.4" 1209 | } 1210 | }, 1211 | "napi-macros": { 1212 | "version": "2.0.0", 1213 | "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", 1214 | "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==" 1215 | }, 1216 | "node-gyp-build": { 1217 | "version": "4.2.3", 1218 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", 1219 | "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" 1220 | }, 1221 | "noise-protocol": { 1222 | "version": "3.0.1", 1223 | "resolved": "https://registry.npmjs.org/noise-protocol/-/noise-protocol-3.0.1.tgz", 1224 | "integrity": "sha512-4rQGZvismeb4tMf91O31oDYLGntkEs4p4wa69+14juHTV4A3COtWyDck9PwBqFjg7S8TPZLCUXUdOnOZQJ5UBA==", 1225 | "requires": { 1226 | "clone": "^2.1.2", 1227 | "hmac-blake2b": "^2.0.0", 1228 | "nanoassert": "^2.0.0", 1229 | "sodium-universal": "^3.0.2" 1230 | }, 1231 | "dependencies": { 1232 | "nanoassert": { 1233 | "version": "2.0.0", 1234 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1235 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1236 | } 1237 | } 1238 | }, 1239 | "normalize-path": { 1240 | "version": "2.1.1", 1241 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", 1242 | "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", 1243 | "requires": { 1244 | "remove-trailing-separator": "^1.0.1" 1245 | } 1246 | }, 1247 | "object-inspect": { 1248 | "version": "1.12.0", 1249 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", 1250 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", 1251 | "dev": true 1252 | }, 1253 | "object-is": { 1254 | "version": "1.1.5", 1255 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", 1256 | "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", 1257 | "dev": true, 1258 | "requires": { 1259 | "call-bind": "^1.0.2", 1260 | "define-properties": "^1.1.3" 1261 | } 1262 | }, 1263 | "object-keys": { 1264 | "version": "1.1.1", 1265 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1266 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 1267 | "dev": true 1268 | }, 1269 | "object.assign": { 1270 | "version": "4.1.2", 1271 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", 1272 | "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", 1273 | "dev": true, 1274 | "requires": { 1275 | "call-bind": "^1.0.0", 1276 | "define-properties": "^1.1.3", 1277 | "has-symbols": "^1.0.1", 1278 | "object-keys": "^1.1.1" 1279 | } 1280 | }, 1281 | "once": { 1282 | "version": "1.4.0", 1283 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1284 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1285 | "requires": { 1286 | "wrappy": "1" 1287 | } 1288 | }, 1289 | "path-is-absolute": { 1290 | "version": "1.0.1", 1291 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1292 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1293 | "dev": true 1294 | }, 1295 | "path-parse": { 1296 | "version": "1.0.7", 1297 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1298 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 1299 | }, 1300 | "pretty-hash": { 1301 | "version": "1.0.1", 1302 | "resolved": "https://registry.npmjs.org/pretty-hash/-/pretty-hash-1.0.1.tgz", 1303 | "integrity": "sha1-FuBXkYje9WvbVliSvNBaXWUySAc=" 1304 | }, 1305 | "process-nextick-args": { 1306 | "version": "2.0.1", 1307 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1308 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1309 | }, 1310 | "protocol-buffers": { 1311 | "version": "4.2.0", 1312 | "resolved": "https://registry.npmjs.org/protocol-buffers/-/protocol-buffers-4.2.0.tgz", 1313 | "integrity": "sha512-hNp56d5uuREVde7UqP+dmBkwzxrhJwYU5nL/mdivyFfkRZdgAgojkyBeU3jKo7ZHrjdSx6Q1CwUmYJI6INt20g==", 1314 | "requires": { 1315 | "generate-function": "^2.0.0", 1316 | "generate-object-property": "^1.2.0", 1317 | "protocol-buffers-encodings": "^1.1.0", 1318 | "protocol-buffers-schema": "^3.1.1", 1319 | "signed-varint": "^2.0.0", 1320 | "varint": "^5.0.0" 1321 | } 1322 | }, 1323 | "protocol-buffers-encodings": { 1324 | "version": "1.1.1", 1325 | "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.1.1.tgz", 1326 | "integrity": "sha512-5aFshI9SbhtcMiDiZZu3g2tMlZeS5lhni//AGJ7V34PQLU5JA91Cva7TIs6inZhYikS3OpnUzAUuL6YtS0CyDA==", 1327 | "requires": { 1328 | "signed-varint": "^2.0.1", 1329 | "varint": "5.0.0" 1330 | }, 1331 | "dependencies": { 1332 | "varint": { 1333 | "version": "5.0.0", 1334 | "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", 1335 | "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8=" 1336 | } 1337 | } 1338 | }, 1339 | "protocol-buffers-schema": { 1340 | "version": "3.4.0", 1341 | "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz", 1342 | "integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA==" 1343 | }, 1344 | "pump": { 1345 | "version": "3.0.0", 1346 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1347 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1348 | "requires": { 1349 | "end-of-stream": "^1.1.0", 1350 | "once": "^1.3.1" 1351 | } 1352 | }, 1353 | "pumpify": { 1354 | "version": "2.0.1", 1355 | "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", 1356 | "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", 1357 | "requires": { 1358 | "duplexify": "^4.1.1", 1359 | "inherits": "^2.0.3", 1360 | "pump": "^3.0.0" 1361 | }, 1362 | "dependencies": { 1363 | "duplexify": { 1364 | "version": "4.1.2", 1365 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", 1366 | "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", 1367 | "requires": { 1368 | "end-of-stream": "^1.4.1", 1369 | "inherits": "^2.0.3", 1370 | "readable-stream": "^3.1.1", 1371 | "stream-shift": "^1.0.0" 1372 | } 1373 | }, 1374 | "readable-stream": { 1375 | "version": "3.6.0", 1376 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1377 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1378 | "requires": { 1379 | "inherits": "^2.0.3", 1380 | "string_decoder": "^1.1.1", 1381 | "util-deprecate": "^1.0.1" 1382 | } 1383 | } 1384 | } 1385 | }, 1386 | "punycode": { 1387 | "version": "1.3.2", 1388 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 1389 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 1390 | }, 1391 | "querystring": { 1392 | "version": "0.2.0", 1393 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 1394 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 1395 | }, 1396 | "queue-tick": { 1397 | "version": "1.0.0", 1398 | "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.0.tgz", 1399 | "integrity": "sha512-ULWhjjE8BmiICGn3G8+1L9wFpERNxkf8ysxkAer4+TFdRefDaXOCV5m92aMB9FtBVmn/8sETXLXY6BfW7hyaWQ==" 1400 | }, 1401 | "random-access-file": { 1402 | "version": "2.2.1", 1403 | "resolved": "https://registry.npmjs.org/random-access-file/-/random-access-file-2.2.1.tgz", 1404 | "integrity": "sha512-RGU0xmDqdOyEiynob1KYSeh8+9c9Td1MJ74GT1viMEYAn8SJ9oBtWCXLsYZukCF46yududHOdM449uRYbzBrZQ==", 1405 | "requires": { 1406 | "mkdirp-classic": "^0.5.2", 1407 | "random-access-storage": "^1.1.1" 1408 | } 1409 | }, 1410 | "random-access-memory": { 1411 | "version": "3.1.1", 1412 | "resolved": "https://registry.npmjs.org/random-access-memory/-/random-access-memory-3.1.1.tgz", 1413 | "integrity": "sha512-Qy1MliJDozZ1A6Hx3UbEnm8PPCfkiG/8CArbnhrxXMx1YRJPWipgPTB9qyhn4Z7WlLvCEqPb6Bd98OayyVuwrA==", 1414 | "requires": { 1415 | "inherits": "^2.0.3", 1416 | "is-options": "^1.0.1", 1417 | "random-access-storage": "^1.1.1" 1418 | } 1419 | }, 1420 | "random-access-s3": { 1421 | "version": "1.0.0", 1422 | "resolved": "https://registry.npmjs.org/random-access-s3/-/random-access-s3-1.0.0.tgz", 1423 | "integrity": "sha1-LlXXJnSQ3MDPocucyepf6Q6Ztzk=", 1424 | "requires": { 1425 | "aws-sdk": "^2.188.0", 1426 | "random-access-storage": "^1.1.0" 1427 | } 1428 | }, 1429 | "random-access-storage": { 1430 | "version": "1.4.1", 1431 | "resolved": "https://registry.npmjs.org/random-access-storage/-/random-access-storage-1.4.1.tgz", 1432 | "integrity": "sha512-DbCc2TIzOxPaHF6KCbr8zLtiYOJQQQCBHUVNHV/SckUQobCBB2YkDtbLdxGnPwPNpJfEyMWxDAm36A2xkbxxtw==", 1433 | "requires": { 1434 | "inherits": "^2.0.3" 1435 | } 1436 | }, 1437 | "readable-stream": { 1438 | "version": "2.3.7", 1439 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 1440 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 1441 | "requires": { 1442 | "core-util-is": "~1.0.0", 1443 | "inherits": "~2.0.3", 1444 | "isarray": "~1.0.0", 1445 | "process-nextick-args": "~2.0.0", 1446 | "safe-buffer": "~5.1.1", 1447 | "string_decoder": "~1.1.1", 1448 | "util-deprecate": "~1.0.1" 1449 | }, 1450 | "dependencies": { 1451 | "safe-buffer": { 1452 | "version": "5.1.2", 1453 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1454 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1455 | } 1456 | } 1457 | }, 1458 | "refpool": { 1459 | "version": "1.2.2", 1460 | "resolved": "https://registry.npmjs.org/refpool/-/refpool-1.2.2.tgz", 1461 | "integrity": "sha512-uxnVlknIezgMMYQu2RDU/OCkyHntFHnC68PqghdKun2z3W3t5CmAt4uDr28TcPP2GQNsTAjvX1+vpHVrjvcolg==", 1462 | "requires": { 1463 | "time-ordered-set": "^1.0.2" 1464 | } 1465 | }, 1466 | "regexp.prototype.flags": { 1467 | "version": "1.4.3", 1468 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 1469 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 1470 | "dev": true, 1471 | "requires": { 1472 | "call-bind": "^1.0.2", 1473 | "define-properties": "^1.1.3", 1474 | "functions-have-names": "^1.2.2" 1475 | } 1476 | }, 1477 | "remove-trailing-separator": { 1478 | "version": "1.1.0", 1479 | "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", 1480 | "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" 1481 | }, 1482 | "resolve": { 1483 | "version": "1.17.0", 1484 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 1485 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 1486 | "requires": { 1487 | "path-parse": "^1.0.6" 1488 | } 1489 | }, 1490 | "resumer": { 1491 | "version": "0.0.0", 1492 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 1493 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 1494 | "dev": true, 1495 | "requires": { 1496 | "through": "~2.3.4" 1497 | } 1498 | }, 1499 | "safe-buffer": { 1500 | "version": "5.2.1", 1501 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1502 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1503 | }, 1504 | "sax": { 1505 | "version": "1.2.1", 1506 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 1507 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 1508 | }, 1509 | "sha256-universal": { 1510 | "version": "1.1.0", 1511 | "resolved": "https://registry.npmjs.org/sha256-universal/-/sha256-universal-1.1.0.tgz", 1512 | "integrity": "sha512-CDpS+UrEMA7UOPdogFJ1HOPNrhj0vH+mzWHT5FaU0qlsEXQ4BBey2Mc5+TfJ5nxyRf77fpABbKmXbL/QIzgz2A==", 1513 | "requires": { 1514 | "sha256-wasm": "^2.1.0" 1515 | } 1516 | }, 1517 | "sha256-wasm": { 1518 | "version": "2.1.0", 1519 | "resolved": "https://registry.npmjs.org/sha256-wasm/-/sha256-wasm-2.1.0.tgz", 1520 | "integrity": "sha512-xhb4uSXGIotJj2Q8pWpchDjkszHeTWVoP7p9NM6TGssL/mGUOLFR7vfIrrmcG99aRRox8YiN0JukZf4ZlLWu1w==", 1521 | "requires": { 1522 | "nanoassert": "^2.0.0" 1523 | }, 1524 | "dependencies": { 1525 | "nanoassert": { 1526 | "version": "2.0.0", 1527 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1528 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1529 | } 1530 | } 1531 | }, 1532 | "sha512-universal": { 1533 | "version": "1.1.0", 1534 | "resolved": "https://registry.npmjs.org/sha512-universal/-/sha512-universal-1.1.0.tgz", 1535 | "integrity": "sha512-2ag5no5DwP9lv6Nk8VkJNujqv5LiJODxReqNp2qiY+O1c1bgiN/PlCRdmjQdBp0k/2XAkdIirEPDpZJoGf9gLw==", 1536 | "requires": { 1537 | "sha512-wasm": "^2.1.0" 1538 | } 1539 | }, 1540 | "sha512-wasm": { 1541 | "version": "2.1.1", 1542 | "resolved": "https://registry.npmjs.org/sha512-wasm/-/sha512-wasm-2.1.1.tgz", 1543 | "integrity": "sha512-v6t7N3YkJzc4UUhel6fpc423O+7pqlKARUrUOAthgBL3LSncb74qnUXljyd8q24+twG7yC/Kh/ouo6wc2L4oVw==", 1544 | "requires": { 1545 | "nanoassert": "^2.0.0" 1546 | }, 1547 | "dependencies": { 1548 | "nanoassert": { 1549 | "version": "2.0.0", 1550 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1551 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1552 | } 1553 | } 1554 | }, 1555 | "side-channel": { 1556 | "version": "1.0.4", 1557 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1558 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1559 | "dev": true, 1560 | "requires": { 1561 | "call-bind": "^1.0.0", 1562 | "get-intrinsic": "^1.0.2", 1563 | "object-inspect": "^1.9.0" 1564 | } 1565 | }, 1566 | "signed-varint": { 1567 | "version": "2.0.1", 1568 | "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz", 1569 | "integrity": "sha1-UKmYnafJjCxh2tEZvJdHDvhSgSk=", 1570 | "requires": { 1571 | "varint": "~5.0.0" 1572 | } 1573 | }, 1574 | "simple-handshake": { 1575 | "version": "3.0.0", 1576 | "resolved": "https://registry.npmjs.org/simple-handshake/-/simple-handshake-3.0.0.tgz", 1577 | "integrity": "sha512-8Te0vlxvhpNCMgwnWFTbRR6Re2l8hq8wyXQc3lY9dPYXFxYwVkh79LhDQHFCOWRavmbiOdfqq1l5HT/73Rn2/w==", 1578 | "requires": { 1579 | "nanoassert": "^2.0.0", 1580 | "noise-protocol": "^3.0.0" 1581 | }, 1582 | "dependencies": { 1583 | "nanoassert": { 1584 | "version": "2.0.0", 1585 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1586 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1587 | } 1588 | } 1589 | }, 1590 | "simple-hypercore-protocol": { 1591 | "version": "2.1.1", 1592 | "resolved": "https://registry.npmjs.org/simple-hypercore-protocol/-/simple-hypercore-protocol-2.1.1.tgz", 1593 | "integrity": "sha512-xKuomRCfDDf+r6PDOD3RD88cjOLcbJ0E3Iz9Z+daB4Sq3FVv6efKYsOytgzTfSZMzkszF9EpYHGIILdVI669qA==", 1594 | "requires": { 1595 | "hypercore-crypto": "^2.1.0", 1596 | "noise-protocol": "^3.0.1", 1597 | "protocol-buffers-encodings": "^1.1.0", 1598 | "simple-handshake": "^3.0.0", 1599 | "simple-message-channels": "^1.2.1", 1600 | "varint": "^5.0.0", 1601 | "xsalsa20-universal": "^1.0.0" 1602 | } 1603 | }, 1604 | "simple-message-channels": { 1605 | "version": "1.2.1", 1606 | "resolved": "https://registry.npmjs.org/simple-message-channels/-/simple-message-channels-1.2.1.tgz", 1607 | "integrity": "sha512-knSr69GKW9sCjzpoy817xQelpOASUQ53TXCBcSLDKLE7GTGpUAhZzOZYrdbX2Ig//m+8AIrNp7sM7HDNHBRzXw==", 1608 | "requires": { 1609 | "varint": "^5.0.0" 1610 | } 1611 | }, 1612 | "siphash24": { 1613 | "version": "1.1.1", 1614 | "resolved": "https://registry.npmjs.org/siphash24/-/siphash24-1.1.1.tgz", 1615 | "integrity": "sha512-dKKwjIoTOa587TARYLlBRXq2lkbu5Iz35XrEVWpelhBP1m8r2BGOy1QlaZe84GTFHG/BTucEUd2btnNc8QzIVA==", 1616 | "requires": { 1617 | "nanoassert": "^1.0.0" 1618 | } 1619 | }, 1620 | "siphash24-universal": { 1621 | "version": "1.0.0", 1622 | "resolved": "https://registry.npmjs.org/siphash24-universal/-/siphash24-universal-1.0.0.tgz", 1623 | "integrity": "sha512-TasWcrGz+ITPEvE6Bhz8hACI39bSuoO9aRBYk601lhFZRpkHikbmmCbD5OkBFenYUmhOCjbOSZDbaHb8+FdWkg==", 1624 | "requires": { 1625 | "siphash24": "^1.1.1" 1626 | } 1627 | }, 1628 | "sodium-javascript": { 1629 | "version": "0.6.3", 1630 | "resolved": "https://registry.npmjs.org/sodium-javascript/-/sodium-javascript-0.6.3.tgz", 1631 | "integrity": "sha512-Z+Xi02nBReXOwXKD8/9auRsh02swxgZlmen0vyoOE8cQTzzPXr4jHx+D7JUgc4S2j7sNza9k86xFLmQX/gEIWA==", 1632 | "requires": { 1633 | "blake2b": "^2.1.1", 1634 | "chacha20-universal": "^1.0.4", 1635 | "nanoassert": "^2.0.0", 1636 | "sha256-universal": "^1.0.1", 1637 | "sha512-universal": "^1.0.1", 1638 | "siphash24": "^1.0.1", 1639 | "xsalsa20": "^1.0.0" 1640 | }, 1641 | "dependencies": { 1642 | "nanoassert": { 1643 | "version": "2.0.0", 1644 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1645 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1646 | } 1647 | } 1648 | }, 1649 | "sodium-native": { 1650 | "version": "3.2.0", 1651 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.2.0.tgz", 1652 | "integrity": "sha512-8aq/vQSegLwsRch8Sb/Bpf9aAqlNe5dp0+NVhb9UjHv42zDZ0D5zX3wBRUbXK9Ejum9uZE6DUgT4vVLlUFRBWg==", 1653 | "requires": { 1654 | "ini": "^1.3.5", 1655 | "node-gyp-build": "^4.2.0" 1656 | } 1657 | }, 1658 | "sodium-universal": { 1659 | "version": "3.0.2", 1660 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-3.0.2.tgz", 1661 | "integrity": "sha512-K/KepORKL1fjAY68z/l9sqgxnEXZ0KCTo+qfMM1m6LkGDnhdEdZiZsf7DcUZYWeZe0/uGOC2fweWusPD7vSj8A==", 1662 | "requires": { 1663 | "blake2b": "^2.1.1", 1664 | "chacha20-universal": "^1.0.4", 1665 | "nanoassert": "^2.0.0", 1666 | "resolve": "^1.17.0", 1667 | "sha256-universal": "^1.0.1", 1668 | "sha512-universal": "^1.0.1", 1669 | "siphash24": "^1.0.1", 1670 | "sodium-javascript": "~0.6.2", 1671 | "sodium-native": "^3.2.0", 1672 | "xsalsa20": "^1.0.0" 1673 | }, 1674 | "dependencies": { 1675 | "nanoassert": { 1676 | "version": "2.0.0", 1677 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1678 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1679 | } 1680 | } 1681 | }, 1682 | "sorted-union-stream": { 1683 | "version": "github:tradle/sorted-union-stream#39c07984716faba16b9b9142aaa8df58bb98937a", 1684 | "from": "github:tradle/sorted-union-stream", 1685 | "requires": { 1686 | "streamx": "^2.6.0" 1687 | } 1688 | }, 1689 | "sparse-bitfield": { 1690 | "version": "3.0.3", 1691 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1692 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 1693 | "requires": { 1694 | "memory-pager": "^1.0.2" 1695 | } 1696 | }, 1697 | "stream-collector": { 1698 | "version": "1.0.1", 1699 | "resolved": "https://registry.npmjs.org/stream-collector/-/stream-collector-1.0.1.tgz", 1700 | "integrity": "sha1-TU5V8XE1YSGyxfZVn5RHBaso2xU=", 1701 | "requires": { 1702 | "once": "^1.3.1" 1703 | } 1704 | }, 1705 | "stream-shift": { 1706 | "version": "1.0.1", 1707 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", 1708 | "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" 1709 | }, 1710 | "streamx": { 1711 | "version": "2.7.0", 1712 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.7.0.tgz", 1713 | "integrity": "sha512-IZKrxBttcIFPfivBLerTrik1WiuhJONmcbVTEj3nArchrZcLQih4hILQiAZnGlyVPhP901i5Pz5t09JF8atltA==", 1714 | "requires": { 1715 | "fast-fifo": "^1.0.0", 1716 | "nanoassert": "^2.0.0" 1717 | }, 1718 | "dependencies": { 1719 | "nanoassert": { 1720 | "version": "2.0.0", 1721 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", 1722 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" 1723 | } 1724 | } 1725 | }, 1726 | "string.prototype.trim": { 1727 | "version": "1.2.6", 1728 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz", 1729 | "integrity": "sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ==", 1730 | "dev": true, 1731 | "requires": { 1732 | "call-bind": "^1.0.2", 1733 | "define-properties": "^1.1.4", 1734 | "es-abstract": "^1.19.5" 1735 | } 1736 | }, 1737 | "string.prototype.trimend": { 1738 | "version": "1.0.5", 1739 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", 1740 | "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", 1741 | "dev": true, 1742 | "requires": { 1743 | "call-bind": "^1.0.2", 1744 | "define-properties": "^1.1.4", 1745 | "es-abstract": "^1.19.5" 1746 | } 1747 | }, 1748 | "string.prototype.trimstart": { 1749 | "version": "1.0.5", 1750 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", 1751 | "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", 1752 | "dev": true, 1753 | "requires": { 1754 | "call-bind": "^1.0.2", 1755 | "define-properties": "^1.1.4", 1756 | "es-abstract": "^1.19.5" 1757 | } 1758 | }, 1759 | "string_decoder": { 1760 | "version": "1.1.1", 1761 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1762 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1763 | "requires": { 1764 | "safe-buffer": "~5.1.0" 1765 | }, 1766 | "dependencies": { 1767 | "safe-buffer": { 1768 | "version": "5.1.2", 1769 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1770 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1771 | } 1772 | } 1773 | }, 1774 | "tape": { 1775 | "version": "5.5.3", 1776 | "resolved": "https://registry.npmjs.org/tape/-/tape-5.5.3.tgz", 1777 | "integrity": "sha512-hPBJZBL9S7bH9vECg/KSM24slGYV589jJr4dmtiJrLD71AL66+8o4b9HdZazXZyvnilqA7eE8z5/flKiy0KsBg==", 1778 | "dev": true, 1779 | "requires": { 1780 | "array.prototype.every": "^1.1.3", 1781 | "call-bind": "^1.0.2", 1782 | "deep-equal": "^2.0.5", 1783 | "defined": "^1.0.0", 1784 | "dotignore": "^0.1.2", 1785 | "for-each": "^0.3.3", 1786 | "get-package-type": "^0.1.0", 1787 | "glob": "^7.2.0", 1788 | "has": "^1.0.3", 1789 | "has-dynamic-import": "^2.0.1", 1790 | "inherits": "^2.0.4", 1791 | "is-regex": "^1.1.4", 1792 | "minimist": "^1.2.6", 1793 | "object-inspect": "^1.12.0", 1794 | "object-is": "^1.1.5", 1795 | "object-keys": "^1.1.1", 1796 | "object.assign": "^4.1.2", 1797 | "resolve": "^2.0.0-next.3", 1798 | "resumer": "^0.0.0", 1799 | "string.prototype.trim": "^1.2.5", 1800 | "through": "^2.3.8" 1801 | }, 1802 | "dependencies": { 1803 | "resolve": { 1804 | "version": "2.0.0-next.3", 1805 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", 1806 | "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", 1807 | "dev": true, 1808 | "requires": { 1809 | "is-core-module": "^2.2.0", 1810 | "path-parse": "^1.0.6" 1811 | } 1812 | } 1813 | } 1814 | }, 1815 | "through": { 1816 | "version": "2.3.8", 1817 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1818 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1819 | "dev": true 1820 | }, 1821 | "thunky": { 1822 | "version": "1.1.0", 1823 | "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", 1824 | "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" 1825 | }, 1826 | "thunky-map": { 1827 | "version": "1.0.1", 1828 | "resolved": "https://registry.npmjs.org/thunky-map/-/thunky-map-1.0.1.tgz", 1829 | "integrity": "sha512-RC34aHdxC9CYpvuO1TLBnsFa2G9KWFvUbbIbYnqX3hWdUfECWUzjDmv1XRgjRsQ9oGVmUZC+pOD4fAUWB6HU4Q==" 1830 | }, 1831 | "time-ordered-set": { 1832 | "version": "1.0.2", 1833 | "resolved": "https://registry.npmjs.org/time-ordered-set/-/time-ordered-set-1.0.2.tgz", 1834 | "integrity": "sha512-vGO99JkxvgX+u+LtOKQEpYf31Kj3i/GNwVstfnh4dyINakMgeZCpew1e3Aj+06hEslhtHEd52g7m5IV+o1K8Mw==" 1835 | }, 1836 | "timeout-refresh": { 1837 | "version": "1.0.2", 1838 | "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-1.0.2.tgz", 1839 | "integrity": "sha512-lsO23gD/EeW53AvEoTOleUFqTIapZTu5NPzD6ndUGs0+G1IuN0+hu2kT6I3AmNX2fiOrcC6umtzMEv1XQmajgQ==" 1840 | }, 1841 | "uint64be": { 1842 | "version": "2.0.2", 1843 | "resolved": "https://registry.npmjs.org/uint64be/-/uint64be-2.0.2.tgz", 1844 | "integrity": "sha512-9QqdvpGQTXgxthP+lY4e/gIBy+RuqcBaC6JVwT5I3bDLgT/btL6twZMR0pI3/Fgah9G/pdwzIprE5gL6v9UvyQ==", 1845 | "requires": { 1846 | "buffer-alloc": "^1.1.0" 1847 | } 1848 | }, 1849 | "unbox-primitive": { 1850 | "version": "1.0.2", 1851 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 1852 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 1853 | "dev": true, 1854 | "requires": { 1855 | "call-bind": "^1.0.2", 1856 | "has-bigints": "^1.0.2", 1857 | "has-symbols": "^1.0.3", 1858 | "which-boxed-primitive": "^1.0.2" 1859 | } 1860 | }, 1861 | "unixify": { 1862 | "version": "1.0.0", 1863 | "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", 1864 | "integrity": "sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA=", 1865 | "requires": { 1866 | "normalize-path": "^2.1.1" 1867 | } 1868 | }, 1869 | "unordered-array-remove": { 1870 | "version": "1.0.2", 1871 | "resolved": "https://registry.npmjs.org/unordered-array-remove/-/unordered-array-remove-1.0.2.tgz", 1872 | "integrity": "sha1-xUbo+I4xegzyZEyX7LV9umbSUO8=" 1873 | }, 1874 | "unordered-set": { 1875 | "version": "2.0.1", 1876 | "resolved": "https://registry.npmjs.org/unordered-set/-/unordered-set-2.0.1.tgz", 1877 | "integrity": "sha512-eUmNTPzdx+q/WvOHW0bgGYLWvWHNT3PTKEQLg0MAQhc0AHASHVHoP/9YytYd4RBVariqno/mEUhVZN98CmD7bg==" 1878 | }, 1879 | "url": { 1880 | "version": "0.10.3", 1881 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 1882 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 1883 | "requires": { 1884 | "punycode": "1.3.2", 1885 | "querystring": "0.2.0" 1886 | } 1887 | }, 1888 | "util-deprecate": { 1889 | "version": "1.0.2", 1890 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1891 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1892 | }, 1893 | "uuid": { 1894 | "version": "3.3.2", 1895 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1896 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1897 | }, 1898 | "varint": { 1899 | "version": "5.0.1", 1900 | "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.1.tgz", 1901 | "integrity": "sha512-nd1E35rKooOyDPhAeHS3hWVrTRBP4Cpn86Zxg/G73C9vA4Un2RXn9pvPF3SVOvTALCDL9wFSCwfI5ENAPzLWDA==" 1902 | }, 1903 | "which-boxed-primitive": { 1904 | "version": "1.0.2", 1905 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 1906 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 1907 | "dev": true, 1908 | "requires": { 1909 | "is-bigint": "^1.0.1", 1910 | "is-boolean-object": "^1.1.0", 1911 | "is-number-object": "^1.0.4", 1912 | "is-string": "^1.0.5", 1913 | "is-symbol": "^1.0.3" 1914 | } 1915 | }, 1916 | "which-collection": { 1917 | "version": "1.0.1", 1918 | "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", 1919 | "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", 1920 | "dev": true, 1921 | "requires": { 1922 | "is-map": "^2.0.1", 1923 | "is-set": "^2.0.1", 1924 | "is-weakmap": "^2.0.1", 1925 | "is-weakset": "^2.0.1" 1926 | } 1927 | }, 1928 | "which-typed-array": { 1929 | "version": "1.1.7", 1930 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", 1931 | "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", 1932 | "dev": true, 1933 | "requires": { 1934 | "available-typed-arrays": "^1.0.5", 1935 | "call-bind": "^1.0.2", 1936 | "es-abstract": "^1.18.5", 1937 | "foreach": "^2.0.5", 1938 | "has-tostringtag": "^1.0.0", 1939 | "is-typed-array": "^1.1.7" 1940 | } 1941 | }, 1942 | "wrappy": { 1943 | "version": "1.0.2", 1944 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1945 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1946 | }, 1947 | "xml2js": { 1948 | "version": "0.4.19", 1949 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 1950 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 1951 | "requires": { 1952 | "sax": ">=0.6.0", 1953 | "xmlbuilder": "~9.0.1" 1954 | } 1955 | }, 1956 | "xmlbuilder": { 1957 | "version": "9.0.7", 1958 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 1959 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 1960 | }, 1961 | "xsalsa20": { 1962 | "version": "1.1.0", 1963 | "resolved": "https://registry.npmjs.org/xsalsa20/-/xsalsa20-1.1.0.tgz", 1964 | "integrity": "sha512-zd3ytX2cm+tcSndRU+krm0eL4TMMpZE7evs5hLRAoOy6gviqLfe3qOlkjF3i5SeAkQUCeJk0lJZrEU56kHRfWw==" 1965 | }, 1966 | "xsalsa20-universal": { 1967 | "version": "1.0.0", 1968 | "resolved": "https://registry.npmjs.org/xsalsa20-universal/-/xsalsa20-universal-1.0.0.tgz", 1969 | "integrity": "sha512-0M/X61wiKKAGAMqsxEyJ0kY6NtjpcMiKinYSSsl4K7ypgvqXDTMwQK6hxnYE1s1Jm7h6YKcN8obDUg2CC+jVGA==", 1970 | "requires": { 1971 | "sodium-native": "^3.0.1", 1972 | "xsalsa20": "^1.1.0" 1973 | } 1974 | } 1975 | } 1976 | } 1977 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "why-hypercore", 3 | "version": "1.0.0", 4 | "description": "As we are exploring the design for running Tradle on Hypercore, this summarizes our findings. You can find a lot more information in this [rapidly growing FAQ](FAQ.md).", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tape test/*.test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tradle/why-hypercore.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/tradle/why-hypercore/issues" 17 | }, 18 | "homepage": "https://github.com/tradle/why-hypercore#readme", 19 | "dependencies": { 20 | "@geut/hyperdrive-promise": "^3.1.3", 21 | "corestore": "^5.8.2", 22 | "hyperbee": "0.0.18", 23 | "hypercore": "^9.12.0", 24 | "hyperdrive": "^10.21.0", 25 | "mountable-hypertrie": "^2.8.0", 26 | "multi-hyperdrive": "^1.1.4", 27 | "random-access-memory": "^3.1.1", 28 | "random-access-s3": "^1.0.0", 29 | "sorted-union-stream": "github:tradle/sorted-union-stream" 30 | }, 31 | "devDependencies": { 32 | "tape": "^5.5.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.ignoreWords": [ 3 | "automerge", 4 | "cobox", 5 | "gmail", 6 | "key", 7 | "peermaps", 8 | "public", 9 | "subfolders", 10 | "ueta" 11 | ], 12 | "cSpell.words": [ 13 | "ceph" 14 | ] 15 | } -------------------------------------------------------------------------------- /test/hyperbeeUnion.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const Hyperbee = require('hyperbee') 3 | const Union = require('sorted-union-stream') 4 | const { LEFT, EQ_LEFT, RIGHT, EQ_RIGHT } = require('sorted-union-stream/constants') 5 | const hypercore = require('hypercore') 6 | const { create } = require('../utils') 7 | 8 | test('hyperbee union - simulating index - deduplicating based on "time" (not key)', t => { 9 | prepare(2) 10 | .then(([sorted1, sorted2]) => { 11 | const u = new Union(sorted1, sorted2, compare) 12 | const values = [ 13 | { a: {id: 1, time: 1070} }, 14 | { a1: {id: 7, time: 1130} }, 15 | { b: {id: 2, time: 1090} }, 16 | { c: {id: 3, time: 1100} }, 17 | { d: {id: 4, time: 1080} }, 18 | { e: {id: 5, time: 1120} }, 19 | { f: {id: 6, time: 1110} }, 20 | ] 21 | u.on('data', function(data) { 22 | // console.log(data.value) 23 | const { key, value } = data 24 | t.same({[key]: value}, values.shift()) 25 | }) 26 | u.on('end', function() { 27 | t.same(0, values.length, 'no more data') 28 | t.end() 29 | }) 30 | }) 31 | }) 32 | 33 | 34 | test('hyperbee union (reverse) - simulating index - deduplicating based on "time" (not key)', t => { 35 | prepare(2, true) 36 | .then(([sorted1, sorted2]) => { 37 | const u = new Union(sorted1, sorted2, compareReverse) 38 | 39 | const values = [ 40 | { f: {id: 6, time: 1110} }, 41 | { e: {id: 5, time: 1120} }, 42 | { d: {id: 4, time: 1080} }, 43 | { c: {id: 3, time: 1100} }, 44 | { b: {id: 2, time: 1090} }, 45 | { a1: {id: 7, time: 1130} }, 46 | { a: {id: 1, time: 1070} }, 47 | ] 48 | u.on('data', function(data) { 49 | // console.log(data.value) 50 | const { key, value } = data 51 | t.same({[key]: value}, values.shift()) 52 | }) 53 | u.on('end', function() { 54 | t.same(0, values.length, 'no more data') 55 | t.end() 56 | }) 57 | }) 58 | }) 59 | 60 | test('hyperbee union of 3 streams - simulating index - deduplicating based on "time" (not key)', t => { 61 | prepare(3) 62 | .then(([sorted1, sorted2, sorted3]) => { 63 | const u2 = new Union(sorted1, sorted2, compare) 64 | const u3 = new Union(u2, sorted3, compare) 65 | 66 | const values = [ 67 | { a: {id: 1, time: 1140} }, 68 | { a1: {id: 7, time: 1200} }, 69 | { b: {id: 2, time: 1160} }, 70 | { c: {id: 3, time: 1170} }, 71 | { d: {id: 4, time: 1150} }, 72 | { e: {id: 5, time: 1190} }, 73 | { f: {id: 6, time: 1180} }, 74 | ] 75 | 76 | let times = 77 | u3.on('data', (data) => { 78 | // console.log(data) 79 | const { key, value } = data 80 | t.same({[key]: value}, values.shift()) 81 | }) 82 | u3.on('end', () => { 83 | t.same(0, values.length, 'no more data') 84 | t.end() 85 | }) 86 | }) 87 | }) 88 | 89 | function compare (a, b) { 90 | if (a.value.id < b.value.id) 91 | return LEFT 92 | if (a.value.id > b.value.id) 93 | return RIGHT 94 | if (a.value.time < b.value.time) 95 | return EQ_RIGHT 96 | return EQ_LEFT 97 | } 98 | 99 | function compareReverse (a, b) { 100 | if (a.value.id < b.value.id) 101 | return RIGHT 102 | if (a.value.id > b.value.id) 103 | return LEFT 104 | if (a.value.time < b.value.time) 105 | return EQ_RIGHT 106 | return EQ_LEFT 107 | } 108 | async function prepare(count, reverse) { 109 | let feeds = await create(count) 110 | let d = 1000 111 | let cnt = 0 112 | function getDate(cnt) { 113 | return d + cnt * 10 114 | } 115 | 116 | let streams = [] 117 | 118 | for (let i=0; i { 9 | const store = new Corestore(ram) 10 | await store.ready() 11 | const [ trie1, trie2, trie3 ] = await create(3, store) 12 | try { 13 | let results = await runAll([ 14 | cb => trie1.ready(cb), 15 | cb => trie2.ready(cb), 16 | cb => trie3.ready(cb), 17 | cb => trie3.put('/c', 'hello', cb), 18 | cb => trie3.put('/d', 'world', cb), 19 | cb => trie2.mount('/b', trie3.key, cb), 20 | cb => trie1.mount('/a', trie2.key, cb), 21 | ]) 22 | let a = await promisifyAndExec(trie1, 'get', '/a/b/c') 23 | let b = await promisifyAndExec(trie1, 'get', '/a/b/d') 24 | t.same(a.value, Buffer.from('hello')) 25 | t.same(b.value, Buffer.from('world')) 26 | t.end() 27 | } catch (errors) { 28 | t.fail('Error: ', errors) 29 | } 30 | }) 31 | 32 | function create(count, store) { 33 | let tries = [] 34 | for (let i=0; i { 10 | const drive = hyperdrive('example') 11 | 12 | const multi = multiHyperdrive(drive) 13 | 14 | drive.writeFile('example.txt', 'Hello World!', () => { 15 | multi.readFile('example.txt', 'utf8', (err, data) => { 16 | t.error(err, 'able to read') 17 | t.equal(data, 'Hello World!', 'got file contents') 18 | 19 | multi.readdir('/', (err2, files) => { 20 | t.error(err2, 'able to read dir') 21 | t.deepEqual(files, ['example.txt'], 'got files from drive') 22 | multi.readdir('/', { includeStats: true }, (err3, stats) => { 23 | t.error(err3, 'able to read dir stats') 24 | t.equal(stats.length, 1, 'got stats from drive') 25 | const [statData] = stats 26 | const { name, stat } = statData 27 | t.equal(name, 'example.txt', 'got expected name') 28 | t.ok(stat, 'got a stat object') 29 | t.end() 30 | }) 31 | }) 32 | }) 33 | }) 34 | }) 35 | 36 | test.skip('Add/Remove drive events', (t) => { 37 | const drive = hyperdrive('example') 38 | 39 | const multi = multiHyperdrive(drive) 40 | 41 | drive.ready(() => { 42 | const drive2 = hyperdrive('example2') 43 | 44 | multi.on('drive-add', (somedrive) => { 45 | t.equal(somedrive, drive2, 'drive emitted on add') 46 | }) 47 | 48 | multi.on('drive-remove', (somedrive) => { 49 | t.equal(somedrive, drive2, 'drive emitted on remove') 50 | t.end() 51 | }) 52 | 53 | multi.addDrive(drive2, () => { 54 | t.pass('Able to add drive') 55 | multi.removeDrive(drive2.key) 56 | }) 57 | }) 58 | }) 59 | test.skip('Write to primary through multi-hyperdrive', (t) => { 60 | const drive = hyperdrive('example') 61 | 62 | const multi = multiHyperdrive(drive) 63 | 64 | multi.writeFile('/example1.txt', 'Hello world!', (err) => { 65 | t.error(err, 'able to write file') 66 | multi.writeFile('/example2.txt', 'Hello, world?', (err) => { 67 | t.error(err, 'able to write another file') 68 | multi.readdir('/', (err, files) => { 69 | t.error(err, 'able to read files from dir') 70 | t.deepEquals(files, ['example.txt', 'example2.txt', 'example1.txt'], 'got expected files in the dir') 71 | t.end() 72 | }) 73 | }) 74 | }) 75 | }) 76 | 77 | test('Read from multiple non-conflicting drives', async (t) => { 78 | t.plan(4) 79 | 80 | let [drive1, drive2] = await createHyperdrives({ namespace: 'example2', sameNamespace: true, count: 2 }) 81 | let multi = multiHyperdrive(drive1) 82 | 83 | multi.addDrive(drive2) 84 | 85 | await new Promise((resolve, reject) => { 86 | prepare((e) => { 87 | if (e) t.error(e) 88 | verify((e) => { 89 | if (e) t.error(e) 90 | cleanup() 91 | resolve() 92 | }) 93 | }) 94 | 95 | function prepare (cb) { 96 | drive1.writeFile('example.txt', 'Hello World! I am here!!!', (e) => { 97 | if (e) return cb(e) 98 | drive2.writeFile('example2.txt', 'Hello World! Tada', (e) => { 99 | if (e) return cb(e) 100 | cb() 101 | }) 102 | }) 103 | } 104 | 105 | function verify (cb) { 106 | multi.readFile('example2.txt', 'utf8', (err, data) => { 107 | t.error(err, 'able to read') 108 | t.equal(data, 'Hello World! Tada', 'got file contents') 109 | 110 | multi.readdir('/', (err2, files) => { 111 | t.error(err2, 'able to read dir') 112 | t.deepEqual(files, ['example.txt', 'example2.txt'], 'got files from drives') 113 | cb() 114 | }) 115 | }) 116 | } 117 | 118 | function cleanup () { 119 | drive1.close() 120 | drive2.close() 121 | } 122 | }) 123 | t.end() 124 | }) 125 | 126 | -------------------------------------------------------------------------------- /test/ras3.test.js: -------------------------------------------------------------------------------- 1 | // For this test, move one of your hypercore folders to S3 2 | 3 | const tape = require('tape') 4 | const ram = require('random-access-memory') 5 | const ras3 = require('random-access-s3') 6 | const hypercore = require('hypercore') 7 | 8 | const MESSAGE = 'Usage: node test/ras3.test.js [S3 bucket] [folder in this bucket with the hypercore]' 9 | 10 | var [ cmd, cmd1, bucket, folder ] = process.argv 11 | if (cmd1.endsWith('.bin/tape')) 12 | ([ bucket, folder] = process.argv.slice(4)) 13 | else 14 | ([ bucket, folder] = process.argv.slice(2)) 15 | 16 | 17 | tape('read from s3', t => { 18 | if (!folder) { 19 | t.fail(MESSAGE) 20 | t.end() 21 | return 22 | } 23 | // console.log(`folder: ${folder}`) 24 | let expected = ['bitfield', 'data', 'key', 'secret_key', 'signatures', 'tree'] 25 | let actual = [] 26 | 27 | let feed = hypercore(filename => { 28 | // console.log(`file: ${filename}`) 29 | actual.push(filename) 30 | return ras3(`${folder}/${filename}`, { bucket }) 31 | }) 32 | feed.ready(() => { 33 | t.same(expected, actual.sort((a, b) => a > b)) 34 | let length = 0 35 | feed.createReadStream({ live: false }) 36 | .on('data', (data) => { 37 | console.log(data.toString()) 38 | }) 39 | .on('end', () => { 40 | t.end() 41 | }) 42 | .on('error', (err) => { 43 | t.fail(err.message) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | const hypercore = require('hypercore') 2 | const hyperdrive = require('hyperdrive') 3 | const Corestore = require('corestore') 4 | const { promisify } = require('util') 5 | const ram = require('random-access-memory') 6 | 7 | const utils = { 8 | loudAsync(asyncFn) { 9 | return async (...args) => { 10 | try { 11 | return await asyncFn(...args) 12 | } catch (err) { 13 | // tslint:disable-next-line:no-console 14 | console.error(err) 15 | throw err 16 | } 17 | } 18 | }, 19 | async create(count) { 20 | if (!count) 21 | count = 1 22 | let feeds = [] 23 | for (let i=0; i { 58 | runNext(ops.shift()) 59 | function runNext (op) { 60 | op(err => { 61 | if (err) return reject(err) 62 | const next = ops.shift() 63 | if (!next) return resolve() 64 | return runNext(next) 65 | }) 66 | } 67 | }) 68 | } 69 | } 70 | module.exports = utils --------------------------------------------------------------------------------