Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: Remove `author` from `Entry`
Alexis Sellier committed 3 years ago
commit dd7052926a1ae263731aa9a3b82ca323f9f49276
parent 5a47023ac085c0e66599cdc005eb74dd19116623
20 files changed +58 -322
modified radicle-cob/src/backend/git/change.rs
@@ -28,7 +28,6 @@ pub mod error {
    use thiserror::Error;

    use crate::signatures::error::Signatures;
-
    use crate::trailers;

    #[derive(Debug, Error)]
    pub enum Create {
@@ -69,8 +68,6 @@ pub mod error {
        #[error("the 'change' found at '{0}' has more than one signature")]
        TooManySignatures(Oid),
        #[error(transparent)]
-
        AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer),
-
        #[error(transparent)]
        ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer),
        #[error("non utf-8 characters in commit message")]
        Utf8(#[from] FromUtf8Error),
@@ -84,13 +81,11 @@ impl change::Storage for git2::Repository {
    type LoadError = error::Load;

    type ObjectId = Oid;
-
    type Author = Oid;
    type Resource = Oid;
    type Signatures = Signature;

    fn create<Signer>(
        &self,
-
        author: Option<Self::Author>,
        resource: Self::Resource,
        signer: &Signer,
        spec: store::Create<Self::ObjectId>,
@@ -119,20 +114,11 @@ impl change::Storage for git2::Repository {
            Signature::from((*key, sig))
        };

-
        let id = write_commit(
-
            self,
-
            author,
-
            resource,
-
            tips,
-
            message,
-
            signature.clone(),
-
            tree,
-
        )?;
+
        let id = write_commit(self, resource, tips, message, signature.clone(), tree)?;
        Ok(Change {
            id,
            revision: revision.into(),
            signature,
-
            author,
            resource,
            manifest,
            contents,
@@ -141,7 +127,7 @@ impl change::Storage for git2::Repository {

    fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
        let commit = Commit::read(self, id.into())?;
-
        let (author, resource) = parse_trailers(commit.trailers())?;
+
        let resource = parse_resource_trailer(commit.trailers())?;
        let mut signatures = Signatures::try_from(&commit)?
            .into_iter()
            .collect::<Vec<_>>();
@@ -160,7 +146,6 @@ impl change::Storage for git2::Repository {
            id,
            revision: tree.id().into(),
            signature: signature.into(),
-
            author,
            resource,
            manifest,
            contents,
@@ -168,26 +153,21 @@ impl change::Storage for git2::Repository {
    }
}

-
fn parse_trailers<'a>(
-
    mut trailers: impl Iterator<Item = &'a OwnedTrailer>,
-
) -> Result<(Option<Oid>, Oid), error::Load> {
-
    let (author, resource) = trailers.try_fold((None, None), |(author, resource), trailer| {
-
        match trailers::AuthorCommitTrailer::try_from(trailer) {
-
            Ok(trailer) => Ok((Some(trailer.oid().into()), resource)),
-
            Err(err) => match err {
-
                trailers::error::InvalidAuthorTrailer::NoTrailer
-
                | trailers::error::InvalidAuthorTrailer::NoValue => Ok((author, resource)),
-
                trailers::error::InvalidAuthorTrailer::WrongToken => {
-
                    let resource = trailers::ResourceCommitTrailer::try_from(trailer)?;
-
                    Ok((author, Some(resource.oid().into())))
-
                }
-
                err => Err(error::Load::from(err)),
-
            },
+
fn parse_resource_trailer<'a>(
+
    trailers: impl Iterator<Item = &'a OwnedTrailer>,
+
) -> Result<Oid, error::Load> {
+
    for trailer in trailers {
+
        match trailers::ResourceCommitTrailer::try_from(trailer) {
+
            Err(trailers::error::InvalidResourceTrailer::WrongToken) => {
+
                continue;
+
            }
+
            Err(err) => return Err(err.into()),
+
            Ok(resource) => return Ok(resource.oid().into()),
        }
-
    })?;
-
    let resource = resource
-
        .ok_or_else(|| error::Load::from(trailers::error::InvalidResourceTrailer::NoTrailer))?;
-
    Ok((author, resource))
+
    }
+
    Err(error::Load::from(
+
        trailers::error::InvalidResourceTrailer::NoTrailer,
+
    ))
}

fn load_manifest(
@@ -223,7 +203,6 @@ fn load_contents(

fn write_commit<O>(
    repo: &git2::Repository,
-
    author: Option<O>,
    resource: O,
    tips: Vec<O>,
    message: String,
@@ -233,16 +212,12 @@ fn write_commit<O>(
where
    O: AsRef<git2::Oid>,
{
-
    let author = author.map(|author| *author.as_ref());
    let resource = *resource.as_ref();

    let mut parents = tips.iter().map(|o| *o.as_ref()).collect::<Vec<_>>();
    parents.push(resource);
-
    parents.extend(author);

-
    let mut trailers: Vec<OwnedTrailer> =
-
        vec![trailers::ResourceCommitTrailer::from(resource).into()];
-
    trailers.extend(author.map(|author| trailers::AuthorCommitTrailer::from(author).into()));
+
    let trailers: Vec<OwnedTrailer> = vec![trailers::ResourceCommitTrailer::from(resource).into()];

    {
        let author = repo.signature()?;
modified radicle-cob/src/change.rs
@@ -13,4 +13,4 @@ use crate::signatures::Signature;
/// A single change in the change graph. The layout of changes in the repository
/// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc)
/// under "Change Commits".
-
pub type Change = store::Change<Oid, Oid, Oid, Signature>;
+
pub type Change = store::Change<Oid, Oid, Signature>;
modified radicle-cob/src/change/store.rs
@@ -14,21 +14,16 @@ pub trait Storage {
    type LoadError: Error + Send + Sync + 'static;

    type ObjectId;
-
    type Author;
    type Resource;
    type Signatures;

    #[allow(clippy::type_complexity)]
    fn create<Signer>(
        &self,
-
        author: Option<Self::Author>,
        authority: Self::Resource,
        signer: &Signer,
        spec: Create<Self::ObjectId>,
-
    ) -> Result<
-
        Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
-
        Self::CreateError,
-
    >
+
    ) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::CreateError>
    where
        Signer: crypto::Signer;

@@ -36,10 +31,7 @@ pub trait Storage {
    fn load(
        &self,
        id: Self::ObjectId,
-
    ) -> Result<
-
        Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
-
        Self::LoadError,
-
    >;
+
    ) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::LoadError>;
}

pub struct Create<Id> {
@@ -51,7 +43,7 @@ pub struct Create<Id> {
}

#[derive(Clone, Debug)]
-
pub struct Change<Author, Resource, Id, Signature> {
+
pub struct Change<Resource, Id, Signature> {
    /// The content address of the `Change` itself.
    pub id: Id,
    /// The content address of the tree of the `Change`.
@@ -59,9 +51,6 @@ pub struct Change<Author, Resource, Id, Signature> {
    /// The cryptographic signature(s) and their public keys of the
    /// authors.
    pub signature: Signature,
-
    /// The author of this change. The `Author` is expected to be a
-
    /// content address to look up the identity of the author.
-
    pub author: Option<Author>,
    /// The parent resource that this change lives under. For example,
    /// this change could be for a patch of a project.
    pub resource: Resource,
@@ -72,7 +61,7 @@ pub struct Change<Author, Resource, Id, Signature> {
    pub contents: Contents,
}

-
impl<Author, Resource, Id, S> fmt::Display for Change<Author, Resource, Id, S>
+
impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
where
    Id: fmt::Display,
{
@@ -81,15 +70,11 @@ where
    }
}

-
impl<Author, Resource, Id, Signatures> Change<Author, Resource, Id, Signatures> {
+
impl<Resource, Id, Signatures> Change<Resource, Id, Signatures> {
    pub fn id(&self) -> &Id {
        &self.id
    }

-
    pub fn author(&self) -> &Option<Author> {
-
        &self.author
-
    }
-

    pub fn typename(&self) -> &TypeName {
        &self.manifest.typename
    }
@@ -103,7 +88,7 @@ impl<Author, Resource, Id, Signatures> Change<Author, Resource, Id, Signatures>
    }
}

-
impl<A, R, Id> Change<A, R, Id, signatures::Signatures>
+
impl<R, Id> Change<R, Id, signatures::Signatures>
where
    Id: AsRef<[u8]>,
{
@@ -114,7 +99,7 @@ where
    }
}

-
impl<A, R, Id> Change<A, R, Id, signatures::Signature>
+
impl<R, Id> Change<R, Id, signatures::Signature>
where
    Id: AsRef<[u8]>,
{
modified radicle-cob/src/change_graph.rs
@@ -37,7 +37,7 @@ impl ChangeGraph {
        oid: &ObjectId,
    ) -> Option<ChangeGraph>
    where
-
        S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signature>,
+
        S: change::Storage<ObjectId = Oid, Resource = Oid, Signatures = Signature>,
    {
        log::info!("loading object '{}' '{}'", typename, oid);
        let mut builder = GraphBuilder::default();
@@ -170,7 +170,6 @@ impl GraphBuilder {
        commit: object::Commit,
        change: Change,
    ) -> impl Iterator<Item = (object::Commit, Oid)> + '_ {
-
        let author_commit = *change.author();
        let resource_commit = *change.resource();
        let commit_id = commit.id;
        if let Entry::Vacant(e) = self.node_indices.entry(commit_id) {
@@ -178,10 +177,7 @@ impl GraphBuilder {
            e.insert(ix);
        }
        commit.parents.into_iter().filter_map(move |parent| {
-
            if Some(parent.id) != author_commit
-
                && parent.id != resource_commit
-
                && !self.has_edge(parent.id, commit_id)
-
            {
+
            if parent.id != resource_commit && !self.has_edge(parent.id, commit_id) {
                Some((parent, commit_id))
            } else {
                None
modified radicle-cob/src/change_graph/evaluation.rs
@@ -67,7 +67,6 @@ fn evaluate_change(
    Ok(history::Entry::new(
        *change.id(),
        change.signature.key,
-
        *change.author(),
        change.resource,
        child_commits.iter().cloned(),
        change.contents().clone(),
modified radicle-cob/src/history.rs
@@ -42,7 +42,6 @@ impl History {
    pub(crate) fn new_from_root<Id>(
        id: Id,
        actor: PublicKey,
-
        author: Option<Oid>,
        resource: Oid,
        contents: Contents,
    ) -> Self
@@ -53,7 +52,6 @@ impl History {
        let root_entry = Entry {
            id,
            actor,
-
            author,
            resource,
            children: vec![],
            contents,
@@ -120,7 +118,6 @@ impl History {
        &mut self,
        new_id: Id,
        new_actor: PublicKey,
-
        new_author: Option<Oid>,
        new_resource: Oid,
        new_contents: Contents,
    ) where
@@ -131,7 +128,6 @@ impl History {
        let new_entry = Entry::new(
            new_id,
            new_actor,
-
            new_author,
            new_resource,
            std::iter::empty::<git2::Oid>(),
            new_contents,
modified radicle-cob/src/history/entry.rs
@@ -45,9 +45,6 @@ pub struct Entry {
    pub(super) id: EntryId,
    /// The actor that authored this entry.
    pub(super) actor: PublicKey,
-
    /// The content-address for this entry's author.
-
    /// TODO: This shouldn't be here?
-
    pub(super) author: Option<Oid>,
    /// The content-address for the resource this entry lives under.
    pub(super) resource: Oid,
    /// The child entries for this entry.
@@ -60,7 +57,6 @@ impl Entry {
    pub fn new<Id1, Id2, ChildIds>(
        id: Id1,
        actor: PublicKey,
-
        author: Option<Oid>,
        resource: Oid,
        children: ChildIds,
        contents: Contents,
@@ -73,7 +69,6 @@ impl Entry {
        Self {
            id: id.into(),
            actor,
-
            author,
            resource,
            children: children.into_iter().map(|id| id.into()).collect(),
            contents,
@@ -95,11 +90,6 @@ impl Entry {
        &self.actor
    }

-
    /// The `Oid` of the author that made this change.
-
    pub fn author(&self) -> Option<&Oid> {
-
        self.author.as_ref()
-
    }
-

    /// The contents of this change
    pub fn contents(&self) -> &Contents {
        &self.contents
modified radicle-cob/src/identity.rs
@@ -8,17 +8,10 @@ use git_ext::Oid;
/// An [`Identity`] represents a content addressed identity
/// (i.e. expected to be stored in a git backend).
///
-
/// It should have:
-
///   * A delegate system
-
///   * A content addressable identifier
-
///   * A unique, stable identifier
+
/// It should have a unique, stable, content addressable identifier.
pub trait Identity {
    type Identifier;

-
    /// Confirm that the given [`crypto::PublicKey`] is a delegate for
-
    /// the identity.
-
    fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool;
-

    /// Provide the content address for the given identity. This is
    /// expected to be the latest address for the identity at the time
    /// of use.
modified radicle-cob/src/lib.rs
@@ -61,11 +61,11 @@
//! automerge document and deserialize into an application defined
//! object.
//!
-
//! This traversal is also the point at which the [`Entry::author`]
+
//! This traversal is also the point at which the [`Entry::actor`]
//! and [`Entry::resource`] can be retrieved to apply any kind of
-
//! filtering logic. For example, a specific `author`'s change may be
+
//! filtering logic. For example, a specific `actor`'s change may be
//! egregious, spouting terrible libel about Radicle. It is at this
-
//! point that the `author`'s change can be filtered out from the
+
//! point that the `actor`'s change can be filtered out from the
//! final product of the traversal.
//!
//! [automerge]: https://automerge.org
@@ -132,7 +132,6 @@ where
            CreateError = git::change::error::Create,
            LoadError = git::change::error::Load,
            ObjectId = git_ext::Oid,
-
            Author = git_ext::Oid,
            Resource = git_ext::Oid,
            Signatures = Signature,
        >,
modified radicle-cob/src/object/collaboration/create.rs
@@ -8,9 +8,7 @@ use crate::Store;
use super::*;

/// The metadata required for creating a new [`CollaborativeObject`].
-
pub struct Create<Author> {
-
    /// The identity of the author for this object's first change.
-
    pub author: Option<Author>,
+
pub struct Create {
    /// The type of history that will be used for this object.
    pub history_type: String,
    /// The CRDT history to initialize this object with.
@@ -21,7 +19,7 @@ pub struct Create<Author> {
    pub message: String,
}

-
impl<Author> Create<Author> {
+
impl Create {
    fn create_spec(&self) -> change::Create<git_ext::Oid> {
        change::Create {
            typename: self.typename.clone(),
@@ -51,46 +49,31 @@ impl<Author> Create<Author> {
///
/// The `args` are the metadata for this [`CollaborativeObject`]. See
/// [`Create`] for further information.
-
pub fn create<S, G, Author, Resource>(
+
pub fn create<S, G, Resource>(
    storage: &S,
    signer: &G,
    resource: &Resource,
    identifier: &S::Identifier,
-
    args: Create<Author>,
+
    args: Create,
) -> Result<CollaborativeObject, error::Create>
where
    S: Store,
    G: crypto::Signer,
-
    Author: Identity,
-
    Author::Identifier: Clone + PartialEq,
    Resource: Identity,
{
    let Create {
-
        author,
        ref contents,
        ref typename,
        ..
    } = &args;

-
    let content = match author {
-
        None => None,
-
        Some(author) => {
-
            if !author.is_delegate(signer.public_key()) {
-
                return Err(error::Create::SignerIsNotAuthor);
-
            } else {
-
                Some(author.content_id())
-
            }
-
        }
-
    };
-

    let init_change = storage
-
        .create(content, resource.content_id(), signer, args.create_spec())
+
        .create(resource.content_id(), signer, args.create_spec())
        .map_err(error::Create::from)?;

    let history = History::new_from_root(
        *init_change.id(),
        init_change.signature.key,
-
        content,
        resource.content_id(),
        contents.clone(),
    );
modified radicle-cob/src/object/collaboration/update.rs
@@ -11,9 +11,7 @@ use crate::{
use super::error;

/// The data required to update an object
-
pub struct Update<Author> {
-
    /// The identity of the author for the update of this object.
-
    pub author: Option<Author>,
+
pub struct Update {
    /// The type of history that will be used for this object.
    pub history_type: String,
    /// The CRDT changes to add to the object.
@@ -44,22 +42,19 @@ pub struct Update<Author> {
///
/// The `args` are the metadata for this [`CollaborativeObject`]
/// udpate. See [`Update`] for further information.
-
pub fn update<S, G, Resource, Author>(
+
pub fn update<S, G, Resource>(
    storage: &S,
    signer: &G,
    resource: &Resource,
    identifier: &S::Identifier,
-
    args: Update<Author>,
+
    args: Update,
) -> Result<CollaborativeObject, error::Update>
where
    S: Store,
    G: crypto::Signer,
-
    Author: Identity,
-
    Author::Identifier: Clone + PartialEq,
    Resource: Identity,
{
    let Update {
-
        author,
        ref typename,
        object_id,
        history_type,
@@ -67,18 +62,6 @@ where
        message,
    } = args;

-
    let author = match author {
-
        None => None,
-
        Some(author) => {
-
            // TODO: Remove?
-
            if !author.is_delegate(signer.public_key()) {
-
                return Err(error::Update::SignerIsNotAuthor);
-
            } else {
-
                Some(author.content_id())
-
            }
-
        }
-
    };
-

    let existing_refs = storage
        .objects(typename, &object_id)
        .map_err(|err| error::Update::Refs { err: Box::new(err) })?;
@@ -88,7 +71,6 @@ where
        .ok_or(error::Update::NoSuchObject)?;

    let change = storage.create(
-
        author,
        resource.content_id(),
        signer,
        change::Create {
@@ -99,13 +81,9 @@ where
            message,
        },
    )?;
-
    object.history.extend(
-
        change.id,
-
        change.signature.key,
-
        author,
-
        change.resource,
-
        changes,
-
    );
+
    object
+
        .history
+
        .extend(change.id, change.signature.key, change.resource, changes);
    storage
        .update(identifier, typename, &object_id, &change)
        .map_err(|err| error::Update::Refs { err: Box::new(err) })?;
modified radicle-cob/src/test/identity/person.rs
@@ -53,52 +53,14 @@ impl Person {
        })
    }

-
    pub fn key(&self) -> crypto::PublicKey {
-
        self.payload.key
-
    }
-

    pub fn name(&self) -> &Name {
        &self.payload.name
    }
-

-
    pub fn find_by_oid(
-
        repo: &git2::Repository,
-
        id: Oid,
-
    ) -> Result<Option<Person>, storage::error::Identity> {
-
        match repo.find_commit(id.into()) {
-
            Ok(commit) => from_commit(repo, commit),
-
            Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(None),
-
            Err(err) => Err(err.into()),
-
        }
-
    }
-
}
-

-
fn from_commit(
-
    repo: &git2::Repository,
-
    commit: git2::Commit,
-
) -> Result<Option<Person>, storage::error::Identity> {
-
    let tree = commit.tree()?;
-
    let entry = tree
-
        .get_name("identity")
-
        .ok_or_else(|| storage::error::Identity::NotFound(tree.id().into()))?;
-
    let blob = match entry.to_object(repo)?.into_blob() {
-
        Ok(blob) => blob,
-
        Err(other) => return Err(storage::error::Identity::NotBlob(other.kind())),
-
    };
-
    let payload = serde_json::de::from_slice(blob.content())?;
-
    Ok(Some(Person {
-
        payload,
-
        content_id: commit.id().into(),
-
    }))
}

impl Identity for Person {
    type Identifier = Urn;

-
    fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
-
        self.key() == *delegation
-
    }
-

    fn content_id(&self) -> Oid {
        self.content_id
    }
modified radicle-cob/src/test/identity/project.rs
@@ -70,56 +70,14 @@ impl Project {
        })
    }

-
    pub fn delegates(&self) -> &BTreeSet<crypto::PublicKey> {
-
        &self.payload.delegates
-
    }
-

-
    pub fn delegate_check(&self, person: &test::Person) -> bool {
-
        self.payload.delegates.contains(&person.key())
-
    }
-

    pub fn name(&self) -> &Name {
        &self.payload.name
    }
-

-
    pub fn find_by_oid(
-
        repo: &git2::Repository,
-
        id: Oid,
-
    ) -> Result<Option<Self>, storage::error::Identity> {
-
        match repo.find_commit(id.into()) {
-
            Ok(commit) => from_commit(repo, commit),
-
            Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(None),
-
            Err(err) => Err(err.into()),
-
        }
-
    }
-
}
-

-
fn from_commit(
-
    repo: &git2::Repository,
-
    commit: git2::Commit,
-
) -> Result<Option<Project>, storage::error::Identity> {
-
    let tree = commit.tree()?;
-
    let entry = tree
-
        .get_name("identity")
-
        .ok_or_else(|| storage::error::Identity::NotFound(tree.id().into()))?;
-
    let blob = match entry.to_object(repo)?.into_blob() {
-
        Ok(blob) => blob,
-
        Err(other) => return Err(storage::error::Identity::NotBlob(other.kind())),
-
    };
-
    let payload = serde_json::de::from_slice(blob.content())?;
-
    Ok(Some(Project {
-
        payload,
-
        content_id: commit.id().into(),
-
    }))
}

impl Identity for RemoteProject {
    type Identifier = Urn;

-
    fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
-
        self.project.delegates().contains(delegation)
-
    }
-

    fn content_id(&self) -> Oid {
        self.project.content_id
    }
modified radicle-cob/src/test/storage.rs
@@ -21,10 +21,6 @@ pub mod error {
        Json(#[from] serde_json::Error),
        #[error(transparent)]
        Git(#[from] git2::Error),
-
        #[error("'identity' was not a blob, found '{0:?}'")]
-
        NotBlob(Option<git2::ObjectType>),
-
        #[error("could not find 'identity' in the tree '{0}'")]
-
        NotFound(git_ext::Oid),
    }

    #[derive(Debug, Error)]
@@ -67,31 +63,29 @@ impl change::Storage for Storage {
    type LoadError = <git2::Repository as change::Storage>::LoadError;

    type ObjectId = <git2::Repository as change::Storage>::ObjectId;
-
    type Author = <git2::Repository as change::Storage>::Author;
    type Resource = <git2::Repository as change::Storage>::Resource;
    type Signatures = <git2::Repository as change::Storage>::Signatures;

    fn create<Signer>(
        &self,
-
        author: Option<Self::Author>,
        authority: Self::Resource,
        signer: &Signer,
        spec: change::Create<Self::ObjectId>,
    ) -> Result<
-
        change::store::Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
+
        change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
        Self::CreateError,
    >
    where
        Signer: crypto::Signer,
    {
-
        self.as_raw().create(author, authority, signer, spec)
+
        self.as_raw().create(authority, signer, spec)
    }

    fn load(
        &self,
        id: Self::ObjectId,
    ) -> Result<
-
        change::store::Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
+
        change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
        Self::LoadError,
    > {
        self.as_raw().load(id)
modified radicle-cob/src/tests.rs
@@ -19,7 +19,7 @@ fn roundtrip() {
    let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
    let proj = test::RemoteProject {
        project: proj,
-
        person: terry.clone(),
+
        person: terry,
    };
    let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
    let cob = create(
@@ -28,7 +28,6 @@ fn roundtrip() {
        &proj,
        &proj.identifier(),
        Create {
-
            author: Some(terry),
            history_type: "test".to_string(),
            contents: Vec::new(),
            typename: typename.clone(),
@@ -52,7 +51,7 @@ fn list_cobs() {
    let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
    let proj = test::RemoteProject {
        project: proj,
-
        person: terry.clone(),
+
        person: terry,
    };
    let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
    let issue_1 = create(
@@ -61,7 +60,6 @@ fn list_cobs() {
        &proj,
        &proj.identifier(),
        Create {
-
            author: Some(terry.clone()),
            history_type: "test".to_string(),
            contents: b"issue 1".to_vec(),
            typename: typename.clone(),
@@ -76,7 +74,6 @@ fn list_cobs() {
        &proj,
        &proj.identifier(),
        Create {
-
            author: Some(terry),
            history_type: "test".to_string(),
            contents: b"issue 2".to_vec(),
            typename: typename.clone(),
@@ -102,7 +99,7 @@ fn update_cob() {
    let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
    let proj = test::RemoteProject {
        project: proj,
-
        person: terry.clone(),
+
        person: terry,
    };
    let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
    let cob = create(
@@ -111,7 +108,6 @@ fn update_cob() {
        &proj,
        &proj.identifier(),
        Create {
-
            author: Some(terry.clone()),
            history_type: "test".to_string(),
            contents: Vec::new(),
            typename: typename.clone(),
@@ -130,7 +126,6 @@ fn update_cob() {
        &proj,
        &proj.identifier(),
        Update {
-
            author: Some(terry),
            changes: b"issue 1".to_vec(),
            history_type: "test".to_string(),
            object_id: *cob.id(),
@@ -158,11 +153,11 @@ fn traverse_cobs() {
    let proj = test::Project::new(&storage, "discworld", *terry_signer.public_key()).unwrap();
    let terry_proj = test::RemoteProject {
        project: proj.clone(),
-
        person: terry.clone(),
+
        person: terry,
    };
    let neil_proj = test::RemoteProject {
        project: proj,
-
        person: neil.clone(),
+
        person: neil,
    };
    let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
    let cob = create(
@@ -171,7 +166,6 @@ fn traverse_cobs() {
        &terry_proj,
        &terry_proj.identifier(),
        Create {
-
            author: Some(terry),
            contents: b"issue 1".to_vec(),
            history_type: "test".to_string(),
            typename: typename.clone(),
@@ -194,7 +188,6 @@ fn traverse_cobs() {
        &neil_proj,
        &neil_proj.identifier(),
        Update {
-
            author: Some(neil),
            changes: b"issue 2".to_vec(),
            history_type: "test".to_string(),
            object_id: *cob.id(),
@@ -206,16 +199,8 @@ fn traverse_cobs() {

    // traverse over the history and filter by changes that were only authorized by terry
    let contents = updated.history().traverse(Vec::new(), |mut acc, entry| {
-
        let author = match entry.author() {
-
            Some(author) => test::Person::find_by_oid(storage.as_raw(), *author).unwrap(),
-
            None => None,
-
        };
-
        let project = test::Project::find_by_oid(storage.as_raw(), entry.resource()).unwrap();
-

-
        if let (Some(author), Some(project)) = (author, project) {
-
            if project.delegate_check(&author) {
-
                acc.push(entry.contents().to_vec())
-
            }
+
        if entry.actor() == terry_signer.public_key() {
+
            acc.push(entry.contents().to_vec());
        }
        ControlFlow::Continue(acc)
    });
modified radicle-cob/src/trailers.rs
@@ -3,20 +3,14 @@
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
// Linking Exception. For full terms see the included LICENSE file.

-
mod author_commit {
-
    super::oid_trailer! {AuthorCommitTrailer, "Rad-Author"}
-
}
mod resource_identity {
    super::oid_trailer! {ResourceCommitTrailer, "Rad-Resource"}
}

pub mod error {
-
    pub use super::author_commit::Error as InvalidAuthorTrailer;
-

    pub use super::resource_identity::Error as InvalidResourceTrailer;
}

-
pub use author_commit::AuthorCommitTrailer;
pub use resource_identity::ResourceCommitTrailer;

/// A macro for generating boilerplate From and TryFrom impls for trailers which
modified radicle/src/cob.rs
@@ -13,48 +13,7 @@ pub use common::*;
use radicle_cob as cob;
use radicle_git_ext::Oid;

-
use crate::{
-
    identity::{project::Identity, Did},
-
    node::NodeId,
-
    storage::git::Repository,
-
};
-

-
/// The `Author` of a [`create`] or [`update`].
-
///
-
/// **Note**: `Author` implements [`identity::Identity`], but since it
-
/// is not content-addressed, the [`identity::Identity::content_id`]
-
/// returns [`git2::Oid::zero`]. This means that if the `author` is
-
/// set in the updates the history entries for those changes will
-
/// contain the zero `Oid` for the `author` field.
-
pub struct Author {
-
    did: Did,
-
}
-

-
impl From<Did> for Author {
-
    fn from(did: Did) -> Self {
-
        Self { did }
-
    }
-
}
-

-
impl From<NodeId> for Author {
-
    fn from(node_id: NodeId) -> Self {
-
        Self {
-
            did: Did::from(node_id),
-
        }
-
    }
-
}
-

-
impl identity::Identity for Author {
-
    type Identifier = String;
-

-
    fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
-
        *self.did == *delegation
-
    }
-

-
    fn content_id(&self) -> Oid {
-
        git2::Oid::zero().into()
-
    }
-
}
+
use crate::{identity::project::Identity, storage::git::Repository};

/// Create a new [`CollaborativeObject`].
///
@@ -62,8 +21,7 @@ impl identity::Identity for Author {
/// stored under.
///
/// The `signer` is used to cryptographically sign the changes made
-
/// for this update. **Note** that the public key for the signer must
-
/// match the key of the `Author` -- if it is set.
+
/// for this update.
///
/// The `project` is used to store its content-address in the history
/// of changes for the collaborative object.
@@ -74,7 +32,7 @@ pub fn create<G>(
    repository: &Repository,
    signer: &G,
    project: &Identity<Oid>,
-
    args: Create<Author>,
+
    args: Create,
) -> Result<CollaborativeObject, error::Create>
where
    G: crypto::Signer,
@@ -118,8 +76,7 @@ pub fn list(
/// stored under.
///
/// The `signer` is used to cryptographically sign the changes made
-
/// for this update. **Note** that the public key for the signer must
-
/// match the key of the `Author` -- if it is set.
+
/// for this update.
///
/// The `project` is used to store its content-address in the history
/// of changes for the collaborative object.
@@ -130,7 +87,7 @@ pub fn update<G>(
    repository: &Repository,
    signer: &G,
    project: &Identity<Oid>,
-
    args: Update<Author>,
+
    args: Update,
) -> Result<CollaborativeObject, error::Update>
where
    G: crypto::Signer,
modified radicle/src/cob/store.rs
@@ -103,7 +103,6 @@ where
            signer,
            &self.project,
            Update {
-
                author: Some(cob::Author::from(*signer.public_key())),
                object_id,
                history_type: HISTORY_TYPE.to_owned(),
                typename: T::type_name().clone(),
@@ -127,7 +126,6 @@ where
            signer,
            &self.project,
            Create {
-
                author: Some(cob::Author::from(*signer.public_key())),
                history_type: HISTORY_TYPE.to_owned(),
                typename: T::type_name().clone(),
                message: message.to_owned(),
modified radicle/src/identity/project.rs
@@ -406,10 +406,6 @@ pub struct Identity<I> {
impl radicle_cob::identity::Identity for Identity<Oid> {
    type Identifier = Oid;

-
    fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
-
        self.doc.delegates.iter().any(|d| d.matches(delegation))
-
    }
-

    fn content_id(&self) -> Oid {
        self.current
    }
modified radicle/src/storage/git.rs
@@ -681,13 +681,11 @@ impl change::Storage for Repository {
    type LoadError = <git2::Repository as change::Storage>::LoadError;

    type ObjectId = <git2::Repository as change::Storage>::ObjectId;
-
    type Author = <git2::Repository as change::Storage>::Author;
    type Resource = <git2::Repository as change::Storage>::Resource;
    type Signatures = <git2::Repository as change::Storage>::Signatures;

    fn create<Signer>(
        &self,
-
        author: Option<Self::Author>,
        authority: Self::Resource,
        signer: &Signer,
        spec: change::Create<Self::ObjectId>,
@@ -695,7 +693,7 @@ impl change::Storage for Repository {
    where
        Signer: crypto::Signer,
    {
-
        self.backend.create(author, authority, signer, spec)
+
        self.backend.create(authority, signer, spec)
    }

    fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {