# Topic Managers

Canonical: https://docs.projectbabbage.com/docs/guides/overlays/topic-managers

Setting up a backend with a Topic Manager

This deep-dive shows you how to build and wire up a custom Topic Manager for the BSV Overlay Services Engine, using the [backend-project-template](https://github.com/p2ppsr/backend-project-template) as your starting point and emulating the behavior of the [hello-overlay](https://github.com/p2ppsr/hello-overlay) example.
 
---

## Why Topic Managers Matter

A **Topic Manager** listens for broadcasted on-chain events (UTXO creations, spends, etc.) and filters/admits those outputs into named “topics” that your Lookup Service and front-end can query. This separation makes your overlay:

- Modular and testable
- Scalable by having processes run independently  
- Easy to extend with new topics 

---

## Prerequisites

Before you begin, ensure you have the following:

- **Node.js** and **Docker**
- **LARS** & **CARS** installed and initialized 
- A **BRC-100 wallet** such as BSV Desktop installed and running
- **Git**  (optional, but recommended)

---
	
## Structure your overlay backend

To scaffold your overlay backend, you can choose one of the following options:

### **Option 1: Quickstart**
Follow the [Adding a Backend Server to Your LARS and CARS Project](https://docs.projectbabbage.com/docs/quickstarts/lars-cars-backend-project). You will have to modify the `backend/src` directory to **only** include the sub-folders `topic-managers` and `lookup-services` along with any helper files that may be referenced in them.

To set up the `topic-managers` directory:
```bash
mkdir -p src/topic-managers
touch src/topic-managers/DemoTopicManager.ts
touch src/topic-managers/DemoWorldTopicDocs.ts
```

### **Option 2: Clone the template**
Cloning either the template for LARS and CARS projects that [only uses a backend](https://github.com/p2ppsr/backend-project-template) or the one that uses [both a frontend and backend](https://github.com/p2ppsr/full-stack-project-template)

```bash
git clone https://github.com/p2ppsr/backend-project-template.git my-project
cd my-project/backend
npm install
```

After setting up your project, the general structure should resemble:

```text
my-project/
├── backend/
│   ├── src/
│   │   ├── lookup-services/
│   │   ├── topic-managers/
│   │   │   ├── TemplateTopicDocs.md.ts
│   │   │   └── TemplateTopicManager.ts
│   │   └── types.ts
│   └── package.json
├── deployment-info.json
└── package.json
```

---

## Implement  the Topic Manager

### HelloWorld Tokens in a nutshell

As mentioned before, this guide will cover how to implement an overlay service similar to the [**hello-overlay**](https://github.com/p2ppsr/hello-overlay) project. This topic manager will admit “hello-tokens” which can be created and broadcasted to be received by your topic manager from a React frontend by adding this:

```ts
const wallet = new WalletClient('auto');
const outputScript = await new PushDrop(wallet).lock(
  [Utils.toArray(message)],
  [1, "HelloWorld"],
  "1",
  "anyone",
  true
);

const { tx } = await wallet.createAction({
  outputs: [{
    satoshis: 1,
    lockingScript: outputScript.toHex(),
    outputDescription: "New HelloWorld Token"
  }],
  options: { acceptDelayedBroadcast: false, randomizeOutputs: false },
  description: "Create a HelloWorld token"
});

if (!tx) throw new Error('Failed to create transaction')

const broadcaster = new TopicBroadcaster(['tm_demo'], { // change to use the name of your topic manager
  networkPreset: (await wallet.getNetwork()).network
});

await broadcaster.broadcast(Transaction.fromAtomicBEEF(tx));
```

Note that these guides work primarily with these hello-tokens but in the future you will likely want to create your own unique tokens with fields that are specific to your application.

### DemoTopicManager.ts

Here's a working implementation of a topic manager that would admit a hello-token:

```ts
import { AdmittanceInstructions, TopicManager } from '@bsv/overlay'
import docs from './DemoTopicDocs.md.js'
import { PushDrop, Signature, Transaction, Utils } from '@bsv/sdk'

export default class DemoTopicManager implements TopicManager {
  /** Identify which outputs to admit into the topic */
  async identifyAdmissibleOutputs(
    beef: number[],
    previousCoins: number[]
  ): Promise<AdmittanceInstructions> {
    const outputsToAdmit: number[] = [];

    try {
      console.log("DemoTopicManager invoked");
      const parsedTx = Transaction.fromBEEF(beef);

      if (!Array.isArray(parsedTx.outputs) || parsedTx.outputs.length === 0) {
        throw new Error("Missing parameter: outputs");
      }

      for (const [index, output] of parsedTx.outputs.entries()) {
        try {
          // decode a PushDrop “hello token” payload
          const result = PushDrop.decode(output.lockingScript);
          const signature = result.fields.pop();

          // 1) Exactly one field before signature
          if (!result.fields || result.fields.length !== 1) continue;

          const message = Utils.toUTF8(result.fields[0]);
          // 2) Message must be ≥2 chars
          if (message.length < 2) continue;

          // 3) Verify signature
          if (!result.lockingPublicKey || !signature) continue;

          const data = result.fields.flat();
          const valid = result.lockingPublicKey.verify(
            data,
            Signature.fromDER(signature)
          );
          if (!valid) throw new Error("Invalid signature");

          outputsToAdmit.push(index);
        } catch (err) {
          console.error(`Output ${index} skipped:`, err);
        }
      }

      if (outputsToAdmit.length === 0) {
        throw new Error("No outputs admitted");
      }

      console.log(
        `Admitted ${outputsToAdmit.length} ` +
        `${outputsToAdmit.length === 1 ? "output" : "outputs"}!`
      );
    } catch (err) {
      if (outputsToAdmit.length === 0 && !previousCoins.length) {
        console.error("Error identifying admissible outputs:", err);
      }
    }

    return { outputsToAdmit, coinsToRetain: [] };
  }

  /** Provide topic-specific documentation */
  async getDocumentation(): Promise<string> {
    return docs;
  }

  /** Provide metadata for the topic (e.g. schema version) */
  async getMetaData(): Promise<{
    name: string
    shortDescription: string
    iconURL?: string
    version?: string
    informationURL?: string
  }> {
    return {
      name: 'Demo Topic Manager',
      shortDescription: 'Admit outputs into a topic'
    }
  }
}
```

---

## Configure LARS/CARS

Before you deploy using either of the deployment tools, add your Topic Manager to `deployment-info.json` so LARS and CARS know what to load. Replace `"tm_demo"` with a unique name for your own topic manager:

```diff
{
  "schema": "bsv-app",
  "schemaVersion": "1.0",
  "topicManagers": {
    "tm_demo": "./backend/src/topic-managers/DemoTopicManager.ts"
  },
  …,  
}
```

---

## What's Next

Now that we have a functional Topic Manager, you can now move onto:

- Implement a Lookup Service to store and query the admitted outputs.
- Add proper documentation and rename the Topic Manager to better suit your project.
- Integrate with your React front-end.
