README.md
gno.land/r/moul/x/wesh/v0
An on-chain directory and device sigchain for Wesh protocol (weshnet / Berty)
identities, built on p/moul/x/wesh.
gnokey maketx call -pkgpath gno.land/r/moul/x/wesh/v0 -func Register \
-args alice -args <accountPK hex> -args <seed hex> -args "Alice" -args <sig hex>
Render then emits a link a Berty client can actually open:
https://berty.tech/id#contact/oZBLFpzghxrATkepWvDPNX9pHYqi6BWgP45x…/name=Alice
What it is for
Wesh is peer-to-peer and end-to-end encrypted, and it has three gaps that only an authenticated, ordered, publicly auditable record can close:
- Resolution. A Berty identity travels out of band and cannot be looked up. Here a name resolves to a contact, and the realm renders the real link.
- Rotation with a paper trail. Resetting the rendezvous seed silently kills every link ever shared. Here every rotation is numbered, signed and kept, so a stale link is recognisably stale rather than merely dead.
- Device revocation. Wesh cannot revoke a device at all. This realm hosts
the missing log: hash-chained, account-signed, and verified on chain with
crypto/ed25519before an entry is accepted.
API
| call | what it does |
|---|---|
Register(cur, name, accountPK, seed, displayName, sig) |
claim a name, publishing the rendezvous seed |
RegisterCommitted(cur, name, accountPK, commitment, displayName, sig) |
claim a name, publishing only H(seed ‖ salt) |
Rotate(cur, payload, sig) |
publish a new seed/commitment at revision+1 |
AppendDevice(cur, prev, op, devicePK, sig) |
append add or revoke to the sigchain |
SetDisplayName(cur, name) · Release(cur) |
update the label · free the name |
Resolve · Link · RendezvousPointAt · DeviceStatus · SigchainHead · NameOf · Count |
read-only, for gnoweb and other realms |
Authentication
Every state-changing call carries an ed25519 signature made with the Wesh account key, over a canonical statement naming the kind, the chain id, the caller's gno address, and a monotonic revision or sequence number. Each field is there to stop one attack:
- without the kind, a binding signature could be replayed as a device one;
- without the chain id, a testnet binding replays on mainnet;
- without the gno address, anyone could publish someone else's account key next to a seed they control and harvest the contact requests;
- without the revision / sequence, a superseded statement could roll a rotation back or fork the sigchain.
All five are covered by a negative test apiece.
What it never stores
No group secret, no device chain key, no message key, no ciphertext: the library it is built on has no type that can hold one. And it does not pretend to enforce revocation inside weshnet: a revoked device's ratchets are already distributed. What revocation buys here is that it becomes public, ordered and attributable, which is strictly more than weshnet has today.
Publishing is opt-in exposure
A published seed lets anyone derive the account's rotating rendezvous point.
That is the right trade for a support line, a shop or a public channel, and the
wrong one for a private account, which is what RegisterCommitted is for.
Library: p/moul/x/wesh.
Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
Dependency graph:

🧪 Highly experimental — potentially vibe-coded. Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: DISCLAIMER.