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.
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
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 | Result |
|---|---|---|
| 1 | ONIX dediregistry client (v1.8.0) over the wire | 6/6 pass |
| 2 | Beckn starter-kit select/init/confirm vs dedid | ACK + on_* callbacks |
| 3 | Tampered key in dedid → adapter validation | 401 rejected |
| 4 | Offline inclusion proof (browser, WebCrypto) | verified |
| 5 | Witness verifies primary is append-only | recorded |
| 6 | Witness detects a forked history | alarm |
| 7 | Checkpoint anchored to a CORD chain (DEDI_ANCHOR_*, adapter-based) | receipt verified in-block |
Witnessed checkpoints are browsable in the
Explorer under the _witness namespace.
All read endpoints, relative to this node. Everything is a plain GET; no auth.
| Endpoint | Returns |
|---|---|
/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/checkpoint | signed 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"
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 joins | Your namespace and registry — a sovereign trust root you own |
| Governance & onboarding follow the platform's policy | Governance is yours: closed-by-default, operator-gated, and every onboarding, rotation, and revocation is an auditable log entry |
| Roles/RBAC are fixed by the platform | RBAC 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 model | Acceptance 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 it | One Go binary + Postgres, docker compose up, fully inspectable |
| Trust because the platform says so | Trust 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.
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 concept | DeDi 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 trusted | Wildcard eligibility constraint — the root's allowlist of child registries |
| Issuance / revocation log | Governance-as-log — every delegation, rotation, and revocation is an ordered, witnessed entry |
| Certificate chain verification | Two inclusion proofs compose — child attested in the root log, then record included in the child |
| CRL / OCSP | Revocation 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.
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.
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.
dediregistry plugin (pkg/plugin/implementation/dediregistry) this node serves, proven over the wire