| + |
---
|
| + |
RIP: 5
|
| + |
Title: Agent Repository
|
| + |
Author: '@fintohaps <fintan.halpenny@gmail.com>, @lorenz <lorenz@leutgeb.xyz>'
|
| + |
Status: Draft
|
| + |
Created: 2024-10-23
|
| + |
License: CC0-1.0
|
| + |
---
|
| + |
|
| + |
RIP #5: Agent Repository
|
| + |
========================
|
| + |
This RIP introduces the concept of an agent repository, a specialized kind of
|
| + |
repository, into the Heartwood protocol. The aim is to define a repository that
|
| + |
can represent agents in the network, holding any required metadata that helps
|
| + |
define this agent.
|
| + |
|
| + |
An *agent* is any entity that we want to identify uniquely, interacting
|
| + |
with others on the Radicle network. Human users are considered agents, but we
|
| + |
also consider computers as agents, e.g. CI/CD systems, security bots, etc.
|
| + |
|
| + |
Overview
|
| + |
--------
|
| + |
In the main section, [Agent Repository](#agent-repository), we define what agent
|
| + |
repositories are, comparing and contrasting them to the existing project
|
| + |
repositories. In particular:
|
| + |
1. How [Repository Identity](#repository-identity) differs from project
|
| + |
repositories, introducing the new `xyz.radicle.agent` payload.
|
| + |
2. Which special [References](#references) agent repositories contain, how they
|
| + |
relate to DIDs and the Heartwood protocol.
|
| + |
3. How agent repositories interact with
|
| + |
[Signing and Verification](#signing-and-verification).
|
| + |
|
| + |
In [Referencing Agent Repositories](#referencing-agent-repositories) we discuss
|
| + |
methods of cross-referencing agent repositories from other repositories. Two
|
| + |
alternative method are suggested: [Git References](#git-references) and [Gossip
|
| + |
Protocol](#gossip-protocol).
|
| + |
|
| + |
Finally, we hint at [Future Work](#future-work). In particular how agent
|
| + |
repositories could be extended to support KERI and how they will enable support
|
| + |
for associating multiple devices with one agent.
|
| + |
|
| + |
Table of Contents
|
| + |
-----------------
|
| + |
1. [Overview](#overview)
|
| + |
2. [Motivation](#motivation)
|
| + |
3. [Agent Repository](#agent-repository)
|
| + |
- [Repository Identity](#repository-identity)
|
| + |
- [Agent Payload](#agent-payload)
|
| + |
- [Controller](#controller)
|
| + |
- [Profile Information](#profile-information)
|
| + |
- [Lifecycle](#lifecycle)
|
| + |
- [Create](#create)
|
| + |
- [Update](#update)
|
| + |
- [Delete](#delete)
|
| + |
- [Verification](#verification)
|
| + |
- [Historical Verification](#historical-verification)
|
| + |
- [References](#references)
|
| + |
- [DID Namespace](#did-namespace)
|
| + |
- [DID Methods](#did-methods)
|
| + |
- [DID Document](#did-document)
|
| + |
- [Example: `did:key`](#example-didkey)
|
| + |
- [Example: `did:keri`](#example-didkeri)
|
| + |
- [Radicle Namespace](#radicle-namespace)
|
| + |
- [Example: Multi-Device](#example-multi-device)
|
| + |
- [Signing and Verification](#signing-and-verification)
|
| + |
4. [Referencing Agent Repositories](#referencing-agent-repositories)
|
| + |
- [Git References](#git-references)
|
| + |
- [Lifecycle](#lifecycle)
|
| + |
- [Create](#create-1)
|
| + |
- [Read](#read)
|
| + |
- [Update](#update-1)
|
| + |
- [Delete](#delete-1)
|
| + |
- [Gossip Protocol](#gossip-protocol)
|
| + |
- [Lifecycle](#lifecycle-1)
|
| + |
- [Create](#create-2)
|
| + |
- [Read](#read-1)
|
| + |
- [Update](#update-2)
|
| + |
- [Delete](#delete-2)
|
| + |
5. [Future Work](#future-work)
|
| + |
- [did:key Agent Repository](#didkey-agent-repository)
|
| + |
- [KERI Agent Repository](#keri-agent-repository)
|
| + |
- [Multi-Device](#multi-device)
|
| + |
6. [Appendix](#appendix)
|
| + |
- [JSON Schema](#json-schema)
|
| + |
7. [Copyright](#copyright)
|
| + |
8. [References](#references)
|
| + |
|
| + |
Motivation
|
| + |
----------
|
| + |
The motivation for this RIP is to define the foundational element of the agent
|
| + |
repository, to enable us to further work on more refined approaches to user
|
| + |
identity and multi-device support – this RIP aims to set the stage for these
|
| + |
future RIPs.
|
| + |
|
| + |
The initial approach was exploring how to approach multi-device support, and
|
| + |
after some thought and discussions, we decided that it would be best to define
|
| + |
multiple RIPs in order to achieve our end goals.
|
| + |
|
| + |
The agent repository came from the idea that we have a generic component that
|
| + |
only needs minimal definition, specified here, and leaves room for extension
|
| + |
points for later RIPs – including KERI and multi-device support.
|
| + |
|
| + |
Furthermore, the agent repository as defined here may be used without
|
| + |
these new, future components, i.e. it integrates with our
|
| + |
existing `did:key` mechanisms.
|
| + |
|
| + |
Agent Repository
|
| + |
----------------
|
| + |
An Agent Repository is a Radicle repository as specificied in RIP-2[^rip-2].
|
| + |
This implies that it is stored the same way as per RIP-3[^storage-layout], and
|
| + |
contains an identity document. The identity document is extended with a new
|
| + |
payload (see [Agent Payload](#agent-payload)).
|
| + |
|
| + |
The purpose of an agent repository is to store and replicate the relationship
|
| + |
between the agent and a DID[^did] it is using to identify itself, along with any
|
| + |
metadata that is useful to the protocol and tooling built on top of Heartwood.
|
| + |
|
| + |
In this RIP, we establish a new kind of repository in the Heartwood protocol,
|
| + |
which specifically aims to represent agents within the Heartwood protocol. An
|
| + |
agent can be any entity that we want to identify uniquely, and which takes
|
| + |
actions within the protocol. The most common use case of an agent repository
|
| + |
will be for the concept of a user or user persona, e.g. I can identify myself as
|
| + |
`alice` for this project, but I may want to establish and use a different
|
| + |
persona – by creating a separate agent repository – for some other projects.
|
| + |
Other use cases we may want to consider are CI/CD agents that will have
|
| + |
permission to merge patches into projects. We leave the potential up to your
|
| + |
imagination, and only define the necessary foundations here.
|
| + |
|
| + |
At a high level, the relation between DIDs of agents to RIDs of agent repository
|
| + |
*at a given point in time* is a partial function. By changing the DID associated
|
| + |
with an agent (discussed below), the function may change over time. However, it
|
| + |
is:
|
| + |
* *surjective*: When creating an agent repository, it must be created *for a
|
| + |
particular DID*. While it is possible to change the associated DID to a
|
| + |
different one, i.e. the RID mapped to by a DID may change over time, it is
|
| + |
not possible to completely dissociate an agent repository from a DID.
|
| + |
* *injective*: A DID is in relation with *at most one* RID (note partiality).
|
| + |
Creating two or more agent repositories for the same DID is not supported.
|
| + |
|
| + |
Note that there's no hope for totality here: for backwards-compatibility, we
|
| + |
cannot require DIDs with the DID method `did:key` to map to an RID. Also,
|
| + |
Radicle will likely only support a very small number of DID methods.
|
| + |
|
| + |
In [Referencing Agent Repositories](#referencing-agent-repositories), a refined
|
| + |
version of this relation is established, clarifying changes over time.
|
| + |
|
| + |
### Repository Identity
|
| + |
As mentioned, the agent repository is still a Heartwood repository, which means
|
| + |
it has an RID and an identity document. In its current state (as of
|
| + |
RIP-4[^canonical-refs] Canonical References), the identity document requires a
|
| + |
`delegates` field and a set of values. Previously, this would be expected to be
|
| + |
filled with `did:key` DIDs. However, with the advent of the agent repository –
|
| + |
and attempting to support multiple DID methods – the underlying DID method can
|
| + |
and should be used in this field. It is recommended to set `delegates` to a
|
| + |
singleton, containing only the DID of the agent, but it is not forbidden to add
|
| + |
other DIDs to the set.
|
| + |
|
| + |
Note that it is not sufficient to add DIDs to the set of delegates in order to
|
| + |
link them to the agent repository – we discuss the association of the DID to the
|
| + |
agent repository in the [Controller](#controller) section.
|
| + |
|
| + |
#### Agent Payload
|
| + |
Similarly to the `xyz.radicle.project` payload for defining metadata about the
|
| + |
project, we define an `agent` payload, under `xyz.radicle.agent`, which can
|
| + |
hold metadata about the agent. We define this payload as follows (with the [JSON
|
| + |
Schema](#json-schema) provided):
|
| + |
|
| + |
```json
|
| + |
{
|
| + |
"xyz.radicle.agent": {
|
| + |
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
| + |
"alias": "alice"
|
| + |
}
|
| + |
}
|
| + |
```
|
| + |
|
| + |
The fields and their values are described as follows:
|
| + |
|
| + |
- The `controller` field specifies the current, controlling DID of the agent
|
| + |
repository, explained further in the [Controller](#controller) section below.
|
| + |
- The `alias` field is a name or nickname for the agent, represented by a
|
| + |
string, which can be no longer than 32 characters. We also restrict the string
|
| + |
to reject any whitespace or control characters – a regular expression is
|
| + |
provided in [JSON Schema](#json-schema).
|
| + |
|
| + |
##### Controller
|
| + |
The controller DID, i.e. the contents of the `controller` field, is the DID of
|
| + |
the agent controlling the repository. By ensuring it holds exactly one DID, we
|
| + |
achieve injection of the (time-dependent) partial function from DIDs to RIDs of
|
| + |
agent repositories.
|
| + |
|
| + |
The `controller` for the repository may be updated to a new DID by updating the
|
| + |
payload – given the update adheres to the verification of the repository
|
| + |
identity. We mandate that when the `controller` is updated, the `delegates` of
|
| + |
the repository are also updated to include the new `controller` – either by
|
| + |
adding the new DID to the set or replacing the old DID with the new one.
|
| + |
|
| + |
As well as this, the DID of the `controller` must be defined in the agent
|
| + |
repository as a [DID Namespace](#did-namespace).
|
| + |
|
| + |
##### Profile Information
|
| + |
As this RIP is concerned with the foundational definition of the agent
|
| + |
repository, it avoids discussing any social metadata as part of the
|
| + |
specification. Instead, we encourage the specification of another RIP to define
|
| + |
something like a `xyz.radicle.profile` payload that can be used across different
|
| + |
user facing tools. This could include metadata such as avatars and website URLs
|
| + |
to other social sites. We also note that the DID specification also allows for
|
| + |
this kind of metadata using the [`alsoKnownAs`][^also-known-as] property of the
|
| + |
DID document.
|
| + |
|
| + |
#### Lifecycle
|
| + |
In this section we briefly specify the lifecycle of an agent repository, in
|
| + |
particular its association with a DID.
|
| + |
|
| + |
##### Create
|
| + |
Creation of an agent repository requires the same steps as creation of a project
|
| + |
repository[^identity-storage]. In addition, the identity document must contain
|
| + |
the controlling DID under both the `delegates` field and the `controller` field,
|
| + |
within the agent payload. The agent payload also defines an `alias` for this
|
| + |
agent.
|
| + |
|
| + |
##### Update
|
| + |
Updating the repository is also subject to the requirements for updating a
|
| + |
Heartwood repository identity, such that it passes verification. As mentioned in
|
| + |
[Controller](#controller), we also require that when changing the DID of the
|
| + |
`controller` field, it must also be inserted into the `delegates` field of the
|
| + |
repository identity – whether it replaces the old DID or is added alongside the
|
| + |
old DID.
|
| + |
|
| + |
##### Delete
|
| + |
As of writing, there is no mechanism to delete repositories in Radicle.
|
| + |
Therefore we do not specify any deletion mechanism. When such a mechanism is
|
| + |
introduced in the future, we expect this deletion mechanism to apply to agent
|
| + |
repositories too. The deletion of a an agent repository should cause removal
|
| + |
of references to the agent repository, see corresponding sections within
|
| + |
[Referencing Agent Repositories](#referencing-agent-repositories).
|
| + |
|
| + |
#### Verification
|
| + |
The verification of an agent repository follows the verification of any
|
| + |
Heartwood repository[^identity-verification]. Furthermore, it must also verify
|
| + |
that:
|
| + |
* the DID is the controller of the agent repository, i.e. the repository
|
| + |
contains an [agent payload](#agent-payload), and the contents of the
|
| + |
`controller` field of the payload match the given DID.
|
| + |
* the DID is an element of the set of `delegates`.
|
| + |
|
| + |
#### Historical Verification
|
| + |
To verify that a given DID was the controller of a given agent repository
|
| + |
for a given commit, ensure that the commit is an ancestor of `rad/id`, and
|
| + |
carry out the verification procedure above for the repository identity *at
|
| + |
that revision*.
|
| + |
|
| + |
If the commit is omitted, then the verifier must assume the latest commit of the
|
| + |
`rad/id`. This is necessary to avoid any attacks from compromised DIDs.
|
| + |
|
| + |
### References
|
| + |
We have established that an agent repository is similar to that of a project
|
| + |
repository. In the upcoming sections, we will discuss the reference layout
|
| + |
required for an agent repository and what is expected to exist within its
|
| + |
namespaces.
|
| + |
|
| + |
The existing layout[^layout-section] will still apply, copied below:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid> # The "physical" Git repository
|
| + |
└─ refs
|
| + |
└─ namespaces # All forks are stored under this namespace
|
| + |
├─ <nid> # One peer's fork is stored here
|
| + |
│ └─ refs
|
| + |
├─ <nid> # Another peer's fork is stored here
|
| + |
│ └─ refs
|
| + |
└─ <nid> # Etc.
|
| + |
└─ refs
|
| + |
|
| + |
We introduce a new reference namespace `did`, and extend the `rad` namespace, in
|
| + |
[DID Namespace](#did-namespace) and [Radicle Namespace](#radicle-namespace). It
|
| + |
is expected that canonical reference rules are defined to allow the promotion of
|
| + |
these namespaces to the top-level `refs`.
|
| + |
|
| + |
#### DID Namespace
|
| + |
According to the specification of Decentralized Identifiers (DIDs)[^did], a DID
|
| + |
is separated into three parts, separated by colons:
|
| + |
`did:<method-name>:<method-specific-id>`.
|
| + |
|
| + |
The namespace we introduce here resembles this structure:
|
| + |
`refs/did/<method-name>/<method-specific-id>`. It is specific to a particular
|
| + |
DID method[^did] and its required metadata. The `<method-name>` defines the
|
| + |
placeholder name for the kind of DID method that this agent is using and the
|
| + |
`<method-specific-id>` refers to the the DID method's specific ID. The contents
|
| + |
of this namespace are discussed in the next section.
|
| + |
|
| + |
Below is an example of what the storage layout looks like given the new DID
|
| + |
namespace[^git-ref-format]:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid> # The "physical" Git repository
|
| + |
└─ refs
|
| + |
├─ rad
|
| + |
│ └─ id # Canonical identity reference
|
| + |
├─ namespaces # All forks are stored under this namespace
|
| + |
│ └─ <nid> # Another peer's fork is stored here
|
| + |
│ └─ refs
|
| + |
│ └─ did # All DID relevant material
|
| + |
│ └─ <method-name> # The DID method name
|
| + |
│ └─ <method-specific-id> # The unique identifier for this DID method
|
| + |
│ └─ ... # Any references required for the DID method
|
| + |
└─ did # The canonical reference namespace for the DID
|
| + |
└─ <method-name>
|
| + |
└─ <method-specific-id>
|
| + |
└─ ...
|
| + |
|
| + |
##### DID Methods
|
| + |
The agent repository is not prescriptive about the references inside the `did`
|
| + |
namespace. The namespace is simply reserved so that methods can effectively use
|
| + |
them for storing metadata and helping to resolve to a DID document, or providing
|
| + |
other data for cryptographic operations.
|
| + |
|
| + |
When a `method` for an agent repository is proposed it must define the
|
| + |
references that are stored under this namespace, and what the contents of those
|
| + |
references are, i.e. Git tags, commits, trees, and blobs.
|
| + |
|
| + |
###### DID Document
|
| + |
Since the DID specification specifies that methods are associated with DID
|
| + |
documents[^did-architecture], it is recommended that a well-known reference is
|
| + |
defined to store the document. This reference is recommended to be named
|
| + |
`refs/did/<method-name>/<method-specific-id>/document`.
|
| + |
|
| + |
###### Example: `did:key`
|
| + |
The simplest example of a DID method as an agent repository is `did:key`.
|
| + |
DIDs take the shape `did:key:<mb-value>`[^did-key].
|
| + |
The reference scheme could be proposed as:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid>
|
| + |
└─ refs
|
| + |
└─ did
|
| + |
└─ key
|
| + |
└─ <mb-value>
|
| + |
|
| + |
The `<mb-value>` is the latter portion of `did:key:<mb-value>`, and this is
|
| + |
already enough information for obtaining the corresponding public key.
|
| + |
Optionally the DID document could be pointed to by that reference, otherwise it
|
| + |
could be an empty Git commit.
|
| + |
|
| + |
###### Example: `did:keri`
|
| + |
To give a taste for a more complex DID method, an agent repository could be
|
| + |
instantiated as a `did:keri` repository. The reference scheme may be proposed
|
| + |
as:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid>
|
| + |
└─ refs
|
| + |
└─ did
|
| + |
└─ keri
|
| + |
└─ EXq5YqaL6L48pf0fu7IUhL0JRaU2_RxFP0AL43wYn148
|
| + |
└─ kel
|
| + |
|
| + |
The `kel` is the KERI Event Log (KEL), which can be used to check the state of
|
| + |
the KERI entity.
|
| + |
|
| + |
The signing and verification of data is completed via the specified approach in
|
| + |
the KERI spec[^keri-spec], via the KEL.
|
| + |
|
| + |
#### Radicle Namespace
|
| + |
A second Git namespace is also reserved for any Heartwood specific extensions to
|
| + |
the agent repository. This namespace is reserved under the existing `rad`
|
| + |
hierarchy as `rad/<module>`, and looks like the following:
|
| + |
|
| + |
Below is an example of what the storage layout looks like given the new
|
| + |
`rad/<module>` namespace:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid> # The "physical" Git repository
|
| + |
└─ refs
|
| + |
├─ rad
|
| + |
│ ├─ id # Canonical identity reference
|
| + |
│ └─ <module> # Canonical module namespace
|
| + |
│ └─ ...
|
| + |
└─ namespaces # All forks are stored under this namespace
|
| + |
└─ <nid> # Another peer's fork is stored here
|
| + |
└─ refs
|
| + |
└─ rad
|
| + |
└─ <module> # The name of the Heartwood protocol module
|
| + |
└─ ... # Any references required for the method
|
| + |
|
| + |
This RIP does not propose any material to go under this namespace – it merely
|
| + |
sets it up for further use. We will propose an example, in the next section,
|
| + |
which will be further developed in a future RIP.
|
| + |
|
| + |
###### Example: Multi-Device
|
| + |
We plan to use the `rad/multidevice` hierarchy to propose a multi-device
|
| + |
approach. That is, we plan to use references to associate nodes to an agent so
|
| + |
that when an agent works on different devices, their device keys can be unified
|
| + |
under their agent identity. The current thoughts on the reference hierarchy is
|
| + |
to have something like the following, but is subject to change:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid>
|
| + |
└─ refs
|
| + |
├─ rad
|
| + |
│ ├─ id
|
| + |
│ └─ multidevice
|
| + |
│ ├─ nodes # All nodes currently associated with the agent
|
| + |
│ │ ├─ <nid> # A node associated with the agent
|
| + |
│ │ └─ <nid> # A second node
|
| + |
│ └─ revoked # Revoked nodes
|
| + |
│ └─ <nid> # A revoked node
|
| + |
└─ namespaces # All forks are stored under this namespace
|
| + |
└─ <nid> # Another peer's fork is stored here
|
| + |
└─ refs
|
| + |
└─ rad # All DID relevant material
|
| + |
└─ multidevice # The method name for categorisation
|
| + |
├─ nodes # All nodes currently associated with the agent
|
| + |
│ ├─ <nid> # A node associated with the agent
|
| + |
│ └─ <nid> # A second node
|
| + |
└─ revoked # Revoked nodes
|
| + |
└─ <nid> # A revoked node
|
| + |
|
| + |
### Signing and Verification
|
| + |
It is required that the DID method defines the way in which it signs data
|
| + |
payloads and verifies signatures. For example, if the method resolves to a DID
|
| + |
document, and that document contains verification methods, then one of these can
|
| + |
be used to perform verification.
|
| + |
|
| + |
This means that the signing and verification are a black box, when it comes to
|
| + |
the protocol using an agent repository for signing and verification. The
|
| + |
standard API for this signing and verification is inspired by the
|
| + |
`signature`[^signature-crate] crate, from the Rust ecosystem. The crate provides
|
| + |
two traits, `Signer`[^signature-signer] and `Verifier`[^signature-verifier]. The
|
| + |
former defines a method `try_sign`, which attempts to sign the bytes provided,
|
| + |
noting that it can fail. The latter defines a method `verify` which attempts to
|
| + |
verify the bytes and signature provided, and only returning `Ok(())` if it
|
| + |
succeeded. It is important to note that the signature here is kept generic. DID
|
| + |
methods will differ in their signature outputs, and in fact, they may have
|
| + |
complex signature schemes, for example, multi-signature schemes.
|
| + |
|
| + |
We want to avoid arbitrarily executing signing and verification operations of
|
| + |
DID methods within clients of the protocol. This means that new DID methods for
|
| + |
agent repositories must be first proposed as their own RIP, so that the signing
|
| + |
and verification methods can be audited, and implemented as part of the
|
| + |
protocol.
|
| + |
|
| + |
The Heartwood protocol already makes heavy use of `ssh-agent`, so new DID
|
| + |
methods may easily use this approach when it comes to signing data.
|
| + |
|
| + |
Referencing Agent Repositories
|
| + |
------------------------------
|
| + |
Agent repositories will become an important part of the Heartwood protocol.
|
| + |
For one, it will be a way to unify the view of a single user across multiple
|
| + |
devices. This means that it will be important to be able to look up an agent
|
| + |
repository from other repositories. In this section we will present a
|
| + |
standardized way of achieving this, which repository implementations must use.
|
| + |
|
| + |
In the following sections we will explore two approaches to being able to
|
| + |
reference agent repositories from other repositories, discussing the pros and
|
| + |
cons of each. However, we decided that [Gossip Protocol](#gossip-protocol) is
|
| + |
the approach we will take, and leave the other options for posterity.
|
| + |
|
| + |
The high level relation between DIDs and RIDs mentioned in
|
| + |
[Agent Repository](#agent-repository) is not precise with regards to changes
|
| + |
over time. However, we have mentioned in
|
| + |
[Historical Verification](#historical-verification) how to verify the relation
|
| + |
between DID and RID across time. In order to capture change over time, when
|
| + |
referencing agent repositories, we not only include the RID, but also the hash
|
| + |
of a commit in the agent repository – where the commit is either the tip of the
|
| + |
`rad/id` reference, or is an ancestor of this tip.
|
| + |
|
| + |
### Git References
|
| + |
A first approach to this problem is by further utilizing the underlying Git
|
| + |
storage of the Radicle storage[^storage-layout]. When a node is using an agent
|
| + |
to perform an action, e.g. creating a new COB, it will create an entry in its
|
| + |
namespace hierarchy that records the DID, RID of the agent repository, and the
|
| + |
commit of the `rad/id` reference at the given time.
|
| + |
|
| + |
The name of the reference will follow the pattern
|
| + |
`did-<method-name>-<method-specific-id>`, e.g.
|
| + |
`did-keri-EXq5YqaL6L48pf0fu7IUhL0JRaU2_RxFP0AL43wYn148`,
|
| + |
`did-key-z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM`,
|
| + |
`did-dns-danubetech.com`, etc.
|
| + |
|
| + |
This means that the reference layout in a repository that is referencing the
|
| + |
agent looks like the following:
|
| + |
|
| + |
<storage>
|
| + |
└─ <rid> # The "physical" Git repository
|
| + |
└─ refs
|
| + |
└─ namespaces # All forks are stored under this namespace
|
| + |
├─ <nid> # One peer's fork is stored here
|
| + |
│ └─ refs
|
| + |
│ ├─ did-key-<suffix> # A did:key repository agent
|
| + |
│ └─ ...
|
| + |
└─ <nid> # Another peer's fork is stored here
|
| + |
└─ refs
|
| + |
├─ did-key-<suffix>
|
| + |
└─ ...
|
| + |
|
| + |
The reference points to a Git commit that contains a Git tree, and that tree
|
| + |
points to a single Git blob, `rid`. The `rid` blob contains the RID in
|
| + |
plain-text, as well as the commit of the `rad/id` reference, e.g.:
|
| + |
|
| + |
rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji 0ca42d376bd566631083c8913cf86bec722da392
|
| + |
|
| + |
#### Lifecycle
|
| + |
|
| + |
##### Create
|
| + |
When someone wants to advertise their DID in another repository, for example
|
| + |
when creating a patch for a project, a reference is created in the repository:
|
| + |
|
| + |
refs/namespaces/<nid>/refs/did-<method-name>-<method-specific-id>
|
| + |
|
| + |
This reference must point to a commit, which is initialized with a single tree
|
| + |
that points to a single blob, named `rid`. The contents of which are defined
|
| + |
above.
|
| + |
|
| + |
Note that this reference should not be updated to point to a new RID. This would
|
| + |
invalidate the fact that a DID and RID have a one-to-one relationship.
|
| + |
|
| + |
When another node wants to verify the DID, it will perform the lookup of this
|
| + |
reference, resolve the RID to the repository, and perform the
|
| + |
[Verification](#verification) of the DID in the agent repository. If the
|
| + |
verification fails, it may decide to not create the reference instead.
|
| + |
|
| + |
Note that multiple entries for the DID reference may be created under different
|
| + |
node namespaces, i.e. `refs/namespaces/<nid>`. It is expected that these should
|
| + |
all point to the same RID.
|
| + |
|
| + |
##### Read
|
| + |
To read a mapping from DID `did:example:id` to RID ("lookup"), iterate over all
|
| + |
references of the form `refs/namespaces/<nid>/refs/did-example-id`. From these
|
| + |
references extract the respective RIDs. Validate all RIDs according to the
|
| + |
conditions mentioned in [Validation](#validation) below, removing all RIDs that
|
| + |
fail validation. If there are multiple candidate RIDs left, the program should
|
| + |
fail and report the discrepancy.
|
| + |
|
| + |
Note that implementations may choose to penalize nodes that have announced
|
| + |
mappings that failed validation.
|
| + |
|
| + |
##### Update
|
| + |
Updating a DID for a given agent repository means that when the user wants
|
| + |
advertise the new DID in another repository, they create a new
|
| + |
`did-<method-name>-<method-specific-id>` reference that points to the same RID
|
| + |
as the previous DID, but with the updated commit of the `rad/id` – which must
|
| + |
contain the new controller.
|
| + |
|
| + |
The old reference should be kept, in order for it to be used in lookups, since
|
| + |
it may be included historical data.
|
| + |
|
| + |
The same verification principles apply, as in the previous section.
|
| + |
|
| + |
We also allow updating the entry to maintain the same RID and DID pair, but
|
| + |
changing the commit SHA of `rad/id`, when this reference is updated.
|
| + |
|
| + |
##### Delete
|
| + |
Deletion of the DID is strongly discouraged, since it may be referenced in data
|
| + |
that is being circulated, and will also prevent any chance of verification from
|
| + |
that DID.
|
| + |
|
| + |
However, to remove the DID from the repository, the references under each
|
| + |
`refs/namespaces/<nid>` that match that DID can be removed.
|
| + |
|
| + |
### Gossip Protocol
|
| + |
One approach to finding out which RIDs identify which agents is through the
|
| + |
gossip protocol. A new gossip message is introduced that advertises which RID is
|
| + |
associated with a particular DID. We can think of this as another kind of
|
| + |
address book. The message has the following shape:
|
| + |
|
| + |
AgentAnnouncement = (
|
| + |
Did,
|
| + |
RepoId,
|
| + |
Commit,
|
| + |
)
|
| + |
|
| + |
It contains the DID which is controlling the agent repository, which is
|
| + |
identified by the `RepoId` (RID), and the commit that is the tip of the `rad/id`
|
| + |
reference at the time.
|
| + |
|
| + |
Nodes broadcast this message when they are made aware of this pairing. In the
|
| + |
case of the node that created the agent repository, it can be broadcasted to the
|
| + |
network immediately. This message is relayed and propagated to the rest of the
|
| + |
network. If a node comes across a DID they are not aware of, then they would
|
| + |
send a request to the network to find out the RID and DID pair.
|
| + |
|
| + |
This information would be stored alongside the rest of the other network
|
| + |
information that is recorded, e.g. in an SQLite database.
|
| + |
|
| + |
We also add a second message, for querying other nodes about a DID:
|
| + |
|
| + |
AgentQuery = (
|
| + |
Did,
|
| + |
)
|
| + |
|
| + |
#### Lifecycle
|
| + |
|
| + |
##### Create
|
| + |
|
| + |
###### Initial Announcement
|
| + |
In the first case, a node will want to create an entry for an RID and DID pair
|
| + |
when the node itself is creating the agent repository for the given DID. The
|
| + |
node can then construct the gossip message, defined above, and broadcast it to
|
| + |
the network.
|
| + |
|
| + |
Any node must not advertise an RID and DID pair where the same DID points to two
|
| + |
different RIDs. This violates our one-to-one relationship, stated in
|
| + |
[Agent Repository](#agent-repository).
|
| + |
|
| + |
###### Forwarding
|
| + |
The other case is when a node receives a gossip message about the RID and DID.
|
| + |
The node may record this entry and should verify that the association is correct
|
| + |
– by fetching the repository and performing the verification (see
|
| + |
[Verification](#verification)) of the agent repository. This prevents nodes
|
| + |
attempting to spoof DID and RID pairings – the DID controller of the agent
|
| + |
repository can sign changes, and thus can also be verified via the same DID.
|
| + |
|
| + |
If a node receives multiple messages for the same DID but different RIDs,
|
| + |
then it must accept, at most, one of them.
|
| + |
|
| + |
##### Read
|
| + |
When a node wants to find out which agent repository is represented by a given
|
| + |
DID, it will first lookup the entry in the storage mechanism it is using for
|
| + |
keeping track of gossip data. If there is an entry, it must be the RID for the
|
| + |
agent repository that this DID is associated with – the node should only persist
|
| + |
these pairs if the DID verifies with the given RID.
|
| + |
|
| + |
Otherwise, if there is no such entry, the node may gossip to the network, asking
|
| + |
for this information – verifying any responses it may receive.
|
| + |
|
| + |
##### Update
|
| + |
To update an RID with a new DID, the advertising node should update their
|
| + |
storage with the new entry – given that it is verifiable – and may relay the new
|
| + |
pair via the gossip message.
|
| + |
|
| + |
The same verification criteria applies to updating as it does for creation.
|
| + |
|
| + |
We also allow updating the commit of the entry, when the `rad/id` reference changes.
|
| + |
|
| + |
##### Delete
|
| + |
Deletion of the DID is strongly discouraged, since it may be referenced in data
|
| + |
that is being circulated, and will also prevent any chance of verification from
|
| + |
that DID.
|
| + |
|
| + |
If it is necessary, then the entry in the storage may be deleted. This may be
|
| + |
useful if the controller of the DID has created two separate RIDs and wants to
|
| + |
converge on using only one – however, there are better methods for this, like
|
| + |
updating the original agent repository instead.
|
| + |
|
| + |
The message for this is to send the `AgentAnnouncement` message with a zeroed
|
| + |
commit. The node may decide to accept the message and delete the entry, however,
|
| + |
it must originate from the DID controller.
|
| + |
|
| + |
Future Work
|
| + |
-----------
|
| + |
|
| + |
### `did:key` Agent Repository
|
| + |
We will use this RIP and define an agent repository specification for the
|
| + |
`did:key` method, ensuring that we can use agent repositories in our current
|
| + |
state – keeping compatibility with the existing architecture.
|
| + |
|
| + |
### KERI Agent Repository
|
| + |
This RIP only goes as far as specifying the agent repository, however, it will
|
| + |
be important for the protocol to have its first instance of an agent repository
|
| + |
and integrate one into the protocol. Riding on the coat tails of this RIP, will
|
| + |
be a RIP that defines an implementation that is based on the KERI
|
| + |
specification[^storage-layout].
|
| + |
|
| + |
### Multi-Device
|
| + |
As well as this in [Radicle Namespace](#radicle-namespace), we mentioned the
|
| + |
concept of being able to manage multiple devices under the same persona. This
|
| + |
RIP carved out the reference space for associating keys within an agent
|
| + |
repository, and the details of this approach will be specified in a future RIP.
|
| + |
|
| + |
Appendix
|
| + |
--------
|
| + |
|
| + |
### JSON Schema
|
| + |
|
| + |
```json
|
| + |
{
|
| + |
"$schema": "http://json-schema.org/draft/2020-12/schema",
|
| + |
"type": "object",
|
| + |
"properties": {
|
| + |
"xyz.radicle.agent": {
|
| + |
"type": "object",
|
| + |
"properties": {
|
| + |
"alias": {
|
| + |
"type": "string",
|
| + |
"maxLength": 32,
|
| + |
"pattern": "^[^\s\p{Cc}]+$"
|
| + |
},
|
| + |
"controller": {
|
| + |
"type": "string",
|
| + |
"pattern": "^did:([a-z0-9]+):((([A-Za-z0-9._%-][A-Za-z0-9._%-]*:)*[A-Za-z0-9._%-]+))$"
|
| + |
}
|
| + |
},
|
| + |
"required": ["alias", "controller"],
|
| + |
"additionalProperties": false
|
| + |
}
|
| + |
},
|
| + |
"required": ["xyz.radicle.agent"],
|
| + |
"additionalProperties": false
|
| + |
}
|
| + |
```
|
| + |
|
| + |
Copyright
|
| + |
---------
|
| + |
This document is licensed under the Creative Commons CC0 1.0 Universal license.
|
| + |
|
| + |
References
|
| + |
----------
|
| + |
[^rip-2]: https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/0002-identity.md
|
| + |
[^storage-layout]: https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/0003-storage-layout.md
|
| + |
[^layout-section]: https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/0003-storage-layout.md#layout
|
| + |
[^did]: https://www.w3.org/TR/did-core/
|
| + |
[^signature-crate]: https://docs.rs/signature/latest/signature/
|
| + |
[^signature-signer]: https://docs.rs/signature/latest/signature/trait.Signer.html
|
| + |
[^signature-verifier]: https://docs.rs/signature/latest/signature/trait.Verifier.html
|
| + |
[^keri-spec]: https://trustoverip.github.io/tswg-keri-specification
|
| + |
<!-- TODO: update to latest version when it is merged -->
|
| + |
[^canonical-refs]: https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/df1bc0f914e4e5592d9f04a73a5c916f2e94bfcd/0004-canonical-references.md
|
| + |
[^check-ref-format]: https://git-scm.com/docs/git-check-ref-format
|
| + |
[^did-key]: https://w3c-ccg.github.io/did-method-key
|
| + |
[^did-keri]: https://weboftrust.github.io/did-keri/
|
| + |
[^git-ref-format]: The `<method-name>` and `<method-specific-id>` must be valid Git reference components, which can be checked via `git-check-ref-format`[^check-ref-format]. If they are not valid path components, then the RIP proposing them must standardize how those characters are escaped or replaced.
|
| + |
[^identity-verification]: https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/4d7bef6e2cfdba76fb781d4ba008c22ca41a8c24/0002-identity.md#verification
|
| + |
[^also-known-as]: https://www.w3.org/TR/did-core/#also-known-as
|
| + |
[^identity-storage]: https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/0002-identity.md#identity-storage
|
| + |
[^did-architecture]: https://www.w3.org/TR/did-core/#architecture-overview
|