> For the complete documentation index, see [llms.txt](https://docs.nfh.global/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nfh.global/build/getting-started-with-fabric.md).

# Getting started with NFH fabric

A hands-on starter kit for running a live beckn network in your own environment within 10 minutes — complete with a consumer node, a provider node, and the…

A hands-on starter kit for running a live beckn network in your own environment within **10 minutes** — complete with a consumer node, a provider node, and the NFH fabric services that tie them together. No prior beckn experience needed.

```mermaid
sequenceDiagram
    participant C as Consumer node
    participant D as Discovery (DISCOVR)
    participant P as Provider node
    C->>D: discover
    D-->>C: on_discover (catalog)
    C->>P: select / init
    P-->>C: on_select / on_init
    C->>P: confirm
    P-->>C: on_confirm (order)
```

***

### What Will You Learn?

By the end of this guide you will have:

1. **A running beckn network** — a Consumer Node (consumer-side platform), a Provider Node (provider-side platform), and the ONIX adapters that connect them, all running in your own environment, local or cloud. You will also see how a crawler-fed **Discovery Service** serves `discover` requests from Consumer nodes.
2. **A working understanding of NFH fabric's DeDi Registry** — how it's used for identity and routing, and how a Provider node makes its own catalog independently discoverable by publishing signed files and registering a catalog index URL — no publish API call involved.
3. **An observable transaction flow** — you will fire real beckn API calls, see the messages get signed and routed, and watch the full `discover → select → init → confirm` lifecycle play out end-to-end.

***

### What is NFH fabric? — A Quick Recap

**NFH fabric** is an open protocol that allows any buyer-side application to transact with any provider-side application across an open network — without either side being locked into a single platform. Think of it like HTTP for transactions: as long as both sides speak the beckn protocol, they can discover each other, negotiate, and transact, regardless of who built them.

Every beckn network has two kinds of application participants:

* **Consumer Node** — the consumer side. A consumer node initiates transactions by sending actions: `discover`, `select`, `init`, `confirm`, and so on.
* **Provider Node** — the provider side. A provider node responds asynchronously: `on_discover`, `on_select`, `on_init`, `on_confirm`, and so on.

These two sides need not talk to each other directly. Every message travels through **ONIX adapters** — middleware that handles digital signing, schema validation, and protocol-level routing. It can also support observability at the network level, business policy enforcement, and much more. This keeps your application code focused on business logic.

Underpinning the network is NFH fabric's **DeDi Registry** — shared, trustless infrastructure every participant relies on for identity and routing lookups. Discovery in this starter kit is served by a **crawler-fed Discovery Service**: a Provider node publishes its catalog to storage it controls, points its Registry entry at that catalog's index, and a crawler independently discovers, verifies, and indexes it — no direct call between a publishing Provider node and a central catalog service is involved. This is covered in detail in [Beckn NFH fabric: Registry and Discovery](#fabric-registry-and-discovery) and [Catalog Publishing](#catalog-publishing).

***

### Prerequisites

Ensure the following tools are installed before you begin:

* **Git** — to clone this repository
* **Docker** and **Docker Compose**
  * [Install Docker](https://docs.docker.com/engine/install/)
  * Docker Compose ships with Docker Desktop; for Linux see the [Compose plugin guide](https://docs.docker.com/compose/install/)
* **Postman** — to send test requests
  * [Download Postman](https://www.postman.com/downloads/)
* **ngrok** — a static domain is needed to run through the full flow, including making anything you publish actually crawlable
  * [Download ngrok](https://ngrok.com/download) · requires a free ngrok account

For cloud VPS deployment you additionally need SSH access to a Linux server (Ubuntu 22.04 recommended) with ports 8081, 8082, and 9000 open in your firewall.

***

### Quick Start: Run the Network Locally

This is the fastest way to see a beckn network in action. Six Docker containers — two adapters, two application simulators, Redis, and a reverse proxy — start up on your laptop and form a complete, working network.

**Step 1 — Clone the repository**

```shell
git clone https://github.com/beckn/starter-kit.git
cd starter-kit/generic-devkit/install
```

**Step 2 — Start the stack**

```shell
docker compose -f docker-compose-generic.yml up
```

The first run pulls the required Docker images. This takes a few minutes once; subsequent starts are fast.

**Step 3 — Confirm all services are healthy**

```shell
docker compose -f docker-compose-generic.yml ps
```

Wait until all containers show `running` or `healthy`:

| Container      | Port | Role                                                                                                                                |
| -------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `beckn-router` | 9000 | Caddy reverse proxy — single entry point, routes `/bap/*` → `onix-bap`, `/bpp/*` → `onix-bpp`, `/beckn/*` → published catalog files |
| `redis`        | 6379 | Shared cache for both adapters                                                                                                      |
| `onix-bap`     | 8081 | Consumer node-side ONIX adapter (caller + receiver)                                                                                 |
| `onix-bpp`     | 8082 | Provider node-side ONIX adapter (caller + receiver + `catalog/publish`)                                                             |
| `sandbox-bap`  | 3001 | Mock consumer node application (receives callbacks)                                                                                 |
| `sandbox-bpp`  | 3002 | Mock provider node application (processes requests)                                                                                 |

**Step 4 — (Optional) Watch the adapters in real time**

Open a second terminal and tail the adapter logs while you send requests:

```shell
docker compose -f docker-compose-generic.yml logs -f onix-bap onix-bpp
```

You will see each message being signed, validated, and routed as you work through the transaction steps.

**Stopping the stack**

```shell
docker compose -f docker-compose-generic.yml down
```

***

### Public-Internet Testing (ngrok)

When you send a `discover` request, the Discovery Service sends `on_discover` back to whatever URL is in `context.bapUri`. That callback URL must be reachable from the public internet — which your laptop is not, by default. Publishing also depends on the tunnel: the catalog index and catalog files your Provider node writes are only crawlable if they're served from a publicly reachable URL (see [Catalog Publishing](#catalog-publishing)).

The stack includes **beckn-router**, a Caddy reverse proxy on port 9000 that sits in front of both adapters and your published catalog:

```
internet
    │
https://<your-static-domain>.ngrok-free.app
    │
ngrok → localhost:9000
    │
beckn-router (Caddy)
    ├── /bap/*     →  onix-bap:8081
    ├── /bpp/*     →  onix-bpp:8082
    └── /beckn/*   →  published catalog files (read-only)
```

One tunnelled port gives you a single stable public URL for the adapters and your published catalog. Set up an ngrok static domain, point the Postman collections' `public_url` variable at it, and keep `catalogBaseURL` in `generic-bpp.yaml` in sync with the same domain — this is the value that gets stamped into the URLs a crawler will try to fetch. Full walkthrough: [starter-kit's Public-Internet Testing section](https://github.com/beckn/starter-kit/blob/main/README.md#public-internet-testing-ngrok).

***

### Deployment: Cloud VPS

The same Docker Compose file works on any Linux VPS. Follow these steps after provisioning your server.

**Recommended server spec:** 2 GB RAM, Ubuntu 22.04.

**Firewall — open these ports:**

| Port | Purpose                                                |
| ---- | ------------------------------------------------------ |
| 22   | SSH                                                    |
| 8081 | onix-bap (consumer node ONIX adapter)                  |
| 8082 | onix-bpp (provider node ONIX adapter)                  |
| 9000 | beckn-router (reverse proxy)                           |
| 3001 | sandbox-bap (optional — for direct application access) |
| 3002 | sandbox-bpp (optional)                                 |

**Install Docker on the server:**

```shell
ssh user@your-server-ip

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
```

**Clone and start:**

```shell
git clone https://github.com/beckn/starter-kit.git
cd starter-kit/generic-devkit/install
docker compose -f docker-compose-generic.yml up -d
```

The `-d` flag runs the stack in the background. Verify with:

```shell
docker compose -f docker-compose-generic.yml ps
docker compose -f docker-compose-generic.yml logs --tail=50
```

**Using a domain name and TLS:**

For a production-like setup, place a reverse proxy in front of the adapters. Example with Caddy (auto-TLS via Let's Encrypt):

```
bap.yourdomain.com {
    reverse_proxy localhost:8081
}

bpp.yourdomain.com {
    reverse_proxy localhost:8082
}
```

Once your domain is live, update the routing configs with the public URLs, and register your own participant identity with the DeDi Registry (see Customising the Starter Kit).

***

### Your First Transaction

With the stack running, import the Postman collections and walk through a complete beckn transaction.

**Import the collections**

1. Open Postman and click **Import**.
2. Navigate to `starter-kit/generic-devkit/postman/`.
3. Import both files:
   * `BAPBecknStarterKit.postman_collection.json` — the buyer side: `discover`, `select`, `init`, `confirm`
   * `BPPBecknStarterKit.postman_collection.json` — the provider side: `on_select`, `on_init`, `on_confirm`, and `publish`

**Set the collection variables**

Each collection has a `bap_adapter_url` / `bpp_adapter_url` variable that tells Postman where the ONIX adapters are reachable. For local deployment:

* `bap_adapter_url` → `http://localhost:8081/bap/caller`
* `bpp_adapter_url` → `http://localhost:8082/bpp/caller`

For a VPS, replace `localhost` with your server's IP or domain.

**Run the transaction flow**

Use the **consumer node collection**, in order:

```
discover  →  select  →  init  →  confirm
```

`discover` does **not** require you to have published anything — it's served from whatever a crawler has already indexed across the network, independent of your own publishing activity. The `on_*` callbacks (`on_discover`, `on_select`, `on_init`, `on_confirm`) are sent asynchronously and received automatically by the sandbox applications — you do not need to trigger them manually. Use the provider node collection for `on_select`/`on_init`/`on_confirm` (to simulate a provider-initiated callback directly) and for `publish` — see [Catalog Publishing](#catalog-publishing) if you want your own catalog to become discoverable.

After each step, check the `onix-bap` and `onix-bpp` logs to see the message being processed.

***

### Catalog Publishing

`onix-bpp` exposes `/catalog/publish` — a same-operator trigger that publishes one or more plain Beckn Catalog objects: it diffs each against what was last published (producing a fresh baseline, an incremental change file, or a no-op), signs the result, and writes a manifest plus a catalog index to disk. This is the `catalog-publisher` plugin for [beckn-onix](https://github.com/beckn/beckn-onix) — see its [design background](https://github.com/beckn/beckn-onix/blob/catalog-publisher/pkg/plugin/implementation/catalogpublisher/README.md) for the full reference.

**Prerequisites for your published catalogs to be discoverable** — neither is needed to use `discover` itself, only to have your own catalogs show up in results:

1. **Publicly available storage for the catalog files** — any static host works (your own domain, a CDN, GitHub Pages, or a tunnel such as ngrok for local testing). `catalogBaseURL` in `generic-bpp.yaml` must point at wherever that storage is actually served from.
2. **A live DeDi Registry entry for your node, with `meta.catalog_index_urls` set.** A crawler only picks up your catalog once your Registry record points at your published index.

Trigger it via the **`publish`** request under the provider node collection's **Catalog Publishing** folder, or directly:

```bash
curl -X POST http://localhost:8082/catalog/publish \
  -H "Content-Type: application/json" \
  -d '{
    "context": { "action": "catalog/publish" },
    "message": {
      "catalogs": [
        { "id": "staging.p-node.fabric.nfh.global/CAT-GENERIC-001", "descriptor": { "name": "Generic Catalog" }, "provider": { "id": "PROV-EXAMPLE-01" }, "resources": [ /* ... */ ] }
      ],
      "publishDirectives": [
        { "catalogId": "staging.p-node.fabric.nfh.global/CAT-GENERIC-001", "catalogType": "REGULAR" }
      ]
    }
  }'
```

The response is synchronous — there's no `on_publish` callback, since this is an unsigned, same-operator call:

```json
{
  "status": "COMPLETED",
  "results": [
    { "catalogId": "staging.p-node.fabric.nfh.global/CAT-GENERIC-001", "status": "ACCEPTED", "version": 1 }
  ]
}
```

Publishing the same `catalogId` again with edited content produces an incremental change file and bumps its version instead of a fresh baseline; publishing it unchanged is a no-op. Inspect `generic-devkit/data/beckn/` on the host directly to see the generated manifest, catalog index, and versioned catalog files. Full request/response reference: [starter-kit's Catalog Publishing section](https://github.com/beckn/starter-kit/blob/main/README.md#catalog-publishing-catalogpublish). For the full walkthrough of this model, see [Catalog Publishing and Discovery → Quickstart](/build/creating-a-network/catalog-publishing-and-discovery.md#quickstart--host-and-sign-your-first-catalog).

***

### How It All Works

Now that you have seen the network in action, here is a deeper look at how the pieces fit together.

#### The Services in This Stack

```
  ┌──────────────────────────────────────────────────────────────┐
  │                      Your Environment                        │
  │                                                              │
  │  ┌──────────────┐     ┌────────────────────────────────────┐ │
  │  │  sandbox-bap │◄───►│  onix-bap  (port 8081)             │ │
  │  │  BAP app     │     │  BAP-side ONIX adapter             │ │
  │  └──────────────┘     │  /bap/caller/   /bap/receiver/     │ │
  │                       └──────────────────────┬─────────────┘ │
  │                                              │               │
  │  ┌──────────────┐     ┌────────────────────────────────────┐ │
  │  │  sandbox-bpp │◄───►│  onix-bpp  (port 8082)             │ │
  │  │  BPP app     │     │  BPP-side ONIX adapter             │ │
  │  └──────────────┘     │  /bpp/receiver/  /bpp/caller/      │ │
  │                       │  /catalog/publish (writes to        │ │
  │                       │   ../data/beckn, served publicly    │ │
  │                       │   via beckn-router)                 │ │
  │                       └──────────────────────┬─────────────┘ │
  │                                              │               │
  │  ┌──────────────────────────────────────┐    │               │
  │  │  redis  (shared cache, port 6379)    │    │               │
  │  └──────────────────────────────────────┘    │               │
  └─────────────────────────────────────────────┼───────────────┘
                                                │
                           ┌────────────────────┴──────────────────┐
                           ▼                                       ▼
          ┌─────────────────────────────┐     ┌─────────────────────────────┐
          │      Beckn NFH fabric       │     │      Discovery Service      │
          │                             │     │   (independent service,     │
          │  DeDi Registry              │     │    crawler-fed)             │
          │  · Identity lookups         │     │                             │
          │  · Dynamic routing          │     │  Receives discover from     │
          │    (resolves BAP/BPP URIs)  │     │  BAPs, serves results from  │
          │  · Holds each node's        │     │  whatever a crawler has     │
          │    catalog_index_urls       │     │  already indexed, returns   │
          │                             │     │  on_discover                │
          └─────────────────────────────┘     └─────────────────────────────┘
                           ▲
                           │ (independently, on its own schedule)
                           │
          A crawler reads catalog_index_urls from the DeDi Registry
          and pulls each node's published catalog files directly --
          no call from the publishing BPP to the Discovery Service.
```

| Service        | Image                          | Port | Role                                                                             |
| -------------- | ------------------------------ | ---- | -------------------------------------------------------------------------------- |
| `beckn-router` | `caddy:alpine`                 | 9000 | Reverse proxy — single entry point for both adapters and published catalog files |
| `onix-bap`     | `fidedocker/onix-adapter`      | 8081 | Consumer node-side protocol adapter                                              |
| `onix-bpp`     | `fidedocker/catalog-publisher` | 8082 | Provider node-side protocol adapter, includes the `catalogpublisher` plugin      |
| `sandbox-bap`  | `fidedocker/sandbox-2.0`       | 3001 | Simulates a consumer node application                                            |
| `sandbox-bpp`  | `fidedocker/sandbox-2.0`       | 3002 | Simulates a provider node application                                            |
| `redis`        | `redis:alpine`                 | 6379 | Shared request/response cache                                                    |

**The ONIX adapter** is the core middleware from the [beckn-onix](https://github.com/beckn/beckn-onix) project. It is a plugin-based Go server that handles signing, signature validation, schema validation, and routing for every beckn message. Both `onix-bap` and `onix-bpp` run the same underlying binary — their behaviour is entirely determined by their config files and which plugins are compiled in.

**The application simulator** (`fidedocker/sandbox-2.0`) is a generic beckn application simulator. It exposes simple HTTP endpoints that receive forwarded messages and generate appropriate responses, so you can observe the full protocol flow without building your own Consumer node or Provider node app yet.

#### NFH fabric: Registry and Discovery

**Beckn NFH fabric** is the shared infrastructure layer that makes an open beckn network possible. Without NFH fabric, each participant would need bilateral agreements with every other participant. NFH fabric removes that requirement by providing shared, trustless services that the whole network relies on. This starter kit uses one Beckn NFH fabric service — the **DeDi Registry** — plus an independent, crawler-fed Discovery Service that runs alongside but outside of NFH fabric.

**DeDi Registry** (`fabric.nfh.global/registry/dedi`)

The DeDi (Decentralised Directory) Registry is the source of truth for participant identity on the network. Every Consumer node and Provider node registers their network ID, public key, and callback URI with the registry. The ONIX adapter consults the registry for two things:

* **Signature validation** — when an inbound message arrives, the adapter looks up the sender's public key in the registry to verify the digital signature. If the key is not found or the signature is invalid, the message is rejected.
* **Routing** — when a message needs to reach a Provider node (e.g., `select`) or return to a Consumer node (e.g., `on_select`), the adapter performs a registry lookup by participant ID to resolve the correct callback URL. This is how the network routes messages without any static config between participants.

The registry is referenced in all four adapter config files under the `registry` plugin:

```yaml
registry:
  id: dediregistry
  config:
    url: https://fabric.nfh.global/registry/dedi
    registryName: subscribers.beckn.one
```

The same registry record is also where a Provider node points crawlers at its own catalog — see `meta.catalog_index_urls` in [Catalog Publishing](#catalog-publishing).

**Discovery Service** (crawler-fed)

The Discovery Service is an independent network service — not part of Beckn NFH fabric — that acts as the search engine for the network. It does **not** receive a direct call from a Provider node's `publish`. Instead, a crawler reads `meta.catalog_index_urls` from each DeDi-registered node's record, independently fetches and verifies that node's published catalog files, and feeds the result into the Discovery Service's own index on its own schedule. When a Consumer node sends a `discover` request, the adapter routes it to the Discovery Service, which serves results from whatever the crawler has already indexed and responds asynchronously with `on_discover`. The Consumer node never contacts a Provider node directly during discovery — direct Consumer-to-Provider communication only begins at `select`.

The Discovery Service endpoint is configured in `generic-routing-BAPCaller.yaml`:

```yaml
target:
  url: "https://<discovery-service>/beckn"
endpoints:
  - discover
```

#### The Full Transaction Flow

Here is the complete picture of how a beckn transaction flows through the network:

```
Provider node side — catalog publishing (independent of any Consumer node transaction, optional)
─────────────────────────────────────────────────────────────────

sandbox-bpp ──publish──► onix-bpp ──writes files──► ../data/beckn
                                                          (served publicly via beckn-router)
sandbox-bpp ◄──synchronous response── onix-bpp
(no on_publish callback -- this is a same-operator, unsigned call)

                                                          │
                                          (independently, on its own schedule)
                                                          ▼
                                       A crawler reads this node's DeDi registry
                                       entry, finds catalog_index_urls, and pulls
                                       the published catalog files directly.


Consumer node side — discovery
─────────────────────────────────────────────────────────────────

sandbox-bap ──discover──► onix-bap ──discover──► Discovery Service
                                                    │
                                    (serves from whatever a crawler
                                     has already indexed, from any
                                     publishing node -- not only this one)
                                                    │
sandbox-bap ◄──on_discover── onix-bap ◄──on_discover───┘
(Consumer node receives list of matching offerings)


Consumer node ↔ Provider node — transaction (select / init / confirm)
─────────────────────────────────────────────────────────────────

sandbox-bap ──select──► onix-bap
                        │ DeDi Registry resolves Provider node URI
                        ▼
                    onix-bpp ──► sandbox-bpp
                        │
                    sandbox-bpp sends on_select response
                        │
                    onix-bpp
                        │ DeDi Registry resolves Consumer node URI
                        ▼
sandbox-bap ◄──on_select── onix-bap

     ... same pattern repeats for init / confirm ...
```

The key insight: **discovery is served from whatever a crawler has already indexed across the network**, independent of when or whether *this* Provider node has published anything, and **post-discovery transactions flow directly between Consumer node and Provider node** with the DeDi Registry providing dynamic routing. Publishing your own catalog (optional) makes it discoverable on the crawler's own schedule, asynchronously — it is never a prerequisite for `discover` to return results from other nodes.

#### Tracing a Request End-to-End

Here is the step-by-step journey of a single `discover` call, showing what happens inside each service:

**1. Postman → onix-bap** You trigger a `discover` by POSTing to onix-bap's caller endpoint (`/bap/caller/discover`). The `bapTxnCaller` module picks it up.

**2. addRoute** — the adapter reads `generic-routing-BAPCaller.yaml` and matches the `discover` action to the configured Discovery Service URL.

**3. sign** — the adapter signs the request body using the Ed25519 private key defined under `keyManager` in `generic-bap.yaml` (identity: `bap.example.com`).

**4. validateSchema** — the Beckn v2.0.0 OpenAPI spec is fetched from GitHub (cached for 1 hour) and the message is validated against it.

**5. Request → Discovery Service** — the signed, validated `discover` message is forwarded to the Discovery Service.

**6. Discovery Service → on\_discover callback** — the Discovery Service searches its crawler-built catalog index and asynchronously POSTs `on_discover` back to onix-bap's receiver endpoint (`/bap/receiver/on_discover`).

**7. validateSign** — the `bapTxnReceiver` module verifies the Discovery Service's digital signature by looking up its public key in the DeDi Registry.

**8. addRoute** — reads `generic-routing-BAPReceiver.yaml`, which routes all `on_*` callbacks to the sandbox-bap webhook.

**9. sandbox-bap receives on\_discover** — the response (list of available offerings) is now stored in the application and visible in logs.

For `select`, `init`, and `confirm`, the flow is similar but the adapter uses the DeDi Registry to resolve the Provider node's URI dynamically (no hardcoded URL needed), and onix-bpp uses the registry to resolve the Consumer node's callback URI for the `on_*` responses.

***

### Repository Structure

```
starter-kit/
│
└── generic-devkit/
    │
    ├── config/                               # All adapter configuration
    │   ├── generic-bap.yaml                  # BAP adapter: modules, plugins, keys
    │   ├── generic-bpp.yaml                  # BPP adapter: modules, plugins, keys (incl. catalogPublish)
    │   ├── generic-routing-BAPCaller.yaml    # Where BAP sends outbound requests
    │   ├── generic-routing-BAPReceiver.yaml  # Where BAP delivers incoming callbacks
    │   ├── generic-routing-BPPCaller.yaml    # Where BPP sends outbound responses
    │   └── generic-routing-BPPReceiver.yaml  # Where BPP delivers incoming requests
    │
    ├── data/beckn/                            # catalog/publish output (see Catalog Publishing)
    │
    ├── install/
    │   ├── docker-compose-generic.yml        # Main compose file (pre-built adapter images)
    │   └── docker-compose-generic-local.yml  # Alternate compose file (locally built image)
    │
    └── postman/
        ├── BAPBecknStarterKit.postman_collection.json   # BAP-side flows
        └── BPPBecknStarterKit.postman_collection.json   # BPP-side flows
```

#### config/ — How Each File Is Used

Each ONIX adapter instance loads one primary config file, which in turn references routing config files for each module.

**`generic-bap.yaml`** — loaded by onix-bap. Defines two modules:

* `bapTxnCaller` at `/bap/caller/` handles **outbound** Consumer node requests. It signs each message, routes it using `generic-routing-BAPCaller.yaml`, and validates the schema.
* `bapTxnReceiver` at `/bap/receiver/` handles **inbound** `on_*` callbacks. It validates the sender's signature (via DeDi Registry lookup), routes the response to sandbox-bap using `generic-routing-BAPReceiver.yaml`, and validates the schema.

**`generic-bpp.yaml`** — loaded by onix-bpp. Defines three modules:

* `bppTxnReceiver` at `/bpp/receiver/` handles **inbound** action requests from Consumer nodes. It validates signatures and routes to sandbox-bpp using `generic-routing-BPPReceiver.yaml`.
* `bppTxnCaller` at `/bpp/caller/` handles **outbound** `on_*` responses. It signs messages and routes them using `generic-routing-BPPCaller.yaml`.
* `catalogPublish` at `/catalog/publish` — a separate module, **not** part of `bppTxnCaller` — handles `catalog/publish` directly: unsigned, same-operator, no routing config involved. See [Catalog Publishing](#catalog-publishing).

**`generic-routing-BAPCaller.yaml`** — outbound routing for the Consumer node:

* `discover` → routes to the Discovery Service
* Transaction actions (`select`, `init`, `confirm`, plus not-yet-implemented `status`, `track`, `update`, `cancel`, `rate`, `support`) → `targetType: bpp`, resolved dynamically via DeDi Registry lookup

**`generic-routing-BAPReceiver.yaml`** — all inbound `on_*` callbacks are routed to sandbox-bap's webhook endpoint.

**`generic-routing-BPPReceiver.yaml`** — all inbound action requests are routed to sandbox-bpp's webhook endpoint.

**`generic-routing-BPPCaller.yaml`** — outbound routing for the Provider node's `on_*` responses → `targetType: bap`, resolved dynamically via DeDi Registry lookup. `catalog/publish` does not route through this file at all — it's handled entirely by the separate `catalogPublish` module above.

Both config files embed a **`keyManager`** section with pre-generated Ed25519 key pairs for testnet participants `bap.example.com` and `bpp.example.com`. These are registered with the DeDi Registry on `beckn.one/testnet` and work out of the box — no changes needed to get started.

#### postman/ — What the Collections Do

**`BAPBecknStarterKit.postman_collection.json`** — the buyer-side flows, organised in two folders:

* **1 — Discovery:** `discover` — sent to onix-bap (`/bap/caller/discover`), which routes it to the Discovery Service.
* **2 — Transaction:** `select` → `init` → `confirm` — sent to onix-bap, which routes each to the Provider node via DeDi Registry lookup.

**`BPPBecknStarterKit.postman_collection.json`** — the provider-side flows, organised in two folders:

* **1 — Transaction:** `on_select`, `on_init`, `on_confirm` — sent to onix-bpp (`/bpp/caller/on_*`). Use these to manually simulate or inspect Provider node responses. In a normal flow sandbox-bpp handles these automatically.
* **2 — Catalog Publishing:** `publish` — sent to onix-bpp at `/catalog/publish` directly (**not** `/bpp/caller/publish` — this is the separate `catalogPublish` module, see [Catalog Publishing](#catalog-publishing)).

***

### Customising the Starter Kit

**Changing the Discovery Service endpoint**

Edit `config/generic-routing-BAPCaller.yaml` to point `discover` at a different Discovery Service.

**Running fully offline (no external dependencies)**

Change the `discover` target in `generic-routing-BAPCaller.yaml` to route directly to onix-bpp's receiver endpoint (`http://onix-bpp:8082/bpp/receiver/`). You will also need to remove or stub the DeDi Registry lookups for the routing to work without network access.

**Using your own participant identity**

The config files contain pre-generated testnet key pairs for `bap.example.com` and `bpp.example.com`, registered on `beckn.one/testnet`. To use your own identity:

1. Generate a new Ed25519 key pair.
2. Register your `networkParticipant` domain and public key with the DeDi Registry at `fabric.nfh.global/registry/dedi`.
3. Update the `keyManager` section in `generic-bap.yaml` and `generic-bpp.yaml` with your new participant ID, key ID, and key material.
4. Update the routing config files to use your `networkId` in place of `beckn.one/testnet`.
5. If you want your own published catalogs to be discoverable, see the registry prerequisite in [Catalog Publishing](#catalog-publishing).

**Replacing the application simulator with your own application**

The application simulator containers (`sandbox-bap` and `sandbox-bpp`) are simple simulators. Replace either with your own application by updating the service image and webhook URLs in the compose file and routing configs. Your application needs to accept POSTed beckn messages at the configured endpoints and be on the same Docker network.

**Using a different beckn domain**

Update the `domain` (or `networkId`) field in all four routing YAML files to match your domain. Point the `schemav2validator` in the adapter config to the appropriate OpenAPI spec URL for schema validation.

***

### Troubleshooting

**Containers fail to start**

Check for port conflicts on 8081, 8082, 9000, 3001, 3002, or 6379:

```shell
docker compose -f docker-compose-generic.yml logs
```

**sandbox-bap or sandbox-bpp stays in `starting` state**

The application containers health-check at `/api/health`. If they don't reach `healthy` within about a minute, inspect their logs:

```shell
docker compose -f docker-compose-generic.yml logs sandbox-bap
docker compose -f docker-compose-generic.yml logs sandbox-bpp
```

**Postman requests return connection errors**

Confirm the stack is fully up (`docker compose ps` shows all six containers as `running` or `healthy`) and that your Postman collection variables point to the right host and port.

**Signature validation failures (`validateSign` errors in logs)**

The most common cause is clock skew. Beckn signatures embed a timestamp with a short validity window. On Linux: `timedatectl` to check clock sync. On Docker Desktop, ensure the VM clock is synchronised.

**`discover` returns no results**

The Discovery Service must be reachable. Check the URL configured in `generic-routing-BAPCaller.yaml` and test connectivity with:

```shell
curl -s https://<discovery-service>/beckn
```

If it times out, the testnet may be temporarily unavailable, or your network may block outbound HTTPS. `discover` does **not** depend on you having called `publish` — it's served from whatever a crawler has already indexed across the network. If you expected to see your *own* published catalog specifically, check that your DeDi registry entry actually carries `meta.catalog_index_urls` — a crawler has no other way to find it, and there's no immediate feedback if that field is missing.

**`catalog/publish` not working**

There is no `on_publish` callback in this model — `catalog/publish` responds synchronously. Check the response body directly: `status: FAILED` with an `error` object means a fatal failure (e.g. signing); a `REJECTED` entry in `results[]` means that specific catalog was invalid (check its `reason`). Also verify the Provider node's signing keys are valid by reviewing the `keyManager` section in `generic-bpp.yaml`, and that `outputRoot`/`catalogBaseURL` are set correctly (see [Catalog Publishing](#catalog-publishing)).

**Images fail to pull**

Ensure Docker has at least 2 GB RAM allocated and a stable internet connection for pulling the required images.

**Stopping and cleaning up**

```shell
# Stop containers
docker compose -f docker-compose-generic.yml down

# Stop and remove volumes and image cache
docker compose -f docker-compose-generic.yml down -v
```

{% hint style="info" %}
Need help? Ask a question, report a bug, request a feature, or request additional documentation — reach out to us [here](https://support.nfh.global/)
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.nfh.global/build/getting-started-with-fabric.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
