URLs as Swift Package Identifiers

Thanks for sharing this, @yim_lee . Responding to your points:

Our proposal doesn't bind identity with location. It starts with that assumption, since that's the case most of the time, but provides several mechanisms for the user, package maintainer, and registry to decouple that relationship.

  • A user can specify a mirror URL for a package URL
  • A user can specify custom registry proxy, which has direct control over how package URLs are located
  • A package maintainer can specify a canonical location for the package
  • A registry may redirect a URL to a different location

Our proposed scheme doesn't create vendor lock-in. A package maintainer can specify a rel="canonical" link in the response for GET /{package}. Swift Package Manager could use this information for deduplication and communicate that to the user (e.g. '"github.com/apple/swift-nio" has moved to "swift.org/server/swift-nio"')

Go uses URIs to identify modules, and that doesn't seem to have been an issue for artifact repositories.

https://www.jfrog.com/confluence/display/JFROG/Go+Registry

https://search.gocenter.io

In response to feedback from the Amazon CodeArtifact team in a previous thread, I created a proof-of-concept that implements Swift package registry support using the AWS SDK:

For what it's worth, newer languages like Go, Rust, and Deno, use URLs to identify packages.

This is a bit of a subjective claim. I think you could say the same about URLs — especially since that's how Swift already specifies external dependencies.

Sorry, but I don't understand why an opaque identifier is unique or unambiguous, but a URI isn't. Can you provide an example of a situation that works for opaque identifiers but not URIs?

I haven't worked extensively with Maven, but my impression is that this naming scheme is not always unambiguous. For instance, Google's Best Practices for Java Libraries guide lists a few examples of naming conventions across different projects:

Examples in open source

Case 1 - Keep Java package name and Maven ID

  • Guava
  • Hibernate
  • Joda Time

Case 2 - Keep Java package name, rename Maven ID

  • guava vs guava-jdk5
    • This technically wasn't a new major version, but it is an example of case 2
      that has caused a lot of problems.
  • javax.servlet:javax.servlet-api:3.1.0 vs javax.servlet:servlet-api:2.5

Case 4 - Rename both Java package and Maven ID

  • Square has established this approach as a policy for its Java libraries.
    • OkHttp (com.squareup.okhttp -> com.squareup.okhttp3)
    • Retrofit (com.squareup.retrofit2 -> com.squareup.retrofit2)
  • Apache Commons Lang (org.apache.commons.lang -> org.apache.commons.lang3)
  • RxJava (rx (version 1.x) -> io.reactivex (version 2.x))
  • JDOM (org.jdom -> org.jdom2)
  • jdeferred (org.jdeferred -> org.jdeferred2)

Case 5 - Bundle old and new packages in the existing Maven ID

  • JUnit (junit.framework (versions 1.x-3.x) -> org.junit (version 4))

I think this is actually an anti-feature.

In our off-thread discussions about this, you've used the example of moving from GitLab to GitHub (or vice-versa). If you believe that these services are interchangeable, this is an innocuous change. However, you might feel differently if a package moves to a service you're not familiar with, perhaps hosted in another country, say China or Russia — or indeed to the US. This speaks to my primary objection to using an opaque package identifier.

Incorporating external dependencies is, fundamentally, a matter of trust.

With URI-based package identifiers, the model is simple: A user says "I trust the package hosted on GitHub.com located at the path /mona/LinkedList, and its external dependencies." Addressability plays an important role in trust as well. If your project depends on [github.com/mona/LinkedList](http://github.com/mona/LinkedList`), you can go to that URL and find the source history.

My understanding of the alternative you've described has a very different trust model. A user not only has to decide what packages to trust, but also what registries to trust, and in what order. The package "org.swift.swift-nio" may appear trustworthy, but lacking fundamental addressability, it's unclear how this can be independently verified.


Our proposal for identifying packages by URI satisfies both of the stated requirements.

We've described in detail how everything works, in the proposal, the service interface description, and OpenAPI specification. We've provided a working implementation and a benchmark harness that anyone can use to try out registries yourself, today. We wrote a reference implementation for the registry server and a proof-of-concept of how artifact repositories can add Swift registry support.

If you feel strongly about an alternative solution — one that we considered earlier in the design process, but ultimately rejected — then I think it'd be very helpful to get into specifics before concluding that to be a better option.

This would not be a trivial drop-in change. Content negotiation and HTTP semantics are a core component of this proposal, and moving from URIs to opaque identifiers would require substantial modification to both the server specification and the client implementation.

4 Likes