IPNS discovery using DIDs

Hi all,

I’d like to share an (informal) proposal on a new IPNS strategy. As background, my work on DSNP has led to a lot of thinking about some of the limitations of IPNS for architecture patterns that require strong consistency guarantees. For example, I’d love to have a way for users to manage their social networking profile on IPFS via a well-known IPNS name, and ensure that their latest edits are always discoverable.

The tradeoffs inherent in the DHT/PubSub strategies for IPNS stem from the challenges of incorporating mutable records in a protocol built for immutable data. DNSLink does a decent job of coupling a mutable record system, albeit a centralized one, with IPFS, so I wanted to examine if there was a way of using consensus systems (that is, systems that provide strong consistency guarantees) as a (potentially) more decentralized form of record-keeping. What eventually occurred to me was the fact that there is an existing protocol for accessing state from these systems in the form of DID documents, and the DID resolver architecture allows for pluggability of the backing system.

So the proposal below can be seen as a sort of DNSLink alternative that defines a DID service type to link key ownership from a DID controller point of view to IPNS record ownership. To do so I’ve (perhaps unwisely) introduced a non-collision-resistant pseudo-hash on the IPFS CID side, but not without my reasons!

Here’s the detailed writeup: DIDLink: Providing IPNS functionality in coordination with decentralized IDs

Would love to gather thoughts/objections/suggestions from the community on the feasibility of this approach, and even if it’s entirely daft, perhaps it might spur some alternative proposals.

If there’s interest, I’ll volunteer myself to build a proof of concept using DSNP on the Frequency blockchain (which I’ve conveniently a basic DID resolver for) and Helia with some hashing hacks — but to be clear, the proposal is generic and open for any system that is able/willing to support the proposed DID service object.

Many thanks to @robin and @bumblefudge for planting some of the idea seeds and looking at an early draft of this.

4 Likes

@wesbiggs this is extremely cool. I’ve seen user/dev feedback indicating some advancement of IPNS capabilities would solve common problems for decentralized applications.

Particularly: a multi-owner managed IPNS key. With DIDs and UCANs, this should be possible

This morning, I was just thinking briefly about how we can make it easier for apps to congrate their application instances in a p2p network without having to stand up a custom relay/coordination server. Can we use IPNS to provide a list of browser-peers via a single known app key? p2p browser applications need to discover each other more easily than they can now.

As more browser instances come online, the DIDLINK containing a CID with a list of browser peers could be updated. This is a wishy-washy solution, but a common problem p2p app creators need to solve. I imagine that distributed IPNS management is the most generic way to do this today, but something more creative is probably required for it to really be useful

1 Like

Thanks for posting :pray:, @robin directed me here so leaving some feedback/thoughts. I may have misunderstood parts of the proposal, if so sorry about that and correct me where I’m wrong.

From what I understand there are 2 main components of the proposal that can be handled independently:

  1. IPNS by virtue of not being anchored to a consensus system chooses a different point in the consistency/availability/partition-tolerance space (CAP theorem - Wikipedia) than works for every use case. By choosing to support a consensus system you can make tradeoffs to increase consistency at the expense of the others.
    • As you noted DNSLink already does this. In order for things to make sense in the DNSLink case people need to agree on what the TLDs mean and how they’re controlled. Generally speaking this means ICANN, but for others like .eth there are other controllers like ENS. Anyone can configure their own DNS resolver to try out a new system. DNSLink is just the API
    • Your rebuttal against DNSLink is that IPFS implementations would need to enshrine alternative DNS systems (e.g. ENS, or whatever) and users would have to get a name from that system. However, the same is true of the DID inside the document (e.g. did:dsnp:123#profile). IPFS implementations would still need code to understand what did:dsnp meant and users would still need to figure out how to reserve 123 for themselves. You could implement the same thing by reserving say .did and doing /ipns/123.dsnp.did (maybe with profile. in front) and hooking up a DNS resolver (either using an external resolver or writing resolver code for use directly in the IPFS implementation, the same as is true for ENS)
    • If the claim is that did:* is simply more common/reasonable than *.did that’s understandable. My counterclaim is:
      1. There are standard APIs for external resolvers for *.did which isn’t true for did:
      2. Inserting did: into subdomains to enable origin isolation in browsers isn’t going to work without a translation layer. So why not just use *.did and call it a day?
  2. Instead of embedding a direct mapping from the DID to an IPFS (or another IPNS) address you chose to use an indirect layer.
    • I’m not sure how valuable this is given the complexity it adds. Adding extra lookups to the resolution path is generally not desirable.
    • The main goal seems to be reducing where bytes are stored and some privacy, but from what it looks like the DID resolver needs to send back at least a CID worth of bytes and your DID resolver seems to be your choke/consensus point anyway. Perhaps I’m missing something here though.

For me neither of these particularly resonate at the moment, although it seems like maybe it’d be a good idea, given the proliferation of DIDs, to create a standard mapping from DID to DNS names in order to foster interoperability with web browsers.

Thanks for the feedback!

Particularly: a multi-owner managed IPNS key. With DIDs and UCANs, this should be possible

At the risk of getting a little off topic, could you give a use case/example? I think I get “multi-owner managed IPNS key”; you mean more than one entity is able to control the destination of an IPNS lookup? In this scheme that would require the owners to have shared access to the IPNS private key (in order to generate fresh signatures over fresh nonces in the link file). This is definitely doable, but I don’t know if the key sharing can be done with UCANs. Then again, I’m not that familiar with UCANs, so might be missing something.

Can we use IPNS to provide a list of browser-peers via a single known app key? p2p browser applications need to discover each other more easily than they can now.

Yes, I think this is a good use case. In this scheme each node would need the private key and DID (and means of controlling the DID, if different).

1 Like

Hi Adin,

Thanks for the review and response. You make some great points.

Usage of indirect file

Your comments made me realize I left out an important piece. I meant to preserve the security model of the IPNS Record. Part of this is the role of the intermediate file (the link file, for want of a better name); it has an analogous probative function as the current IPNS Record. That is, its signature proves that the link was made by an entity in possession of the key. The nonce provides for an unbounded number of link files to be created securely in this fashion without needing to be distributed by a separate mechanism the way IPNS Records currently do with DHT/PubSub.

In the original IPNS Record, the fact that the IPNS public key is also used as the IPNS name enables this proof to work. In the DIDLink scheme, because the IPNS name is the DID service id, we additionally need to prove that the DID record is controlled by the same entity as the link file. I missed this in the initial draft, so you were absolutely right to point out that the chokepoint in the trust model was not fundamentally different from DNSLink. Indeed, without this proof in the DID document, Bob can copy Alice’s service entry and claim he, rather than she, is the controller of the link file.

However, we can easily add authentication here by attaching a signature over the (fully qualified) DID service id to the DID service record. I’ve changed the proposal document to have an updated example and treatment of this:

  "service": [
    {
      "id":"did:dsnp:123#profile",
      "type": "DIDLink",
      "serviceEndpoint": "ipfs://{CID_of_DIDLink_file}",
      "ttl": 300,
      "verificationMethod": "did:key:z6M...XEd", // or reference another key in this DID document
      "proofValue": "z5k...umD" // sign "did:dsnp:123#profile" with
    }
  ]

Now, to manage an IPNS link, Alice needs her DID document to include proof of ownership of the key that she uses to create the link file. Bob can’t publish a DID document with a copy of Alice’s service entry and claim ownership of the link, as the signature won’t validate against his DID. (Similar to the way an SSL cert generally won’t validate when accessing a site via a CNAME entry on a different domain.)

This allows for a zero trust environment with regard to the intermediaries between client and consensus system: I can’t write a resolver that executes a man-in-the-middle attack, for example.

The updated scheme with name ownership proof could also work over actual DNS (in that sense, DNS is a form of consensus system), but the TXT record would need to include a public key and a signature over the DNS name, and reference the link file. This would address the man-in-the-middle attack that is possible today. I still don’t love DNS because of its centralization and potential for observability (think authoritarian governments or nosy ISPs), though a trusted DoH server helps somewhat.

Using the DNSLink API

  1. There are standard APIs for external resolvers for *.did which isn’t true for did:
  2. Inserting did: into subdomains to enable origin isolation in browsers isn’t going to work without a translation layer.

To be honest, the usage of DNSLink names (DIDs) as browser origins was not something I had considered, but this is a good point.

[…] maybe it’d be a good idea, given the proliferation of DIDs, to create a standard mapping from DID to DNS names in order to foster interoperability with web browsers.

I think I’d be fine with inverted mappings of DIDs to pseudo-DNS names. My suggestion would be to have did:dsnp:123#profile become 123.dsnp.did/profile, and make the IPNS system smart enough to recognize this as a DIDLink.

Discoverability of DID methods

IPFS implementations would still need code to understand what did:dsnp meant

This is a fair point and there’s no getting away from some form of configuration of DID resolvers, whether static or dynamic. For some of the use cases I’m imagining, dynamic would be doable, as the applications consuming the links would know what systems the links would be coming from.

I’m less familiar with how an in-browser IPNS resolver works, and whether this could be easily configured. In any case, tapping into an existing set of libraries for DID resolution seems simpler than writing custom lookup code for each supported consensus system; there’s also a fallback network option to use a universal resolver deployment.

I would suggest that the verification algorithm be implemented as a core form of IPNS, though. That is, *.did[/{path}] names would get routed to a handler that would:

  1. Rewrite the “domain” to recover the DID
  2. Find a registered resolver for the DID and invoke it
  3. Look for the service record matching {path} and verify its signature
  4. Retrieve the link file and verify its signature and shared control
  5. Return the target CID

That said, the existing APIs would at least make it straightforward to create a proof of concept, with the addition of the new multihash proposed.

I think it would be worth looking a little more deeply into UCANs, because they are precisely a “means of controlling a DID, if different”-- a way of delegating capabilities like “update my DID doc” or “publish IPNS records/indirect files/etc on my behalf” without moving around or sharing private keys or getting into threshold signatures, but instead sharing these intermediate tokens/JSON blobs that have to be chain-verified at authZ checkpoints in more trustless/decentralized services. It can all seem really esoteric until it clicks, but it’s basically an OAuth-like system for creating revocable and chainable authorization tokens so that things like DID methods and DHTs and CRDTs can distribute authorization across trust boundaries. For instance, see the working group specs or the ucanto library that web3 storage made for a generalized OCap interface-- it composes nicely with various kinds of dids (not just the did:keys used in web3.storage implementations!).

My high-level read is that this is actually a pretty interesting idea, because as Adin put it, it makes different CAP theorem tradeoffs that are probably worth making to have different mutable pointers and pinning/availability-guarantees centered on DIDs instead of TLDs. Making it adequately generalized so that it works across different styles of DID method (for instance, can you imagine this working for a variant of did:web, or did:dht in, say, pkarr mode, or… heck even a multichain did:pkh variant?) might be a fun research project-- should I keep an ear to the ground for others that might want to prototype against another blockchain-based DID method?

Another thought that came to mind is that DID-linked resources are… a thing that exist, that the DID spec leaves entirely in each DID method’s court to define as a mapping from paths and/or query parameters to serviceEndpoints or to other URI-transformations if not endpoint-based. A veritable warren of rabbitholes fork out from this thread, if you’re curious.

2 Likes

To put it more explicitly, i’m supportive in theory of a multi-DID-method DNSLink alternative/swap-in with different properties, because I would imagine there are use-cases where DIDs make more sense than PeerIDs for both CID publishers/end-users and intermediate actors that persist/mirror/fetch/etc. I have a vague intuition that prototyping this across multiple DID methods will be a lot easier if you use UCANs to bridge vanilla implementations of each method to the DIDLink specifics than if you fork each method’s implementations to hardcode the DIDLink stuff into a custom implementation/server/node/etc of each DID method… from there, the ideal spec for this would be generic on both AuthZ layer and DID layer, even if you’re only confident it works well with your DID method and OAuth or UCANs :smiley:

A CID-powered DID-linked-resource scheme for one or more DID methods might be fun to design, if and only if there are DID-linked usecases or interop targets justifying it. Barring that, it’s more a “mention this in the prior art and explain how it’s different” kind of recommendation.

NB @taoshengshi – I remember you were curious about use-cases where DIDs make more sense than PeerIDs for rotation and/or reputation properties. This might be such a use-case!

1 Like

Thanks.

If I understand you correctly: there are a number of approaches that various systems are using already for DID-linked resources. This might eventually become standardized by the DID Linked Resources working group. Rather than attempting to re-specify DID syntax specifically for the IPNS use case, we should allow different DID method resolvers to adapt their target systems’ DID document linked resource syntax to allow for more generic discovery. In that sense, the proposal over-specifies when it comes to the DID side of things. This all makes sense to me.

I believe UCANs can be worked into the link file structure, which I think addresses @SgtPooki’s use case, among others. The link would still be “owned” by the DID (as interpreted by the resolver), but this allows the DID controller to delegate signature authority for new link files. This also makes sense. The entity that makes the link file also needs permission to update consensus system state related to the IPNS name (DID+fragment, however we represent it), but the precise mechanism for doing that can be left as a system-specific implementation detail, as is the storage of the link file CID; from the perspective of the IPNS resolution core code, an IPNS name can be resolved to a link file CID; the link file can be validated to ensure it was created with the proper UCAN-based authorization.

There is a slight semantic mismatch between the expiration time of the UCAN and the lifetime of the validity of the link. That is, it may be impossible to prove that the link file was created during the time period when the UCAN was valid. But assuming the attacker in this scenario does not have the ability to update the DID document to reference the new link, this is probably a manageable issue.

I like where this is going. I will work on reshaping the proposal.

It’s not actually a standing/chartered “working group” anywhere, and I’m not sure anyone involved wants it to be any time soon; I also don’t know that it will ever be standardized, so much as maybe some day various implementations of the concept will see value in standardizing or at least publishing/stabilized a shared interface or specific query param names in common, assumptions about serviceEndpoints, etc. just a corpus of prior art worth learning from, not necessarily an interop target I wouldn’t think?

Maybe? I wasn’t implying that IS the case, just that it COULD be depending on CAP theorem choices, ecosystem dynamics, yadda yadda.

I mean, if the goal were “replace IPNS ASAP because X population/use-case/ecosystem already uses one DID method” I would say, it specifies just enough! But for lack of user stories and requirements documents/validation, it specifies a lot :wink:

Happy to read the next iteration more carefully than I read this one, at least!