Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: abstract namespace identifier
Fintan Halpenny committed 11 months ago
commit 9988b63bb204496cab00b584cca60ecb7beeb036
parent 1f4fcc5e6a3dadef99284bc3845ada11c74ccd97
24 files changed +105 -81
modified radicle-cli/src/commands/id.rs
@@ -7,6 +7,7 @@ use anyhow::{anyhow, Context};
use radicle::cob::identity::{self, IdentityMut, Revision, RevisionId};
use radicle::identity::{doc, Doc, Identity, PayloadError, RawDoc, Visibility};
use radicle::node::device::Device;
+
use radicle::node::NodeId;
use radicle::prelude::{Did, RepoId};
use radicle::storage::refs;
use radicle::storage::{ReadRepository, ReadStorage as _, WriteRepository};
@@ -731,7 +732,7 @@ fn update<R, G>(
    signer: &Device<G>,
) -> anyhow::Result<Revision>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
    G: crypto::signature::Signer<crypto::Signature>,
{
    if let Some((title, description)) = edit_title_description(title, description)? {
modified radicle-cli/src/commands/inbox.rs
@@ -12,7 +12,7 @@ use radicle::issue::cache::Issues as _;
use radicle::node::notifications;
use radicle::node::notifications::*;
use radicle::patch::cache::Patches as _;
-
use radicle::prelude::{Profile, RepoId};
+
use radicle::prelude::{NodeId, Profile, RepoId};
use radicle::storage::{BranchName, ReadRepository, ReadStorage};
use radicle::{cob, git, Storage};

@@ -261,7 +261,7 @@ fn list_repo<'a, R: ReadStorage>(
    profile: &Profile,
) -> anyhow::Result<Option<term::VStack<'a>>>
where
-
    <R as ReadStorage>::Repository: cob::Store,
+
    <R as ReadStorage>::Repository: cob::Store<Namespace = NodeId>,
{
    let mut table = term::Table::new(term::TableOptions {
        spacing: 3,
modified radicle-cli/src/commands/issue.rs
@@ -13,6 +13,7 @@ use radicle::cob::{issue, thread};
use radicle::crypto;
use radicle::issue::cache::Issues as _;
use radicle::node::device::Device;
+
use radicle::node::NodeId;
use radicle::prelude::{Did, RepoId};
use radicle::profile;
use radicle::storage;
@@ -815,7 +816,7 @@ fn open<R, G>(
    profile: &Profile,
) -> anyhow::Result<()>
where
-
    R: ReadRepository + WriteRepository + cob::Store,
+
    R: ReadRepository + WriteRepository + cob::Store<Namespace = NodeId>,
    G: crypto::signature::Signer<crypto::Signature>,
{
    let (title, description) = if let (Some(t), Some(d)) = (title.as_ref(), description.as_ref()) {
@@ -849,7 +850,7 @@ fn edit<'a, 'g, R, G>(
    signer: &Device<G>,
) -> anyhow::Result<issue::IssueMut<'a, 'g, R, cob::cache::StoreWriter>>
where
-
    R: WriteRepository + ReadRepository + cob::Store,
+
    R: WriteRepository + ReadRepository + cob::Store<Namespace = NodeId>,
    G: crypto::signature::Signer<crypto::Signature>,
{
    let id = id.resolve(&repo.backend)?;
@@ -891,7 +892,7 @@ where
}

/// Get a comment from the user, by prompting.
-
pub fn prompt_comment<R: WriteRepository + radicle::cob::Store>(
+
pub fn prompt_comment<R: WriteRepository + radicle::cob::Store<Namespace = NodeId>>(
    message: Message,
    reply_to: Option<Rev>,
    issue: &issue::Issue,
modified radicle-cli/src/commands/job.rs
@@ -6,7 +6,7 @@ use anyhow::{anyhow, Context as _};
use radicle::cob::job::{JobStore, Reason, State};
use radicle::crypto;
use radicle::node::device::Device;
-
use radicle::node::Handle;
+
use radicle::node::{Handle, NodeId};
use radicle::storage::{WriteRepository, WriteStorage};
use radicle::{cob, Node};

@@ -269,7 +269,7 @@ fn trigger<R, G>(
    quiet: bool,
) -> anyhow::Result<()>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
    G: crypto::signature::Signer<crypto::Signature>,
{
    let commit = commit.resolve(&repo.backend)?;
@@ -289,7 +289,7 @@ fn start<R, G>(
    signer: &Device<G>,
) -> anyhow::Result<()>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
    G: crypto::signature::Signer<crypto::Signature>,
{
    let job_id = job_id.resolve(&repo.backend)?;
@@ -301,7 +301,9 @@ where
}

// TODO: This should use the COB cache for performance.
-
fn list<R: WriteRepository + cob::Store>(store: &JobStore<R>) -> anyhow::Result<()> {
+
fn list<R: WriteRepository + cob::Store<Namespace = NodeId>>(
+
    store: &JobStore<R>,
+
) -> anyhow::Result<()> {
    if store.is_empty()? {
        term::print(term::format::italic("Nothing to show."));
        return Ok(());
@@ -343,7 +345,7 @@ fn list<R: WriteRepository + cob::Store>(store: &JobStore<R>) -> anyhow::Result<
    Ok(())
}

-
fn show<R: WriteRepository + cob::Store>(
+
fn show<R: WriteRepository + cob::Store<Namespace = NodeId>>(
    job_id: &Rev,
    store: &JobStore<R>,
    repo: &radicle::storage::git::Repository,
@@ -366,7 +368,7 @@ fn finish<R, G>(
    signer: &Device<G>,
) -> anyhow::Result<()>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
    G: crypto::signature::Signer<crypto::Signature>,
{
    let job_id = job_id.resolve(&repo.backend)?;
modified radicle-cli/src/terminal/cob.rs
@@ -3,6 +3,7 @@ use radicle::{
        self,
        cache::{MigrateCallback, MigrateProgress},
    },
+
    prelude::NodeId,
    profile,
    storage::ReadRepository,
    Profile,
@@ -61,7 +62,7 @@ pub fn patches<'a, R>(
    repository: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreReader>, anyhow::Error>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    profile.patches(repository).map_err(with_hint)
}
@@ -72,7 +73,7 @@ pub fn patches_mut<'a, R>(
    repository: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreWriter>, anyhow::Error>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    profile.patches_mut(repository).map_err(with_hint)
}
@@ -83,7 +84,7 @@ pub fn issues<'a, R>(
    repository: &'a R,
) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreReader>, anyhow::Error>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    profile.issues(repository).map_err(with_hint)
}
@@ -94,7 +95,7 @@ pub fn issues_mut<'a, R>(
    repository: &'a R,
) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreWriter>, anyhow::Error>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    profile.issues_mut(repository).map_err(with_hint)
}
modified radicle-cob/Cargo.toml
@@ -55,4 +55,4 @@ qcheck-macros = { version = "1", default-features = false }
[dev-dependencies.radicle-crypto]
path = "../radicle-crypto"
version = "0"
-
features = ["test"]
+
features = ["test", "radicle-git-ext"]
modified radicle-cob/src/object/collaboration/create.rs
@@ -1,7 +1,6 @@
// Copyright © 2022 The Radicle Link Contributors

use nonempty::NonEmpty;
-
use radicle_crypto::PublicKey;

use crate::Embed;
use crate::Evaluate;
@@ -58,7 +57,7 @@ pub fn create<T, S, G>(
    signer: &G,
    resource: Option<Oid>,
    related: Vec<Oid>,
-
    identifier: &PublicKey,
+
    identifier: &S::Namespace,
    args: Create,
) -> Result<CollaborativeObject<T>, error::Create>
where
modified radicle-cob/src/object/collaboration/remove.rs
@@ -1,7 +1,5 @@
// Copyright © 2022 The Radicle Link Contributors

-
use radicle_crypto::PublicKey;
-

use crate::{ObjectId, Store, TypeName};

use super::error;
@@ -17,7 +15,7 @@ use super::error;
/// type.
pub fn remove<S>(
    storage: &S,
-
    identifier: &PublicKey,
+
    identifier: &S::Namespace,
    typename: &TypeName,
    oid: &ObjectId,
) -> Result<(), error::Remove>
modified radicle-cob/src/object/collaboration/update.rs
@@ -3,7 +3,6 @@ use std::iter;

use git_ext::Oid;
use nonempty::NonEmpty;
-
use radicle_crypto::PublicKey;

use crate::{
    change, change_graph::ChangeGraph, history::EntryId, CollaborativeObject, Embed, Evaluate,
@@ -61,7 +60,7 @@ pub fn update<T, S, G>(
    signer: &G,
    resource: Option<Oid>,
    related: Vec<Oid>,
-
    identifier: &PublicKey,
+
    identifier: &S::Namespace,
    args: Update,
) -> Result<Updated<T>, error::Update>
where
modified radicle-cob/src/object/storage.rs
@@ -4,7 +4,6 @@ use std::{collections::BTreeMap, error::Error};

use git_ext::ref_format::RefString;
use git_ext::Oid;
-
use radicle_crypto::PublicKey;

use crate::change::EntryId;
use crate::{ObjectId, TypeName};
@@ -59,6 +58,8 @@ pub trait Storage {
    type UpdateError: Error + Send + Sync + 'static;
    type RemoveError: Error + Send + Sync + 'static;

+
    type Namespace;
+

    /// Get all references which point to a head of the change graph for a
    /// particular object
    fn objects(
@@ -74,7 +75,7 @@ pub trait Storage {
    /// Update a ref to a particular collaborative object
    fn update(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &TypeName,
        object_id: &ObjectId,
        entry: &EntryId,
@@ -83,7 +84,7 @@ pub trait Storage {
    /// Remove a ref to a particular collaborative object
    fn remove(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &TypeName,
        object_id: &ObjectId,
    ) -> Result<(), Self::RemoveError>;
modified radicle-cob/src/test/storage.rs
@@ -1,6 +1,6 @@
use std::{collections::BTreeMap, convert::TryFrom as _};

-
use radicle_crypto::PublicKey;
+
use radicle_git_ext::ref_format::{refname, Component};
use tempfile::TempDir;

use crate::{
@@ -105,6 +105,8 @@ impl object::Storage for Storage {
    type UpdateError = git2::Error;
    type RemoveError = git2::Error;

+
    type Namespace = crypto::PublicKey;
+

    fn objects(
        &self,
        typename: &crate::TypeName,
@@ -148,12 +150,16 @@ impl object::Storage for Storage {

    fn update(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &crate::TypeName,
        object_id: &ObjectId,
        entry: &change::EntryId,
    ) -> Result<(), Self::UpdateError> {
-
        let name = format!("refs/rad/{}/cobs/{}/{}", identifier, typename, object_id);
+
        let name = refname!("refs/rad")
+
            .join(Component::from(namespace))
+
            .join(refname!("cobs"))
+
            .join::<Component>(typename.into())
+
            .join::<Component>(object_id.into());
        self.raw
            .reference(&name, (*entry).into(), true, "new change")?;
        Ok(())
@@ -161,11 +167,15 @@ impl object::Storage for Storage {

    fn remove(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &crate::TypeName,
        object_id: &ObjectId,
    ) -> Result<(), Self::RemoveError> {
-
        let name = format!("refs/rad/{}/cobs/{}/{}", identifier, typename, object_id);
+
        let name = refname!("refs/rad")
+
            .join(Component::from(namespace))
+
            .join(refname!("cobs"))
+
            .join::<Component>(typename.into())
+
            .join::<Component>(object_id.into());
        self.raw.find_reference(&name)?.delete()?;

        Ok(())
modified radicle-node/src/worker/fetch.rs
@@ -8,6 +8,7 @@ use localtime::LocalTime;
use radicle::cob::TypedId;
use radicle::crypto::PublicKey;
use radicle::identity::DocAt;
+
use radicle::prelude::NodeId;
use radicle::prelude::RepoId;
use radicle::storage::refs::RefsAt;
use radicle::storage::{
@@ -299,7 +300,7 @@ fn cache_cobs<S, C>(
    cache: &mut C,
) -> Result<(), error::Cache>
where
-
    S: ReadRepository + cob::Store,
+
    S: ReadRepository + cob::Store<Namespace = NodeId>,
    C: cob::cache::Update<cob::issue::Issue> + cob::cache::Update<cob::patch::Patch>,
    C: cob::cache::Remove<cob::issue::Issue> + cob::cache::Remove<cob::patch::Patch>,
{
modified radicle-remote-helper/src/lib.rs
@@ -13,6 +13,7 @@ use std::{env, fmt, io};

use thiserror::Error;

+
use radicle::prelude::NodeId;
use radicle::storage::git::transport::local::{Url, UrlError};
use radicle::storage::{ReadRepository, WriteStorage};
use radicle::{cob, profile};
@@ -262,7 +263,7 @@ pub(crate) fn warn(s: impl fmt::Display) {
}

/// Get the patch store.
-
pub(crate) fn patches<'a, R: ReadRepository + cob::Store>(
+
pub(crate) fn patches<'a, R: ReadRepository + cob::Store<Namespace = NodeId>>(
    profile: &Profile,
    repo: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreReader>, list::Error> {
modified radicle-remote-helper/src/list.rs
@@ -4,6 +4,7 @@ use thiserror::Error;

use radicle::cob;
use radicle::git;
+
use radicle::prelude::NodeId;
use radicle::storage::git::transport::local::Url;
use radicle::storage::ReadRepository;
use radicle::Profile;
@@ -34,7 +35,7 @@ pub enum Error {
}

/// List refs for fetching (`git fetch` and `git ls-remote`).
-
pub fn for_fetch<R: ReadRepository + cob::Store + 'static>(
+
pub fn for_fetch<R: ReadRepository + cob::Store<Namespace = NodeId> + 'static>(
    url: &Url,
    profile: &Profile,
    stored: &R,
@@ -83,7 +84,7 @@ pub fn for_push<R: ReadRepository>(profile: &Profile, stored: &R) -> Result<(),
}

/// List canonical patch references. These are magic refs that can be used to pull patch updates.
-
fn patch_refs<R: ReadRepository + cob::Store + 'static>(
+
fn patch_refs<R: ReadRepository + cob::Store<Namespace = NodeId> + 'static>(
    profile: &Profile,
    stored: &R,
) -> Result<(), Error> {
modified radicle/src/cob/identity.rs
@@ -11,6 +11,7 @@ use thiserror::Error;

use crate::identity::doc::Doc;
use crate::node::device::Device;
+
use crate::node::NodeId;
use crate::{
    cob,
    cob::{
@@ -192,13 +193,14 @@ impl Identity {
        }
    }

-
    pub fn initialize<'a, R: WriteRepository + cob::Store, G>(
+
    pub fn initialize<'a, R, G>(
        doc: &Doc,
        store: &'a R,
        signer: &Device<G>,
    ) -> Result<IdentityMut<'a, R>, cob::store::Error>
    where
        G: crypto::signature::Signer<crypto::Signature>,
+
        R: WriteRepository + cob::Store<Namespace = NodeId>,
    {
        let mut store = cob::store::Store::open(store)?;
        let (id, identity) = Transaction::<Identity, _>::initial(
@@ -227,7 +229,7 @@ impl Identity {
    }

    /// Get a proposal mutably.
-
    pub fn get_mut<'a, R: WriteRepository + cob::Store>(
+
    pub fn get_mut<'a, R: WriteRepository + cob::Store<Namespace = NodeId>>(
        id: &ObjectId,
        repo: &'a R,
    ) -> Result<IdentityMut<'a, R>, store::Error> {
@@ -248,7 +250,7 @@ impl Identity {
        Self::get(&oid, repo).map_err(RepositoryError::from)
    }

-
    pub fn load_mut<R: WriteRepository + cob::Store>(
+
    pub fn load_mut<R: WriteRepository + cob::Store<Namespace = NodeId>>(
        repo: &R,
    ) -> Result<IdentityMut<R>, RepositoryError> {
        let oid = repo.identity_root()?;
@@ -892,7 +894,7 @@ impl<R> fmt::Debug for IdentityMut<'_, R> {

impl<R> IdentityMut<'_, R>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
{
    /// Reload the identity data from storage.
    pub fn reload(&mut self) -> Result<(), store::Error> {
modified radicle/src/cob/issue.rs
@@ -17,6 +17,7 @@ use crate::cob::thread::{Comment, CommentId, Thread};
use crate::cob::{op, store, ActorId, Embed, EntryId, ObjectId, TypeName};
use crate::identity::doc::DocError;
use crate::node::device::Device;
+
use crate::node::NodeId;
use crate::prelude::{Did, Doc, ReadRepository, RepoId};
use crate::storage::{HasRepoId, RepositoryError, WriteRepository};

@@ -589,7 +590,7 @@ impl<R, C> std::fmt::Debug for IssueMut<'_, '_, R, C> {

impl<R, C> IssueMut<'_, '_, R, C>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
    C: cob::cache::Update<Issue>,
{
    /// Reload the issue data from storage.
@@ -792,7 +793,7 @@ impl IssueCounts {

impl<'a, R> Issues<'a, R>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    /// Open an issues store.
    pub fn open(repository: &'a R) -> Result<Self, RepositoryError> {
@@ -805,7 +806,7 @@ where

impl<'a, R> Issues<'a, R>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
{
    /// Create a new issue.
    pub fn create<'g, G, C>(
modified radicle/src/cob/issue/cache.rs
@@ -10,6 +10,7 @@ use crate::cob::cache::{Remove, StoreReader, StoreWriter, Update};
use crate::cob::store;
use crate::cob::{Embed, Label, ObjectId, TypeName, Uri};
use crate::node::device::Device;
+
use crate::node::NodeId;
use crate::prelude::{Did, RepoId};
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};

@@ -107,7 +108,7 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
        signer: &Device<G>,
    ) -> Result<IssueMut<'a, 'g, R, C>, super::Error>
    where
-
        R: ReadRepository + WriteRepository + cob::Store,
+
        R: ReadRepository + WriteRepository + cob::Store<Namespace = NodeId>,
        G: crypto::signature::Signer<crypto::Signature>,
        C: Update<Issue>,
    {
@@ -127,7 +128,7 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
    pub fn remove<G>(&mut self, id: &IssueId, signer: &Device<G>) -> Result<(), super::Error>
    where
        G: crypto::signature::Signer<crypto::Signature>,
-
        R: ReadRepository + SignRepository + cob::Store,
+
        R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
        C: Remove<Issue>,
    {
        self.store.remove(id, signer)?;
@@ -199,7 +200,7 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {

impl<'a, R> Cache<super::Issues<'a, R>, cache::NoCache>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    /// Get a `Cache` that does no write-through modifications and
    /// uses the [`super::Issues`] store for all reads and writes.
modified radicle/src/cob/job.rs
@@ -21,6 +21,7 @@ use crate::cob::{EntryId, ObjectId, TypeName};
use crate::crypto::ssh::ExtendedSignature;
use crate::git;
use crate::node::device::Device;
+
use crate::node::NodeId;
use crate::prelude::ReadRepository;
use crate::storage::{Oid, WriteRepository};

@@ -315,7 +316,7 @@ impl<R> std::fmt::Debug for JobMut<'_, '_, R> {

impl<R> JobMut<'_, '_, R>
where
-
    R: WriteRepository + cob::Store,
+
    R: WriteRepository + cob::Store<Namespace = NodeId>,
{
    /// Reload the COB from storage.
    pub fn reload(&mut self) -> Result<(), store::Error> {
@@ -397,7 +398,7 @@ impl<'a, R> Deref for JobStore<'a, R> {

impl<'a, R> JobStore<'a, R>
where
-
    R: WriteRepository + ReadRepository + cob::Store,
+
    R: WriteRepository + ReadRepository + cob::Store<Namespace = NodeId>,
{
    pub fn open(repository: &'a R) -> Result<Self, store::Error> {
        let raw = store::Store::open(repository)?;
modified radicle/src/cob/patch.rs
@@ -1976,7 +1976,7 @@ pub struct PatchMut<'a, 'g, R, C> {
impl<'a, 'g, R, C> PatchMut<'a, 'g, R, C>
where
    C: cob::cache::Update<Patch>,
-
    R: ReadRepository + SignRepository + cob::Store,
+
    R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
{
    pub fn new(id: ObjectId, patch: Patch, cache: &'g mut Cache<Patches<'a, R>, C>) -> Self {
        Self {
@@ -2507,7 +2507,7 @@ where

impl<'a, R> Patches<'a, R>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    /// Open a patches store.
    pub fn open(repository: &'a R) -> Result<Self, RepositoryError> {
@@ -2590,7 +2590,7 @@ where

impl<'a, R> Patches<'a, R>
where
-
    R: ReadRepository + SignRepository + cob::Store,
+
    R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
{
    /// Open a new patch.
    pub fn create<'g, C, G>(
modified radicle/src/cob/patch/cache.rs
@@ -15,8 +15,8 @@ use crate::prelude::RepoId;
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};

use super::{
-
    ByRevision, MergeTarget, Patch, PatchCounts, PatchId, PatchMut, Revision, RevisionId, State,
-
    Status,
+
    ByRevision, MergeTarget, NodeId, Patch, PatchCounts, PatchId, PatchMut, Revision, RevisionId,
+
    State, Status,
};

/// A set of read-only methods for a [`Patch`] store.
@@ -119,7 +119,7 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
        signer: &Device<G>,
    ) -> Result<PatchMut<'a, 'g, R, C>, super::Error>
    where
-
        R: WriteRepository + cob::Store,
+
        R: WriteRepository + cob::Store<Namespace = NodeId>,
        G: crypto::signature::Signer<crypto::Signature>,
        C: Update<Patch>,
    {
@@ -149,7 +149,7 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
        signer: &Device<G>,
    ) -> Result<PatchMut<'a, 'g, R, C>, super::Error>
    where
-
        R: WriteRepository + cob::Store,
+
        R: WriteRepository + cob::Store<Namespace = NodeId>,
        G: crypto::signature::Signer<crypto::Signature>,
        C: Update<Patch>,
    {
@@ -170,7 +170,7 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
    pub fn remove<G>(&mut self, id: &PatchId, signer: &Device<G>) -> Result<(), super::Error>
    where
        G: crypto::signature::Signer<crypto::Signature>,
-
        R: ReadRepository + SignRepository + cob::Store,
+
        R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
        C: Remove<Patch>,
    {
        self.store.remove(id, signer)?;
@@ -276,7 +276,7 @@ where

impl<'a, R> Cache<super::Patches<'a, R>, cache::NoCache>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    /// Get a `Cache` that does no write-through modifications and
    /// uses the [`super::Patches`] store for all reads and writes.
@@ -488,7 +488,7 @@ impl Iterator for NoCacheIter<'_> {

impl<R> Patches for Cache<super::Patches<'_, R>, cache::NoCache>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
{
    type Error = super::Error;
    type Iter<'b>
modified radicle/src/cob/store.rs
@@ -174,7 +174,7 @@ where

impl<'a, T, R> Store<'a, T, R>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
    T: CobWithType,
{
    /// Open a new generic store.
@@ -190,7 +190,7 @@ where

impl<T, R> Store<'_, T, R>
where
-
    R: ReadRepository + cob::Store,
+
    R: ReadRepository + cob::Store<Namespace = NodeId>,
    T: Cob + cob::Evaluate<R>,
{
    pub fn transaction(
@@ -204,7 +204,7 @@ where

impl<T, R> Store<'_, T, R>
where
-
    R: ReadRepository + SignRepository + cob::Store,
+
    R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
    T: Cob + cob::Evaluate<R>,
    T::Action: Serialize,
{
@@ -423,7 +423,7 @@ where
        Self: From<Tx>,
        G: crypto::signature::Signer<crypto::Signature>,
        F: FnOnce(&mut Tx, &R) -> Result<(), Error>,
-
        R: ReadRepository + SignRepository + cob::Store,
+
        R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
        T::Action: Serialize + Clone,
    {
        let mut tx = Tx::from(Transaction::default());
@@ -487,7 +487,7 @@ where
        signer: &Device<G>,
    ) -> Result<(T, EntryId), Error>
    where
-
        R: ReadRepository + SignRepository + cob::Store,
+
        R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
        T::Action: Serialize + Clone,
        G: crypto::signature::Signer<crypto::Signature>,
    {
modified radicle/src/profile.rs
@@ -605,7 +605,7 @@ impl Home {
        repository: &'a R,
    ) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreReader>, Error>
    where
-
        R: ReadRepository + cob::Store,
+
        R: ReadRepository + cob::Store<Namespace = NodeId>,
    {
        let db = self.cobs_db()?;
        let store = cob::issue::Issues::open(repository)?;
@@ -621,7 +621,7 @@ impl Home {
        repository: &'a R,
    ) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreWriter>, Error>
    where
-
        R: ReadRepository + cob::Store,
+
        R: ReadRepository + cob::Store<Namespace = NodeId>,
    {
        let db = self.cobs_db_mut()?;
        let store = cob::issue::Issues::open(repository)?;
@@ -637,7 +637,7 @@ impl Home {
        repository: &'a R,
    ) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreReader>, Error>
    where
-
        R: ReadRepository + cob::Store,
+
        R: ReadRepository + cob::Store<Namespace = NodeId>,
    {
        let db = self.cobs_db()?;
        let store = cob::patch::Patches::open(repository)?;
@@ -653,7 +653,7 @@ impl Home {
        repository: &'a R,
    ) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreWriter>, Error>
    where
-
        R: ReadRepository + cob::Store,
+
        R: ReadRepository + cob::Store<Namespace = NodeId>,
    {
        let db = self.cobs_db_mut()?;
        let store = cob::patch::Patches::open(repository)?;
modified radicle/src/storage/git/cob.rs
@@ -11,20 +11,18 @@ use storage::RepositoryError;
use storage::SignRepository;
use storage::ValidateRepository;

+
use crate::git;
use crate::git::*;
use crate::identity;
use crate::identity::doc::DocError;
use crate::node::device::Device;
+
use crate::node::NodeId;
use crate::storage;
use crate::storage::Error;
use crate::storage::{
    git::{Remote, Remotes, Validations},
    ReadRepository, Verified,
};
-
use crate::{
-
    git, identity,
-
    identity::{doc::DocError, PublicKey},
-
};

use super::{RemoteId, Repository};

@@ -92,6 +90,8 @@ impl cob::object::Storage for Repository {
    type UpdateError = git2::Error;
    type RemoveError = git2::Error;

+
    type Namespace = NodeId;
+

    fn objects(
        &self,
        typename: &cob::TypeName,
@@ -146,13 +146,13 @@ impl cob::object::Storage for Repository {

    fn update(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &cob::TypeName,
        object_id: &cob::ObjectId,
        entry: &cob::EntryId,
    ) -> Result<(), Self::UpdateError> {
        self.backend.reference(
-
            git::refs::storage::cob(identifier, typename, object_id).as_str(),
+
            git::refs::storage::cob(namespace, typename, object_id).as_str(),
            (*entry).into(),
            true,
            &format!(
@@ -166,13 +166,13 @@ impl cob::object::Storage for Repository {

    fn remove(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &cob::TypeName,
        object_id: &cob::ObjectId,
    ) -> Result<(), Self::RemoveError> {
        let mut reference = self
            .backend
-
            .find_reference(git::refs::storage::cob(identifier, typename, object_id).as_str())?;
+
            .find_reference(git::refs::storage::cob(namespace, typename, object_id).as_str())?;

        reference.delete()
    }
@@ -377,6 +377,8 @@ impl<R: storage::WriteRepository> cob::object::Storage for DraftStore<'_, R> {
    type UpdateError = git2::Error;
    type RemoveError = git2::Error;

+
    type Namespace = NodeId;
+

    fn objects(
        &self,
        typename: &cob::TypeName,
@@ -421,13 +423,13 @@ impl<R: storage::WriteRepository> cob::object::Storage for DraftStore<'_, R> {

    fn update(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &cob::TypeName,
        object_id: &cob::ObjectId,
        entry: &cob::history::EntryId,
    ) -> Result<(), Self::UpdateError> {
        self.repo.raw().reference(
-
            git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
+
            git::refs::storage::draft::cob(namespace, typename, object_id).as_str(),
            (*entry).into(),
            true,
            &format!(
@@ -441,12 +443,12 @@ impl<R: storage::WriteRepository> cob::object::Storage for DraftStore<'_, R> {

    fn remove(
        &self,
-
        identifier: &PublicKey,
+
        namespace: &Self::Namespace,
        typename: &cob::TypeName,
        object_id: &cob::ObjectId,
    ) -> Result<(), Self::RemoveError> {
        let mut reference = self.repo.raw().find_reference(
-
            git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
+
            git::refs::storage::draft::cob(namespace, typename, object_id).as_str(),
        )?;

        reference.delete()
modified radicle/src/test/storage.rs
@@ -364,6 +364,8 @@ impl radicle_cob::object::Storage for MockRepository {
    type UpdateError = Infallible;
    type RemoveError = Infallible;

+
    type Namespace = crypto::PublicKey;
+

    fn objects(
        &self,
        _typename: &radicle_cob::TypeName,
@@ -384,7 +386,7 @@ impl radicle_cob::object::Storage for MockRepository {

    fn update(
        &self,
-
        _identifier: &radicle_crypto::PublicKey,
+
        _namespace: &Self::Namespace,
        _typename: &radicle_cob::TypeName,
        _object_id: &radicle_cob::ObjectId,
        _entry: &radicle_cob::EntryId,
@@ -394,7 +396,7 @@ impl radicle_cob::object::Storage for MockRepository {

    fn remove(
        &self,
-
        _identifier: &radicle_crypto::PublicKey,
+
        _namespace: &Self::Namespace,
        _typename: &radicle_cob::TypeName,
        _object_id: &radicle_cob::ObjectId,
    ) -> Result<(), Self::RemoveError> {