Radish alpha
r
Radicle Improvement Proposals (RIPs)
Radicle
Git (anonymous pull)
Log in to clone via SSH
R
Richard Levitte
RIP 4: URI Scheme 16 days ago 43301b47e5643de823fc14c51e4826dde54db3fb History
rips 0004-general-uri-scheme data rad-uri.abnf
; This grammar is written using ABNF, as defined in RFC 5234
; (https://datatracker.ietf.org/doc/html/rfc5234), from which ALPHA, DIGIT
; and HEXDIG are borrowed
;
; Furthermore, this grammar is based on and specialises the generic URI
; specification (https://datatracker.ietf.org/doc/html/rfc3986) for Radicle,
; from which 'query', 'fragment', 'host', 'port', 'unreserved', 'pchar', ...
; are borrowed

; tag::overview[]
; RFC 3986 defines the syntax for 'query' and 'fragment'
rad                     = rad-scheme ":" rad-auth-and-resource rad-resource-type-and-identity [ "?" query ] [ "#" fragment ]
                        / rad-scheme ":" rad-legacy

rad-scheme              = "rad"

; RFC 3986 allows an absolute resource path without authority, i.e. a URI
; that looks like this: rad:/{RID}/{NID}
; We don't include that sort of URI in this scheme, because it's, strictly
; speaking, unnecessary in this context, and may seem confusing.
rad-auth-and-resource   = "//" [ rad-auth ] "/" rad-resource    ; abs. resource "path" with authority
                        / rad-resource                          ; rel. resource "path"

; rad-legacy are URLs defined by RIP #3.  That URL doesn't quite hold up
; against the general URI syntax defined in RFC3986, in so far that a node
; (identified with NID or NID@host) is the conceptual authority, not
; a repository.
; To be noted is that RIDs and NIDs are distinct enough that there should not
; be any ambiguity between rad-auth-and-resource and rad-legacy.
; Of all current implementations, this mostly impacts the git remote helper
; git-remote-rad.
rad-legacy              = "//" rad-resource
; end::overview[]

; The NID is conceptually ambiguous from an RFC 3986 syntax perspective:
;
;       authority   = [ userinfo "@" ] host [ ":" port ]
;
; Is the NID 'userinfo' or 'host'?  In a sense, it currently is a little bit
; of both.  Furthermore, Radicle requires that if a hostname (+ port) is
; given, the NID must be given too.  Therefore, instead of using 'authority'
; strictly as given by RFC 3986, we allow ourselves a slightly different
; syntax, which is fine.  Do note also, that a 'host' can still be made to
; look like an NID, so programs dealing directly with the Radicle network
; will have to be prepared to handle that ambiguity appropriately.
; tag::authority[]
;
; TODO: There already are ways to resolve its NID from a hostname.
; For example, if `radicle-httpd` runs next to the node, then it can be
; queried for its NID via HTTPS. Also, there are ways to use DNS TXT records,
; which is also done for DNS-SD.
; We would therefore likely make the `rad-node` optional, or even omit it.
; If `rad-node` is sufficiently different from a DNS name (it probably is
; not), then we can even omit "@".
rad-auth                = rad-node [ "@" rad-host-and-port ]

; TODO: Port might be optional in the future, once we have convinced IANA
; to assign one to us. There currently is no default port.
rad-host-and-port       = host ":" port

; rad-node is a semantic symbol, to signify intent
rad-node                = rad-nid

; An NID is an ID that is ultimately a public key in the Radicle network.
; This is used to uniquely represent a node, or a user (also a node), or a
; namespace within a repository.
; It is a [multibase] and [multicodec] encoded string, using [base58btc] as the multibase
; and the codec for the underlying Ed25519 public key.
;
; It is always 48 characters long, including the initial "z6Mk",
; the `z` corresponding to the base, and `6Mk` corresponding to the codec.
;
; [base58btc]: https://digitalbazaar.github.io/base58-spec/
; [multibase]: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html
; [multicodec]: https://github.com/multiformats/multicodec/
;
; TODO: New NIDs in the future? Even this might change, as we allow rotating
; keys for nodes. But this is even further out than stable IDs for users.
rad-nid                 = "z6Mk" 44( base58btc )
; end::authority[]

; tag::resource[]
rad-resource            = rad-repository [ "/" rad-namespace ]

; rad-repository is a semantic symbol, to signify intent
rad-repository          = rad-rid

; rad-namespace is a semantic symbol, to signify intent
rad-namespace           = rad-nid

; An RID uniquely represents a git repository in the Radicle network.
; It is a [multibase] encoded string of a Git OID, using [base58btc] as the multibase.
;
; It is 28 or 29 characters long, including the initial multibase identifier "z".
;
; [base58btc]: https://digitalbazaar.github.io/base58-spec/
; [multibase]: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html
;
; TODO: In the near future (as soon as Git repositories with SHA-256 object
; format become popular, which will be in late 2026), we will have to extend
; the syntax of RIDs. Leave a note here so that implementors know this and
; make design decisions accordingly.
rad-rid                 = "z" 27*28( base58btc )
; end::resource[]

; tag::resource-type-and-identity[]
; 'rad-resource-type-and-identity' is empty by default, and more resource
; types and identities will be added further on, using the ABNF =/ operator,
; see RFC 5234 "3.3. Incremental Alternatives: Rule1 =/ Rule2"
rad-resource-type-and-identity = ""

; tag::git-type-and-identity[]
; For git specific resource types
rad-resource-type-and-identity =/ git-commit / git-tree / git-blob / git-tag

git-commit              = "/" "commit" "/" git-obj-or-ref
git-tree                = "/" "tree" "/" git-obj
git-blob                = "/" "blob" "/" git-obj
git-tag                 = "/" "tag" "/" git-obj-or-ref

; Note: this rule will always be ambiguous, as something that looks like a
; git object ID could very well be a branch or a tag.
; Some git commands will warn when such refs are used, but not all.
git-obj-or-ref          = git-obj / git-ref
; Currently, only sha1 identities are supported.
git-obj                 = git-sha1
; HEXDIG is defined in RFC 5234
git-sha1                = 40HEXDIG

; unreserved is defined in RFC 3986.  Not that this syntax allows much
; more than what can be specified for a git reference.  It's left to
; the implementation to parse and apply semantics properly.
; Our best reference for how to interpret and parse git references is
; https://git-scm.com/docs/protocol-common.html
git-ref                 = 1*unreserved *( "/" 1*unreserved )
; end::git-type-and-identity[]

; tag::rad-cob-type-and-identity[]
; For Radicle collaborative object resource types
rad-resource-type-and-identity =/ rad-cob / rad-cobs

rad-cob                 = "/" "cob" "/" rad-cob-type "/" rad-cob-obj
rad-cobs                = "/" "cob" "/" rad-cob-type

; The COB type is like a reversed FQDN.  The syntax spec is derived from
; https://datatracker.ietf.org/doc/html/rfc1034#section-3.5
rad-cob-type            = rad-cob-label 1*( "." rad-cob-label )
rad-cob-label           = 1*( ALPHA / DIGIT ) *( "-" 1*( ALPHA / DIGIT ) )

; syntactically, radicle COB objects and object revisions are git objects
; semantically, they are as well, at least for now
rad-cob-obj             = git-obj
; end::rad-cob-type-and-identity[]
; end::resource-type-and-identity[]

; tag::rfc5234[]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Copied from RFC 5234

ALPHA          =  %x41-5A / %x61-7A     ; A-Z / a-z
DIGIT          =  %x30-39               ; 0-9
HEXDIG         =  DIGIT / "A" / "B" / "C" / "D" / "E" / "F"

; Copied from RFC 3986
host          = IP-literal / IPv4address / reg-name
port          = *DIGIT

IP-literal    = "[" ( IPv6address / IPvFuture  ) "]"

IPvFuture     = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )

IPv6address   =                            6( h16 ":" ) ls32
              /                       "::" 5( h16 ":" ) ls32
              / [               h16 ] "::" 4( h16 ":" ) ls32
              / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
              / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
              / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
              / [ *4( h16 ":" ) h16 ] "::"              ls32
              / [ *5( h16 ":" ) h16 ] "::"              h16
              / [ *6( h16 ":" ) h16 ] "::"

h16           = 1*4HEXDIG
ls32          = ( h16 ":" h16 ) / IPv4address
IPv4address   = dec-octet "." dec-octet "." dec-octet "." dec-octet
dec-octet     = DIGIT                 ; 0-9
              / %x31-39 DIGIT         ; 10-99
              / "1" 2DIGIT            ; 100-199
              / "2" %x30-34 DIGIT     ; 200-249
              / "25" %x30-35          ; 250-255

reg-name      = *( unreserved / pct-encoded / sub-delims )

pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"

query         = *( pchar / "/" / "?" )

fragment      = *( pchar / "/" / "?" )

pct-encoded   = "%" HEXDIG HEXDIG

unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
              / "*" / "+" / "," / ";" / "="
; end::rfc5234[]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Base58 Bitcoin Alphabet
base58btc = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"