DeDi Node

A self-hostable, tamper-evident public registry backed by a Merkle transparency log (signed checkpoints + inclusion proofs), not a blockchain. It is a drop-in for the Beckn ONIX registry, and any node can independently witness another.

Beckn message validated against this registry

sequenceDiagram
    autonumber
    participant BAP as ONIX · BAP
    participant BPP as ONIX · BPP
    participant D as dedid
    BAP->>BPP: select (signed)
    BPP->>D: GET /lookup/bap.example.com/subscribers.beckn.one/{keyId}
    D-->>BPP: signing_public_key (+ inclusion proof)
    BPP->>BPP: verify signature ✓
    BPP-->>BAP: on_select (signed)
    BAP->>D: lookup BPP key → verify ACK ✓
    Note over D: bad/absent key → 401 reject

Decentralised trust: one node witnesses another

sequenceDiagram
    autonumber
    participant A as Node A · primary
    participant B as Node B · witness
    B->>A: GET /log/checkpoint (signed)
    B->>A: GET /log/proof/consistency?old&new
    B->>B: verify append-only (RFC 6962)
    B->>B: record verdict in own log
    Note over B: forged history → alarm

Test cases

#TestResult
1ONIX dediregistry client (v1.8.0) over the wire6/6 pass
2Beckn starter-kit select/init/confirm vs dedidACK + on_* callbacks
3Tampered key in dedid → adapter validation401 rejected
4Offline inclusion proof (browser, WebCrypto)verified
5Witness verifies primary is append-onlyrecorded
6Witness detects a forked historyalarm
7Checkpoint anchored to a CORD chain (DEDI_ANCHOR_*, adapter-based)receipt verified in-block

Witnessed checkpoints are browsable in the Explorer under the _witness namespace.

API

All read endpoints, relative to this node. Everything is a plain GET; no auth.

EndpointReturns
/dedi/lookup/{namespace}/{registry}/{record}latest live version of a record
/dedi/lookup/{namespace}/{registry}registry metadata
/dedi/lookup/{namespace}namespace metadata
/dedi/query/{namespace}[/{registry}]filtered search over records
/dedi/versions/{namespace}[/{registry}[/{record}]]full version history
/dedi/log/checkpointsigned tree head (C2SP checkpoint)
/dedi/log/proof/consistency?old=&new=append-only proof between two tree sizes

Lookup modifiers: ?proof=inclusion attaches a Merkle inclusion proof and the signed checkpoint it verifies against; ?version_id= pins an exact version; ?as_on= resolves the record as of an RFC 3339 timestamp. Example against the public reference node (seeded with Beckn identities; on your own node, use your host and a record you seeded):

curl -s "https://dedi.proto.theflywheel.in/dedi/lookup/bpp.example.com/subscribers.beckn.one/76EU7ofwRCF1aobQkShARrf1PAUsNpHqWUJoynPu9w45YFKmzqaPmy?proof=inclusion"

Why your own node — the global fabric vs a registry you own

A fair question: a hosted global fabric like fabric.nfh.global already exists — why run your own? Because the registry is the trust root of a network, and on a shared global fabric the two things that matter most — governance (who gets on the network) and RBAC (who may change what, and for which namespace) — are the platform's to set, not yours. Self-hosting an open-source node moves both onto a registry you own and can extend.

Shared global fabric (hosted)Your own DeDi node (self-hosted, open source)
One central registry every network joinsYour namespace and registry — a sovereign trust root you own
Governance & onboarding follow the platform's policyGovernance is yours: closed-by-default, operator-gated, and every onboarding, rotation, and revocation is an auditable log entry
Roles/RBAC are fixed by the platformRBAC is yours to shape and extend — scoped publisher keys bound to a namespace/registry, k-of-n council approval for consortium networks (governance.md)
Acceptance & allowlist policy is the platform's modelAcceptance policy is yours to manage — namespace/registry eligibility (which parties resolve, e.g. the wildcard eligibility constraint) and scoped roles, set on your own node
Hosted black box — you can neither self-host nor inspect itOne Go binary + Postgres, docker compose up, fully inspectable
Trust because the platform says soTrust because any relying party can verify the proof, and an independent witness catches tampering
Blockchain-anchored (platform weight)Transparency log; chain anchoring is an optional env flag

A concrete case. Say a BPP does not want to reject requests from parties outside a fixed whitelist — or, the other way, wants to tighten exactly who it will serve. That acceptance policy has to be managed somewhere. On a shared global fabric it is the platform's model to define; on a node you own it is a governance knob you hold — an allowlist / eligibility rule and the roles around it, changeable per network as these cases come up.

Interop is a boundary, not a rip-out: a stock ONIX adapter still pins the registry to fabric.nfh.global as a signed constant, so pointing a full deployment at your own node needs a patched adapter build. The honest framing is a sovereign trust core you govern, with an interop shim at the edge — not “replace the global fabric.” Full rationale: why.md.

Scaling by delegation: one root, many registries (CA-style)

Another question that comes up: if the registry is shared, how do you still give a state, a sector, or a consortium its own bounded control — one root with many smaller registries under it, the way a CA has a root and intermediates? DeDi delegates exactly that way. A root registry is a registry-of-registries — its records are the child registries, each with a verifier key and a scope. The root is the single, shared trust anchor; children hold scoped authority over their own branch. That gives bounded local control without fragmenting the anchor into one-registry-per-party.

graph TD
    R["root registry (trust anchor)"]
    R -->|"scoped key + logged grant"| A["child registry: State A"]
    R -->|"scoped key + logged grant"| B["child registry: Sector B"]
    A --> Ar["records: participants + keys"]
    B --> Br["records: participants + keys"]
CA conceptDeDi equivalent
Root CA (trust anchor)Root registry whose records are the child registries (key, scope, endpoint)
Intermediate CA (delegated, scoped signing)Scoped publisher key bound to a child's namespace/registry
Which intermediates are trustedWildcard eligibility constraint — the root's allowlist of child registries
Issuance / revocation logGovernance-as-log — every delegation, rotation, and revocation is an ordered, witnessed entry
Certificate chain verificationTwo inclusion proofs compose — child attested in the root log, then record included in the child
CRL / OCSPRevocation is a new versioned record with a revoked state — logged and witnessed, never a deletion

Two topologies, one trust model — choose per child:

Verification is by proof, not by who hosts — so a child can be promoted from a branch to its own node without changing the trust model. Acceptance (whether a provider actually serves a resolved party) stays the provider's own runtime call; this hierarchy resolves identity and delegation, not who-serves-whom. The primitives here — namespace tree, scoped publisher keys, governance-as-log, eligibility, witnessing, inclusion proofs — exist today; a turnkey root→child delegation flow is the M2 publisher-plane direction, not a one-click feature yet.

Getting registered

Onboarding is operator-gated: there is no self-service registration, by design — who exists on a network is a governed decision, and every onboarding or revocation is an auditable entry in the transparency log. To register a participant on the networks served by this node, email contact@theflywheel.in with your subscriber id, role (BAP/BPP), namespace, and Ed25519 signing public key. Details: governance.

Run your own node

dedid is self-hostable: one binary, one Postgres. Requires Docker and Go ≥ 1.25.

git clone https://github.com/theflywheel/DeDi-node.git && cd DeDi-node
make keygen                # writes keys/dedid.key, prints your public verifier key
docker compose up -d       # postgres + dedid on http://localhost:8080
curl -s localhost:8080/dedi/log/checkpoint   # your own signed tree head

Load records with go run ./cmd/dedid seed -file examples/beckn-seed.json (set DEDI_DB_URL=postgres://dedi:dedi@localhost:5433/dedi?sslmode=disable). Distribute your verifier key; anyone, including another dedid node acting as a witness, can then verify your log independently. Trust planes are feature flags on one binary: standalone, witnessed (DEDI_WITNESS_*), and ledger-anchored (DEDI_ANCHOR_*, CORD adapter first). See deployment modes.

Protocol & references