Skip to content

.canton Naming CIP#209

Open
dave-axymos wants to merge 3 commits into
canton-foundation:mainfrom
Axymos:main
Open

.canton Naming CIP#209
dave-axymos wants to merge 3 commits into
canton-foundation:mainfrom
Axymos:main

Conversation

@dave-axymos
Copy link
Copy Markdown

Axymos launched xNS as a naming service on Canton Network in late 2025.

Through the 'Identity and Metadata' SIG, we were invited to provide a CIP for how we could decentralise this as we move forward so that

  • multiple registrars could also offer names in the .canton namespace
  • but all registrars are backed by a single shared canonical registry, to prevent fragmentation
  • the service could outlive any individual company as needed

This is an early draft of the CIP and we'd welcome any questions/comments/suggestions.

Copy link
Copy Markdown
Contributor

@meiersi-da meiersi-da left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @dave-axymos . This looks like a good first step. The current write-up is rather technical, and requires quite a few things to "reverse engineered".

What do you think about structuring the document to clearly separate:

  1. the UX to be built: what problems are solved, and what UX flows are provided to solve them
  2. the economic model: who does what and why are they willing to do this?
  3. the technical implementation: split into
    1. component view: dApp(s), backends running at registrars, .dar packages vetted by users, .dar packages vetted by registrars only
    2. information flows: how does the information flow across the components to for the different UX flows described in (1)
    3. key design decisions: e.g., how to ensure uniqueness of names
    4. link to PoC implementation (where available)

Comment thread cip-XXXX-canton-naming/cip-XXXX-canton-naming.md Outdated
Comment thread cip-XXXX-canton-naming/cip-XXXX-canton-naming.md Outdated

Governance of the service is managed by consensus among the approved registrars. Parameters of the service (like min pricing, vote thresholds etc) can be agreed upon by the governance layer and then are stored in the Name Registry contract itself. Registrars compete to provide name sales, renewals, and support to end users, but every name they sell is recorded in the same canonical `NameRegistry` contract.

The reference implementation (to follow) is a DAML contract package targeting Canton SDK 3.6.0. All authorisation flows through DAML's signatory model; the DAR would be vetted, so holders can exercise their own choices directly via the JSON Ledger API.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 3.6, that is not yet out. Did you mean 3.5, which is what provides contract keys?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember hitting an issue when trying to compile against 3.5 — I'll compile against that target again and double-check!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, if 3.5 provides contract keys does that mean that they're available now on devnet @meiersi-da ?

-> assertMsg "Payment >= minPriceFloor"
-> lookupByKey @NameRecord -> assertMsg "Name not already registered"
-> Fee split via chained TransferFactory calls:
1. Treasury transfer (paymentHoldingCids -> DRO); capture senderChangeCids
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally avoid having the DSO receiver transfers. We want it to only facilitate the flows, but not partake in the flows.

3. Sibling[1] transfer (senderChangeCids from step 2); ...
Each transfer consumes its inputHoldingCids and returns new change holdings.
Registrar retains their fee as the final change holdings (no transfer needed).
-> create NameRecord (live immediately; usable from this point)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me who pays whom and how much.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this needs to be nailed down and better explaiend.

Our thinking at the moment was that if there's some off-chain work.co-ordination needed between registrars then there would be fee split going on to account for that level of effort.

Then we'd also on-chain enforce things like a floor for prices


#### Transfer

Names can be transferred either in the case of:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea. In that case, I'd suggest to represent names as CIP-56 assets by making each name a holding with InstrumentId with admin = dso; id = 'name' and amount = 1.0.

This way these names can be held and managed using a token standard wallet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And they can be traded.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that sounds really good - I was toying with this before and I can't remember why we moved away from it at the time. I'll revisit it in the reference impl to trial. thanks!


#### Dispute lifecycle

Disputes occur in the case that a disputing registrar claims the registration should not stand — e.g. a rogue registrar registering an agreed reserved name, or a holder using the name in a way that violates registrar conduct policy.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What defines an "agreed reserved name"?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What defines an "agreed reserved name"?

At the moment for v1 live on chain, we've a reserve list that we don't let people register out of the gate — mainly profanity and clear phishing attempts e.g. stopping end users signing up as hsbc.canton, just to allow a decision to be made on that approach.

I think there's multiple options we could explore here:

  • free rein — any name goes
  • literally register the names that we think may want to be held (e.g. the names of each validator/supervalidator say) with a long expiry
  • an off-chain list (which we have here at the moment)

I think we need to go away and strengthen up here the "what" and the "why" both and a path to someone claiming a reservered name etc if that's the why we go down.

-> CounterStake (takes registryCid) -> fetches live registry -> sets voteDeadline (now + registry.voteWindow)
-> continue to step 4
|
4. Registrars vote via AddVote (True = for dispute, False = against)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like it might be quite a bit of work. What's the motivation for registrars to partake in these votes?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the challenge to reframe here! :)

Again this is probably something that we're porting over from a trustless model. We're thinking of a flow similar to a Token Curated Registry where e.g.

  1. Companies agree to stake X to become a registrar
  2. In the event of a dispute, a contesting registrar can trigger a vote
  3. There's a fee split between registrars voting
    a. each voting registrar receives financial incentive to vote
    b. winning registrar (disputer or challenger) receives a fee

In fact, based on the "do we need a reserve list" above, maybe this piece could naturally fall away - if there are no reserved names then there's no need to dispute on chain

-> assertMsg "Registrar not in allowlist"
-> assertMsg "Invalid name format" (isValidName — lowercase, .canton suffix, no leading/trailing hyphens, 1–63 chars)
-> assertMsg "Payment >= minPriceFloor"
-> lookupByKey @NameRecord -> assertMsg "Name not already registered"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no uniqueness guarantee for contract keys in Canton 3.x

I suspect that name allocation will have to be funnelled through an on-ledger representation of the map of all names (e.g. as a prefix tree) to ensure uniqueness even when there are many different nodes submitting name allocation transactions.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we were looking to leverage contract keys was to do make sure that we gate our look-up on the fact that the multi-hosted party (the 'DRO') is the maintainer of the key and then it's choosing not to create duplicates.

So having a flow of:

  1. Registrar e.g. Axymos does a look-up on chain to see if a name is available
  2. If it is, calls NameRegistry.RegisterName() - which does a look-up of all active contracts owned collectively by the registrar pool and checks that it doesn't exist
  3. So then using len(results) == 0 as a proxy check for uniqueness.
  4. Since the DRO is the key maintainer and since all registrars are registering as the DRO then our thoughts were that were safe enough to gate it

Couple of thoughts:

  • we were leaning on the atomicity and sequencing of transactions on the network to make sure that race conditions prevented the second registration
  • in the case of a "rogue registrar" (e.g. someone offboarded from the allowlist but still a host of the DRO) could bypass the path of driving all registrations through RegisterName() and instead just hit createCmd on NameRecord directly.

One thing we had that I seem to have accidentally dropped from NameRecord was the concept of activation. So thinking that the flow would be:

  1. registrar calls NameRegistry.RegisterName()
  2. registrats calls NameRecord.Activate()

Then resolving a name requires that it has been activated. I'm coming from a trustless EVM world so maybe that's overkill and we've better practices in Canton Network around stuff like this (e.g. literal contractual sign-up, say)

4. **On-chain integrity** — Names are guaranteed unique via on-chain checks. We rely on the underlying infrastructure to solve for the race conditions/atomicity of these registrations. All authorisation flows through DAML's signatory model.


### Open Questions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also missing clarity on the end-to-end flow for end-user actions: how will users build and submit their transactions (e.g., will they use a ".canton dApp" built as part of this CIP, or will they use their token standard wallet)? what transactions are built and submitted by end-users, which ones by registrars?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So existing in production for us, Axymos as registrar are submitting all TXs on the user behalf (as our DAR is running on our node, solely) - we were trying to move to a position where users would be able to do more with their own assets, so were thinking if this becomes a CIP standard, that

  • end-users would be able to have the DAR vetted on their node, so could interact directly, signing TXs as themselves
  • hosting nodes would be able to query the NameRegistry "natively" from their own node (if added as an observer to the registry, or maybe if the DSO is a singleton observer? Note sure if that puts a burden on the DSO though)
  • non-hosting nodes or external companies could still use APIs provided by any registrar to query the records.

At the moment, we're thinking that end users would co-sign TXs for things that they particular are actioning like if they choose to release a name (Credential_ArchiveAsHolder()) or for TransferWithApproval (to support transfer/sales/etc)

You're right that we've more focused the CIP on the on-chain elements that would allow registrars to build a full end-to-end product on top, rather than including elements like what a full dApp would look like.

We can definitely add more colour here to give an example, though we imagine that elements of UX etc will be driven by individual registrars building on top of this. But definitely will try to make the "bones" more obvious of who is signing what when, from where etc.

And I think if we do implement elements like CIP-56 then maybe we get more "for free" from the core standards (we're currently modelling it based on the credential inferface but maybe Token is more of a fit)

@dave-axymos
Copy link
Copy Markdown
Author

Thanks @dave-axymos . This looks like a good first step. The current write-up is rather technical, and requires quite a few things to "reverse engineered".

What do you think about structuring the document to clearly separate:

1. the UX to be built: what problems are solved, and what UX flows are provided to solve them

2. the economic model: who does what and why are they willing to do this?

3. the technical implementation: split into
   
   1. component view: dApp(s), backends running at registrars, .dar packages vetted by users, .dar packages vetted by registrars only
   2. information flows: how does the information flow across the components to for the different UX flows described in (1)
   3. key design decisions: e.g., how to ensure uniqueness of names
   4. link to PoC implementation (where available)

that sounds good, thanks @meiersi-da will restructure along those lines!


## Reference Implementation

Reference implementation to follow. Currently targeting Canton SDK 3.6.0 (LF target 2.3-staging), which is still in an alpha phase.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're working against contract keys, still in alpha, so are only on Sandbox at the moment, and we're also aiming to align with standards coming down the track like the RegistryV1.Credential, so have that stubbed out.

Other than that though, everything is based on working DAML code (though harder to test some aspects on a single node so sure there's more edge cases to find!)

But I'll try to make time to tidy our reference implementation and get into a shareable state soon.

dave-axymos and others added 2 commits May 15, 2026 21:17
Co-authored-by: Simon Meier <simon@digitalasset.com>
Signed-off-by: dave-axymos <dave@axymos.com>
Co-authored-by: Simon Meier <simon@digitalasset.com>
Signed-off-by: dave-axymos <dave@axymos.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants