1package users23import (4 "strings"56 "gno.land/p/nt/bptree/v0"7)89// canonicalStore maps canonicalized full names to the original name10// that was registered. Keyed by the result of Canonicalize.11//12// Multiple controllers (namereg/v0, future registries, governance,13// genesis bootstrapping) all share this single store via RegisterUser14// and the bypass variant RegisterUserIgnoreCanonical. Cross-controller15// canonical-collision detection is uniform and atomic with the16// nameStore write.17var canonicalStore = bptree.NewBPTree32()1819// Canonicalize returns the canonical form of a name. The substitutions20// collapse single-character visual confusables that arise across the21// allowed [a-z0-9] character set, and strip the three separators that22// can sneak between identical alphanumeric runs.23//24// - {l, i, 1} → i25// - {0, o} → o26// - {-, ., _} → stripped27// - all other characters unchanged28//29// CONTRACT: stable. Future controllers that want to share this30// canonical namespace MUST use this exact function — do not roll your31// own. Adding new substitutions later is a breaking change because it32// would silently re-key existing entries in canonicalStore.33//34// Input contract: ASCII-only. r/sys/users.validateName already rejects35// non-ASCII at the registration boundary, so by the time a name reaches36// Canonicalize through the standard write path it is guaranteed to be37// ASCII. Direct callers from other realms must honor this contract;38// non-ASCII bytes are passed through as-is and will produce undefined39// collision behavior.40//41// Multi-char confusables (m↔rn, nn↔m, cl↔d) are NOT canonicalized.42// They require fixed-point substring substitution rounds, which is out43// of scope for the unified store.44//45// Pure: no state access. Safe to call from anywhere.46func Canonicalize(name string) string {47 var b strings.Builder48 b.Grow(len(name))49 for i := 0; i < len(name); i++ {50 c := name[i]51 switch c {52 case 'l', '1':53 b.WriteByte('i')54 case '0':55 b.WriteByte('o')56 case '-', '.', '_':57 // strip58 default:59 b.WriteByte(c)60 }61 }62 return b.String()63}64AddControllerAtGenesis(interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string)
Canonicalize(name string) string
Controllers() [].uverse.address
GetReadonlyAddrStore() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree
GetReadOnlyNameStore() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree
IsCanonicalTaken(name string) (existing string, taken bool)
IsController(addr string) bool
IsNameTaken(name string) bool
NewErrNotWhitelisted(int, caller interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}) struct{Caller string}
ProposeControllerAdditionAndRemoval(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, toAdd string, toRemove string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeControllerRemoval(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeDeleteUser(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeNewController(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeRegisterUser(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string, addr string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeUpdateName(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, newName string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
RegisterUser(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string, address_XXX string) interface {Error func() string}
RegisterUserIgnoreCanonical(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string, address_XXX string) interface {Error func() string}
Render(string) string
ResolveAddress(addr string) *gno.land/r/sys/users.UserData
ResolveAny(input string) (*gno.land/r/sys/users.UserData, bool)
ResolveName(name string) (data *gno.land/r/sys/users.UserData, isCurrent bool)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.