Browser
ui.netofnet.theflywheel.in — pick a persona, search, see catalogs side-by-side.
AMUL + MH VISTAAR fan-out across real Bharat Vistaar Dev and a mocked MH network. Stage (b.1) live.
NetOfNet is a Beckn "Network of Networks" demo. A single client (AMUL App or MH VISTAAR) submits one search; the Amul BAP fans it out to two Beckn networks in parallel and aggregates the on_search callbacks side-by-side. (A BAP — Beckn Application Platform — is the standard Beckn role that bridges synchronous client apps to the async/callback world of a Beckn network.)
Two stages are live:
| Stage | What runs | Status |
|---|---|---|
stage-a-v1 | Both legs are local mocks (registries, gateways, BPPs) — full Beckn message flow with stand-in catalogs. | Tag retained for reproducibility. |
stage-b1-v1 | MOA leg = real Bharat Vistaar Dev sandbox (DA, GoI). MH leg is still mocked. | Current. |
The whole thing runs on one Hetzner host (sdcrs-demo, 91.99.29.19), fronted by host nginx with per-subdomain Let's Encrypt certs. All bound to 127.0.0.1; nginx is the only 0.0.0.0 surface.
Open ui.netofnet.theflywheel.in, pick a persona, type any query, hit search. Stage (b.1) BPPs vary in how they treat the query string — Vistaar matches on message.intent.item.descriptor.name, the MH mock returns its full catalog regardless.
curl -sS -X POST -H "Content-Type: application/json" \
-d '{"persona":"amul","query":"KCC","user_id":"farmer-grape-47"}' \
https://ui.netofnet.theflywheel.in/api/search | jq .Expected (abridged):
{
"elapsed_ms": 2605,
"moa": {
"context": {
"domain": "schemes:vistaar",
"bpp_id": "bpp-network-playground-sandbox-vistaar.da.gov.in"
},
"message": { "catalog": { "providers": [{
"descriptor": { "name": "SchemeFinder" },
"items": [{
"id": "PMKISAN-101",
"descriptor": { "name": "Kisan Credit Card", "short_desc": "..." }
}]
}] } }
},
"mh": {
"context": { "domain": "retail", "bpp_id": "bpp-mh.netofnet.theflywheel.in" },
"message": { "catalog": { "bpp/providers": [
{ "id": "mh-weather", "items": [{"id":"mh-w-basic","descriptor":{"name":"Weather Advisory (MH)"}}] },
{ "id": "mh-market", "items": [{"id":"mh-m-prices","descriptor":{"name":"Mandi Prices (MH)"}}] }
] } }
},
"traces": {
"moa": { "transaction_id": "...", "elapsed_ms": 2599, "label": "MOA — Vistaar Dev (Schemes)" },
"mh": { "transaction_id": "...", "elapsed_ms": 266, "label": "MH (mock)" }
}
}# Public callback URL (Vistaar Dev posts on_search here)
curl -sS https://bap-amul.netofnet.theflywheel.in/health
curl -sS https://bap-amul.netofnet.theflywheel.in/api/legs | jq .
# Real Vistaar Dev sandbox (no auth, accepts unsigned envelopes for now)
curl -sS -X POST -H "Content-Type: application/json" \
-d '{
"context": {"domain":"schemes:vistaar","action":"search","version":"1.1.0",
"bap_id":"amul-dev","bap_uri":"https://bap-amul.netofnet.theflywheel.in",
"transaction_id":"manual-1","message_id":"msg-1",
"timestamp":"2026-04-27T07:00:00.000Z","ttl":"PT10M"},
"message": {"intent":{"category":{"descriptor":{"code":"schemes-agri"}},
"item":{"descriptor":{"name":"KCC"}}}}}' \
http://43.205.18.120/search
# MH mock chain (host-bound)
curl -sS https://bap-mh.netofnet.theflywheel.in/api/legs # not exposed; use bap-amul instead
curl -sS https://customdata-mh.netofnet.theflywheel.in/healthThe MOA leg's http://43.205.18.120/search is a BPP, not a Beckn gateway. The on_search callback comes from bpp-network-playground-sandbox-vistaar.da.gov.in (DA, GoI). So our MOA leg is BAP → BPP direct — no gateway tier in this demo.
That's a legitimate Beckn shape: a BAP that knows the BPP (from registry lookup or out-of-band) can call /search on it directly, ACK is synchronous, on_search arrives async on the BAP's own callback URL. Gateways are only needed when a BAP is broadcasting across many BPPs in a domain it doesn't yet know.
Public TLS termination + per-subdomain Let's Encrypt certs sit in front of the BAP (host nginx → 127.0.0.1:15010) — infra plumbing, not part of the Beckn protocol path. Skipped in the diagram so the protocol shape stays legible.
Containers across three docker-compose stacks on sdcrs-demo. Amul BAP + UI on amul and platform; the mocked MH chain on mh. The MOA mocks were retired in stage (b.1) — source preserved under networks/_archive/moa-mock/ for reproducing stage (a).
| Service | What it does | Repo path |
|---|---|---|
amul-bap | Beckn BAP — per-leg envelope builder, implements POST /on_search, correlates async callbacks by transaction_id, exposes /api/legs introspection | networks/amul/orchestrator/ |
mh-bap | beckn-onix Go adapter, Ed25519 signing, custom customdata-enricher Go plugin | networks/mh/, networks/mh/plugins/customdata-enricher/ |
mh-customdata | Rule-based intent enrichment from yaml | networks/mh/custom-data |
mh-gateway | Minimal Beckn gateway: ACK + forward to BPPs | networks/shared/mock-gateway |
mh-bpp | Minimal Beckn BPP: ACK + async on_search from catalog.json | networks/shared/mock-bpp |
mh-registry | fidedocker/registry (Java/Jetty); seeded at runtime via Ansible | unchanged image |
netofnet-ui | Next.js 14 — persona selector, search form, two-column results | platform/ui |
# Bootstrap (idempotent)
ansible-playbook ansible/playbooks/site.yml
# After a code change
git push && ansible-playbook ansible/playbooks/deploy.yml
# Read-only smoke (no mutations)
ansible-playbook ansible/playbooks/verify.ymlverify.yml from a cold cache reports ok=20 failed=0 rescued=0.
stage-b1-v1, 30+ commits)