Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
generic-devkit/postman/.DS_Store
generic-devkit/install/ngrok.yml
generic-devkit/data/
55 changes: 53 additions & 2 deletions generic-devkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ select → on_select → init → on_init → confirm → on_confirm
→ status / on_status → track / on_track
→ update / on_update → cancel / on_cancel
→ rate / on_rate → support / on_support
(+ catalog/publish → catalog/on_publish)
(+ catalog/publish -- a DS-internal trigger, not part of the signed transaction flow above)
```

---
Expand Down Expand Up @@ -80,7 +80,6 @@ Use the **BAP collection** to drive the transaction lifecycle in order:
| 8 | `cancel` | Fulfillment |
| 9 | `rate` | Post-Fulfillment |
| 10 | `support` | Post-Fulfillment |
| 11 | `catalog/publish` | Catalog Publishing |

Each request returns an `ACK`. The corresponding `on_*` callback from the BPP arrives at `sandbox-bap` and can be viewed in the BAP logs:

Expand All @@ -93,6 +92,58 @@ Use the **BPP collection** to simulate BPP-initiated callbacks directly (e.g. un

---

## Catalog Publisher (`catalog/publish`)

`onix-bpp` exposes `/catalog/publish` — a DS-internal 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 + catalog index under the handler's `outputRoot` (`/beckn` in the container, `generic-devkit/data/beckn` on the host — see `docker-compose-generic-local.yml`). This is an unsigned, same-operator call, **not** the full signed, async `catalog/publish` Beckn action beckn.yaml describes (context/action envelope, routing, `on_publish` callback) — that is a materially larger scope this devkit does not implement yet. See [beckn-onix's catalogpublisher README](https://github.com/beckn/beckn-onix/blob/catalog-publisher/pkg/plugin/implementation/catalogpublisher/README.md) for the full design background.

### Trigger it

Use the **`publish`** request under the BPP collection's **`2 — Catalog Publishing`** folder, or call it 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", "visibleTo": ["beckn.one/testnet", "nfh.global/testnet"], "catalogType": "REGULAR" }
]
}
}'
```

Request body matches beckn.yaml's real `CatalogPublishAction` envelope shape (`context`/`message.catalogs[]`/`message.publishDirectives[]`) — `context` only carries `action` since every other `Context` field is optional and none are meaningful for this unsigned, same-operator call. `publishDirectives[]` entries are matched to a catalog by `catalogId`; `catalogType` (`MASTER`/`REGULAR`) is required by the spec, and `visibleTo` restricts which networks may fetch that catalog (empty/omitted means public) — both map straight onto the same-named fields in the published catalog index. Each catalog's own top-level `"id"` is used verbatim as its catalogId — it is not derived from a domain, so submit the full id you want published. `retire` (a list of catalogIds) and `forceBaseline` (bypass diffing, publish a fresh baseline) are this handler's own additions with no beckn.yaml equivalent — accepted as siblings of `context`/`message`, alongside or instead of `message.catalogs`.

### Sample response

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

`status` is always present (`COMPLETED`/`FAILED` for the call as a whole); each entry in `results` borrows beckn.yaml's `CatalogProcessingResult` vocabulary (`ACCEPTED`/`REJECTED`) per catalog — a bad submission (e.g. missing `id`) is `REJECTED` with a `reason`, without failing the rest of the batch:

```json
{
"status": "COMPLETED",
"results": [
{ "catalogId": "", "status": "REJECTED", "reason": "missing catalogId" }
]
}
```

A fatal failure (e.g. signing failure) returns `200` with `status: FAILED` and an `error` object instead. Publishing the same catalogId again with edited `resources`/`offers` 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 to see the generated manifest, catalog index, and versioned catalog files directly.

---

## Configuration Reference

### Collection Variables (Postman)
Expand Down
96 changes: 94 additions & 2 deletions generic-devkit/config/generic-bpp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ modules:
keyManager:
id: simplekeymanager
config:
# Testnet sandbox subscriber registered on beckn.one/testnet
# Replace with your own registered subscriber ID and keys for production
subscriberId: bpp.example.com
keyId: 76EU7ofwRCF1aobQkShARrf1PAUsNpHqWUJoynPu9w45YFKmzqaPmy
signingPrivateKey: 393fVJJsfqssJCRAjedKR2cKAjSLrjU3t+bICQ5KzYM=
Expand Down Expand Up @@ -160,3 +158,97 @@ modules:
- validateSchema
- sign
- validateAckSign

# ─────────────────────────────────────────────────────────────────
# catalog/publish — DS-internal trigger for the catalog-publisher
# plugin. Unsigned, same-operator call (not a network-facing Beckn
# action): no validateSign/addRoute/signAck. Given one or more plain
# Beckn Catalog objects, it diffs against what was last published
# (baseline vs. incremental change file), signs the result, and
# writes it under outputRoot. Public catalogs only in this phase.
# Listens at: http://localhost:8082/catalog/publish
# See beckn-onix's pkg/plugin/implementation/catalogpublisher/README.md
# ─────────────────────────────────────────────────────────────────
- name: catalogPublish
path: /catalog/publish
handler:
type: catalogPublish
role: bpp
outputRoot: /beckn
httpClientConfig:
maxIdleConns: 1000
maxIdleConnsPerHost: 200
idleConnTimeout: 300s
responseHeaderTimeout: 5s
plugins:
# registry+cache exist only to satisfy keyManager's constructor
# (KeyManagerProvider.New always requires a RegistryLookup); the
# publisher itself never calls either.
registry:
id: dediregistry
config:
timeout: 10
retry_max: 3
retry_wait_min: 100ms
retry_wait_max: 500ms
cache:
id: cache
config:
addr: redis:6379
keyManager:
id: simplekeymanager
config:
subscriberId: staging.p-node.fabric.nfh.global
keyId: 76EU7aSyU6nCGC9i2svAkZUwJvCpBx92LvBrAZR48YVtRg3SjH86vM
signingPrivateKey: psy4wPmeHbD7rkF3JIYL2hbNien62BNCtr6T1C2DoaI=
signingPublicKey: gFoAD0w7SgnB3UaqMr7wK0uq4J0XWmfo4g+WPyhIXgY=
encrPrivateKey: psy4wPmeHbD7rkF3JIYL2hbNien62BNCtr6T1C2DoaI=
encrPublicKey: gFoAD0w7SgnB3UaqMr7wK0uq4J0XWmfo4g+WPyhIXgY=
catalogPublisher:
id: catalogpublisher
config:
# No subscriberId here -- the handler derives it automatically
# from keyManager.config.subscriberId above (the single source
# of truth for which keyset gets loaded). Set it explicitly
# here only if it must legitimately differ from keyManager's.
# indexSchemaURL is likewise not configurable -- it's a
# hardcoded package constant pending a shared becknconstants
# home for canonical schema URLs.
nextUpdateIn: "336h"
fileValidityIn: "336h"
# catalogBaseURL: the one public URL prefix outputRoot is
# served under -- beckn-router's ngrok tunnel, at the /beckn/*
# route added to install/Caddyfile (serves ../data/beckn
# statically). Update this if your ngrok domain changes.
catalogBaseURL: "https://angular-absently-gab.ngrok-free.dev/beckn"
# manifestSubscriberId: the DeDi-native namespace/registry/
# recordName path for this node's manifest record -- NOT the
# same as keyManager.config.subscriberId above (a plain Beckn
# subscriberId used for keyset lookup/signing; setting *that*
# to a 3-part path would break signing instead). Used only by
# the manifestLoader check below. Replace with your real
# record's namespace/registry/recordName.
manifestSubscriberId: "nfh.global/staging-nodes/staging-p-node"
# Validates submitted catalogs before they're published: the
# handler wraps them in {context:{action:"catalog/publish"},
# message:{catalogs:[...]}} and validates that envelope against
# catalog/publish's real message.catalogs[]:Catalog[] schema (no
# type/location set -- same as bppTxnReceiver/bppTxnCaller above,
# so it resolves the canonical protocol-specifications-v2 beckn.yaml
# via becknConstants). checkPolicy (opapolicychecker) intentionally
# left out here -- not needed yet.
schemaValidator:
id: schemav2validator
config:
cacheTTL: "3600"
# Read-only: checks (via dediregistry, using catalogPublisher.
# config.manifestSubscriberId above -- same self-lookup
# schemaversionmediator already does at cold start, via its own
# nodeId config) whether the node manifest already declares this
# publisher's catalog index under catalog.catalogIndexes. If not, a
# proposed updated manifest is staged locally under
# outputRoot/index/node-manifest.staged.yaml and a warning is
# returned -- the real manifest on DeDi is never written to by
# this handler; review the staged file and push it yourself.
manifestLoader:
id: manifestloader
13 changes: 11 additions & 2 deletions generic-devkit/install/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
# beckn-router — single public entry point for both adapters
# Port 9000 is mapped to the host; ngrok tunnels this one port.
#
# /bap/* → onix-bap:8081 (BAP receiver and caller)
# /bpp/* → onix-bpp:8082 (BPP receiver and caller)
# /bap/* → onix-bap:8081 (BAP receiver and caller)
# /bpp/* → onix-bpp:8082 (BPP receiver and caller)
# /beckn/* → static files under ../data/beckn (catalogPublish's
# outputRoot) -- the manifest/index/catalog files a crawler fetches.
# Mirrors outputRoot's own layout 1:1, so catalogBaseURL in
# generic-bpp.yaml is this tunnel's URL + "/beckn".
:9000 {
handle /bap/* {
reverse_proxy onix-bap:8081
}
handle /bpp/* {
reverse_proxy onix-bpp:8082
}
handle /beckn/* {
root * /srv/beckn
uri strip_prefix /beckn
file_server
}
}
33 changes: 33 additions & 0 deletions generic-devkit/install/docker-compose-generic-local.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
services:
# ============================================================
# beckn-router — Caddy reverse proxy, single entry point for
# both adapters. Exposes port 9000 on the host.
#
# /bap/* → onix-bap:8081
# /bpp/* → onix-bpp:8082
#
# For public-internet callbacks, tunnel port 9000 via ngrok:
# cp ngrok.yml.example ngrok.yml # fill in authtoken + domain
# ngrok start --all --config ngrok.yml
# Then set PUBLIC_URL in your Postman collection to the tunnel URL.
# ============================================================
beckn-router:
image: caddy:alpine
container_name: beckn-router
ports:
- "9000:9000"
networks:
- beckn_network
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ../data/beckn:/srv/beckn:ro
depends_on:
- onix-bap
- onix-bpp
restart: unless-stopped

# ============================================================
# Redis — shared cache for both adapters
# ============================================================
Expand Down Expand Up @@ -42,6 +69,11 @@ services:
# onix-bpp — Beckn adapter acting as BPP
# Caller (outbound): http://localhost:8082/bpp/caller/
# Receiver (inbound): http://localhost:8082/bpp/receiver/
# Catalog publish (internal trigger): http://localhost:8082/catalog/publish
# -- writes manifest/index/catalog files under /beckn in the
# container; ../data/beckn on the host, so they persist across
# `docker compose down`/`up` and can be inspected or moved
# elsewhere.
# ============================================================
onix-bpp:
image: beckn-onix:latest
Expand All @@ -59,6 +91,7 @@ services:
REDIS_ADDR: redis:6379
volumes:
- ../config:/app/config
- ../data/beckn:/beckn
command: ["./server", "--config=/app/config/generic-bpp.yaml"]

# ============================================================
Expand Down
11 changes: 9 additions & 2 deletions generic-devkit/install/docker-compose-generic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ services:
- beckn_network
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ../data/beckn:/srv/beckn:ro
depends_on:
- onix-bap
- onix-bpp
Expand Down Expand Up @@ -47,7 +48,7 @@ services:
# Receiver (inbound): http://localhost:8081/bap/receiver/
# ============================================================
onix-bap:
image: fidedocker/onix-adapter
image: fidedocker/onix-adapter:1.7.5
container_name: onix-bap
platform: linux/amd64
networks:
Expand All @@ -68,9 +69,14 @@ services:
# onix-bpp — Beckn adapter acting as BPP
# Caller (outbound): http://localhost:8082/bpp/caller/
# Receiver (inbound): http://localhost:8082/bpp/receiver/
# Catalog publish (internal trigger): http://localhost:8082/catalog/publish
# -- writes manifest/index/catalog files under /beckn in the
# container; ../data/beckn on the host, so they persist across
# `docker compose down`/`up` and can be inspected or moved
# elsewhere.
# ============================================================
onix-bpp:
image: fidedocker/onix-adapter
image: fidedocker/onix-adapter:1.7.5
container_name: onix-bpp
platform: linux/amd64
networks:
Expand All @@ -85,6 +91,7 @@ services:
REDIS_ADDR: redis:6379
volumes:
- ../config:/app/config
- ../data/beckn:/beckn
command: ["./server", "--config=/app/config/generic-bpp.yaml"]

# ============================================================
Expand Down
Loading