Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
radicle/sigrefs: Merge `SignedRefs{,At}`
Lorenz Leutgeb committed 1 month ago
commit ec36d1c3aee2ae6fa2fab240392320e0323427c1
parent 0701123
15 files changed +147 -193
modified crates/radicle-cli/src/commands/inspect.rs
@@ -75,7 +75,7 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
                    term::format::tertiary(remote.to_human()),
                    term::format::secondary(refs.at),
                    match sigrefs {
-
                        Ok(refs) => {
+
                        Ok(Some(refs)) => {
                            let mut level = refs.feature_level();

                            // For their own refs, be more strict, and interpret
@@ -101,6 +101,9 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
                        Err(err) => {
                            term::format::negative(err.to_string())
                        }
+
                        Ok(None) => {
+
                            term::format::negative("missing".to_string())
+
                        }
                    }
                );
            }
modified crates/radicle-fetch/src/sigrefs.rs
@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use std::ops::Not as _;

-
pub use radicle::storage::refs::SignedRefsAt;
+
pub use radicle::storage::refs::SignedRefs;
pub use radicle::storage::{git::Validation, Validations};
use radicle::{crypto::PublicKey, storage::ValidateRepository};

@@ -23,7 +23,7 @@ pub mod error {

pub(crate) fn validate(
    repo: &impl ValidateRepository,
-
    sigrefs: SignedRefsAt,
+
    sigrefs: SignedRefs,
) -> Result<Option<Validations>, radicle::storage::Error> {
    let remote = radicle::storage::Remote::new(sigrefs);
    let validations = repo.validate_remote(&remote)?;
@@ -33,5 +33,5 @@ pub(crate) fn validate(
/// The sigrefs found for each remote.
pub(crate) type RemoteRefs = BTreeMap<
    PublicKey,
-
    Result<Option<SignedRefsAt>, radicle::storage::refs::sigrefs::read::error::Read>,
+
    Result<Option<SignedRefs>, radicle::storage::refs::sigrefs::read::error::Read>,
>;
modified crates/radicle-fetch/src/state.rs
@@ -17,7 +17,7 @@ use crate::git;
use crate::git::packfile::Keepfile;
use crate::git::refs::{Applied, Update};
use crate::git::repository;
-
use crate::sigrefs::SignedRefsAt;
+
use crate::sigrefs::SignedRefs;
use crate::stage;
use crate::stage::ProtocolStage;
use crate::{refs, sigrefs, transport, Handle};
@@ -529,8 +529,8 @@ impl FetchState {
                (Ok(Some(refs)), false) => {
                    let level_reachable = refs.feature_level();

-
                    match SignedRefsAt::load(remote, handle.repository()) {
-
                        Ok(Some(SignedRefsAt { at, .. })) => {
+
                    match SignedRefs::load(remote, handle.repository()) {
+
                        Ok(Some(SignedRefs { at, .. })) => {
                            // Prune non-delegates if they're behind or
                            // diverged. A diverged case is non-fatal for
                            // delegates.
@@ -576,8 +576,8 @@ impl FetchState {
                (Ok(Some(refs)), true) => {
                    let level_reachable = refs.feature_level();

-
                    match SignedRefsAt::load(remote, handle.repository()) {
-
                        Ok(Some(SignedRefsAt { at, .. })) => {
+
                    match SignedRefs::load(remote, handle.repository()) {
+
                        Ok(Some(SignedRefs { at, .. })) => {
                            let ancestry = repository::ancestry(handle.repository(), at, refs.at)?;
                            if matches!(ancestry, repository::Ancestry::Behind) {
                                log::trace!(
@@ -754,10 +754,10 @@ where
    pub fn load(
        &self,
        remote: &PublicKey,
-
    ) -> Result<Option<SignedRefsAt>, radicle::storage::refs::sigrefs::read::error::Read> {
+
    ) -> Result<Option<SignedRefs>, radicle::storage::refs::sigrefs::read::error::Read> {
        match self.state.sigrefs.get(remote) {
-
            None => SignedRefsAt::load(*remote, self.handle.repository()),
-
            Some(tip) => SignedRefsAt::load_at(*tip, *remote, self.handle.repository()).map(Some),
+
            None => SignedRefs::load(*remote, self.handle.repository()),
+
            Some(tip) => SignedRefs::load_at(*tip, *remote, self.handle.repository()),
        }
    }

@@ -799,7 +799,7 @@ where
    // with it.
    fn validate_remote(&self, remote: &Remote) -> Result<Validations, storage::Error> {
        // Contains a copy of the signed refs of this remote.
-
        let mut signed = BTreeMap::from((*remote.refs.sigrefs).clone());
+
        let mut signed = BTreeMap::from((*remote.refs).clone());
        let mut validations = Validations::default();
        let mut has_sigrefs = false;

modified crates/radicle-node/src/test/peer.rs
@@ -17,7 +17,7 @@ use radicle::node::UserAgent;
use radicle::node::{address, Alias, ConnectOptions};
use radicle::rad;
use radicle::storage::refs;
-
use radicle::storage::refs::{RefsAt, SignedRefsAt};
+
use radicle::storage::refs::{RefsAt, SignedRefs};
use radicle::storage::{ReadRepository, RemoteRepository};
use radicle::Storage;

@@ -362,7 +362,7 @@ where
        ann.into().signed(self.signer()).into()
    }

-
    pub fn signed_refs_at(&self, root: Oid) -> SignedRefsAt {
+
    pub fn signed_refs_at(&self, root: Oid) -> SignedRefs {
        arbitrary::with_gen(8, |g| {
            refs::arbitrary::signed_refs_at(g, root, self.signer())
        })
modified crates/radicle/src/identity/doc/update.rs
@@ -250,13 +250,13 @@ fn verify_delegates(
    let mut missing = Vec::with_capacity(dids.len());

    for did in dids {
-
        match refs::SignedRefsAt::load((*did).into(), repo)
+
        match refs::SignedRefs::load((*did).into(), repo)
            .map_err(|err| storage::Error::Refs(storage::refs::Error::Read(err)))?
        {
            None => {
                missing.push(error::DelegateVerification::MissingDelegate { did: *did });
            }
-
            Some(refs::SignedRefsAt { sigrefs, .. }) => {
+
            Some(sigrefs) => {
                if sigrefs.get(&canonical).is_none() {
                    missing.push(error::DelegateVerification::MissingDefaultBranch {
                        branch: canonical.to_ref_string(),
modified crates/radicle/src/rad.rs
@@ -15,7 +15,7 @@ use crate::identity::project::{Project, ProjectName};
use crate::node::device::Device;
use crate::storage::git::transport;
use crate::storage::git::Repository;
-
use crate::storage::refs::SignedRefsAt;
+
use crate::storage::refs::SignedRefs;
use crate::storage::RepositoryError;
use crate::storage::{ReadRepository as _, RemoteId, SignRepository as _};
use crate::storage::{WriteRepository, WriteStorage};
@@ -55,7 +55,7 @@ pub fn init<G, S>(
    visibility: Visibility,
    signer: &Device<G>,
    storage: S,
-
) -> Result<(RepoId, identity::Doc, SignedRefsAt), InitError>
+
) -> Result<(RepoId, identity::Doc, SignedRefs), InitError>
where
    G: crypto::signature::Signer<crypto::Signature>,
    S: WriteStorage,
@@ -102,7 +102,7 @@ fn init_configure<G>(
    url: &git::Url,
    identity: git::Oid,
    signer: &Device<G>,
-
) -> Result<SignedRefsAt, InitError>
+
) -> Result<SignedRefs, InitError>
where
    G: crypto::signature::Signer<crypto::Signature>,
{
modified crates/radicle/src/storage.rs
@@ -26,7 +26,7 @@ use crate::identity::{Identity, RepoId};
use crate::node::device::Device;
use crate::node::SyncedAt;
use crate::storage::git::NAMESPACES_GLOB;
-
use crate::storage::refs::{FeatureLevel, Refs, SignedRefsAt};
+
use crate::storage::refs::{FeatureLevel, Refs, SignedRefs};

use self::refs::RefsAt;
use crate::git::UserInfo;
@@ -36,13 +36,13 @@ pub enum SignedRefsInfo {
    /// Repositories with this set to `None` are ones that are seeded but not forked.
    None,
    /// Local signed refs, if any.
-
    Some(refs::SignedRefsAt),
+
    Some(refs::SignedRefs),
    NeedsMigration,
}

impl SignedRefsInfo {
    pub(crate) fn new(
-
        result: Result<Option<SignedRefsAt>, refs::sigrefs::read::error::Read>,
+
        result: Result<Option<SignedRefs>, refs::sigrefs::read::error::Read>,
    ) -> Result<Self, refs::sigrefs::read::error::Read> {
        Ok(match result {
            Ok(Some(refs))
@@ -385,7 +385,7 @@ impl IntoIterator for Remotes {
    }
}

-
impl From<Remotes> for RandomMap<RemoteId, SignedRefsAt> {
+
impl From<Remotes> for RandomMap<RemoteId, SignedRefs> {
    fn from(other: Remotes) -> Self {
        let mut remotes = RandomMap::with_hasher(fastrand::Rng::new().into());

@@ -401,12 +401,12 @@ impl From<Remotes> for RandomMap<RemoteId, SignedRefsAt> {
pub struct Remote {
    /// Git references published under this remote, and their hashes.
    #[serde(flatten)]
-
    pub refs: SignedRefsAt,
+
    pub refs: SignedRefs,
}

impl Remote {
    /// Create a new remotes object.
-
    pub fn new(refs: impl Into<SignedRefsAt>) -> Self {
+
    pub fn new(refs: impl Into<SignedRefs>) -> Self {
        Self { refs: refs.into() }
    }

@@ -428,7 +428,7 @@ impl Remote {
}

impl Deref for Remote {
-
    type Target = SignedRefsAt;
+
    type Target = SignedRefs;

    fn deref(&self) -> &Self::Target {
        &self.refs
@@ -697,14 +697,14 @@ pub trait WriteRepository: ReadRepository + SignRepository {
/// Allows signing refs.
pub trait SignRepository {
    /// Sign the repository's refs under the `refs/rad/sigrefs` branch.
-
    fn sign_refs<G>(&self, signer: &Device<G>) -> Result<SignedRefsAt, RepositoryError>
+
    fn sign_refs<G>(&self, signer: &Device<G>) -> Result<SignedRefs, RepositoryError>
    where
        G: crypto::signature::Signer<crypto::Signature>;

    /// Sign the repository's refs under the `refs/rad/sigrefs` branch, even if unchanged.
    ///
    /// Most users will prefer [`Self::sign_refs`].
-
    fn force_sign_refs<G>(&self, signer: &Device<G>) -> Result<SignedRefsAt, RepositoryError>
+
    fn force_sign_refs<G>(&self, signer: &Device<G>) -> Result<SignedRefs, RepositoryError>
    where
        G: crypto::signature::Signer<crypto::Signature>;
}
modified crates/radicle/src/storage/git.rs
@@ -18,7 +18,7 @@ use crate::identity::doc::DocError;
use crate::identity::{CanonicalRefs, Doc, DocAt, RepoId};
use crate::identity::{Identity, Project};
use crate::node::device::Device;
-
use crate::storage::refs::{FeatureLevel, Refs, SignedRefsAt};
+
use crate::storage::refs::{FeatureLevel, Refs, SignedRefs};
use crate::storage::{refs, SignedRefsInfo};
use crate::storage::{
    ReadRepository, ReadStorage, Remote, Remotes, RepositoryInfo, SetHead, SignRepository,
@@ -166,7 +166,7 @@ impl ReadStorage for Storage {
                }
            };
            // Nb. This will be `None` if they were not found.
-
            let refs = SignedRefsInfo::new(refs::SignedRefsAt::load(self.info.key, &repo))
+
            let refs = SignedRefsInfo::new(refs::SignedRefs::load(self.info.key, &repo))
                .map_err(|err| Error::Refs(refs::Error::Read(err)))?;

            let synced_at = match &refs {
@@ -201,7 +201,7 @@ impl WriteStorage for Storage {
        let repo = self.repository(rid)?;
        // N.b. we remove the repository if the `local` peer has no
        // `rad/sigrefs`. There's no risk of them corrupting data.
-
        let has_sigrefs = SignedRefsAt::load(self.info.key, &repo)
+
        let has_sigrefs = SignedRefs::load(self.info.key, &repo)
            .map_err(|err| RepositoryError::from(refs::Error::Read(err)))?
            .is_some();
        if has_sigrefs {
@@ -258,7 +258,7 @@ impl Storage {
            let repo = self.repository(*rid)?;
            let (_, head) = repo.head()?;

-
            let refs = SignedRefsInfo::new(refs::SignedRefsAt::load(self.info.key, &repo))
+
            let refs = SignedRefsInfo::new(refs::SignedRefs::load(self.info.key, &repo))
                .map_err(|err| Error::Refs(refs::Error::Read(err)))?;

            let synced_at = match &refs {
@@ -631,7 +631,7 @@ impl RemoteRepository for Repository {
    }

    fn remote(&self, remote: &RemoteId) -> Result<Remote, refs::Error> {
-
        let refs = SignedRefsAt::load(*remote, self)?;
+
        let refs = SignedRefs::load(*remote, self)?;
        let refs = refs.ok_or_else(|| {
            refs::Error::Read(refs::sigrefs::read::error::Read::MissingSigrefs {
                namespace: *remote,
@@ -656,7 +656,7 @@ impl RemoteRepository for Repository {
impl ValidateRepository for Repository {
    fn validate_remote(&self, remote: &Remote) -> Result<Validations, Error> {
        // Contains a copy of the signed refs of this remote.
-
        let mut signed = BTreeMap::from((*remote.refs.sigrefs).clone());
+
        let mut signed = BTreeMap::from((*remote.refs).clone());
        let mut failures = Validations::default();
        let mut has_sigrefs = false;

@@ -1007,14 +1007,14 @@ impl SignRepository for Repository {
    fn sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
        &self,
        signer: &Device<G>,
-
    ) -> Result<SignedRefsAt, RepositoryError> {
+
    ) -> Result<SignedRefs, RepositoryError> {
        self.sign_refs_with(signer, false)
    }

    fn force_sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
        &self,
        signer: &Device<G>,
-
    ) -> Result<SignedRefsAt, RepositoryError> {
+
    ) -> Result<SignedRefs, RepositoryError> {
        self.sign_refs_with(signer, true)
    }
}
@@ -1024,7 +1024,7 @@ impl Repository {
        &self,
        signer: &Device<G>,
        force: bool,
-
    ) -> Result<SignedRefsAt, RepositoryError> {
+
    ) -> Result<SignedRefs, RepositoryError> {
        let remote = signer.public_key();
        // Ensure the root reference is set, which is checked during sigref verification.
        if self
modified crates/radicle/src/storage/git/cob.rs
@@ -239,7 +239,7 @@ where
    fn sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
        &self,
        signer: &Device<G>,
-
    ) -> Result<storage::refs::SignedRefsAt, RepositoryError> {
+
    ) -> Result<storage::refs::SignedRefs, RepositoryError> {
        // Since this is a draft store, we do not actually want to sign the refs.
        // Instead, we just return the existing signed refs.
        let remote = self.repo.remote(signer.public_key())?;
@@ -250,7 +250,7 @@ where
    fn force_sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
        &self,
        signer: &Device<G>,
-
    ) -> Result<storage::refs::SignedRefsAt, RepositoryError> {
+
    ) -> Result<storage::refs::SignedRefs, RepositoryError> {
        self.sign_refs(signer)
    }
}
modified crates/radicle/src/storage/refs.rs
@@ -21,7 +21,7 @@ use crate::git::raw::ErrorExt as _;
use crate::git::Oid;
use crate::storage;
use crate::storage::refs::sigrefs::read::Tip;
-
use crate::storage::{ReadRepository, RemoteId};
+
use crate::storage::RemoteId;

pub use crate::git::refs::storage::*;

@@ -76,7 +76,7 @@ impl Refs {
        committer: sigrefs::git::Committer,
        repo: &R,
        signer: &S,
-
    ) -> Result<SignedRefsAt, Error>
+
    ) -> Result<SignedRefs, Error>
    where
        R: sigrefs::git::object::Reader + sigrefs::git::object::Writer,
        R: sigrefs::git::reference::Reader + sigrefs::git::reference::Writer,
@@ -94,7 +94,7 @@ impl Refs {
        committer: sigrefs::git::Committer,
        repo: &R,
        signer: &S,
-
    ) -> Result<SignedRefsAt, Error>
+
    ) -> Result<SignedRefs, Error>
    where
        R: sigrefs::git::object::Reader + sigrefs::git::object::Writer,
        R: sigrefs::git::reference::Reader + sigrefs::git::reference::Writer,
@@ -112,7 +112,7 @@ impl Refs {
        repo: &R,
        signer: &S,
        force: bool,
-
    ) -> Result<SignedRefsAt, Error>
+
    ) -> Result<SignedRefs, Error>
    where
        R: sigrefs::git::object::Reader + sigrefs::git::object::Writer,
        R: sigrefs::git::reference::Reader + sigrefs::git::reference::Writer,
@@ -257,12 +257,6 @@ impl From<Refs> for BTreeMap<git::fmt::RefString, Oid> {
    }
}

-
impl From<SignedRefs> for Refs {
-
    fn from(signed: SignedRefs) -> Self {
-
        signed.refs
-
    }
-
}
-

impl<I> From<I> for Refs
where
    I: Iterator<Item = (git::fmt::RefString, Oid)>,
@@ -333,96 +327,10 @@ impl std::fmt::Display for FeatureLevel {
    }
}

-
/// Combination of [`Refs`] and a [`Signature`]. The signature is a cryptographic
-
/// signature over the refs. This allows us to easily verify if a set of refs
-
/// came from a particular key.
-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
-
pub struct SignedRefs {
-
    /// The signed refs.
-
    refs: Refs,
-
    /// The signature of the signer over the refs.
-
    #[serde(skip)]
-
    signature: Signature,
-
    /// This is the remote under which these refs exist, and the public key of the signer.
-
    id: PublicKey,
-

-
    #[serde(skip)]
-
    level: FeatureLevel,
-

-
    /// The [`Oid`] of the parent commit of the commit in which.
-
    #[serde(skip)]
-
    parent: Option<Oid>,
-
}
-

-
impl SignedRefs {
-
    /// Returns the [`NodeId`] of the [`SignedRefs`].
-
    pub fn id(&self) -> NodeId {
-
        self.id
-
    }
-

-
    /// Returns the [`Refs`] of the [`SignedRefs`].
-
    pub fn refs(&self) -> &Refs {
-
        &self.refs
-
    }
-

-
    /// Returns the [`FeatureLevel`] computed for the signed references.
-
    pub fn feature_level(&self) -> FeatureLevel {
-
        self.level
-
    }
-

-
    /// The [`Oid`] of the parent commit, or [`None`] if these signed references
-
    /// were found at a root commit.
-
    pub fn parent(&self) -> Option<&Oid> {
-
        self.parent.as_ref()
-
    }
-

-
    pub fn load<R>(remote: RemoteId, repo: &R) -> Result<Self, sigrefs::read::error::Read>
-
    where
-
        R: HasRepoId,
-
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
-
    {
-
        Self::load_internal(remote, repo, sigrefs::read::Tip::Reference(remote))
-
    }
-

-
    pub fn load_at<R>(
-
        oid: Oid,
-
        remote: RemoteId,
-
        repo: &R,
-
    ) -> Result<Self, sigrefs::read::error::Read>
-
    where
-
        R: HasRepoId,
-
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
-
    {
-
        Self::load_internal(remote, repo, sigrefs::read::Tip::Commit(oid))
-
    }
-

-
    fn load_internal<R>(
-
        remote: RemoteId,
-
        repo: &R,
-
        tip: Tip,
-
    ) -> Result<Self, sigrefs::read::error::Read>
-
    where
-
        R: HasRepoId,
-
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
-
    {
-
        let root = repo.rid();
-
        let latest = sigrefs::SignedRefsReader::new(root, tip, repo, &remote).read()?;
-
        Ok(latest.into_sigrefs_at(remote).sigrefs)
-
    }
-
}
-

-
impl Deref for SignedRefs {
-
    type Target = Refs;
-

-
    fn deref(&self) -> &Self::Target {
-
        &self.refs
-
    }
-
}
-

/// The content-addressable information required to load a remote's
/// `rad/sigrefs`.
///
-
/// Use [`RefsAt::load`] to produce a [`SignedRefsAt`].
+
/// Use [`RefsAt::load`] to produce [`SignedRefs`].
///
/// `RefsAt` can also be used for communicating announcements of updates
/// references to other nodes.
@@ -450,12 +358,12 @@ impl RefsAt {
        Ok(RefsAt { remote, at })
    }

-
    pub fn load<R>(&self, repo: &R) -> Result<SignedRefsAt, sigrefs::read::error::Read>
+
    pub fn load<R>(&self, repo: &R) -> Result<Option<SignedRefs>, sigrefs::read::error::Read>
    where
        R: HasRepoId,
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
    {
-
        SignedRefsAt::load_at(self.at, self.remote, repo)
+
        SignedRefs::load_at(self.at, self.remote, repo)
    }

    pub fn path(&self) -> &git::fmt::Qualified<'_> {
@@ -471,13 +379,52 @@ impl std::fmt::Display for RefsAt {

/// Verified [`SignedRefs`] that keeps track of their content address
/// [`Oid`].
+
///
+
/// The signature is a cryptographic signature over the refs.
+
/// This allows us to easily verify if a set of refs
+
/// came from a particular key.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
-
pub struct SignedRefsAt {
-
    pub sigrefs: SignedRefs,
+
pub struct SignedRefs {
+
    /// The signed refs.
+
    refs: Refs,
+
    /// The signature of the signer over the refs.
+
    #[serde(skip)]
+
    signature: Signature,
+
    /// This is the remote under which these refs exist, and the public key of the signer.
+
    id: PublicKey,
+

+
    #[serde(skip)]
+
    level: FeatureLevel,
+

+
    /// The [`Oid`] of the parent commit of the commit in which.
+
    #[serde(skip)]
+
    parent: Option<Oid>,
+

    pub at: Oid,
}

-
impl SignedRefsAt {
+
impl SignedRefs {
+
    /// Returns the [`NodeId`] of the [`SignedRefs`].
+
    pub fn id(&self) -> NodeId {
+
        self.id
+
    }
+

+
    /// Returns the [`Refs`] of the [`SignedRefs`].
+
    pub fn refs(&self) -> &Refs {
+
        &self.refs
+
    }
+

+
    /// Returns the [`FeatureLevel`] computed for the signed references.
+
    pub fn feature_level(&self) -> FeatureLevel {
+
        self.level
+
    }
+

+
    /// The [`Oid`] of the parent commit, or [`None`] if these signed references
+
    /// were found at a root commit.
+
    pub fn parent(&self) -> Option<&Oid> {
+
        self.parent.as_ref()
+
    }
+

    /// Load the [`SignedRefs`] found under `remote`'s [`SIGREFS_BRANCH`].
    ///
    /// This will return `None` if the branch was not found, all other
@@ -485,42 +432,53 @@ impl SignedRefsAt {
    pub fn load<R>(remote: RemoteId, repo: &R) -> Result<Option<Self>, sigrefs::read::error::Read>
    where
        R: HasRepoId,
-
        R: ReadRepository,
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
    {
-
        let at = match RefsAt::new(repo, remote) {
-
            Ok(RefsAt { at, .. }) => at,
-
            Err(sigrefs::read::error::Read::MissingSigrefs { .. }) => return Ok(None),
-
            Err(e) => return Err(e),
-
        };
-
        Self::load_at(at, remote, repo).map(Some)
+
        Self::load_internal(remote, repo, sigrefs::read::Tip::Reference(remote))
    }

    pub fn load_at<R>(
-
        at: Oid,
+
        oid: Oid,
        remote: RemoteId,
        repo: &R,
-
    ) -> Result<Self, sigrefs::read::error::Read>
+
    ) -> Result<Option<Self>, sigrefs::read::error::Read>
    where
        R: HasRepoId,
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
    {
-
        Ok(Self {
-
            sigrefs: SignedRefs::load_at(at, remote, repo)?,
-
            at,
-
        })
+
        Self::load_internal(remote, repo, sigrefs::read::Tip::Commit(oid))
+
    }
+

+
    fn load_internal<R>(
+
        remote: RemoteId,
+
        repo: &R,
+
        tip: Tip,
+
    ) -> Result<Option<Self>, sigrefs::read::error::Read>
+
    where
+
        R: HasRepoId,
+
        R: sigrefs::git::object::Reader + sigrefs::git::reference::Reader,
+
    {
+
        let root = repo.rid();
+
        match sigrefs::SignedRefsReader::new(root, tip, repo, &remote).read() {
+
            Ok(latest) => Ok(Some(latest.into_sigrefs_at(remote))),
+
            Err(sigrefs::read::error::Read::MissingSigrefs { namespace }) => {
+
                debug_assert_eq!(namespace, remote);
+
                Ok(None)
+
            }
+
            Err(err) => Err(err),
+
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = (&git::fmt::RefString, &Oid)> {
-
        self.sigrefs.refs.iter()
+
        self.refs.iter()
    }
}

-
impl Deref for SignedRefsAt {
-
    type Target = SignedRefs;
+
impl Deref for SignedRefs {
+
    type Target = Refs;

    fn deref(&self) -> &Self::Target {
-
        &self.sigrefs
+
        &self.refs
    }
}

@@ -678,7 +636,7 @@ mod tests {

        {
            // Sanity check: make sure the default branches don't already match between Alice and Bob.
-
            let alice_paris_sigrefs = SignedRefsAt::load(*alice.public_key(), &paris)
+
            let alice_paris_sigrefs = SignedRefs::load(*alice.public_key(), &paris)
                .unwrap()
                .unwrap();
            assert_ne!(
modified crates/radicle/src/storage/refs/arbitrary.rs
@@ -6,7 +6,7 @@ use crate::node::device::Device;

use super::*;

-
pub fn signed_refs_at<S>(g: &mut qcheck::Gen, root: Oid, signer: &Device<S>) -> SignedRefsAt
+
pub fn signed_refs_at<S>(g: &mut qcheck::Gen, root: Oid, signer: &Device<S>) -> SignedRefs
where
    S: crypto::signature::Signer<crypto::Signature>,
{
@@ -22,15 +22,12 @@ where
    }

    let signature = crypto::signature::Signer::sign(signer, &refs.canonical());
-
    let sigrefs = SignedRefs {
+
    SignedRefs {
        refs,
        signature,
        id: *signer.node_id(),
        level: level.unwrap_or_else(|| FeatureLevel::arbitrary(g)),
        parent: Arbitrary::arbitrary(g),
-
    };
-
    SignedRefsAt {
-
        sigrefs,
        at: Oid::from_sha1(Arbitrary::arbitrary(g)),
    }
}
modified crates/radicle/src/storage/refs/sigrefs/read.rs
@@ -19,8 +19,8 @@ use crate::git;
use crate::identity::doc;
use crate::storage::refs::sigrefs::git::{object, reference};
use crate::storage::refs::{
-
    FeatureLevel, Refs, SignedRefs, SignedRefsAt, IDENTITY_ROOT, REFS_BLOB_PATH,
-
    SIGNATURE_BLOB_PATH, SIGREFS_BRANCH,
+
    FeatureLevel, Refs, SignedRefs, IDENTITY_ROOT, REFS_BLOB_PATH, SIGNATURE_BLOB_PATH,
+
    SIGREFS_BRANCH,
};

/// A `rad/sigrefs` that has passed the following verification checks:
@@ -50,15 +50,13 @@ impl VerifiedCommit {
        self.level
    }

-
    pub(crate) fn into_sigrefs_at(self, id: PublicKey) -> SignedRefsAt {
-
        SignedRefsAt {
-
            sigrefs: SignedRefs {
-
                refs: self.commit.refs,
-
                signature: self.commit.signature,
-
                id,
-
                level: self.level,
-
                parent: self.commit.parent,
-
            },
+
    pub(crate) fn into_sigrefs_at(self, id: PublicKey) -> SignedRefs {
+
        SignedRefs {
+
            refs: self.commit.refs,
+
            signature: self.commit.signature,
+
            id,
+
            level: self.level,
+
            parent: self.commit.parent,
            at: self.commit.oid,
        }
    }
modified crates/radicle/src/storage/refs/sigrefs/write.rs
@@ -16,11 +16,11 @@ use crate::git;
use crate::storage::refs::sigrefs::git::{object, reference, Committer};
use crate::storage::refs::sigrefs::read::CommitReader;
use crate::storage::refs::sigrefs::{read, VerifiedCommit};
+
use crate::storage::refs::SignedRefs;
use crate::storage::refs::{
    FeatureLevel, Refs, IDENTITY_ROOT, REFS_BLOB_PATH, SIGNATURE_BLOB_PATH, SIGREFS_BRANCH,
    SIGREFS_PARENT,
};
-
use crate::storage::refs::{SignedRefs, SignedRefsAt};

/// The result of attempting to write signed references using
/// [`SignedRefsWriter`].
@@ -204,16 +204,14 @@ impl Commit {
        self.refs
    }

-
    pub(crate) fn into_sigrefs_at(self, id: PublicKey, level: FeatureLevel) -> SignedRefsAt {
-
        SignedRefsAt {
+
    pub(crate) fn into_sigrefs_at(self, id: PublicKey, level: FeatureLevel) -> SignedRefs {
+
        SignedRefs {
            at: self.oid,
-
            sigrefs: SignedRefs {
-
                id,
-
                signature: self.signature,
-
                refs: self.refs,
-
                level,
-
                parent: self.parent,
-
            },
+
            id,
+
            signature: self.signature,
+
            refs: self.refs,
+
            level,
+
            parent: self.parent,
        }
    }
}
modified crates/radicle/src/test/fixtures.rs
@@ -10,7 +10,7 @@ use crate::node::Alias;
use crate::rad;
use crate::storage::git::transport;
use crate::storage::git::Storage;
-
use crate::storage::refs::SignedRefsAt;
+
use crate::storage::refs::SignedRefs;

/// The birth of the radicle project, January 1st, 2018.
pub const RADICLE_EPOCH: i64 = 1514817556;
@@ -72,7 +72,7 @@ pub fn project<P, G>(
    path: P,
    storage: &Storage,
    signer: &Device<G>,
-
) -> Result<(RepoId, SignedRefsAt, git::raw::Repository, git::raw::Oid), rad::InitError>
+
) -> Result<(RepoId, SignedRefs, git::raw::Repository, git::raw::Oid), rad::InitError>
where
    P: AsRef<Path>,
    G: crypto::signature::Signer<crypto::Signature>,
modified crates/radicle/src/test/storage.rs
@@ -134,7 +134,7 @@ impl WriteStorage for MockStorage {
pub struct MockRepository {
    pub id: RepoId,
    pub doc: DocAt,
-
    pub remotes: HashMap<NodeId, refs::SignedRefsAt>,
+
    pub remotes: HashMap<NodeId, refs::SignedRefs>,
}

impl MockRepository {
@@ -170,7 +170,7 @@ impl self::refs::sigrefs::git::reference::Reader for MockRepository {
                if reference == *refs::SIGREFS_BRANCH {
                    Ok(Some(refs.at))
                } else {
-
                    Ok(refs.sigrefs.get(&reference))
+
                    Ok(refs.get(&reference))
                }
            }
        }
@@ -292,7 +292,7 @@ impl ReadRepository for MockRepository {
        if reference == &*refs::SIGREFS_BRANCH {
            Ok(refs.at)
        } else {
-
            refs.sigrefs.get(reference).ok_or_else(not_found)
+
            refs.get(reference).ok_or_else(not_found)
        }
    }

@@ -370,14 +370,14 @@ impl SignRepository for MockRepository {
    fn sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
        &self,
        _signer: &Device<G>,
-
    ) -> Result<crate::storage::refs::SignedRefsAt, RepositoryError> {
+
    ) -> Result<crate::storage::refs::SignedRefs, RepositoryError> {
        todo!()
    }

    fn force_sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
        &self,
        _signer: &Device<G>,
-
    ) -> Result<crate::storage::refs::SignedRefsAt, RepositoryError> {
+
    ) -> Result<crate::storage::refs::SignedRefs, RepositoryError> {
        todo!()
    }
}