# Work with many Hypercores using Corestore



An append-only log like Hypercore is powerful on its own, but it's most useful as a building-block for constructing larger data structures, such as databases or filesystems.
Building these data structures often requires many cores, each with different responsibilities. For example, [Hyperdrive](/p2p/reference/building-blocks/hyperdrive) uses one core to store file metadata and another to store file contents.

[`Corestore`](/p2p/reference/helpers/corestore) is a [Hypercore](/p2p/reference/building-blocks/hypercore) factory that makes it easier to manage large collections of named Hypercores. This guide demonstrates a pattern often in use: co-replicating many cores using Corestore, where several 'internal cores' are linked to from a primary core. Only the primary core is announced on the swarm—the keys for the others are recorded inside that core.

In [Replicate and persist with Hypercore](/p2p/how-to/store-and-replicate/replicate-and-persist-with-hypercore), only a single Hypercore instance was replicated. But in this guide, you'll replicate a single Corestore instance, which will internally manage the replication of a collection of Hypercores. You'll achieve this with two Pear Terminal Applications: [`multicore-writer-app`](#create-the-multicore-writer-app) and [`multicore-reader-app`](#create-the-multicore-reader-app).

<Callout type="info">
  **This guide is about the Pear-end, not the shell.** The code below lives in the Bare [worker](/pear/explanation/workers)—the peer-to-peer logic, not the user interface. Because the Pear-end never imports DOM APIs and never assumes a UI framework, the same worker is portable across **desktop (Electron)**, **mobile (React Native via Bare iOS / Bare Android)**, and **terminal**. The example apps ship an Electron shell, but only the UI half changes per platform—the logic here stays the same. See [Runtime and languages](/pear/explanation/runtime-and-languages) for the cross-platform model and current support.
</Callout>

<Callout type="info">
  This guide is a terminal-only walkthrough. For the **desktop equivalent**—the same Corestore co-replication pattern wired into an Electron + Bare worker shell—follow the [Getting Started path](/p2p/getting-started), which builds a `pear://` chat app on top of the [`hello-pear-electron` template](/p2p/getting-started/from-a-template/start-from-hello-pear-electron).
</Callout>

<Callout type="info">
  **Use one Corestore instance per application.** Multiple Corestores over the same storage cause file-locking errors and duplicate core storage. A single Corestore:

  * Reduces open file handles.
  * Reduces storage by deduping Hypercore storage.
  * Requires only one replication stream per peer connection.
  * Simplifies referring to Hypercores by name.

  If named cores collide across components, namespace them ([`store.namespace('a')`](/p2p/reference/helpers/corestore#storenamespacename))—retrieving cores by `key` is unaffected by namespacing.
</Callout>

## Create the multicore writer app [#create-the-multicore-writer-app]

### Create the multicore writer app directory and add dependencies [#create-the-multicore-writer-app-directory-and-add-dependencies]

Create the `multicore-writer-app` project with these commands:

```sh example=corestore-multi step=setup
mkdir multicore-writer-app
cd multicore-writer-app
npm init -y
npm pkg set type="module"
npm install bare-process corestore hyperswarm b4a
```

This will install the following dependencies:

* [`bare-process`](https://www.npmjs.com/package/bare-process): A module for working with processes.
* [`corestore`](/p2p/reference/helpers/corestore): A module for working with Corestore.
* [`hyperswarm`](/p2p/reference/building-blocks/hyperswarm): A module for working with Hyperswarm.
* [`b4a`](https://www.npmjs.com/package/b4a): A module for working with buffers.

### Add the multicore writer app logic [#add-the-multicore-writer-app-logic]

Create the `multicore-writer-app/index.js` file with the following content:

The `multicore-writer-app` uses a Corestore instance to create three Hypercores, which are then replicated with other peers using `Hyperswarm`:

* One Corestore and one Hyperswarm back the whole app (L7–L8). `core1` bootstraps the system—its first block records the keys of `core2` and `core3` (L22–L26), so a reader only needs `core1`'s key to discover the rest. Writing the bootstrap list before announcing on the swarm ensures `core1.get(0)` resolves as soon as a reader connects.
* The three named cores are created up front (L13–L15); names map to local key pairs and are never sent to readers.
* Only `core1`'s discovery key is announced on the swarm (L32)—`core2` and `core3` ride along because [`store.replicate(conn)`](/p2p/reference/helpers/corestore#storereplicateoptsorstream) replicates every loaded core over a single stream (L36).
* The main core key is logged so it can be passed to the [`multicore-reader-app`](#create-the-multicore-reader-app) (L28).
* Terminal input is routed by length: short messages append to `core2`, long ones to `core3` (L39–L46).

```javascript file=<rootDir>/examples/how-to/store-and-replicate/work-with-many-hypercores-using-corestore/multicore-writer-app/index.js title="multicore-writer-app/index.js" lineNumbers {7-8,13-15,22-26,28,32,36,39-46} skip="example-import"

import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore'
import b4a from 'b4a'
import process from 'bare-process'

const store = new Corestore('./multicore-writer-storage')
const swarm = new Hyperswarm()
process.once('SIGINT', () => swarm.destroy().then(() => process.exit(0), () => process.exit(0)))

// A name is a purely-local, and maps to a key pair. It's not visible to readers.
// Since a name always corresponds to a key pair, these are all writable
const core1 = store.get({ name: 'core-1', valueEncoding: 'json' })
const core2 = store.get({ name: 'core-2' })
const core3 = store.get({ name: 'core-3' })
await Promise.all([core1.ready(), core2.ready(), core3.ready()])

// Since Corestore does not exchange keys, they need to be exchanged elsewhere.
// Here, we'll record the other keys in the first block of core1. Do this
// *before* announcing on the swarm so that as soon as a reader connects,
// `core1.get(0)` resolves and the bootstrap key list is available.
if (core1.length === 0) {
  await core1.append({
    otherKeys: [core2, core3].map((core) => b4a.toString(core.key, 'hex'))
  })
}

console.log('main core key:', b4a.toString(core1.key, 'hex'))

// Here we'll only join the swarm with the core1's discovery key
// We don't need to announce core2 and core3, because they'll be replicated with core1
swarm.join(core1.discoveryKey)

// Corestore replication internally manages to replicate every loaded core
// Corestore *does not* exchange keys (read capabilities) during replication.
swarm.on('connection', (conn) => store.replicate(conn))

// Record all short messages in core2, and all long ones in core3
process.stdin.on('data', (data) => {
  if (data.length < 5) {
    console.log('appending short data to core2')
    core2.append(data)
  } else {
    console.log('appending long data to core3')
    core3.append(data)
  }
})
```

## Create the multicore reader app [#create-the-multicore-reader-app]

The `multicore-reader-app` connects to the previous peer with `Hyperswarm` and replicates the local `Corestore` instance to receive the data from it. This requires the copied key to be supplied as an argument when executing the file, which will then be used to create a core with the same public key as the other peer (that is, the same discovery key for both the reader and writer peers).

### Create the multicore reader app directory and add dependencies [#create-the-multicore-reader-app-directory-and-add-dependencies]

Create the `multicore-reader-app` project with these commands:

```sh example=corestore-multi step=setup
mkdir multicore-reader-app
cd multicore-reader-app
npm init -y
npm pkg set type="module"
npm install corestore hyperswarm b4a bare-process
```

This will install the following dependencies:

* [`bare-process`](https://www.npmjs.com/package/bare-process): A module for working with processes.
* [`corestore`](/p2p/reference/helpers/corestore): A module for working with Corestore.
* [`hyperswarm`](/p2p/reference/building-blocks/hyperswarm): A module for working with Hyperswarm.
* [`b4a`](https://www.npmjs.com/package/b4a): A module for working with buffers.

### Add the multicore reader app logic [#add-the-multicore-reader-app-logic]

Create the `multicore-reader-app/index.js` file with the following content. The reader takes the writer's main core key as a command-line argument (L6–L8) and opens its own Corestore (L10–L11). On each connection it replicates the whole store (L17), then gets `core1` from the supplied key, joins the swarm on its discovery key, and flushes discovery (L20–L25). [`core.get(0)`](/p2p/reference/building-blocks/hypercore#await-coregetindex-options) then blocks until the writer's first block—the bootstrap key list—has actually replicated; unlike [`core.update()`](/p2p/reference/building-blocks/hypercore#await-coreupdateoptions) it waits for the data itself, and a timeout surfaces a clear error if the writer is never reachable instead of reading an empty core or hanging (L27–L36). It reads the bootstrap key list from that block (L39) and, for every key, gets the corresponding core and logs each new block as it is appended (L40–L50):

```javascript file=<rootDir>/examples/how-to/store-and-replicate/work-with-many-hypercores-using-corestore/multicore-reader-app/index.js title="multicore-reader-app/index.js" lineNumbers {6-8,10-11,17,20-25,27-36,39,40-50} skip="example-import"
import process from 'bare-process'
import Corestore from 'corestore'
import Hyperswarm from 'hyperswarm'
import b4a from 'b4a'

if (!Bare.argv[2]) throw new Error('provide a key')

const key = b4a.from(Bare.argv[2], 'hex')

const store = new Corestore('./multicore-reader-storage')
await store.ready()

const swarm = new Hyperswarm()
process.once('SIGINT', () => swarm.destroy().then(() => process.exit(0), () => process.exit(0)))

// replication of corestore instance on every connection
swarm.on('connection', (conn) => store.replicate(conn))

// creation/getting of a hypercore instance using the key passed
const core = store.get({ key, valueEncoding: 'json' })
// wait till all the properties of the hypercore instance are initialized
await core.ready()

swarm.join(core.discoveryKey)
await swarm.flush()

// core.get(0) blocks until the writer's first block (the bootstrap key list) has
// replicated from a connected peer — unlike core.update(), it waits for the data
// itself rather than just peer discovery. The timeout surfaces a clear error if
// the writer is never reachable, instead of leaving the reader hanging forever.
let firstBlock
try {
  firstBlock = await core.get(0, { timeout: 30000 })
} catch {
  throw new Error('Could not connect to the writer peer')
}

// read the bootstrap key list (the other core keys) from the first block
const { otherKeys } = firstBlock
for (const key of otherKeys) {
  const core = store.get({ key: b4a.from(key, 'hex') })
  // on every append to the hypercore, 
  // download the latest block of the core and log it to the console
  core.on('append', () => {
    const seq = core.length - 1
    core.get(seq).then(block => {
      console.log(`Block ${seq} in Core ${key}: ${block}`) 
    })
  })
}
```

## Run the writer and reader [#run-the-writer-and-reader]

### Run the multicore writer app [#run-the-multicore-writer-app]

In one terminal, run `multicore-writer-app` with `bare`.

```sh example=corestore-multi step=run process=writer expect="main core key:" capture-key="main core key: ([0-9a-f]+)" timeout=20000
bare multicore-writer-app
```

The `multicore-writer-app` will output the main core key.

### Run the multicore reader app [#run-the-multicore-reader-app]

In another terminal, open the `multicore-reader-app` and pass it the key:

```sh example=corestore-multi step=run process=reader cmd="bare multicore-reader-app ${key}" expect-alive=8000 timeout=45000
bare multicore-reader-app <SUPPLY THE KEY HERE>
```

As inputs are made to the terminal running the writer application, outputs should be shown in the terminal running the reader application.

## See also [#see-also]

* [Share append-only databases with Hyperbee](/p2p/how-to/store-and-replicate/share-append-only-databases-with-hyperbee)—key/value store on top of Hypercore.
* [Build a multiwriter app with Autobase](/p2p/how-to/store-and-replicate/build-a-multiwriter-app-with-autobase)—lift the one-writer-per-core limit by linearizing several writers into one view.
* [Create a full peer-to-peer filesystem with Hyperdrive](/p2p/how-to/stream-and-share-media/create-a-full-peer-to-peer-filesystem-with-hyperdrive)—filesystem on top of two Hypercores.
* [Host multiple rooms in one chat app](/p2p/how-to/connect-to-peers/host-multiple-rooms-in-one-chat-app)—this co-replication pattern applied in a full app, where one Corestore backs many rooms.
* [Corestore reference](/p2p/reference/helpers/corestore)—full API for the store factory, namespacing, and replication surface used here.
* [Hypercore reference](/p2p/reference/building-blocks/hypercore)—full API for the individual append-only log each Corestore session manages.
