# Create a full peer-to-peer filesystem with Hyperdrive



This guide will show you how to create a full peer-to-peer filesystem with Hyperdrive.

[Hyperdrive](/p2p/reference/building-blocks/hyperdrive) is a wrapper around two [Hypercores](/p2p/reference/building-blocks/hypercore):

* one is a [Hyperbee](/p2p/reference/building-blocks/hyperbee) index for storing file metadata, and
* the other is used to store file contents.

In this guide, you will create three Pear Terminal Applications:

* [`drive-writer-app`](#create-the-drive-writer-app) that mirrors a local directory into a Hyperdrive,
* [`drive-reader-app`](#create-the-drive-reader-app) that replicates the Hyperdrive with a reader peer, and
* [`drive-bee-reader-app`](#inspect-the-hyperdrive-as-a-hyperbee) that inspects the Hyperdrive as a Hyperbee

When the writer modifies its drive—adding, removing, or changing files—the reader's local copy updates to match. To do this, you need two additional tools:

* [`MirrorDrive`](/p2p/reference/helpers/mirrordrive): the diff-and-sync engine that mirrors one drive into another (used here via `local.mirror(drive)` and [`drive.mirror(local)`](/p2p/reference/building-blocks/hyperdrive#drivemirrorout-options)), and
* [`LocalDrive`](/p2p/reference/helpers/localdrive): a local filesystem adapter that exposes a Hyperdrive-like interface over a directory on disk.

These tools handle all interactions between Hyperdrives and the local filesystem.

<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 focuses on the Pear/Bare logic**—plain terminal apps, no UI. For the same file-sharing capability wired into a full desktop app with a front end, see [Share files in a peer-to-peer app](/p2p/how-to/stream-and-share-media/share-files-in-a-peer-to-peer-app) and its worked example [`pear-file-sharing`](https://github.com/holepunchto/pear-docs/tree/preview/examples/how-to/stream-and-share-media/file-sharing). The drive logic is identical; only the UI half changes.
</Callout>

## Create the `drive-writer-app` [#create-the-drive-writer-app]

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

Start by creating the `drive-writer-app` project with these commands:

```sh example=hyperdrive-fs step=setup
mkdir drive-writer-app
cd drive-writer-app
npm init -y
npm pkg set type="module"
npm install corestore localdrive hyperswarm hyperdrive debounceify b4a bare-process
```

This will install the following dependencies:

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

### Add the `drive-writer-app` logic [#add-the-drive-writer-app-logic]

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

```javascript file=<rootDir>/examples/how-to/stream-and-share-media/create-a-full-peer-to-peer-filesystem-with-hyperdrive/drive-writer-app/index.js title="drive-writer-app/index.js" lineNumbers {11-16,19-23,26,31-34,39-42,45-49} skip="example-import"

import process from 'bare-process'
import Hyperswarm from 'hyperswarm'
import Hyperdrive from 'hyperdrive'
import Localdrive from 'localdrive'
import Corestore from 'corestore'
import debounce from 'debounceify'
import b4a from 'b4a'

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

// replication of the corestore instance on connection with other peers
swarm.on('connection', conn => store.replicate(conn))

// A local drive provides a Hyperdrive interface to a local directory
const local = new Localdrive('./writer-dir')

// A Hyperdrive takes a Corestore because it needs to create many cores
// One for a file metadata Hyperbee, and one for a content Hypercore
const drive = new Hyperdrive(store)

// wait till the properties of the hyperdrive instance are initialized
await drive.ready()

// Import changes from the local drive into the Hyperdrive
const mirror = debounce(mirrorDrive)

const discovery = swarm.join(drive.discoveryKey)
await discovery.flushed()

console.log('drive key:', b4a.toString(drive.key, 'hex'))

// start the mirroring process (i.e copying) of content from writer-dir to the drive
// whenever something is entered (other than '/n' or Enter )in the command-line
process.stdin.setEncoding('utf-8')
process.stdin.on('data', (data) => {
  if (!data.match('\n')) return
  mirror()
})

// this function copies the contents from writer-dir directory to the drive
async function mirrorDrive () {
  console.log('started mirroring changes from \'./writer-dir\' into the drive...')
  const mirror = local.mirror(drive)
  await mirror.done()
  console.log('finished mirroring:', mirror.count)
}
```

The `drive-writer-app` creates a [`Corestore`](/p2p/reference/helpers/corestore) and replicates it over [`Hyperswarm`](/p2p/reference/building-blocks/hyperswarm) so other peers can fetch the data (L11–L16). It wraps the local `./writer-dir` directory in a [`Localdrive`](/p2p/reference/helpers/localdrive) and creates the [`Hyperdrive`](/p2p/reference/building-blocks/hyperdrive) on top of the store (L19–L23), then waits for it to initialize (L26). After joining the swarm on the drive's discovery key, it prints the drive key that [`drive-reader-app`](#create-the-drive-reader-app) will need (L31–L34). Pressing `Enter` in the terminal triggers a debounced mirror (L39–L42), and `mirrorDrive` copies the contents of `./writer-dir` into the drive (L45–L49).

Run the `drive-writer-app` with:

```sh example=hyperdrive-fs step=run process=writer expect="drive key:" capture-key="drive key: ([0-9a-f]+)" timeout=45000
bare drive-writer-app
```

It outputs a key which will be passed to [`drive-reader-app`](#create-the-drive-reader-app) upon execution.

## Create the `drive-reader-app` [#create-the-drive-reader-app]

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

Leave the [`drive-writer-app`](#create-the-drive-writer-app) running and in a new terminal create the `drive-reader-app` project with the following commands:

```sh example=hyperdrive-fs step=setup
mkdir drive-reader-app
cd drive-reader-app
npm init -y
npm pkg set type="module"
npm install corestore localdrive hyperswarm hyperdrive debounceify b4a bare-process
```

This will install the following dependencies:

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

### Add the `drive-reader-app` logic [#add-the-drive-reader-app-logic]

Create the `drive-reader-app/index.js` file with the following content:

```javascript file=<rootDir>/examples/how-to/stream-and-share-media/create-a-full-peer-to-peer-filesystem-with-hyperdrive/drive-reader-app/index.js title="drive-reader-app/index.js" lineNumbers {10-12,15-21,24-27,32-36,39-42,44-48} skip="example-import"

import process from 'bare-process'
import Hyperswarm from 'hyperswarm'
import Hyperdrive from 'hyperdrive'
import Localdrive from 'localdrive'
import Corestore from 'corestore'
import debounce from 'debounceify'
import b4a from 'b4a'

const key = Bare.argv[2]

if (!key) throw new Error('provide a key')

// create a Corestore instance
const store = new Corestore('./drive-reader-storage')

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

// replication of store on connection with other peers
swarm.on('connection', conn => store.replicate(conn))

// create a local copy of the remote drive
const local = new Localdrive('./reader-dir')

// create a hyperdrive using the public key passed as a command-line argument
const drive = new Hyperdrive(store, b4a.from(key, 'hex'))

// wait till all the properties of the drive are initialized
await drive.ready()

const mirror = debounce(mirrorDrive)

// call the mirror function whenever content gets appended 
// to the Hypercore instance of the hyperdrive
drive.core.on('append', mirror)

// join a topic
swarm.join(drive.discoveryKey, { client: true, server: false })

// start the mirroring process (i.e copying the contents from remote drive to local dir)
mirror()

async function mirrorDrive () {
  console.log('started mirroring remote drive into \'./reader-dir\'...')
  const mirror = drive.mirror(local)
  await mirror.done()
  console.log('finished mirroring:', mirror.count)
}
```

The `drive-reader-app` reads the writer's drive key from the command-line arguments (L10–L12) and replicates a [`Corestore`](/p2p/reference/helpers/corestore) over [`Hyperswarm`](/p2p/reference/building-blocks/hyperswarm) (L15–L21). It wraps the local `./reader-dir` directory in a [`Localdrive`](/p2p/reference/helpers/localdrive) and opens the remote [`Hyperdrive`](/p2p/reference/building-blocks/hyperdrive) from that key (L24–L27). It re-runs a debounced mirror whenever new content is appended to the drive's core (L32–L36), joins the swarm as a client and runs the first mirror immediately (L39–L42). `mirrorDrive` copies the contents of the remote drive into `./reader-dir` and logs when each pass completes (L44–L48).

### Run the `drive-reader-app` [#run-the-drive-reader-app]

Run the `drive-reader-app` with `bare`, passing the key printed by the [`drive-writer-app`](#create-the-drive-writer-app):

```sh example=hyperdrive-fs step=run process=reader cmd="bare drive-reader-app ${key}" expect="started mirroring remote drive" timeout=45000
bare drive-reader-app <SUPPLY_KEY_HERE>
```

`LocalDrive` does not create the directory passed to it until something has been written, so create `writer-dir` (`mkdir writer-dir`) beside the app directories before importing files. The next section walks through a full round trip from `writer-dir` to `reader-dir`.

Just as a [Hyperbee](/p2p/reference/building-blocks/hyperbee) is **just** a [Hypercore](/p2p/reference/building-blocks/hypercore), a Hyperdrive is **just** a Hyperbee - which is **just** a Hypercore.

## Test the filesystem [#test-the-filesystem]

With both apps still running, add, remove, or modify files inside `writer-dir`, then press `Enter` in the writer's terminal to import those local changes into the drive. Observe that the new changes mirror into `reader-dir`.

For example, create one file in the writer directory:

```sh example=hyperdrive-fs step=setup
mkdir -p writer-dir
printf 'hello from hyperdrive\n' > writer-dir/hello.txt
```

## Inspect the Hyperdrive as a Hyperbee [#inspect-the-hyperdrive-as-a-hyperbee]

`Hyperdrive` exposes its metadata index as [`drive.db`](/p2p/reference/building-blocks/hyperdrive#drivedb), which is the underlying Hyperbee backing the file structure.
You can inspect that Hyperbee directly when you want to see the raw file-entry metadata that [`drive.entry()`](/p2p/reference/building-blocks/hyperdrive#await-driveentrypath-options) wraps, or when you want to access the metadata index directly.

### Create the `drive-bee-reader-app` directory and add dependencies [#create-the-drive-bee-reader-app-directory-and-add-dependencies]

In a new terminal, create the `drive-bee-reader-app` project with these commands:

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

This will install the following dependencies:

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

### Add the `drive-bee-reader-app` logic [#add-the-drive-bee-reader-app-logic]

Create the `drive-bee-reader-app/index.js` file with the following content:

```javascript file=<rootDir>/examples/how-to/stream-and-share-media/create-a-full-peer-to-peer-filesystem-with-hyperdrive/drive-bee-reader-app/index.js title="drive-bee-reader-app/index.js" lineNumbers {8-10,12-17,19-21,24,29-37,39,41-49} skip="example-import"

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

const key = Bare.argv[2]

if (!key) throw new Error('provide a key')

const store = new Corestore('./drive-bee-reader-storage')

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

swarm.on('connection', conn => store.replicate(conn))

const drive = new Hyperdrive(store, b4a.from(key, 'hex'))
await drive.ready()
await drive.db.ready()

// Hyperdrive stores file metadata in the "files" sub-bee.
const files = drive.db.sub('files', { keyEncoding: 'utf-8' })

const discovery = swarm.join(drive.discoveryKey, { client: true, server: false })
await discovery.flushed()

let dbEntry = null
for (let attempt = 0; attempt < 60; attempt++) {
  await drive.update()
  dbEntry = await files.peek()
  if (dbEntry) break
  await new Promise(resolve => setTimeout(resolve, 500))
}

if (!dbEntry) throw new Error('expected at least one file entry to appear in drive.db')

const driveEntry = await drive.entry(dbEntry.key)

console.log('hyperbee entry:', JSON.stringify({
  key: dbEntry.key,
  value: dbEntry.value
}))

console.log('drive entry:', JSON.stringify({
  key: driveEntry.key,
  value: driveEntry.value
}))

await swarm.destroy()
await drive.close()
```

Like the reader, this app reads the drive key from the command line (L8–L10) and replicates a [`Corestore`](/p2p/reference/helpers/corestore) over [`Hyperswarm`](/p2p/reference/building-blocks/hyperswarm) (L12–L17). It opens the [`Hyperdrive`](/p2p/reference/building-blocks/hyperdrive) and awaits both [`drive.ready()`](/p2p/reference/building-blocks/hyperdrive#await-driveready) and `drive.db.ready()` (L19–L21), then reaches into the metadata index directly: file entries live in the `files` sub-[Hyperbee](/p2p/reference/building-blocks/hyperbee) (L24). It polls [`drive.update()`](/p2p/reference/building-blocks/hyperdrive#await-driveupdateoptions) until the first entry replicates in (L29–L37), looks the same file up through the higher-level `drive.entry()` API (L39), and logs both the raw Hyperbee entry and the `drive.entry()` view side by side (L41–L49).

### Run the `drive-bee-reader-app` [#run-the-drive-bee-reader-app]

Run the `drive-bee-reader-app` with `bare`, passing the key printed by the [`drive-writer-app`](#create-the-drive-writer-app):

```sh example=hyperdrive-fs step=run process=bee-reader cmd="bare drive-bee-reader-app ${key}" expect="hyperbee entry:" timeout=45000
bare drive-bee-reader-app <SUPPLY_KEY_HERE>
```

## See also [#see-also]

* [Share files in a peer-to-peer app](/p2p/how-to/stream-and-share-media/share-files-in-a-peer-to-peer-app)—the same Hyperdrive sharing wrapped in a desktop UI.
* [Storage and distribution](/pear/explanation/storage-and-distribution)—where these drives live on disk and how peers receive new versions.
* [Deploy your application](/pear/how-to/operate-an-app/manual-deployment/deployment)—staging and seeding when you're ready to ship.
* [Hyperdrive reference](/p2p/reference/building-blocks/hyperdrive)—full API for the distributed filesystem used in this guide.
* [Corestore reference](/p2p/reference/helpers/corestore)—full API for the shared storage and replication manager passed to Hyperdrive.
* [Localdrive reference](/p2p/reference/helpers/localdrive)—full API for the local filesystem adapter mirrored to and from Hyperdrive.
* [Mirrordrive reference](/p2p/reference/helpers/mirrordrive)—full API for the diff-and-sync engine driving `drive.mirror(...)` and `local.mirror(...)` here.
