# Add blind peering to a chat app





This guide shows you how to **layer [blind peering](/p2p/explanation/blind-peering) on top of [`pear-chat`](https://github.com/holepunchto/pear-docs/tree/preview/examples/getting-started/pear-chat)** so the room stays reachable even when none of its writers are connected. The reference implementation is [`pear-chat-blind-peering`](https://github.com/holepunchto/pear-docs/tree/preview/examples/how-to/blind-peering/add-blind-peering-to-a-chat-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>

This is a **delta-only** how-to. The shared Electron + PearRuntime + Bare worker scaffold—with plain-JSON messages over a [`framed-stream`](https://www.npmjs.com/package/framed-stream) pipe and a vanilla HTML renderer—is built step by step in [Reshape into a production app](/p2p/getting-started/build-a-peer-to-peer-chat/reshape-into-a-production-app)—read that first.

* [Keep data available with blind peering](/p2p/how-to/blind-peering/keep-data-available-with-blind-peering)—the standalone `blind-peering` client this guide layers onto the chat scaffold.

## Before you begin [#before-you-begin]

* A working clone of `pear-chat` (or your own app built from [Reshape into a production app](/p2p/getting-started/build-a-peer-to-peer-chat/reshape-into-a-production-app)).
* Comfort with [Autobase](/p2p/reference/building-blocks/autobase) and [Hyperswarm](/p2p/reference/building-blocks/hyperswarm).

## What changes [#what-changes]

| Layer             | Change                                                                                                                                                                                                            |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Dependencies      | Add [`blind-peering`](https://www.npmjs.com/package/blind-peering).                                                                                                                                               |
| Worker entrypoint | Declare a `--blind-peer-key` flag in `workers/index.js` so the keys reach the worker.                                                                                                                             |
| Worker            | Instantiate a `BlindPeering` client against the swarm, point it at one or more blind-peer public keys, register the room's [Autobase](/p2p/reference/building-blocks/autobase), and tear it down before the room. |
| Renderer          | Optional: surface the configured blind-peer keys, or let users supply their own.                                                                                                                                  |

Everything else—`electron/`, `spec/`, the vanilla `renderer/`, the forge config, the build assets—stays as in the getting-started chat app.

## Steps [#steps]

<Steps>
  <Step>
    ### Add the dependency [#add-the-dependency]

    ```bash skip="desktop-gui"
    npm install blind-peering
    ```
  </Step>

  <Step>
    ### Declare the `--blind-peer-key` flag [#declare-the---blind-peer-key-flag]

    The worker only sees the flags `workers/index.js` declares with [`paparam`](https://www.npmjs.com/package/paparam). Add `--blind-peer-key` to the `command(...)` block so the keys you pass on the command line reach `WorkerTask`—without this, the runtime bails with `UNKNOWN_FLAG: blind-peer-key`:

    ```diff title="workers/index.js" skip="snippet-illustration"
     const cmd = command('pear-chat-blind-peering',
    +  flag('--blind-peer-key|-b <blindPeerKey>', 'Blind peer key').multiple(),
       flag('--invite|-i <invite>', 'Room invite'),
       flag('--name|-n <name>', 'Your name'),
       flag('--reset', 'Reset')
     )
    ```

    `.multiple()` lets the flag repeat, so `cmd.flags.blindPeerKey` is an **array** of keys. `cmd.flags` is already passed to `WorkerTask`, which reads it as `opts.blindPeerKey` in the next step.
  </Step>

  <Step>
    ### Wire a `BlindPeering` into `WorkerTask` [#wire-a-blindpeering-into-workertask]

    In `workers/worker-task.js`, instantiate [`BlindPeering`](https://www.npmjs.com/package/blind-peering) against the swarm and a [Corestore](/p2p/reference/helpers/corestore) namespace, pointing it at the public keys of the blind peers you want to mirror to (L23–L25, reading the keys collected from `opts.blindPeerKey` at L15). Register the room's Autobase once it is open (L36). Close it **before** the room so its connections release cleanly (L55):

    ```js file=<rootDir>/examples/how-to/blind-peering/add-blind-peering-to-a-chat-app/workers/worker-task.js title="workers/worker-task.js" lineNumbers {1,15,23-25,36,55} skip="example-import"
    const BlindPeering = require('blind-peering')
    const Corestore = require('corestore')
    const debounce = require('debounceify')
    const Hyperswarm = require('hyperswarm')
    const ReadyResource = require('ready-resource')

    const ChatRoom = require('./chat-room')

    class WorkerTask extends ReadyResource {
      constructor (pipe, storage, opts = {}) {
        super()

        this.pipe = pipe
        this.storage = storage
        this.blindPeerKeys = opts.blindPeerKey || []
        this.invite = opts.invite
        this.name = opts.name || `User ${Date.now()}`

        this.store = new Corestore(storage)
        this.swarm = new Hyperswarm()
        this.swarm.on('connection', (conn) => this.store.replicate(conn))

        this.blindPeering = new BlindPeering(this.swarm.dht, this.store.namespace('blind-peering'), {
          keys: this.blindPeerKeys
        })

        this.room = new ChatRoom(this.store, this.swarm, this.invite)
        this.debounceMessages = debounce(() => this._messages())
        this.room.on('update', () => this.debounceMessages())
      }

      async _open () {
        await this.store.ready()
        await this.room.ready()

        await this.blindPeering.addAutobase(this.room.base)

        this.pipe.on('data', async (data) => {
          let message
          try {
            message = JSON.parse(data)
          } catch {
            return
          }
          if (message.type === 'add-message') {
            await this.room.addMessage(message.text, { name: this.name, at: Date.now() })
          }
        })
        await this.debounceMessages()

        this.pipe.write(JSON.stringify({ type: 'invite', invite: await this.room.getInvite() }))
      }

      async _close () {
        await this.blindPeering.close()
        await this.room.close()
        await this.swarm.destroy()
        await this.store.close()
      }

      async _messages () {
        const messages = await this.room.getMessages()
        messages.sort((a, b) => a.info.at - b.info.at)
        this.pipe.write(JSON.stringify({ type: 'messages', messages }))
      }
    }

    module.exports = WorkerTask
    ```

    The blind peers replicate and seed the room's encrypted [Autobase](/p2p/reference/building-blocks/autobase) without holding its read key. Point the client at blind-peer keys you run yourself (the [`blind-peer-cli`](https://github.com/holepunchto/blind-peer-cli) server prints `Listening at <key>` on startup) or shared ones.

    <Callout type="info">
      See [Blind peering](/p2p/explanation/blind-peering) for the threat model (a blind peer replicates encrypted data but never holds the room encryption key).
    </Callout>
  </Step>

  <Step>
    ### Confirm the teardown order [#confirm-the-teardown-order]

    The order in `_close` matters (L54–L58): the room holds [`blind-pairing`](https://www.npmjs.com/package/blind-pairing) handles that depend on the swarm, and the blind-peering client holds its own connections to the blind peers. Always close the client first (L55), then the room, then the swarm, then the store (L56–L58).
  </Step>
</Steps>

## Run it [#run-it]

You need three processes:

1. A **blind peer** that stays online to keep the room available, and
2. **two chat peers** (`user1` and `user2`) that can come and go. Open a terminal for each.

### 1. Start a blind peer [#1-start-a-blind-peer]

Run one with [`blind-peer-cli`](https://github.com/holepunchto/blind-peer-cli) (or point at a shared one you trust). On startup it logs a `Listening at <key>` line—copy that key, you pass it to both users as `--blind-peer-key`.

```bash skip="blind-peer"
npm install -g blind-peer-cli
blind-peer
# {"level":30, ... ,"msg":"Listening at es4n7ty45odd1udfqyi9xz58mrbheuhdnxgdufsn9gz6e5uhsqco"}
```

### 2. Start `user1` (creates the room) [#2-start-user1-creates-the-room]

Pass the blind-peer key so the room's [Autobase](/p2p/reference/building-blocks/autobase) is mirrored. The app prints an `Invite:` line on stdout—copy it for `user2`:

```bash skip="desktop-gui"
npm run build
npm start -- --storage /tmp/bp-user1 --name user1 --blind-peer-key <BLIND_PEER_KEY>
```

### 3. Start `user2` (joins the room) [#3-start-user2-joins-the-room]

Pass the invite from `user1` and the same blind-peer key:

```bash skip="desktop-gui"
npm start -- --storage /tmp/bp-user2 --name user2 --invite <INVITE> --blind-peer-key <BLIND_PEER_KEY>
```

Both peers register the room's Autobase with the blind peer (you can repeat `--blind-peer-key` to use several). Now quit `user1`: `user2` still receives the existing history and stays in sync, because the blind peer keeps replicating the encrypted room even though neither writer is online. Restart `user1` and it catches up from the blind peer too—without ever handing it the room's read key.

## Where to go next [#where-to-go-next]

* [Keep data available with blind peering](/p2p/how-to/blind-peering/keep-data-available-with-blind-peering)—the same `blind-peering` client on its own, without the Electron shell.
* [Blind peering](/p2p/explanation/blind-peering)—why a third-party replicator does not need to be trusted.
* [blind-peering reference](/p2p/reference/helpers/blind-peering)—every client option and method.
* [Add Keet identity to a chat app](/p2p/how-to/manage-identity/add-keet-identity-to-a-chat-app)—keep your relay's identity portable.
* [Build desktop distributables](/pear/how-to/operate-an-app/build-and-package/build-desktop-distributables)—package the relay app for macOS, Windows, and Linux.
