mirror of
https://seed.radicle.dev/rad:z3trNYnLWS11cJWC6BbxDs5niGo82.git
synced 2026-10-18 19:10:39 +08:00
Building on the work that Lorenz Leutgeb did in patch
rad:z3trNYnLWS11cJWC6BbxDs5niGo82/patch/19ba16634fbde97156255ed97069d0aa3ab2bf12,
the URI scheme is brought to a final RIP document, with full ABNF and Nearley
grammars as well as examples, and a couple of scripts to extract and test those
grammars against those examples.
Compared to the URL scheme in RIP 3, this RIP brings a subtly different URL form,
while also supporting the RIP 3 scheme (now considered legacy). In short, these
two forms are supported by this RIP:
- rad://{rid}/{nid} (considered legacy)
- rad://{rad-auth}/{rid}[/{namespace}][/{resource-type-and-identifier}]
(rad-auth is the authority part, which can take the form of {nid}@{host-and-port}
or parts thereof)
This works, because the {rid} and the {nid} are distinguishable, as a {nid} always
starts with z6Mk, while a {rid} does start with a z, but never with z6Mk, and they
have different lengths (a {rid} is always 28 or 29 characters long, which a {nid}
is always 48 characters long).
Co-authored-by: Fintan Halpenny <fintan.halpenn@gmail.com>
Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.dev>
868 lines
30 KiB
Plaintext
868 lines
30 KiB
Plaintext
= RIP 4: General `rad:` URI Scheme
|
|
Lorenz Leutgeb <lorenz.leutgeb@radicle.dev>; Richard Levitte <richard@levitte.org>
|
|
:doctype: article
|
|
:toc: preamble
|
|
:toclevels: 3
|
|
:status: Draft
|
|
:copyright: CC0-1.0
|
|
:lang: en
|
|
|
|
--
|
|
In <<RIP2>>, a `rad:` URN is briefly discussed as a simple Radicle
|
|
identifier. In <<RIP3>>, the `rad://` URL is introduced, made to allow
|
|
`git-remote-rad` to be a fruitful interface between Git and Radicle.
|
|
|
|
In this RIP, we discuss some modifications that are needed to have a general
|
|
URI scheme that fits in the syntax defined by <<URI>>, as well as further
|
|
extensions that allow reference to more aspects of Radicle as well as Git on a
|
|
Radicle network.
|
|
|
|
All extensions and modifications are intended not to introduce impossible
|
|
clashes with the existing schemes.
|
|
--
|
|
|
|
== Status Quo
|
|
|
|
References to resources on Radicle (both traditional Git objects as well as
|
|
collaborative objects, such as patches and issues) are communicated between
|
|
individuals mostly by relying on installations of <<radicle-explorer>>,
|
|
a web application that provides an HTTPS interface to the Radicle network.
|
|
|
|
When Alice wants to direct Bob's attention to a specific issue that she filed
|
|
on Radicle, you will likely see her send a URI of the form
|
|
`https://app.example.com/nodes/seed.example.com/rad:…/issues/…` to Bob.
|
|
URIs using the `rad:` schema are exchanged more rarely, and almost exclusively
|
|
to refer to repositories as a whole, e.g. in order to invoke `rad clone`,
|
|
`git clone`, etc.
|
|
|
|
As of 2024-04-23, the Radicle team also opted to have `rad:…`
|
|
URIs in Zulip messages automatically replaced by links to
|
|
`https://app.radicle.xyz/nodes/garden.radicle.xyz/rad:…`, limiting the
|
|
usefulness of exchange of `rad:` URIs.<<zulip-mangle>>
|
|
|
|
== Terminology
|
|
|
|
Node ID:: a Node ID, or NID for short, is an identifier for a Radicle node and a Radicle user, at the time of writing.
|
|
It is a <<multibase>> (<<base58btc>>) and <<multicodec>> encoded Ed25519 public key.
|
|
|
|
Namespace:: a namespace, in the Radicle ecosystem, commonly refers to a Git namespace, i.e. `refs/namespaces`,
|
|
that uses a *Node ID* as the namespace, e.g., `refs/namespaces/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi`.
|
|
|
|
Repository ID:: a Repository ID, or RID for short, is an identifier for a Radicle repository.
|
|
It is a <<multibase>> (<<base58btc>>) encoded Git object identifier, at the time of writing.
|
|
|
|
== Challenges
|
|
|
|
:url-cgit: https://git.zx2c4.com/cgit/
|
|
:url-sourcegraph: https://sourcegraph.com/
|
|
|
|
Every user should be able to individually control how they want to interact
|
|
with the network. Users may prefer to interact with Radicle via…
|
|
|
|
* a command line interface in their shell.
|
|
* a specific client program, such as:
|
|
** VSCode with a suitable extension, <<radicle-vscode>>
|
|
** a JetBrains IDE with a suitable plugin, <<radicle-jetbrains>>.
|
|
* <<radicle-explorer>>, hosted locally or by relying on a specific hosting
|
|
provider.
|
|
* another custom program or handler, e.g. redirection to an instance of
|
|
{url-cgit}[cgit] or {url-sourcegraph}[Sourcegraph].
|
|
|
|
Exchanging `https:` URIs that point at a specific hosting provider of
|
|
<<radicle-explorer>> is not portable. Individuals that read/receive such a URI
|
|
might prefer to access the repository with another method and then must modify
|
|
the URI (extract repository address, etc.) before access.
|
|
|
|
For the case of a reference to a repository, this is rather simple, but as
|
|
soon as references to resources _within_ the repositories are exchanged, there
|
|
are currently no standardized options. The two most common ones are referring
|
|
to an instance of `radicle-explorer` by means of a `https:`-URI, or
|
|
communicating in natural language, often in a context where the repository ID
|
|
is clear to all communicating parties. They will simply say "patch c863fe".
|
|
|
|
Automatic rewriting of `rad:` URIs to a specific hosting provider of
|
|
`radicle-explorer` contributes to additional centralization of access to the
|
|
network. In case the specific hosting provider goes offline, is censored, or
|
|
censors content, the impact is multiplied.
|
|
|
|
== Intention
|
|
|
|
The intention with this RIP is to define a `rad:` URI scheme with which one
|
|
can refer to:
|
|
|
|
. a Git repository in the Radicle network, which also includes the namespace
|
|
for the contributions of a particular user (currently represented with a NID).
|
|
. optionally, a repository resource of some sort; to properly identify
|
|
the resource:
|
|
.. the resource's type
|
|
.. a resource identity, specific to the resource type
|
|
|
|
****
|
|
We recognise that the concept of namespace and how that relates to a user is
|
|
a bit ambiguous. A more detailed explanation should appear in another RIP.
|
|
|
|
For the moment, the user and their local node are identified with the same
|
|
identity, the NID. Since the namespace for a user uses its identity, it may
|
|
appear confusing. However, the fact remains that a namespace stores a user's
|
|
contribution to the repository it lives within.
|
|
|
|
In the future, users may be identified differently, and when that happens, the
|
|
namespace is expected to change accordingly, so that it is clear that it is the
|
|
user's contributions.
|
|
****
|
|
|
|
This can be seen as a hierarchy, expressed like this:
|
|
|
|
repo-id [ "/" namespace ] [ "/" resource-type "/" resource-id ]
|
|
|
|
The `rad:` URI scheme is intended to be used to refer to / represent
|
|
repositories and resources within repositories, in such a way that it
|
|
can be used by applications, but also be understood by humans wishing
|
|
to get to the resource being referred to with what knowledge tools
|
|
they have available (such as the commands `git` and `rad`).
|
|
|
|
It does not imply any particular network protocol, or even that all
|
|
the parts of the scheme must be implemeted by all applications, and
|
|
applications may also make some parts of the scheme more mandatory
|
|
than what this RIP implies.
|
|
|
|
As a very concrete example of an application that handles `rad:` URIs,
|
|
`git-remote-rad` can probably never be expected to handle more than a
|
|
repository and namespace, a `rad:` URI with more than that should be
|
|
considered an error. On the other hand, `git-remote-rad` requires the
|
|
presence of a namespace, i.e. makes it mandatory.
|
|
|
|
== Formalization and General Extension of the `rad:` URI Scheme
|
|
|
|
In this RIP, we denote the `rad:` scheme as a _URI_ scheme and try to
|
|
generally follow the nomenclature from <<URI>>.
|
|
|
|
=== The `web+rad:` URI Scheme
|
|
|
|
<<HTML>> defines that unless a URL scheme is known and on their safelist, it
|
|
may be registered as a custom URL handler if prefixed with `web+`. See their
|
|
section on <<HTML-registerhandler,registering handlers>>.
|
|
|
|
For this RIP, it means that `web+rad:` must be compatible with `rad:` in every
|
|
way, i.e prepending the string `"web+"` to a valid `rad:` URI must result in a
|
|
valid `web+rad:`-URI, and removing the first four symbols from a `web+rad:`
|
|
URI must result in a valid `rad:` URI.
|
|
|
|
This should make it as easy as possible for users to register URI handlers for
|
|
both `web+rad:` and `rad:` on their system, according to their preferences.
|
|
|
|
=== The Old `rad:` URI Scheme
|
|
|
|
The scheme as defined in <<RIP2>> and <<RIP3>> was really pretty simple, and could
|
|
be summarized with just a few lines of <<ABNF>>:
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
rad = rad-scheme ":" rad-rid ; (RIP #2)
|
|
/ rad-scheme ":" "//" rad-rid [ "/" rad-nid ] ; (RIP #3)
|
|
rad-scheme = "rad"
|
|
rad-rid = "z" 27*28(ALPHA / DIGIT)
|
|
rad-nid = "z6Mk" 44(ALPHA / DIGIT)
|
|
----
|
|
|
|
This is workable in practice, but the URL form is arguably not aligning with
|
|
the ideas behind the 'authority' as described in <<URI-Authority>>. The
|
|
'authority' carries with it the idea of a host, possibly with some user
|
|
information. In a Radicle network, it can be argued that the NID represents
|
|
that much more than the RID does, but the NID used in the old `rad:` URI
|
|
scheme does not represent that either; it represents a namespace within a
|
|
repository.
|
|
|
|
=== The New `rad:` URI Scheme: General Approach
|
|
|
|
To align with <<URI, RFC 3986>>, the `rad:` scheme is renewed to include a
|
|
possible authority, which is the particular node / host that the data should
|
|
be found on. In most cases, (much like in `file:` URIs), the authority is
|
|
expected to be empty or not there at all.
|
|
|
|
Other than that, the new URI scheme is extended with additional syntax to
|
|
meet the <<Challenges, challenges>> given above.
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tag=overview]
|
|
----
|
|
|
|
==== The Authority
|
|
|
|
The NID is conceptually ambiguous from an <<URI, RFC 3986>> syntax perspective:
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
authority = [ userinfo "@" ] host [ ":" port ]
|
|
----
|
|
|
|
This does not quite align semantically with how Radicle nodes are identified.
|
|
Radicle nodes are identified with their NID, or with `{NID}@{hostname}:{port}`.
|
|
This does align with <<URI, RFC 3986>> syntax well enough, with just a little
|
|
bit of redefinition of the authority for use in Radicle context.
|
|
|
|
****
|
|
The authority is most closely related to the address given to the Radicle CLI
|
|
command `rad node connect`.
|
|
****
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tag=authority]
|
|
----
|
|
|
|
==== The Repository
|
|
|
|
After the authority (if there is one) comes the repository to refer to.
|
|
|
|
With Radicle, the main anchoring resource is the repository, so its ID must
|
|
always be present, and specifying it alone refers to the synthesis of all
|
|
participants' contributions.
|
|
|
|
To refer to individual participants' separate contribution, their namespace
|
|
must also be included in the URI, separate from the repository ID with a slash
|
|
("/"). It shall be noted, though, that the namespace is only meaningful in
|
|
some specific circumstances, to be defined later.
|
|
|
|
****
|
|
Radicle repositories are _currently_ Git repositories. We expect that other
|
|
version control (VCS) implementations may be used as well, and in that case,
|
|
we expect the VCS implementation type to be reflected in the repository
|
|
identity.
|
|
****
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tag=resource]
|
|
----
|
|
|
|
Here are a few examples of what a reference to a whole repository could look
|
|
like:
|
|
|
|
:tc00a: pass:c,q[footnote:tc00a[nearley-test result in link:data/test/tc00a-expected.json[]]]
|
|
:tc00b: pass:c,q[footnote:tc00b[nearley-test result in link:data/test/tc00b-expected.json[]]]
|
|
:tc00c: pass:c,q[footnote:tc00c[nearley-test result in link:data/test/tc00c-expected.json[]]]
|
|
:tc01a: pass:c,q[footnote:tc01a[nearley-test result in link:data/test/tc01a-expected.json[]]]
|
|
:tc01b: pass:c,q[footnote:tc01b[nearley-test result in link:data/test/tc01b-expected.json[]]]
|
|
:tc01c: pass:c,q[footnote:tc01c[nearley-test result in link:data/test/tc01c-expected.json[]]]
|
|
:tc02: pass:c,q[footnote:tc02[nearley-test result in link:data/test/tc02-expected.json[]]]
|
|
:tc03: pass:c,q[footnote:tc03[nearley-test result in link:data/test/tc03-expected.json[]]]
|
|
:tc04: pass:c,q[footnote:tc04[nearley-test result in link:data/test/tc04-expected.json[]]]
|
|
:tc05: pass:c,q[footnote:tc05[nearley-test result in link:data/test/tc05-expected.json[]]]
|
|
|
|
[options="header",cols="2a,1"]
|
|
|===
|
|
|URI
|
|
|Refers to
|
|
|
|
|
|
|
....
|
|
include::data/test/tc00a[]
|
|
....
|
|
{tc00a}
|
|
|Specific repo (RIP #2 URN form, still valid)
|
|
|
|
|
|
|
....
|
|
include::data/test/tc00b[]
|
|
....
|
|
{tc00b}
|
|
|Specific repo (RIP #3 URL form, considered legacy)
|
|
|
|
|
|
|
....
|
|
include::data/test/tc00c[]
|
|
....
|
|
{tc00c}
|
|
|Specific repo (new URI form with empty authority)
|
|
|
|
|
|
|
....
|
|
include::data/test/tc01a[]
|
|
....
|
|
{tc01a}
|
|
|Specific repo and namespace (new URI form, relative)
|
|
|
|
|
|
|
....
|
|
include::data/test/tc01b[]
|
|
....
|
|
{tc01b}
|
|
|Specific repo and namespace (RIP #3 URL form, considered legacy)
|
|
|
|
|
|
|
....
|
|
include::data/test/tc01c[]
|
|
....
|
|
{tc01c}
|
|
|Specific repo and namespace (new URI form with empty authority)
|
|
|
|
|
|
|
....
|
|
include::data/test/tc02[]
|
|
....
|
|
{tc02}
|
|
|Specific repo, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`
|
|
|
|
|
|
|
....
|
|
include::data/test/tc03[]
|
|
....
|
|
{tc03}
|
|
|Specific repo and namespace, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`
|
|
|
|
|
|
|
....
|
|
include::data/test/tc04[]
|
|
....
|
|
{tc04}
|
|
|Specific repo, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`, available on the host `seed.example.com:8776`
|
|
|
|
|
|
|
....
|
|
include::data/test/tc05[]
|
|
....
|
|
{tc05}
|
|
|Specific repo and namespace, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`, available on the host `seed.example.com:8776`
|
|
|===
|
|
|
|
(to test these URIs against a grammar, see <<Testing>> below)
|
|
|
|
==== The Resource Type and Identity
|
|
|
|
While <<The repository,the repository>> allows you to refer to a Git repository,
|
|
it may also be desirable to refer to one or more specific resources within
|
|
that repository. To do this, <<The repository,the repository>> may be followed
|
|
by a "/", a resource type and identity.
|
|
|
|
By default, the resource type and identity is nothing, left to be added
|
|
to further on in this RIP, or in future RIPs:
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tags=resource-type-and-identity;!*]
|
|
----
|
|
|
|
==== The Query
|
|
|
|
The query part of the `rad:` URI inherits the syntax from <<URI>>.
|
|
The semantics of the query are to be defined with each resource type.
|
|
|
|
==== The Fragment
|
|
|
|
The fragment part of the URI inherits the syntax and semantics from <<URI>>.
|
|
|
|
=== Relative URIs
|
|
|
|
<<URI-Reference-Resolution>> discusses the resolution of relative (absolute
|
|
or rootless alike) URIs, based on a base URI.
|
|
|
|
This RIP has nothing specific to say about this, apart from supporting it.
|
|
|
|
== Resource Types and Identities
|
|
|
|
These are the resource types and associated identities defined in this
|
|
RIP. Future RIPs may amend them, as well as define new resource types
|
|
with associated identities.
|
|
|
|
=== Resource Types Specific to Git
|
|
|
|
There are four Git specific resource types:
|
|
|
|
* `commit`
|
|
* `tree`
|
|
* `blob`
|
|
* `tag`
|
|
|
|
In a `rad:` URI, these resource types are used to refer to Git object types
|
|
with the same name in a repository. These resource types must be followed by
|
|
an identifier that refers to a specific Git object of that type.
|
|
|
|
Some of these resource types may also be accompanied by <<The Query,query>>
|
|
parameters to further refine what the `rad:` URI refers to. This RIP defines
|
|
the following <<The Query,query>> parameters, all of which may be given
|
|
multiple times:
|
|
|
|
* `tree=dir1/dir2/…`, to "dig out" a tree given by the path.
|
|
* `blob=dir1/dir2/…`, to "dig out" a blob given by the path.
|
|
* `path=dir1/dir2/…`, to "dig out" a tree or a blob given by the path.
|
|
|
|
****
|
|
Applications are by and large allowed to define other <<The Query,query>>
|
|
parameters than what is defined here. We hope that future RIPs will help
|
|
standardise at least the most common ones.
|
|
|
|
A few possible examples that may be useful:
|
|
|
|
* `rad:{rid}/commit/{git-obj-or-ref}?history`, to view the history of a commit.
|
|
* `rad:{rid}/blob/{git-obj}?raw`, to view/download the raw contents of a blob,
|
|
where one might otherwise reasonably expected it to be wrapped in some
|
|
markup, such as HTML.
|
|
****
|
|
|
|
`commit`::
|
|
+
|
|
--
|
|
The identifier that follows this resource type may be:
|
|
|
|
* a Git object ID
|
|
* a Git reference (typically, a branch or a tag name)
|
|
|
|
In all cases, this resource type refers to a single commit object.
|
|
|
|
With this resource type, it makes sense to use the <<The Query,query>>
|
|
parameters `tree`, `blob` and `path` to refine the `rad:` URI.
|
|
--
|
|
|
|
`tree`::
|
|
+
|
|
--
|
|
The identifier that follows this type may be must be a Git object identifier.
|
|
|
|
With this resource type, it makes sense to use the <<The Query,query>>
|
|
parameters `tree`, `blob` and `path` to refine the `rad:` URI.
|
|
--
|
|
|
|
`blob`::
|
|
+
|
|
--
|
|
The identifier that follows this type must be a Git object identifier.
|
|
|
|
With this resource type, it makes no sense to use any of the <<The
|
|
Query,query>> defined above.
|
|
--
|
|
|
|
`tag`::
|
|
+
|
|
--
|
|
The identifier that follows this type may be:
|
|
|
|
* a Git object ID
|
|
* a Git reference (i.e. a tag name)
|
|
|
|
In all cases, this resource type refers to a single tag object.
|
|
|
|
With this resource type, it makes sense to use the <<The Query,query>>
|
|
parameters `tree`, `blob` and `path` to refine the `rad:` URI.
|
|
--
|
|
|
|
In <<ABNF>> terms, the Git specific resource types are defined like this:
|
|
|
|
// [source,abnf]
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tag=git-type-and-identity]
|
|
----
|
|
|
|
Here are a few examples of what a reference to a Git specific resource could
|
|
look like:
|
|
|
|
:tc10a: pass:c,q[footnote:tc10a[nearley-test result in link:data/test/tc10a-expected.json[]]]
|
|
:tc10b: pass:c,q[footnote:tc10b[nearley-test result in link:data/test/tc10b-expected.json[]]]
|
|
:tc12: pass:c,q[footnote:tc12[nearley-test result in link:data/test/tc12-expected.json[]]]
|
|
:tc13: pass:c,q[footnote:tc13[nearley-test result in link:data/test/tc13-expected.json[]]]
|
|
|
|
[options="header",cols="2a,1"]
|
|
|===
|
|
|URI
|
|
|Refers to
|
|
|
|
|
|
|
....
|
|
include::data/test/tc10a[]
|
|
....
|
|
{tc10a}
|
|
|Specific commit in repo
|
|
|
|
|
|
|
....
|
|
include::data/test/tc10b[]
|
|
....
|
|
{tc10b}
|
|
|Specific commit in repo
|
|
|
|
|
|
|
....
|
|
include::data/test/tc12[]
|
|
....
|
|
{tc12}
|
|
|Specific commit on a specific node, using the explicit name for the commit object
|
|
|
|
|
|
|
....
|
|
include::data/test/tc13[]
|
|
....
|
|
{tc13}
|
|
|Specific commit in a specific repo and namespace, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`, available on the host `seed.example.com:8776`
|
|
|===
|
|
|
|
(to test these URIs against a grammar, see <<Testing>> below)
|
|
|
|
And with queries:
|
|
|
|
:tc40a: pass:c,q[footnote:tc40a[nearley-test result in link:data/test/tc40a-expected.json[]]]
|
|
:tc40b: pass:c,q[footnote:tc40b[nearley-test result in link:data/test/tc40b-expected.json[]]]
|
|
:tc41a: pass:c,q[footnote:tc41a[nearley-test result in link:data/test/tc41a-expected.json[]]]
|
|
:tc41b: pass:c,q[footnote:tc41b[nearley-test result in link:data/test/tc41b-expected.json[]]]
|
|
:tc42a: pass:c,q[footnote:tc42a[nearley-test result in link:data/test/tc42a-expected.json[]]]
|
|
:tc42b: pass:c,q[footnote:tc42b[nearley-test result in link:data/test/tc42b-expected.json[]]]
|
|
:tc43a: pass:c,q[footnote:tc43a[nearley-test result in link:data/test/tc43a-expected.json[]]]
|
|
:tc43b: pass:c,q[footnote:tc43b[nearley-test result in link:data/test/tc43b-expected.json[]]]
|
|
:tc44: pass:c,q[footnote:tc44[nearley-test result in link:data/test/tc44-expected.json[]]]
|
|
|
|
[options="header",cols="2a,1"]
|
|
|===
|
|
|URI
|
|
|Refers to
|
|
|
|
|
|
|
....
|
|
include::data/test/tc40a[]
|
|
....
|
|
{tc40a}
|
|
|The `src` directory of the repo at tree object `3eb47e9`
|
|
|
|
|
|
|
....
|
|
include::data/test/tc40b[]
|
|
....
|
|
{tc40b}
|
|
|The `src` directory of the repo at tree object `3eb47e9`
|
|
|
|
|
|
|
....
|
|
include::data/test/tc41a[]
|
|
....
|
|
{tc41a}
|
|
|The `README.md` file of the repo at the "master" reference
|
|
|
|
|
|
|
....
|
|
include::data/test/tc41b[]
|
|
....
|
|
{tc41b}
|
|
|The `README.md` file of the repo at the "master" reference
|
|
|
|
|
|
|
....
|
|
include::data/test/tc42a[]
|
|
....
|
|
{tc42a}
|
|
|The `foo/doc` directory of the repo at the "baz" reference
|
|
|
|
|
|
|
....
|
|
include::data/test/tc42b[]
|
|
....
|
|
{tc42b}
|
|
|The `foo/doc` directory of the repo at the "baz" reference
|
|
|
|
|
|
|
....
|
|
include::data/test/tc43a[]
|
|
....
|
|
{tc43a}
|
|
|The `doc` and `src` directories of the repo at the "baz/foo" reference.
|
|
|
|
|
|
|
....
|
|
include::data/test/tc43b[]
|
|
....
|
|
{tc43b}
|
|
|The `doc` and `src` directories directory of the repo at the "baz/foo" reference.
|
|
|
|
|
|
|
....
|
|
include::data/test/tc44[]
|
|
....
|
|
{tc44}
|
|
|The commit notes for commit `e9cf5263…`.
|
|
|===
|
|
|
|
(to test these URIs against a grammar, see <<Testing>> below)
|
|
|
|
****
|
|
// Git version that is current at the time of writing.
|
|
:gitversion: 2.54.0
|
|
|
|
// Base URL of Git documentation.
|
|
:gitdocs: https://git-scm.com/docs
|
|
|
|
// Allows for convenient reference to definitions in the Git glossary.
|
|
// "gitgloss" is just short for "a definition in the Git glossary".
|
|
:gitgloss: {gitdocs}/gitglossary/{gitversion}#Documentation/gitglossary.txt-
|
|
|
|
:gitrevparse: {gitdocs}/git-rev-parse/{gitversion}#Documentation/git-rev-parse.txt-
|
|
|
|
[discrete]
|
|
==== Addressing Git Resources by Reference Names
|
|
|
|
[discrete]
|
|
===== Types of Resources That May Be Addressed
|
|
|
|
Resources of the types "commit" and "tag", may, in Git jargon, be
|
|
"committish". There are at least two definitions in the Git documentation
|
|
that differ slightly:
|
|
|
|
[quote,'{gitgloss}commit-ishalsocommittish[Git glossary entry for "committish"]']
|
|
A {gitgloss}commit_object[commit object] or an {gitgloss}object[object]
|
|
that can be recursively {gitgloss}dereference[dereferenced] to a commit
|
|
object. The following are all commit-ishes:
|
|
a commit object,
|
|
a {gitgloss}tag_object[tag object] that points to a commit object,
|
|
a tag object that points to a tag object that points to a commit object,
|
|
etc.
|
|
|
|
[quote,'{gitrevparse}--verify[Git documentation of `git rev-parse --verify`]']
|
|
a commit-ish (i.e. a commit, or an annotated tag that points at a commit)
|
|
|
|
We thus restrict ourselves to only refer to commits and tags via references,
|
|
since no tree or blob object may ever be committish.
|
|
|
|
[discrete]
|
|
===== Ambiguity in Addressing by Reference Name
|
|
|
|
Note that Git references are ambiguous, even within one repository (or reference
|
|
namespace).
|
|
|
|
It is possible to create the following set of references in one namespace:
|
|
|
|
* `refs/heads/main`
|
|
* `refs/tags/main`
|
|
* `refs/heads/refs/heads/main`
|
|
|
|
We do not define how such ambiguity is to be resolved, but note that applications
|
|
will likely want to implement some mechanism for disambiguation.
|
|
|
|
See also {gitrevparse}refnameegmasterheadsmasterrefsheadsmaster[the section
|
|
_Specifying Revisions_ in the documentation of `git rev-parse`].
|
|
|
|
****
|
|
|
|
=== Resources Types Specific to Radicle Collaborative Objects
|
|
|
|
Alongside the usual Git objects, Radicle also implements a generalized
|
|
collaborative object (usually referred to as COB), identified with a COB type
|
|
and a COB object.
|
|
|
|
For general COB references in a `rad:` URI, these resource types are defined
|
|
to refer to a Radicle COB / multiple COBs:
|
|
|
|
`cob`::
|
|
+
|
|
--
|
|
In a `rad:` URI, this must be followed a slash (`/`) and the COB type. The
|
|
COB type _may_ be followed by another slash (`/`) and the COB object.
|
|
|
|
If the `rad:` URI includes the COB object, then it refers to a single COB.
|
|
|
|
If the `rad:` URI does not include the COB object, then it refers to the set
|
|
of COB objects of the given COB type.
|
|
--
|
|
|
|
`cob` resources may also be accompanied by a set of <<The Query,query>>
|
|
parameters to further refine what the `rad:` URI refers to.
|
|
|
|
****
|
|
Because we realise that we cannot know all future COB types and how they
|
|
function, we cannot do too much in terms of definitions.
|
|
We leave it to the COB owners as well as applications to work out what query params they
|
|
will handle, and hopefully, some of them can be standardised in future RIPs.
|
|
|
|
A hypothetical example: `rad:{RID}/cob/example.radicle.sparkle?q=glitter`.
|
|
****
|
|
|
|
In <<ABNF>> terms, the COB resource types are defined like this:
|
|
|
|
// [source, abnf]
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tag=rad-cob-type-and-identity]
|
|
----
|
|
|
|
Here are a few examples of what a reference to other collaborative objects
|
|
could look like:
|
|
|
|
:tc30: pass:c,q[footnote:tc30[nearley-test result in link:data/test/tc30-expected.json[]]]
|
|
:tc31: pass:c,q[footnote:tc31[nearley-test result in link:data/test/tc31-expected.json[]]]
|
|
:tc32: pass:c,q[footnote:tc32[nearley-test result in link:data/test/tc32-expected.json[]]]
|
|
|
|
[options="header",cols="2a,1"]
|
|
|===
|
|
|URI
|
|
|Refers to
|
|
|
|
|
|
|
....
|
|
include::data/test/tc30[]
|
|
....
|
|
{tc30}
|
|
|Specific COB of the type `org.example`
|
|
|
|
|
|
|
....
|
|
include::data/test/tc31[]
|
|
....
|
|
{tc31}
|
|
|Specific COB of the type `org.example` on specific node
|
|
|
|
|
|
|
....
|
|
include::data/test/tc32[]
|
|
....
|
|
{tc32}
|
|
|The set of COBs of the type `org.example`
|
|
|===
|
|
|
|
== Implementation Requirements and Recommendations
|
|
|
|
* Implementations may implement only part of this specification, depending on
|
|
what makes sense in the context of what they do. It is advisable, however, if
|
|
they can parse enough of a valid `rad:` URI to allow for <<Future work,future work>>.
|
|
For example, it is hard to imagine that `git-remote-rad` would ever support
|
|
more elaborate URLs than those involving an RID and an NID:
|
|
|
|
** `rad:{RID}`
|
|
** `rad:///{RID}`
|
|
** `rad:{RID}/{NID}`
|
|
** `rad:///{RID}/{NID}`
|
|
** `rad://{RID}/{NID}` (legacy)
|
|
|
|
* Implementations that receive a URI they cannot handle - because of
|
|
unrecognised resource type, identifier, or query params - should signal an
|
|
error.
|
|
+
|
|
For fragments, there is no recommendation, that is left entirely to the
|
|
authors of those softwares.
|
|
|
|
* Software for human interaction on the web should be able to automatically
|
|
convert between `rad:` and `web+rad:` to create links. For example, in a
|
|
markdown context, it would be quite viable for this:
|
|
+
|
|
`rad:z3gqcJU…CSGazv5/commit/72db6d…`
|
|
+
|
|
to be converted to:
|
|
+
|
|
`[rad:z3gqcJU…CSGazv5/commit/72db6d…](web+rad:z3gqcJU…CSGazv5/commit/72db6d…)`
|
|
|
|
* Implementations that are aware of the context may support relative URIs as
|
|
they see fit. For example, a program that handles Radicle patches and issues
|
|
could assume the base URI `rad:///{RID}` (given that `{RID}` is the ID of
|
|
the repository that is being handled), allowing comments to refer to issue and
|
|
other patches in the same repository with `patch/{PATCH-ID}` or `issue/{ISSUE-ID}`.
|
|
|
|
== Future Work
|
|
|
|
Future work may include:
|
|
|
|
* amending <<The authority,the authority>> with Radicle network
|
|
specification syntax (pending future formalization of that idea).
|
|
* amending <<The authority,the authority>> with multi-device IDs (also
|
|
pending formalization of that idea).
|
|
* adding more resource types, and their respective syntaxes
|
|
* extending existing resource types.
|
|
* adding other query types
|
|
* viewing the history/graph of the underlying Git repository (in analogy to `git log`/`git log --graph`).
|
|
* viewing revision ranges of the underlying Git repository (in analogy to `git diff`, `git log`).
|
|
|
|
== Closing Thoughts
|
|
|
|
This RIP started with a status quo and challenges. It is the hope of the
|
|
authors that the challenges are sufficiently addressed, while also leaving
|
|
doors open for more extensions in the future.
|
|
|
|
== Copyright
|
|
|
|
This document is licensed under the Creative Commons CC0 1.0 Universal license.
|
|
|
|
[bibliography]
|
|
== References
|
|
|
|
// https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/#bibliography
|
|
// says that anchors like [[[ABNF]]] should render to [ANBF], but experience
|
|
// says that this is not quite true. However, [[ABNF]][ABNF] seems to render
|
|
// what I hoped for. /RL
|
|
|
|
[[ABNF]][ABNF]:: https://datatracker.ietf.org/doc/html/rfc5234[RFC 5234 - Augmented BNF for Syntax Specifications: ABNF]
|
|
[[HTML]][HTML]:: Web Hypertext Application Technology Working Group: _HTML Living Standard_ (<https://html.spec.whatwg.org>)
|
|
[[HTML-registerhandler]][HTML-registerhandler]:: Sec. 8.9.1.4, _Custom scheme handlers: the `registerProtocolHandler()` method_ (<https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers>)
|
|
[[Nearley]][Nearley]:: Kartik Chandra's Nearley grammar (<https://nearley.js.org/>)
|
|
[[radicle-explorer]][radicle-explorer]:: Radicle Web Interface - link:web+rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5[rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5] +
|
|
(<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5>)
|
|
[[radicle-jetbrains]][radicle-jetbrains]:: Radicle JetBrains plugin - link:web+rad:z3WHS4GSf8hChLjGYfPkJY7vCxsBK[rad:z3WHS4GSf8hChLjGYfPkJY7vCxsBK] +
|
|
(<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3WHS4GSf8hChLjGYfPkJY7vCxsBK>)
|
|
[[radicle-vscode]][radicle-vscode]:: Radicle VSCode extension - link:web+rad:z3Makm6fsQQXmpSFE43DZqwupaEhk[rad:z3Makm6fsQQXmpSFE43DZqwupaEhk] +
|
|
(<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3Makm6fsQQXmpSFE43DZqwupaEhk>), +
|
|
link:web+vscode:extension/radicle-ide-plugins-team.radicle[vscode:extension/radicle-ide-plugins-team.radicle] +
|
|
(<https://marketplace.visualstudio.com/items?itemName=radicle-ide-plugins-team.radicle>)
|
|
[[RIP2]][RIP2]:: @cloudhead: Identity - link:web+rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/1c402116983be19e754fb14aa7ce38145f0a4b09?path=0002-identity.md[rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/1c402116983be19e754fb14aa7ce38145f0a4b09?path=0002-identity.md] +
|
|
(<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/1c402116983be19e754fb14aa7ce38145f0a4b09/0002-identity.md>)
|
|
[[RIP3]][RIP3]:: @fintohaps: Radicle Improvement Proposal #3: Storage Layout - link:web+rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/329dee9a4b65169ea3889a7da239892b705d0d68?path=0003-storage-layout.md#url[rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/329dee9a4b65169ea3889a7da239892b705d0d68?path=0003-storage-layout.md#url] +
|
|
(<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/329dee9a4b65169ea3889a7da239892b705d0d68/0003-storage-layout.md#url>)
|
|
[[URI]][URI]:: https://datatracker.ietf.org/doc/html/rfc3986[RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax]
|
|
[[URI-Authority]][URI-Authority]:: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2[RFC 3986 - 3.2. Authority]
|
|
[[URI-Reference-Resolution]][URI-Reference-Resolution]:: https://datatracker.ietf.org/doc/html/rfc3986#section-5[RFC 3986 - 5. Reference Resolution]
|
|
[[zulip-mangle]][zulip-mangle]:: Zulip Topic: _`rad:` gets mangled by Zulip_ (#general, 2024-04-23) (<https://radicle.zulipchat.com/#narrow/stream/369274-general/topic/.60rad.3A.60.20gets.20mangled.20by.20Zulip>)
|
|
[[base58btc]][base58btc]:: https://datatracker.ietf.org/doc/html/draft-msporny-base58-02[Base58 Specification]
|
|
[[multibase]][multibase]:: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html[Multibase Specification]
|
|
[[multicodec]][multicodec]:: https://github.com/multiformats/multicodec/[Multiformats: multicodec]
|
|
|
|
[appendix]
|
|
== Full <<ABNF>> grammar
|
|
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.abnf[tag=!rfc5234]
|
|
----
|
|
|
|
[appendix]
|
|
== Full <<Nearley>> grammar
|
|
|
|
[%nowrap]
|
|
----
|
|
include::data/rad-uri.ne[]
|
|
----
|
|
|
|
[appendix]
|
|
== Testing
|
|
|
|
All the examples sprinkled in this RIP are tested against the <<Nearley>>
|
|
grammar. Test inputs and expected parse results are in `data/test/`, where
|
|
each `tc__` file contains a valid URI and the corresponding
|
|
`tc__-expected.json` contains the expected parse result as JSON.
|
|
|
|
The test suite can be run with:
|
|
|
|
[,shell]
|
|
----
|
|
nix flake check
|
|
----
|
|
|
|
This compiles the grammar with `nearleyc`, runs `nearley-test` on each test
|
|
input, and compares the JSON output against the expected result.
|
|
|
|
To test a single URI manually:
|
|
|
|
[,shell]
|
|
[%nowrap]
|
|
----
|
|
# [one time] Compile the grammar into javascript
|
|
nearleyc -o rad-uri.js data/rad-uri.ne
|
|
|
|
# Test the chosen URI string ($URI)
|
|
nearley-test -q -i "$URI" rad-uri.js
|
|
----
|
|
|
|
`nearley-test` will return all possible results for the URI, which means that
|
|
where there are syntactic ambiguities, there will be more than one result.
|
|
For testing purposes, it is enough if one of the results match the expected
|
|
results linked from the footnotes associated with the examples in the tables
|
|
above.
|