Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle: Rename many more methods using new naming
cloudhead committed 2 years ago
commit 6ca7da276839e5217b340dbfff8cd9095d8464c5
parent 1b026ae0ee3cdc7865ea874a14faab809594c0c1
43 files changed +488 -505
added radicle-cli/examples/rad-seed-and-follow.md
@@ -0,0 +1,15 @@
+
To configure our node's seeding and follow policy, we can use the `rad seed`
+
and `rad follow` commands.
+
For example, let's follow a remote node we know about, and alias it to "eve":
+

+
```
+
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias eve
+
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (eve)
+
```
+

+
Now let's seed one of Eve's repositories:
+

+
```
+
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed --no-fetch
+
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
+
```
deleted radicle-cli/examples/rad-track.md
@@ -1,14 +0,0 @@
-
To configure our node's tracking policy, we can use the `rad track` command.
-
For example, let's track a remote node we know about, and alias it to "eve":
-

-
```
-
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias eve
-
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (eve)
-
```
-

-
Now let's track one of Eve's repositories:
-

-
```
-
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed --no-fetch
-
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
-
```
modified radicle-cli/src/commands/clean.rs
@@ -22,7 +22,7 @@ Usage
    local operator or a delegate of the repository.

    Note that remotes will still be fetched as long as they are
-
    tracked and/or the tracking scope is "all".
+
    followed and/or the follow scope is "all".

Options

modified radicle-cli/src/commands/follow.rs
@@ -100,8 +100,8 @@ pub fn follow(
    let followed = match node.follow(nid, alias.clone()) {
        Ok(updated) => updated,
        Err(e) if e.is_connection_err() => {
-
            let mut config = profile.tracking_mut()?;
-
            config.track_node(&nid, alias.as_deref())?
+
            let mut config = profile.policies_mut()?;
+
            config.follow(&nid, alias.as_deref())?
        }
        Err(e) => return Err(e.into()),
    };
modified radicle-cli/src/commands/init.rs
@@ -60,7 +60,7 @@ pub struct Options {
    pub scope: Scope,
    pub set_upstream: bool,
    pub verbose: bool,
-
    pub track: bool,
+
    pub seed: bool,
}

impl Args for Options {
@@ -77,7 +77,7 @@ impl Args for Options {
        let mut set_upstream = false;
        let mut setup_signing = false;
        let mut scope = Scope::All;
-
        let mut track = true;
+
        let mut seed = true;
        let mut verbose = false;
        let mut visibility = None;

@@ -129,8 +129,8 @@ impl Args for Options {
                Long("no-confirm") => {
                    interactive = Interactive::No;
                }
-
                Long("no-track") => {
-
                    track = false;
+
                Long("no-seed") => {
+
                    seed = false;
                }
                Long("private") => {
                    visibility = Some(Visibility::private([]));
@@ -161,7 +161,7 @@ impl Args for Options {
                interactive,
                set_upstream,
                setup_signing,
-
                track,
+
                seed,
                visibility,
                verbose,
            },
@@ -279,10 +279,10 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
                term::blob(json::to_string_pretty(&proj)?);
            }

-
            // It's important to track our own repositories to make sure that our node signals
+
            // It's important to seed our own repositories to make sure that our node signals
            // interest for them. This ensures that messages relating to them are relayed to us.
-
            if options.track {
-
                cli::project::track(id, options.scope, &mut node, profile)?;
+
            if options.seed {
+
                cli::project::seed(id, options.scope, &mut node, profile)?;
            }

            if options.set_upstream || git::branch_remote(&repo, proj.default_branch()).is_err() {
modified radicle-cli/src/commands/inspect.rs
@@ -173,8 +173,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
            }
        }
        Target::Policy => {
-
            let tracking = profile.tracking()?;
-
            if let Some(repo) = tracking.repo_policy(&rid)? {
+
            let tracking = profile.policies()?;
+
            if let Some(repo) = tracking.seed_policy(&rid)? {
                let tracking = match repo.policy {
                    Policy::Allow => term::format::positive("tracked"),
                    Policy::Block => term::format::negative("blocked"),
modified radicle-cli/src/commands/ls.rs
@@ -17,7 +17,7 @@ Usage
    rad ls [<option>...]

    By default, this command shows you all repositories that you have forked or initialized.
-
    If you wish to see all tracked repositories, use the `--all` option.
+
    If you wish to see all seeded repositories, use the `--all` option.

Options

modified radicle-cli/src/commands/node/policies.rs
@@ -7,7 +7,7 @@ use crate::terminal as term;
use term::Element;

pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
-
    let store = profile.tracking()?;
+
    let store = profile.policies()?;
    let mut t = term::Table::new(term::table::TableOptions::bordered());
    t.push([
        term::format::default(String::from("RID")),
@@ -16,7 +16,7 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
    ]);
    t.divider();

-
    for policy::Repo { id, scope, policy } in store.repo_policies()? {
+
    for policy::Repo { id, scope, policy } in store.seed_policies()? {
        let id = id.to_string();
        let scope = scope.to_string();
        let policy = policy.to_string();
@@ -33,7 +33,7 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
}

pub fn following(profile: &Profile) -> anyhow::Result<()> {
-
    let store = profile.tracking()?;
+
    let store = profile.policies()?;
    let aliases = profile.aliases();
    let mut t = term::Table::new(term::table::TableOptions::bordered());
    t.push([
@@ -43,7 +43,7 @@ pub fn following(profile: &Profile) -> anyhow::Result<()> {
    ]);
    t.divider();

-
    for policy::Node { id, alias, policy } in store.node_policies()? {
+
    for policy::Node { id, alias, policy } in store.follow_policies()? {
        t.push([
            term::format::highlight(Did::from(id).to_string()),
            match alias {
modified radicle-cli/src/commands/patch/common.rs
@@ -23,7 +23,7 @@ fn get_branch(git_ref: git::Qualified) -> git::RefString {
    std::iter::once(head).chain(tail).collect()
}

-
/// Determine the merge target for this patch. This can be any tracked remote's "default" branch,
+
/// Determine the merge target for this patch. This can be any followed remote's "default" branch,
/// as well as your own (eg. `rad/master`).
pub fn get_merge_target(
    storage: &Repository,
modified radicle-cli/src/commands/seed.rs
@@ -131,7 +131,7 @@ pub fn update(
    node: &mut Node,
    profile: &Profile,
) -> Result<(), anyhow::Error> {
-
    let updated = project::track(rid, scope, node, profile)?;
+
    let updated = project::seed(rid, scope, node, profile)?;
    let outcome = if updated { "updated" } else { "exists" };

    term::success!(
@@ -143,7 +143,7 @@ pub fn update(
}

pub fn delete(rid: Id, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
-
    if project::untrack(rid, node, profile)? {
+
    if project::unseed(rid, node, profile)? {
        term::success!("Seeding policy for {} removed", term::format::tertiary(rid));
    }
    Ok(())
modified radicle-cli/src/commands/sync.rs
@@ -279,8 +279,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
        }
        Operation::Synchronize(SyncMode::Repo { mode, direction }) => {
            if [SyncDirection::Fetch, SyncDirection::Both].contains(&direction) {
-
                if !profile.tracking()?.is_repo_tracked(&rid)? {
-
                    anyhow::bail!("repository {rid} is not tracked");
+
                if !profile.policies()?.is_repo_seeded(&rid)? {
+
                    anyhow::bail!("repository {rid} is not seeded");
                }
                let results = fetch(rid, mode.clone(), options.timeout, &mut node)?;
                let success = results.success().count();
modified radicle-cli/src/commands/unfollow.rs
@@ -79,8 +79,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
    let unfollowed = match node.unfollow(nid) {
        Ok(updated) => updated,
        Err(e) if e.is_connection_err() => {
-
            let mut config = profile.tracking_mut()?;
-
            config.untrack_node(&nid)?
+
            let mut config = profile.policies_mut()?;
+
            config.unfollow(&nid)?
        }
        Err(e) => return Err(e.into()),
    };
modified radicle-cli/src/project.rs
@@ -52,9 +52,9 @@ impl<'a> SetupRemote<'a> {
    }
}

-
/// Track a repository by first trying to track through the node, and if the node isn't running,
-
/// by updating the tracking database directly.
-
pub fn track(
+
/// Seed a repository by first trying to seed through the node, and if the node isn't running,
+
/// by updating the policy database directly.
+
pub fn seed(
    rid: Id,
    scope: Scope,
    node: &mut Node,
@@ -63,21 +63,21 @@ pub fn track(
    match node.seed(rid, scope) {
        Ok(updated) => Ok(updated),
        Err(e) if e.is_connection_err() => {
-
            let mut config = profile.tracking_mut()?;
-
            config.track_repo(&rid, scope).map_err(|e| e.into())
+
            let mut config = profile.policies_mut()?;
+
            config.seed(&rid, scope).map_err(|e| e.into())
        }
        Err(e) => Err(e.into()),
    }
}

-
/// Untrack a repository by first trying to untrack through the node, and if the node isn't running,
-
/// by updating the tracking database directly.
-
pub fn untrack(rid: Id, node: &mut Node, profile: &Profile) -> Result<bool, anyhow::Error> {
+
/// Unseed a repository by first trying to unseed through the node, and if the node isn't running,
+
/// by updating the policy database directly.
+
pub fn unseed(rid: Id, node: &mut Node, profile: &Profile) -> Result<bool, anyhow::Error> {
    match node.unseed(rid) {
        Ok(updated) => Ok(updated),
        Err(e) if e.is_connection_err() => {
-
            let mut config = profile.tracking_mut()?;
-
            config.untrack_repo(&rid).map_err(|e| e.into())
+
            let mut config = profile.policies_mut()?;
+
            config.unseed(&rid).map_err(|e| e.into())
        }
        Err(e) => Err(e.into()),
    }
modified radicle-cli/tests/commands.rs
@@ -741,14 +741,14 @@ fn rad_clean() {
}

#[test]
-
fn rad_track() {
+
fn rad_seed_and_follow() {
    let mut environment = Environment::new();
    let alice = environment.node(Config::test(Alias::new("alice")));
    let working = tempfile::tempdir().unwrap();
    let alice = alice.spawn();

    test(
-
        "examples/rad-track.md",
+
        "examples/rad-seed-and-follow.md",
        working.path(),
        Some(&alice.home),
        [],
@@ -897,7 +897,7 @@ fn rad_sync_without_node() {
    let mut eve = environment.node(Config::test(Alias::new("eve")));

    let rid = Id::from_urn("rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5").unwrap();
-
    eve.tracking.track_repo(&rid, Scope::All).unwrap();
+
    eve.policies.seed(&rid, Scope::All).unwrap();

    formula(&environment.tmp(), "examples/rad-sync-without-node.md")
        .unwrap()
modified radicle-fetch/src/handle.rs
@@ -9,14 +9,14 @@ use radicle::prelude::Doc;
use radicle::storage::git::Repository;
use radicle::storage::ReadRepository;

-
use crate::tracking::{BlockList, Tracked};
+
use crate::policy::{Allowed, BlockList};
use crate::transport::{ConnectionStream, Transport};

/// The handle used for pulling or cloning changes from a remote peer.
pub struct Handle<S> {
    pub(crate) local: PublicKey,
    pub(crate) repo: Repository,
-
    pub(crate) tracked: Tracked,
+
    pub(crate) allowed: Allowed,
    pub(crate) transport: Transport<S>,
    /// The set of keys we will ignore when fetching from a
    /// remote. This set can be constructed using the tracking
@@ -34,7 +34,7 @@ impl<S> Handle<S> {
    pub fn new(
        local: PublicKey,
        repo: Repository,
-
        tracked: Tracked,
+
        follow: Allowed,
        blocked: BlockList,
        connection: S,
    ) -> Result<Self, error::Init>
@@ -47,7 +47,7 @@ impl<S> Handle<S> {
        Ok(Self {
            local,
            repo,
-
            tracked,
+
            allowed: follow,
            transport,
            blocked,
            interrupt: Arc::new(AtomicBool::new(false)),
@@ -78,8 +78,8 @@ impl<S> Handle<S> {
        Ok(self.repo.identity_doc_at(head)?.doc)
    }

-
    pub fn tracked(&self) -> Tracked {
-
        self.tracked.clone()
+
    pub fn allowed(&self) -> Allowed {
+
        self.allowed.clone()
    }
}

@@ -101,13 +101,13 @@ pub mod error {

    #[derive(Debug, Error)]
    pub enum Tracking {
-
        #[error("failed to find tracking policy for {rid}")]
+
        #[error("failed to find policy for {rid}")]
        FailedPolicy {
            rid: Id,
            #[source]
            err: policy::store::Error,
        },
-
        #[error("cannot fetch {rid} as it is not tracked")]
+
        #[error("cannot fetch {rid} as it is not seeded")]
        BlockedPolicy { rid: Id },
        #[error("failed to get tracking nodes for {rid}")]
        FailedNodes {
modified radicle-fetch/src/lib.rs
@@ -1,6 +1,6 @@
pub mod git;
pub mod handle;
-
pub mod tracking;
+
pub mod policy;
pub mod transport;

pub(crate) mod sigrefs;
@@ -10,8 +10,8 @@ mod stage;
mod state;

pub use handle::Handle;
+
pub use policy::{Allowed, BlockList, Scope};
pub use state::{FetchLimit, FetchResult};
-
pub use tracking::{BlockList, Scope, Tracked};
pub use transport::Transport;

use std::io;
added radicle-fetch/src/policy.rs
@@ -0,0 +1,108 @@
+
use std::collections::HashSet;
+

+
use radicle::crypto::PublicKey;
+
use radicle::node::policy::config::Config;
+
use radicle::node::policy::store::Read;
+
use radicle::prelude::Id;
+

+
pub use radicle::node::policy::{Policy, Scope};
+

+
#[derive(Clone, Debug)]
+
pub enum Allowed {
+
    All,
+
    Followed { remotes: HashSet<PublicKey> },
+
}
+

+
impl Allowed {
+
    pub fn from_config(rid: Id, config: &Config<Read>) -> Result<Self, error::Policy> {
+
        let entry = config
+
            .repo_policy(&rid)
+
            .map_err(|err| error::Policy::FailedPolicy { rid, err })?;
+
        match entry.policy {
+
            Policy::Block => {
+
                log::error!(target: "fetch", "Attempted to fetch non-seeded repo {rid}");
+
                Err(error::Policy::BlockedPolicy { rid })
+
            }
+
            Policy::Allow => match entry.scope {
+
                Scope::All => Ok(Self::All),
+
                Scope::Followed => {
+
                    let nodes = config
+
                        .follow_policies()
+
                        .map_err(|err| error::Policy::FailedNodes { rid, err })?;
+
                    let followed: HashSet<_> = nodes
+
                        .filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
+
                        .collect();
+

+
                    Ok(Allowed::Followed { remotes: followed })
+
                }
+
            },
+
        }
+
    }
+
}
+

+
/// A set of [`PublicKey`]s to ignore when fetching from a remote.
+
#[derive(Clone, Debug)]
+
pub struct BlockList(HashSet<PublicKey>);
+

+
impl FromIterator<PublicKey> for BlockList {
+
    fn from_iter<T: IntoIterator<Item = PublicKey>>(iter: T) -> Self {
+
        Self(iter.into_iter().collect())
+
    }
+
}
+

+
impl Extend<PublicKey> for BlockList {
+
    fn extend<T: IntoIterator<Item = PublicKey>>(&mut self, iter: T) {
+
        self.0.extend(iter)
+
    }
+
}
+

+
impl BlockList {
+
    pub fn is_blocked(&self, key: &PublicKey) -> bool {
+
        self.0.contains(key)
+
    }
+

+
    pub fn from_config(config: &Config<Read>) -> Result<BlockList, error::Blocked> {
+
        Ok(config
+
            .follow_policies()?
+
            .filter_map(|entry| (entry.policy == Policy::Block).then_some(entry.id))
+
            .collect())
+
    }
+
}
+

+
pub mod error {
+
    use radicle::node::policy;
+
    use radicle::prelude::Id;
+
    use radicle::storage;
+
    use thiserror::Error;
+

+
    #[derive(Debug, Error)]
+
    #[error(transparent)]
+
    pub struct Blocked(#[from] policy::config::Error);
+

+
    #[derive(Debug, Error)]
+
    pub enum Policy {
+
        #[error("failed to find policy for {rid}")]
+
        FailedPolicy {
+
            rid: Id,
+
            #[source]
+
            err: policy::store::Error,
+
        },
+
        #[error("cannot fetch {rid} as it is not seeded")]
+
        BlockedPolicy { rid: Id },
+
        #[error("failed to get followed nodes for {rid}")]
+
        FailedNodes {
+
            rid: Id,
+
            #[source]
+
            err: policy::store::Error,
+
        },
+

+
        #[error(transparent)]
+
        Storage(#[from] storage::Error),
+

+
        #[error(transparent)]
+
        Git(#[from] radicle::git::raw::Error),
+

+
        #[error(transparent)]
+
        Refs(#[from] storage::refs::Error),
+
    }
+
}
modified radicle-fetch/src/stage.rs
@@ -12,7 +12,7 @@
//!      as an anchor for the rest of the fetch, i.e. provides initial
//!      delegate data for the repository.
//!   2. [`SpecialRefs`]: fetches the special references, `rad/id` and
-
//!      `rad/sigrefs`, for each configured namespace, i.e. tracked
+
//!      `rad/sigrefs`, for each configured namespace, i.e. followed
//!      and delegate peers if the scope is "followed" and all peers is the
//!      scope is all.
//!   3. [`DataRefs`]: fetches the `Oid`s for each reference listed in
@@ -40,12 +40,12 @@ use radicle::storage::refs::{RefsAt, Special};
use radicle::storage::ReadRepository;

use crate::git::refs::{Policy, Update, Updates};
+
use crate::policy::BlockList;
use crate::refs::{ReceivedRef, ReceivedRefname};
use crate::sigrefs;
use crate::state::FetchState;
-
use crate::tracking::BlockList;
use crate::transport::WantsHaves;
-
use crate::{refs, tracking};
+
use crate::{policy, refs};

pub mod error {
    use radicle::crypto::PublicKey;
@@ -214,12 +214,11 @@ impl ProtocolStage for CanonicalId {
}

/// The [`ProtocolStage`] for fetching special refs from the set of
-
/// remotes in `tracked` and `delegates`.
+
/// remotes in `followed` and `delegates`.
///
-
/// This step asks for all tracked and delegate remote's `rad/id` and
-
/// `rad/sigrefs`, iff the scope is
-
/// [`tracking::Scope::Trusted`]. Otherwise, it asks for all
-
/// namespaces.
+
/// This step asks for all followed and delegate remote's `rad/id` and
+
/// `rad/sigrefs`, iff the scope is [`policy::Scope::Followed`].
+
/// Otherwise, it asks for all namespaces.
///
/// It ensures that all delegate refs were fetched.
#[derive(Debug)]
@@ -229,7 +228,7 @@ pub struct SpecialRefs {
    /// The node that is being fetched from.
    pub remote: PublicKey,
    /// The set of nodes to be fetched.
-
    pub tracked: tracking::Tracked,
+
    pub followed: policy::Allowed,
    /// The set of delegates to be fetched, with the local node
    /// removed in the case of a `pull`.
    pub delegates: BTreeSet<PublicKey>,
@@ -239,9 +238,9 @@ pub struct SpecialRefs {

impl ProtocolStage for SpecialRefs {
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
-
        match &self.tracked {
-
            tracking::Tracked::All => Some(NonEmpty::new("refs/namespaces".into())),
-
            tracking::Tracked::Followed { remotes } => NonEmpty::collect(
+
        match &self.followed {
+
            policy::Allowed::All => Some(NonEmpty::new("refs/namespaces".into())),
+
            policy::Allowed::Followed { remotes } => NonEmpty::collect(
                remotes
                    .iter()
                    .chain(self.delegates.iter())
modified radicle-fetch/src/state.rs
@@ -323,13 +323,13 @@ impl FetchState {
                Ok(signed_refs)
            }
            None => {
-
                let tracked = handle.tracked();
-
                log::trace!(target: "fetch", "Tracked nodes {:?}", tracked);
+
                let followed = handle.allowed();
+
                log::trace!(target: "fetch", "Followed nodes {:?}", followed);
                let special_refs = stage::SpecialRefs {
                    blocked: handle.blocked.clone(),
                    remote,
                    delegates: delegates.clone(),
-
                    tracked,
+
                    followed,
                    limit: limit.special,
                };
                log::trace!(target: "fetch", "{special_refs:?}");
deleted radicle-fetch/src/tracking.rs
@@ -1,108 +0,0 @@
-
use std::collections::HashSet;
-

-
use radicle::crypto::PublicKey;
-
use radicle::node::policy::config::Config;
-
use radicle::node::policy::store::Read;
-
use radicle::prelude::Id;
-

-
pub use radicle::node::policy::{Policy, Scope};
-

-
#[derive(Clone, Debug)]
-
pub enum Tracked {
-
    All,
-
    Followed { remotes: HashSet<PublicKey> },
-
}
-

-
impl Tracked {
-
    pub fn from_config(rid: Id, config: &Config<Read>) -> Result<Self, error::Tracking> {
-
        let entry = config
-
            .repo_policy(&rid)
-
            .map_err(|err| error::Tracking::FailedPolicy { rid, err })?;
-
        match entry.policy {
-
            Policy::Block => {
-
                log::error!(target: "fetch", "Attempted to fetch untracked repo {rid}");
-
                Err(error::Tracking::BlockedPolicy { rid })
-
            }
-
            Policy::Allow => match entry.scope {
-
                Scope::All => Ok(Self::All),
-
                Scope::Followed => {
-
                    let nodes = config
-
                        .node_policies()
-
                        .map_err(|err| error::Tracking::FailedNodes { rid, err })?;
-
                    let followed: HashSet<_> = nodes
-
                        .filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
-
                        .collect();
-

-
                    Ok(Tracked::Followed { remotes: followed })
-
                }
-
            },
-
        }
-
    }
-
}
-

-
/// A set of [`PublicKey`]s to ignore when fetching from a remote.
-
#[derive(Clone, Debug)]
-
pub struct BlockList(HashSet<PublicKey>);
-

-
impl FromIterator<PublicKey> for BlockList {
-
    fn from_iter<T: IntoIterator<Item = PublicKey>>(iter: T) -> Self {
-
        Self(iter.into_iter().collect())
-
    }
-
}
-

-
impl Extend<PublicKey> for BlockList {
-
    fn extend<T: IntoIterator<Item = PublicKey>>(&mut self, iter: T) {
-
        self.0.extend(iter)
-
    }
-
}
-

-
impl BlockList {
-
    pub fn is_blocked(&self, key: &PublicKey) -> bool {
-
        self.0.contains(key)
-
    }
-

-
    pub fn from_config(config: &Config<Read>) -> Result<BlockList, error::Blocked> {
-
        Ok(config
-
            .node_policies()?
-
            .filter_map(|entry| (entry.policy == Policy::Block).then_some(entry.id))
-
            .collect())
-
    }
-
}
-

-
pub mod error {
-
    use radicle::node::policy;
-
    use radicle::prelude::Id;
-
    use radicle::storage;
-
    use thiserror::Error;
-

-
    #[derive(Debug, Error)]
-
    #[error(transparent)]
-
    pub struct Blocked(#[from] policy::config::Error);
-

-
    #[derive(Debug, Error)]
-
    pub enum Tracking {
-
        #[error("failed to find tracking policy for {rid}")]
-
        FailedPolicy {
-
            rid: Id,
-
            #[source]
-
            err: policy::store::Error,
-
        },
-
        #[error("cannot fetch {rid} as it is not tracked")]
-
        BlockedPolicy { rid: Id },
-
        #[error("failed to get tracking nodes for {rid}")]
-
        FailedNodes {
-
            rid: Id,
-
            #[source]
-
            err: policy::store::Error,
-
        },
-

-
        #[error(transparent)]
-
        Storage(#[from] storage::Error),
-

-
        #[error(transparent)]
-
        Git(#[from] radicle::git::raw::Error),
-

-
        #[error(transparent)]
-
        Refs(#[from] storage::refs::Error),
-
    }
-
}
modified radicle-httpd/src/api.rs
@@ -64,7 +64,7 @@ impl Context {
        let issues = issue::Issues::open(&repo)?.counts()?;
        let patches = patch::Patches::open(&repo)?.counts()?;
        let db = &self.profile.database()?;
-
        let trackings = db.count(&id).unwrap_or_default();
+
        let seeding = db.count(&id).unwrap_or_default();

        Ok(project::Info {
            payload,
@@ -74,7 +74,7 @@ impl Context {
            issues,
            patches,
            id,
-
            trackings,
+
            seeding,
        })
    }

@@ -213,7 +213,7 @@ mod project {
        pub patches: cob::patch::PatchCounts,
        pub issues: cob::issue::IssueCounts,
        pub id: Id,
-
        pub trackings: usize,
+
        pub seeding: usize,
    }
}

modified radicle-httpd/src/api/v1/delegates.rs
@@ -78,7 +78,7 @@ async fn delegates_projects_handler(
            };

            let delegates = id.doc.delegates;
-
            let trackings = db.count(&id.rid).unwrap_or_default();
+
            let seeding = db.count(&id.rid).unwrap_or_default();

            Some(Info {
                payload,
@@ -88,7 +88,7 @@ async fn delegates_projects_handler(
                issues,
                patches,
                id: id.rid,
-
                trackings,
+
                seeding,
            })
        })
        .skip(page * per_page)
@@ -146,7 +146,7 @@ mod routes {
                  "closed": 0,
                },
                "id": "rad:zLuTzcmoWMcdK37xqArS8eckp9vK",
-
                "trackings": 0,
+
                "seeding": 0,
              },
              {
                "name": "hello-world",
@@ -168,7 +168,7 @@ mod routes {
                  "closed": 0,
                },
                "id": RID,
-
                "trackings": 0,
+
                "seeding": 0,
              },
            ])
        );
@@ -207,7 +207,7 @@ mod routes {
                  "closed": 0,
                },
                "id": RID,
-
                "trackings": 0,
+
                "seeding": 0,
              }
            ])
        );
modified radicle-httpd/src/api/v1/node.rs
@@ -51,13 +51,13 @@ async fn node_handler(State(ctx): State<Context>) -> impl IntoResponse {
    Ok::<_, Error>(Json(response))
}

-
/// Return local tracking repos information.
+
/// Return local repo policies information.
/// `GET /node/policies/repos`
async fn node_policies_repos_handler(State(ctx): State<Context>) -> impl IntoResponse {
-
    let tracking = ctx.profile.tracking()?;
+
    let policies = ctx.profile.policies()?;
    let mut repos = Vec::new();

-
    for policy::Repo { id, scope, policy } in tracking.repo_policies()? {
+
    for policy::Repo { id, scope, policy } in policies.seed_policies()? {
        repos.push(json!({
            "id": id,
            "scope": scope,
@@ -68,7 +68,7 @@ async fn node_policies_repos_handler(State(ctx): State<Context>) -> impl IntoRes
    Ok::<_, Error>(Json(repos))
}

-
/// Track a new repo.
+
/// Seed a new repo.
/// `PUT /node/policies/repos/:rid`
async fn node_policies_seed_handler(
    State(ctx): State<Context>,
@@ -90,7 +90,7 @@ async fn node_policies_seed_handler(
    Ok::<_, Error>((StatusCode::OK, Json(json!({ "success": true }))))
}

-
/// Untrack a repo.
+
/// Unseed a repo.
/// `DELETE /node/policies/repos/:rid`
async fn node_policies_unseed_handler(
    State(ctx): State<Context>,
modified radicle-httpd/src/api/v1/projects.rs
@@ -119,7 +119,7 @@ async fn project_root_handler(
                return None;
            };
            let delegates = id.doc.delegates;
-
            let trackings = db.count(&id.rid).unwrap_or_default();
+
            let seeding = db.count(&id.rid).unwrap_or_default();

            Some(Info {
                payload,
@@ -129,7 +129,7 @@ async fn project_root_handler(
                issues,
                patches,
                id: id.rid,
-
                trackings,
+
                seeding,
            })
        })
        .skip(page * per_page)
@@ -994,7 +994,7 @@ mod routes {
                  "closed": 0,
                },
                "id": "rad:zLuTzcmoWMcdK37xqArS8eckp9vK",
-
                "trackings": 0,
+
                "seeding": 0,
              },
              {
                "name": "hello-world",
@@ -1016,7 +1016,7 @@ mod routes {
                  "closed": 0,
                },
                "id": RID,
-
                "trackings": 0,
+
                "seeding": 0,
              },
            ])
        );
@@ -1051,7 +1051,7 @@ mod routes {
                  "closed": 0,
                },
                "id": RID,
-
                "trackings": 0,
+
                "seeding": 0,
              }
            ])
        );
@@ -1086,7 +1086,7 @@ mod routes {
                 "closed": 0,
               },
               "id": RID,
-
               "trackings": 0,
+
               "seeding": 0,
            })
        );
    }
modified radicle-httpd/src/test.rs
@@ -96,7 +96,7 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G

    crate::logger::init().ok();

-
    profile.tracking_mut().unwrap();
+
    profile.policies_mut().unwrap();
    profile.database_mut().unwrap(); // Create the database.

    let workdir = dir.join("hello-world-private");
modified radicle-node/src/control.rs
@@ -280,7 +280,7 @@ mod tests {
    }

    #[test]
-
    fn test_track_untrack() {
+
    fn test_seed_unseed() {
        let tmp = tempfile::tempdir().unwrap();
        let socket = tmp.path().join("node.sock");
        let proj = test::arbitrary::gen::<Id>(1);
modified radicle-node/src/runtime.rs
@@ -44,9 +44,9 @@ pub enum Error {
    /// A node database error.
    #[error("node database error: {0}")]
    Database(#[from] node::db::Error),
-
    /// A tracking database error.
-
    #[error("tracking database error: {0}")]
-
    Tracking(#[from] policy::Error),
+
    /// A policies database error.
+
    #[error("policies database error: {0}")]
+
    Policy(#[from] policy::Error),
    /// A gossip database error.
    #[error("gossip database error: {0}")]
    Gossip(#[from] gossip::Error),
@@ -145,11 +145,11 @@ impl Runtime {
        log::info!(target: "node", "Opening node database..");
        let mut db: service::Stores<_> = home.database_mut()?.into();

-
        log::info!(target: "node", "Opening tracking policy configuration..");
-
        let tracking = home.tracking_mut()?;
-
        let tracking = policy::Config::new(policy, scope, tracking);
+
        log::info!(target: "node", "Opening policy database..");
+
        let policies = home.policies_mut()?;
+
        let policies = policy::Config::new(policy, scope, policies);

-
        log::info!(target: "node", "Default tracking policy set to '{}'", &policy);
+
        log::info!(target: "node", "Default seeding policy set to '{}'", &policy);
        log::info!(target: "node", "Initializing service ({:?})..", network);

        let announcement = if let Some(ann) = fs::read(node_dir.join(node::NODE_ANNOUNCEMENT_FILE))
@@ -202,7 +202,7 @@ impl Runtime {
            clock,
            db,
            storage.clone(),
-
            tracking,
+
            policies,
            signer.clone(),
            rng,
            announcement,
@@ -237,7 +237,7 @@ impl Runtime {
        let fetch = worker::FetchConfig {
            policy,
            scope,
-
            tracking_db: home.node().join(node::TRACKING_DB_FILE),
+
            policies_db: home.node().join(node::POLICIES_DB_FILE),
            limit: FetchLimit::default(),
            local: nid,
            expiry: worker::garbage::Expiry::default(),
modified radicle-node/src/runtime/handle.rs
@@ -203,25 +203,25 @@ impl radicle::node::Handle for Handle {

    fn follow(&mut self, id: NodeId, alias: Option<Alias>) -> Result<bool, Error> {
        let (sender, receiver) = chan::bounded(1);
-
        self.command(service::Command::TrackNode(id, alias, sender))?;
+
        self.command(service::Command::Follow(id, alias, sender))?;
        receiver.recv().map_err(Error::from)
    }

    fn unfollow(&mut self, id: NodeId) -> Result<bool, Error> {
        let (sender, receiver) = chan::bounded(1);
-
        self.command(service::Command::UntrackNode(id, sender))?;
+
        self.command(service::Command::Unfollow(id, sender))?;
        receiver.recv().map_err(Error::from)
    }

    fn seed(&mut self, id: Id, scope: policy::Scope) -> Result<bool, Error> {
        let (sender, receiver) = chan::bounded(1);
-
        self.command(service::Command::TrackRepo(id, scope, sender))?;
+
        self.command(service::Command::Seed(id, scope, sender))?;
        receiver.recv().map_err(Error::from)
    }

    fn unseed(&mut self, id: Id) -> Result<bool, Error> {
        let (sender, receiver) = chan::bounded(1);
-
        self.command(service::Command::UntrackRepo(id, sender))?;
+
        self.command(service::Command::Unseed(id, sender))?;
        receiver.recv().map_err(Error::from)
    }

modified radicle-node/src/service.rs
@@ -140,7 +140,7 @@ pub enum Error {
    #[error(transparent)]
    Seeds(#[from] seed::Error),
    #[error(transparent)]
-
    Tracking(#[from] policy::Error),
+
    Policy(#[from] policy::Error),
    #[error(transparent)]
    Repository(#[from] radicle::storage::RepositoryError),
    #[error("namespaces error: {0}")]
@@ -173,14 +173,14 @@ pub enum Command {
    Seeds(Id, chan::Sender<Seeds>),
    /// Fetch the given repository from the network.
    Fetch(Id, NodeId, time::Duration, chan::Sender<FetchResult>),
-
    /// Track the given repository.
-
    TrackRepo(Id, Scope, chan::Sender<bool>),
-
    /// Untrack the given repository.
-
    UntrackRepo(Id, chan::Sender<bool>),
-
    /// Track the given node.
-
    TrackNode(NodeId, Option<Alias>, chan::Sender<bool>),
-
    /// Untrack the given node.
-
    UntrackNode(NodeId, chan::Sender<bool>),
+
    /// Seed the given repository.
+
    Seed(Id, Scope, chan::Sender<bool>),
+
    /// Unseed the given repository.
+
    Unseed(Id, chan::Sender<bool>),
+
    /// Follow the given node.
+
    Follow(NodeId, Option<Alias>, chan::Sender<bool>),
+
    /// Unfollow the given node.
+
    Unfollow(NodeId, chan::Sender<bool>),
    /// Query the internal service state.
    QueryState(Arc<QueryState>, chan::Sender<Result<(), CommandError>>),
}
@@ -196,10 +196,10 @@ impl fmt::Debug for Command {
            Self::Config(_) => write!(f, "Config"),
            Self::Seeds(id, _) => write!(f, "Seeds({id})"),
            Self::Fetch(id, node, _, _) => write!(f, "Fetch({id}, {node})"),
-
            Self::TrackRepo(id, scope, _) => write!(f, "TrackRepo({id}, {scope})"),
-
            Self::UntrackRepo(id, _) => write!(f, "UntrackRepo({id})"),
-
            Self::TrackNode(id, _, _) => write!(f, "TrackNode({id})"),
-
            Self::UntrackNode(id, _) => write!(f, "UntrackNode({id})"),
+
            Self::Seed(id, scope, _) => write!(f, "Seed({id}, {scope})"),
+
            Self::Unseed(id, _) => write!(f, "Unseed({id})"),
+
            Self::Follow(id, _, _) => write!(f, "Follow({id})"),
+
            Self::Unfollow(id, _) => write!(f, "Unfollow({id})"),
            Self::QueryState { .. } => write!(f, "QueryState(..)"),
        }
    }
@@ -213,7 +213,7 @@ pub enum CommandError {
    #[error(transparent)]
    Routing(#[from] routing::Error),
    #[error(transparent)]
-
    Tracking(#[from] policy::Error),
+
    Policy(#[from] policy::Error),
}

/// Error returned by [`Service::try_fetch`].
@@ -304,8 +304,8 @@ pub struct Service<D, S, G> {
    storage: S,
    /// Node database.
    db: Stores<D>,
-
    /// Tracking policy configuration.
-
    tracking: policy::Config<Write>,
+
    /// Policy configuration.
+
    policies: policy::Config<Write>,
    /// Peer sessions, currently or recently connected.
    sessions: Sessions,
    /// Clock. Tells the time.
@@ -322,7 +322,7 @@ pub struct Service<D, S, G> {
    queue: VecDeque<(Id, NodeId)>,
    /// Request/connection rate limitter.
    limiter: RateLimiter,
-
    /// Current tracked repository bloom filter.
+
    /// Current seeded repositories bloom filter.
    filter: Filter,
    /// Last time the service was idle.
    last_idle: LocalTime,
@@ -364,7 +364,7 @@ where
        clock: LocalTime,
        db: Stores<D>,
        storage: S,
-
        tracking: policy::Config<Write>,
+
        policies: policy::Config<Write>,
        signer: G,
        rng: Rng,
        node: NodeAnnouncement,
@@ -375,7 +375,7 @@ where
        Self {
            config,
            storage,
-
            tracking,
+
            policies,
            signer,
            rng,
            node,
@@ -402,42 +402,37 @@ where
        self.outbox.next()
    }

-
    /// Track a repository.
-
    /// Returns whether or not the tracking policy was updated.
-
    pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result<bool, policy::Error> {
-
        let updated = self.tracking.track_repo(id, scope)?;
+
    /// Seed a repository.
+
    /// Returns whether or not the repo policy was updated.
+
    pub fn seed(&mut self, id: &Id, scope: Scope) -> Result<bool, policy::Error> {
+
        let updated = self.policies.seed(id, scope)?;
        self.filter.insert(id);

        Ok(updated)
    }

-
    /// Untrack a repository.
-
    /// Returns whether or not the tracking policy was updated.
-
    /// Note that when untracking, we don't announce anything to the network. This is because by
+
    /// Unseed a repository.
+
    /// Returns whether or not the repo policy was updated.
+
    /// Note that when unseeding, we don't announce anything to the network. This is because by
    /// simply not announcing it anymore, it will eventually be pruned by nodes.
-
    pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, policy::Error> {
-
        let updated = self.tracking.untrack_repo(id)?;
-
        // Nb. This is potentially slow if we have lots of projects. We should probably
-
        // only re-compute the filter when we've untracked a certain amount of projects
+
    pub fn unseed(&mut self, id: &Id) -> Result<bool, policy::Error> {
+
        let updated = self.policies.unseed(id)?;
+
        // Nb. This is potentially slow if we have lots of repos. We should probably
+
        // only re-compute the filter when we've unseeded a certain amount of repos
        // and the filter is really out of date.
        //
        // TODO: Share this code with initialization code.
        self.filter = Filter::new(
-
            self.tracking
-
                .repo_policies()?
+
            self.policies
+
                .seed_policies()?
                .filter_map(|t| (t.policy == Policy::Allow).then_some(t.id)),
        );
        Ok(updated)
    }

-
    /// Check whether we are tracking a certain repository.
-
    pub fn is_tracking(&self, id: &Id) -> Result<bool, policy::Error> {
-
        self.tracking.is_repo_tracked(id)
-
    }
-

-
    /// Find the closest `n` peers by proximity in tracking graphs.
+
    /// Find the closest `n` peers by proximity in seeding graphs.
    /// Returns a sorted list from the closest peer to the furthest.
-
    /// Peers with more trackings in common score score higher.
+
    /// Peers with more seedings in common score score higher.
    #[allow(unused)]
    pub fn closest_peers(&self, n: usize) -> Vec<NodeId> {
        todo!()
@@ -463,9 +458,9 @@ where
        &mut self.storage
    }

-
    /// Get the tracking policy.
-
    pub fn tracking(&self) -> &policy::Config<Write> {
-
        &self.tracking
+
    /// Get the node policies.
+
    pub fn policies(&self) -> &policy::Config<Write> {
+
        &self.policies
    }

    /// Get the local signer.
@@ -520,8 +515,8 @@ where
        for (id, addr) in addrs.into_iter().map(|ca| ca.into()) {
            self.connect(id, addr);
        }
-
        // Ensure that our inventory is recorded in our routing table, and we are tracking
-
        // all of it. It can happen that inventory is not properly tracked if for eg. the
+
        // Ensure that our inventory is recorded in our routing table, and we are seeding
+
        // all of it. It can happen that inventory is not properly seeded if for eg. the
        // user creates a new repository while the node is stopped.
        let rids = self.storage.inventory()?;
        self.db.routing_mut().insert(&rids, nid, time.as_millis())?;
@@ -534,8 +529,8 @@ where
        for rid in rids {
            let repo = self.storage.repository(rid)?;

-
            if !self.is_tracking(&rid)? {
-
                warn!(target: "service", "Local repository {rid} is not tracked");
+
            if !self.policies.is_seeding(&rid)? {
+
                warn!(target: "service", "Local repository {rid} is not seeded");
            }
            // If we have no owned refs for this repo, then there's nothing to announce.
            let Ok(updated_at) = SyncedAt::load(&repo, nid) else {
@@ -566,10 +561,10 @@ where
            }
        }

-
        // Setup subscription filter for tracked repos.
+
        // Setup subscription filter for seeded repos.
        self.filter = Filter::new(
-
            self.tracking
-
                .repo_policies()?
+
            self.policies
+
                .seed_policies()?
                .filter_map(|t| (t.policy == Policy::Allow).then_some(t.id)),
        );
        // Try to establish some connections.
@@ -677,12 +672,12 @@ where
            Command::Fetch(rid, seed, timeout, resp) => {
                self.fetch(rid, &seed, timeout, Some(resp));
            }
-
            Command::TrackRepo(rid, scope, resp) => {
-
                // Update our tracking policy.
-
                let tracked = self
-
                    .track_repo(&rid, scope)
-
                    .expect("Service::command: error tracking repository");
-
                resp.send(tracked).ok();
+
            Command::Seed(rid, scope, resp) => {
+
                // Update our seeding policy.
+
                let seeded = self
+
                    .seed(&rid, scope)
+
                    .expect("Service::command: error seeding repository");
+
                resp.send(seeded).ok();

                // Let all our peers know that we're interested in this repo from now on.
                self.outbox.broadcast(
@@ -690,25 +685,25 @@ where
                    self.sessions.connected().map(|(_, s)| s),
                );
            }
-
            Command::UntrackRepo(id, resp) => {
-
                let untracked = self
-
                    .untrack_repo(&id)
-
                    .expect("Service::command: error untracking repository");
-
                resp.send(untracked).ok();
+
            Command::Unseed(id, resp) => {
+
                let updated = self
+
                    .unseed(&id)
+
                    .expect("Service::command: error unseeding repository");
+
                resp.send(updated).ok();
            }
-
            Command::TrackNode(id, alias, resp) => {
-
                let tracked = self
-
                    .tracking
-
                    .track_node(&id, alias.as_deref())
-
                    .expect("Service::command: error tracking node");
-
                resp.send(tracked).ok();
+
            Command::Follow(id, alias, resp) => {
+
                let seeded = self
+
                    .policies
+
                    .follow(&id, alias.as_deref())
+
                    .expect("Service::command: error following node");
+
                resp.send(seeded).ok();
            }
-
            Command::UntrackNode(id, resp) => {
-
                let untracked = self
-
                    .tracking
-
                    .untrack_node(&id)
-
                    .expect("Service::command: error untracking node");
-
                resp.send(untracked).ok();
+
            Command::Unfollow(id, resp) => {
+
                let updated = self
+
                    .policies
+
                    .unfollow(&id)
+
                    .expect("Service::command: error unfollowing node");
+
                resp.send(updated).ok();
            }
            Command::AnnounceRefs(id, resp) => match self.announce_refs(id, [self.node_id()]) {
                Ok(refs) => match refs.as_slice() {
@@ -833,7 +828,7 @@ where
            from,
            subscribers: vec![],
        });
-
        let namespaces = self.tracking.namespaces_for(&self.storage, &rid)?;
+
        let namespaces = self.policies.namespaces_for(&self.storage, &rid)?;

        self.outbox
            .fetch(session, rid, namespaces, refs_at, timeout);
@@ -1190,10 +1185,10 @@ where
                            sub.filter.insert(id);
                        }

-
                        // If we're tracking and connected to the announcer, and we don't have
+
                        // If we're seeding and connected to the announcer, and we don't have
                        // the inventory, fetch it from the announcer.
-
                        if self.tracking.is_repo_tracked(id).expect(
-
                            "Service::handle_announcement: error accessing tracking configuration",
+
                        if self.policies.is_seeding(id).expect(
+
                            "Service::handle_announcement: error accessing seeding configuration",
                        ) {
                            // Only if we do not have the repository locally do we fetch here.
                            // If we do have it, only fetch after receiving a ref announcement.
@@ -1202,7 +1197,7 @@ where
                                    // Do nothing.
                                }
                                Ok(false) => {
-
                                    debug!(target: "service", "Missing tracked inventory {id}; initiating fetch..");
+
                                    debug!(target: "service", "Missing seeded inventory {id}; initiating fetch..");
                                    self.fetch(*id, announcer, FETCH_TIMEOUT, None);
                                }
                                Err(e) => {
@@ -1262,8 +1257,8 @@ where
                }

                // TODO: Buffer/throttle fetches.
-
                let repo_entry = self.tracking.repo_policy(&message.rid).expect(
-
                    "Service::handle_announcement: error accessing repo tracking configuration",
+
                let repo_entry = self.policies.repo_policy(&message.rid).expect(
+
                    "Service::handle_announcement: error accessing repo seeding configuration",
                );

                if repo_entry.policy == Policy::Allow {
@@ -1336,7 +1331,7 @@ where
                } else {
                    debug!(
                        target: "service",
-
                        "Ignoring refs announcement from {announcer}: repository {} isn't tracked",
+
                        "Ignoring refs announcement from {announcer}: repository {} isn't seeded",
                        message.rid
                    );
                }
@@ -1425,7 +1420,7 @@ where
        match scope {
            policy::Scope::All => Ok(refs),
            policy::Scope::Followed => {
-
                match self.tracking.namespaces_for(&self.storage, &message.rid) {
+
                match self.policies.namespaces_for(&self.storage, &message.rid) {
                    Ok(Namespaces::All) => Ok(refs),
                    Ok(Namespaces::Followed(mut followed)) => {
                        // Get the set of followed nodes except self.
@@ -1618,9 +1613,11 @@ where
                    info!(target: "service", "Routing table updated for {rid} with seed {from}");
                    self.emitter.emit(Event::SeedDiscovered { rid, nid: from });

-
                    if self.tracking.is_repo_tracked(&rid).expect(
-
                        "Service::process_inventory: error accessing tracking configuration",
-
                    ) {
+
                    if self
+
                        .policies
+
                        .is_seeding(&rid)
+
                        .expect("Service::process_inventory: error accessing seeding configuration")
+
                    {
                        // TODO: We should fetch here if we're already connected, case this seed has
                        // refs we don't have.
                    }
@@ -1797,7 +1794,7 @@ where
        }

        // Then, add peers we know about but have no information about the sync status.
-
        // These peers have announced that they track the repository via an inventory
+
        // These peers have announced that they seed the repository via an inventory
        // announcement, but we haven't received any ref announcements from them.
        for nid in self.db.routing().get(rid)? {
            if nid == self.node_id() {
@@ -1815,7 +1812,7 @@ where
        Ok(seeds)
    }

-
    /// Return a new filter object, based on our tracking policy.
+
    /// Return a new filter object, based on our seeding policy.
    fn filter(&self) -> Filter {
        if self.config.policy == Policy::Allow {
            // TODO: Remove bits for blocked repos.
@@ -1914,12 +1911,12 @@ where
        }
    }

-
    /// Fetch all repositories that are tracked but missing from our inventory.
+
    /// Fetch all repositories that are seeded but missing from our inventory.
    fn fetch_missing_inventory(&mut self) -> Result<(), Error> {
        let inventory = self.storage().inventory()?;
        let missing = self
-
            .tracking
-
            .repo_policies()?
+
            .policies
+
            .seed_policies()?
            .filter_map(|t| (t.policy == Policy::Allow).then_some(t.id))
            .filter(|rid| !inventory.contains(rid));

@@ -1938,7 +1935,7 @@ where
                        // Another way to handle this would be to update our database, saying
                        // that we're trying to fetch a certain repo. We would then just
                        // iterate over those entries in the above circumstances. This is
-
                        // merely an optimization though, we can also iterate over all tracked
+
                        // merely an optimization though, we can also iterate over all seeded
                        // repos and check which ones are not in our inventory.
                        debug!(target: "service", "No connected seeds found for {rid}..");
                    }
modified radicle-node/src/test/environment.rs
@@ -21,7 +21,7 @@ use radicle::identity::{Id, Visibility};
use radicle::node::policy::store as policy;
use radicle::node::routing::Store;
use radicle::node::Database;
-
use radicle::node::{Alias, TRACKING_DB_FILE};
+
use radicle::node::{Alias, POLICIES_DB_FILE};
use radicle::node::{ConnectOptions, Handle as _};
use radicle::profile;
use radicle::profile::Home;
@@ -81,8 +81,8 @@ impl Environment {
        let profile = self.profile(&config.alias);
        let signer = MemorySigner::load(&profile.keystore, None).unwrap();

-
        let tracking_db = profile.home.node().join(TRACKING_DB_FILE);
-
        let tracking = policy::Config::open(tracking_db).unwrap();
+
        let policies_db = profile.home.node().join(POLICIES_DB_FILE);
+
        let policies = policy::Store::open(policies_db).unwrap();
        let db = profile.database_mut().unwrap();
        let db = service::Stores::from(db);

@@ -92,7 +92,7 @@ impl Environment {
            config,
            signer,
            db,
-
            tracking,
+
            policies,
            storage: profile.storage,
        }
    }
@@ -103,7 +103,7 @@ impl Environment {
        let home = Home::new(self.tmp().join("home").join(alias).join(".radicle")).unwrap();
        let keystore = Keystore::new(&home.keys());
        let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
-
        let tracking_db = home.node().join(TRACKING_DB_FILE);
+
        let policies_db = home.node().join(POLICIES_DB_FILE);
        let alias = Alias::from_str(alias).unwrap();
        let config = profile::Config {
            node: node::Config::new(alias.clone()),
@@ -122,7 +122,7 @@ impl Environment {
        )
        .unwrap();

-
        policy::Config::open(tracking_db).unwrap();
+
        policy::Store::open(policies_db).unwrap();
        home.database_mut().unwrap(); // Just create the database.

        transport::local::register(storage.clone());
@@ -149,7 +149,7 @@ pub struct Node<G> {
    pub storage: Storage,
    pub config: Config,
    pub db: service::Stores<Database>,
-
    pub tracking: policy::Config<policy::Write>,
+
    pub policies: policy::Store<policy::Write>,
}

/// Handle to a running node.
@@ -348,7 +348,7 @@ impl Node<MockSigner> {
            },
        )
        .unwrap();
-
        let tracking = home.tracking_mut().unwrap();
+
        let policies = home.policies_mut().unwrap();
        let db = home.database_mut().unwrap();
        let db = service::Stores::from(db);

@@ -360,7 +360,7 @@ impl Node<MockSigner> {
            storage,
            config,
            db,
-
            tracking,
+
            policies,
        }
    }
}
@@ -419,8 +419,8 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
        .unwrap();

        assert!(self
-
            .tracking
-
            .track_repo(&id, node::policy::Scope::Followed)
+
            .policies
+
            .seed(&id, node::policy::Scope::Followed)
            .unwrap());

        log::debug!(
modified radicle-node/src/test/handle.rs
@@ -15,8 +15,8 @@ use crate::service::NodeId;
#[derive(Default, Clone)]
pub struct Handle {
    pub updates: Arc<Mutex<Vec<Id>>>,
-
    pub tracking_repos: Arc<Mutex<HashSet<Id>>>,
-
    pub tracking_nodes: Arc<Mutex<HashSet<NodeId>>>,
+
    pub seeding: Arc<Mutex<HashSet<Id>>>,
+
    pub following: Arc<Mutex<HashSet<NodeId>>>,
}

impl radicle::node::Handle for Handle {
@@ -61,15 +61,15 @@ impl radicle::node::Handle for Handle {
    }

    fn seed(&mut self, id: Id, _scope: policy::Scope) -> Result<bool, Self::Error> {
-
        Ok(self.tracking_repos.lock().unwrap().insert(id))
+
        Ok(self.seeding.lock().unwrap().insert(id))
    }

    fn unseed(&mut self, id: Id) -> Result<bool, Self::Error> {
-
        Ok(self.tracking_repos.lock().unwrap().remove(&id))
+
        Ok(self.seeding.lock().unwrap().remove(&id))
    }

    fn follow(&mut self, id: NodeId, _alias: Option<Alias>) -> Result<bool, Self::Error> {
-
        Ok(self.tracking_nodes.lock().unwrap().insert(id))
+
        Ok(self.following.lock().unwrap().insert(id))
    }

    fn subscribe(
@@ -80,7 +80,7 @@ impl radicle::node::Handle for Handle {
    }

    fn unfollow(&mut self, id: NodeId) -> Result<bool, Self::Error> {
-
        Ok(self.tracking_nodes.lock().unwrap().remove(&id))
+
        Ok(self.following.lock().unwrap().remove(&id))
    }

    fn announce_refs(&mut self, id: Id) -> Result<RefsAt, Self::Error> {
modified radicle-node/src/test/peer.rs
@@ -162,8 +162,8 @@ where
        storage: S,
        mut config: Config<G>,
    ) -> Self {
-
        let tracking = policy::Store::<policy::store::Write>::memory().unwrap();
-
        let mut tracking = policy::Config::new(config.policy, config.scope, tracking);
+
        let policies = policy::Store::<policy::store::Write>::memory().unwrap();
+
        let mut policies = policy::Config::new(config.policy, config.scope, policies);
        let id = *config.signer.public_key();
        let ip = ip.into();
        let local_addr = net::SocketAddr::new(ip, config.rng.u16(..));
@@ -172,7 +172,7 @@ where
        config.config.external_addresses.push(local_addr.into());

        for rid in storage.inventory().unwrap() {
-
            tracking.track_repo(&rid, Scope::Followed).unwrap();
+
            policies.seed(&rid, Scope::Followed).unwrap();
        }
        let announcement = service::gossip::node(&config.config, config.local_time.as_secs());
        let emitter: Emitter<Event> = Default::default();
@@ -181,7 +181,7 @@ where
            config.local_time,
            config.db,
            storage,
-
            tracking,
+
            policies,
            config.signer,
            config.rng.clone(),
            announcement,
modified radicle-node/src/tests.rs
@@ -388,25 +388,21 @@ fn test_inventory_pruning() {
}

#[test]
-
fn test_tracking() {
+
fn test_seeding() {
    let mut alice = Peer::new("alice", [7, 7, 7, 7]);
    let proj_id: identity::Id = test::arbitrary::gen(1);

    let (sender, receiver) = chan::bounded(1);
-
    alice.command(Command::TrackRepo(
-
        proj_id,
-
        policy::Scope::default(),
-
        sender,
-
    ));
+
    alice.command(Command::Seed(proj_id, policy::Scope::default(), sender));
    let policy_change = receiver.recv().map_err(runtime::HandleError::from).unwrap();
    assert!(policy_change);
-
    assert!(alice.tracking().is_repo_tracked(&proj_id).unwrap());
+
    assert!(alice.policies().is_seeding(&proj_id).unwrap());

    let (sender, receiver) = chan::bounded(1);
-
    alice.command(Command::UntrackRepo(proj_id, sender));
+
    alice.command(Command::Unseed(proj_id, sender));
    let policy_change = receiver.recv().map_err(runtime::HandleError::from).unwrap();
    assert!(policy_change);
-
    assert!(!alice.tracking().is_repo_tracked(&proj_id).unwrap());
+
    assert!(!alice.policies().is_seeding(&proj_id).unwrap());
}

#[test]
@@ -504,7 +500,7 @@ fn test_announcement_rebroadcast_duplicates() {
        anns.insert(bob.node_announcement());

        for rid in rids {
-
            alice.track_repo(&rid, policy::Scope::All).unwrap();
+
            alice.seed(&rid, policy::Scope::All).unwrap();
            anns.insert(carol.refs_announcement(rid));
            anns.insert(bob.refs_announcement(rid));
        }
@@ -676,9 +672,9 @@ fn test_refs_announcement_relay() {
    };
    let bob_inv = bob.storage().inventory().unwrap();

-
    alice.track_repo(&bob_inv[0], policy::Scope::All).unwrap();
-
    alice.track_repo(&bob_inv[1], policy::Scope::All).unwrap();
-
    alice.track_repo(&bob_inv[2], policy::Scope::All).unwrap();
+
    alice.seed(&bob_inv[0], policy::Scope::All).unwrap();
+
    alice.seed(&bob_inv[1], policy::Scope::All).unwrap();
+
    alice.seed(&bob_inv[2], policy::Scope::All).unwrap();
    alice.connect_to(&bob);
    alice.connect_to(&eve);
    alice.receive(eve.id(), Message::Subscribe(Subscribe::all()));
@@ -742,7 +738,7 @@ fn test_refs_announcement_fetch_trusted_no_inventory() {
    let bob_inv = bob.storage().inventory().unwrap();
    let rid = bob_inv[0];

-
    alice.track_repo(&rid, policy::Scope::Followed).unwrap();
+
    alice.seed(&rid, policy::Scope::Followed).unwrap();
    alice.connect_to(&bob);

    // Alice receives Bob's refs.
@@ -798,7 +794,7 @@ fn test_refs_announcement_followed() {

    // Alice uses Scope::Followed, and did not track Bob yet.
    alice.connect_to(&bob);
-
    alice.track_repo(&rid, policy::Scope::Followed).unwrap();
+
    alice.seed(&rid, policy::Scope::Followed).unwrap();

    // Alice receives Bob's refs
    alice.receive(bob.id(), bob.refs_announcement(rid));
@@ -811,7 +807,7 @@ fn test_refs_announcement_followed() {

    // Alice starts to track Bob.
    let (sender, receiver) = chan::bounded(1);
-
    alice.command(Command::TrackNode(
+
    alice.command(Command::Follow(
        bob.id,
        Some(node::Alias::new("bob")),
        sender,
@@ -834,7 +830,7 @@ fn test_refs_announcement_no_subscribe() {
    let eve = Peer::new("eve", [9, 9, 9, 9]);
    let id = arbitrary::gen(1);

-
    alice.track_repo(&id, policy::Scope::All).unwrap();
+
    alice.seed(&id, policy::Scope::All).unwrap();
    alice.connect_to(&bob);
    alice.connect_to(&eve);
    alice.receive(bob.id(), bob.refs_announcement(rid));
@@ -863,7 +859,7 @@ fn test_refs_announcement_offline() {
    let inv = alice.inventory();
    let rid = inv.first().unwrap();
    let mut bob = Peer::new("bob", [8, 8, 8, 8]);
-
    bob.track_repo(rid, policy::Scope::All).unwrap();
+
    bob.seed(rid, policy::Scope::All).unwrap();

    // Make sure alice's service wasn't initialized before.
    assert!(alice.initialize());
@@ -1248,14 +1244,14 @@ fn test_maintain_connections_failed_attempt() {
}

#[test]
-
fn test_track_repo_subscribe() {
+
fn test_seed_repo_subscribe() {
    let mut alice = Peer::new("alice", [7, 7, 7, 7]);
    let bob = Peer::new("bob", [8, 8, 8, 8]);
    let rid = arbitrary::gen::<Id>(1);
    let (send, recv) = chan::bounded(1);

    alice.connect_to(&bob);
-
    alice.command(Command::TrackRepo(rid, policy::Scope::default(), send));
+
    alice.command(Command::Seed(rid, policy::Scope::default(), send));
    assert!(recv.recv().unwrap());

    assert_matches!(
@@ -1275,7 +1271,7 @@ fn test_fetch_missing_inventory_on_gossip() {
    let bob = Peer::new("bob", [8, 8, 8, 8]);
    let now = LocalTime::now();

-
    alice.track_repo(&rid, node::policy::Scope::All).unwrap();
+
    alice.seed(&rid, node::policy::Scope::All).unwrap();
    alice.connect_to(&bob);
    alice.receive(
        bob.id(),
@@ -1300,7 +1296,7 @@ fn test_fetch_missing_inventory_on_schedule() {
    let bob = Peer::new("bob", [8, 8, 8, 8]);
    let now = LocalTime::now();

-
    alice.track_repo(&rid, node::policy::Scope::All).unwrap();
+
    alice.seed(&rid, node::policy::Scope::All).unwrap();
    alice.connect_to(&bob);
    alice.receive(
        bob.id(),
@@ -1441,7 +1437,7 @@ fn test_refs_synced_event() {
    });
    let msg = ann.signed(bob.signer());

-
    alice.track_repo(&acme, policy::Scope::All).unwrap();
+
    alice.seed(&acme, policy::Scope::All).unwrap();
    alice.connect_to(&bob);
    alice.receive(bob.id, Message::Announcement(msg));

@@ -1540,14 +1536,14 @@ fn test_push_and_pull() {

    // Bob seeds Alice's project.
    let (sender, _) = chan::bounded(1);
-
    bob.command(service::Command::TrackRepo(
+
    bob.command(service::Command::Seed(
        proj_id,
        policy::Scope::default(),
        sender,
    ));
    // Eve seeds Alice's project.
    let (sender, _) = chan::bounded(1);
-
    eve.command(service::Command::TrackRepo(
+
    eve.command(service::Command::Seed(
        proj_id,
        policy::Scope::default(),
        sender,
modified radicle-node/src/tests/e2e.rs
@@ -163,8 +163,8 @@ fn test_replication() {
    let inventory = alice.storage.inventory().unwrap();
    assert!(inventory.is_empty());

-
    let tracked = alice.handle.seed(acme, Scope::All).unwrap();
-
    assert!(tracked);
+
    let updated = alice.handle.seed(acme, Scope::All).unwrap();
+
    assert!(updated);

    let seeds = alice.handle.seeds(acme).unwrap();
    assert!(seeds.is_connected(&bob.id));
@@ -314,8 +314,8 @@ fn test_migrated_clone() {
    alice.connect(&bob);
    converge([&alice, &bob]);

-
    let tracked = bob.handle.seed(acme, Scope::All).unwrap();
-
    assert!(tracked);
+
    let updated = bob.handle.seed(acme, Scope::All).unwrap();
+
    assert!(updated);

    let result = bob.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();
    assert!(result.is_success());
modified radicle-node/src/worker.rs
@@ -50,11 +50,11 @@ pub enum FetchError {
    #[error(transparent)]
    Storage(#[from] radicle::storage::Error),
    #[error(transparent)]
-
    TrackingConfig(#[from] radicle::node::policy::store::Error),
+
    PolicyStore(#[from] radicle::node::policy::store::Error),
    #[error(transparent)]
-
    Tracked(#[from] radicle_fetch::tracking::error::Tracking),
+
    Policy(#[from] radicle_fetch::policy::error::Policy),
    #[error(transparent)]
-
    Blocked(#[from] radicle_fetch::tracking::error::Blocked),
+
    Blocked(#[from] radicle_fetch::policy::error::Blocked),
}

impl FetchError {
@@ -156,8 +156,8 @@ pub struct FetchConfig {
    pub policy: Policy,
    /// Default scope, if a scope for a specific repository was not found.
    pub scope: policy::Scope,
-
    /// Path to the tracking database.
-
    pub tracking_db: PathBuf,
+
    /// Path to the policies database.
+
    pub policies_db: PathBuf,
    /// Data limits when fetching from a remote.
    pub limit: FetchLimit,
    /// Public key of the local peer.
@@ -278,18 +278,18 @@ impl Worker {
        let FetchConfig {
            policy,
            scope,
-
            tracking_db,
+
            policies_db,
            limit,
            local,
            expiry,
        } = &self.fetch_config;
-
        let policies = policy::Config::new(*policy, *scope, policy::Store::reader(tracking_db)?);
+
        let policies = policy::Config::new(*policy, *scope, policy::Store::reader(policies_db)?);
        // N.b. if the `rid` is blocked this will return an error, so
        // we won't continue with any further set up of the fetch.
-
        let tracked = radicle_fetch::Tracked::from_config(rid, &policies)?;
+
        let allowed = radicle_fetch::Allowed::from_config(rid, &policies)?;
        let blocked = radicle_fetch::BlockList::from_config(&policies)?;

-
        let handle = fetch::Handle::new(rid, *local, &self.storage, tracked, blocked, channels)?;
+
        let handle = fetch::Handle::new(rid, *local, &self.storage, allowed, blocked, channels)?;
        let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;

        if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
modified radicle-node/src/worker/fetch.rs
@@ -7,7 +7,7 @@ use radicle::prelude::Id;
use radicle::storage::refs::RefsAt;
use radicle::storage::{ReadStorage as _, RefUpdate, WriteRepository as _};
use radicle::Storage;
-
use radicle_fetch::{BlockList, FetchLimit, Tracked};
+
use radicle_fetch::{Allowed, BlockList, FetchLimit};

use super::channels::ChannelsFlush;

@@ -34,18 +34,18 @@ impl Handle {
        rid: Id,
        local: PublicKey,
        storage: &Storage,
-
        tracked: Tracked,
+
        follow: Allowed,
        blocked: BlockList,
        channels: ChannelsFlush,
    ) -> Result<Self, error::Handle> {
        let exists = storage.contains(&rid)?;
        if exists {
            let repo = storage.repository(rid)?;
-
            let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
+
            let handle = radicle_fetch::Handle::new(local, repo, follow, blocked, channels)?;
            Ok(Handle::Pull { handle })
        } else {
            let (repo, tmp) = storage.lock_repository(rid)?;
-
            let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
+
            let handle = radicle_fetch::Handle::new(local, repo, follow, blocked, channels)?;
            Ok(Handle::Clone { handle, tmp })
        }
    }
modified radicle/src/node.rs
@@ -47,8 +47,8 @@ pub const DEFAULT_TIMEOUT: time::Duration = time::Duration::from_secs(9);
pub const MAX_ALIAS_LENGTH: usize = 32;
/// Filename of node database under the node directory.
pub const NODE_DB_FILE: &str = "node.db";
-
/// Filename of tracking table database under the node directory.
-
pub const TRACKING_DB_FILE: &str = "tracking.db";
+
/// Filename of policies database under the node directory.
+
pub const POLICIES_DB_FILE: &str = "policies.db";
/// Filename of last node announcement, when running in debug mode.
#[cfg(debug_assertions)]
pub const NODE_ANNOUNCEMENT_FILE: &str = "announcement.wire.debug";
modified radicle/src/node/config.rs
@@ -211,10 +211,10 @@ pub struct Config {
    /// Configured service limits.
    #[serde(default)]
    pub limits: Limits,
-
    /// Default tracking policy.
+
    /// Default seeding policy.
    #[serde(default)]
    pub policy: Policy,
-
    /// Default tracking scope.
+
    /// Default seeding scope.
    #[serde(default)]
    pub scope: Scope,
}
modified radicle/src/node/policy.rs
@@ -111,7 +111,7 @@ impl fmt::Display for Scope {
}

#[derive(Debug, Error)]
-
#[error("invalid tracking scope: {0:?}")]
+
#[error("invalid seeding scope: {0:?}")]
pub struct ParseScopeError(String);

impl FromStr for Scope {
modified radicle/src/node/policy/config.rs
@@ -10,21 +10,21 @@ use crate::prelude::{Id, NodeId};
use crate::storage::{Namespaces, ReadRepository as _, ReadStorage, RepositoryError};

pub use crate::node::policy::store;
-
pub use crate::node::policy::store::Config as Store;
pub use crate::node::policy::store::Error;
+
pub use crate::node::policy::store::Store;
pub use crate::node::policy::{Alias, Node, Policy, Repo, Scope};

#[derive(Debug, Error)]
pub enum NamespacesError {
-
    #[error("failed to find tracking policy for {rid}")]
+
    #[error("failed to find policy for {rid}")]
    FailedPolicy {
        rid: Id,
        #[source]
        err: Error,
    },
-
    #[error("cannot fetch {rid} as it is not tracked")]
+
    #[error("cannot fetch {rid} as it is not seeded")]
    BlockedPolicy { rid: Id },
-
    #[error("failed to get tracking nodes for {rid}")]
+
    #[error("failed to get node policies for {rid}")]
    FailedNodes {
        rid: Id,
        #[source]
@@ -42,7 +42,7 @@ pub enum NamespacesError {
    NoFollowed { rid: Id },
}

-
/// Tracking configuration.
+
/// Policies configuration.
pub struct Config<T> {
    /// Default policy, if a policy for a specific node or repository was not found.
    policy: Policy,
@@ -65,7 +65,7 @@ impl<T> fmt::Debug for Config<T> {
}

impl<T> Config<T> {
-
    /// Create a new tracking configuration.
+
    /// Create a new policy configuration.
    pub fn new(policy: Policy, scope: Scope, store: Store<T>) -> Self {
        Self {
            policy,
@@ -74,32 +74,32 @@ impl<T> Config<T> {
        }
    }

-
    /// Check if a repository is tracked.
-
    pub fn is_repo_tracked(&self, id: &Id) -> Result<bool, Error> {
+
    /// Check if a repository is seeded.
+
    pub fn is_seeding(&self, id: &Id) -> Result<bool, Error> {
        self.repo_policy(id)
            .map(|entry| entry.policy == Policy::Allow)
    }

-
    /// Check if a node is tracked.
-
    pub fn is_node_tracked(&self, id: &NodeId) -> Result<bool, Error> {
+
    /// Check if a node is followed.
+
    pub fn is_following(&self, id: &NodeId) -> Result<bool, Error> {
        self.node_policy(id)
            .map(|entry| entry.policy == Policy::Allow)
    }

-
    /// Get a node's tracking information.
+
    /// Get a node's following information.
    /// Returns the default policy if the node isn't found.
    pub fn node_policy(&self, id: &NodeId) -> Result<Node, Error> {
-
        Ok(self.store.node_policy(id)?.unwrap_or(Node {
+
        Ok(self.store.follow_policy(id)?.unwrap_or(Node {
            id: *id,
            alias: None,
            policy: self.policy,
        }))
    }

-
    /// Get a repository's tracking information.
+
    /// Get a repository's seediing information.
    /// Returns the default policy if the repo isn't found.
    pub fn repo_policy(&self, id: &Id) -> Result<Repo, Error> {
-
        Ok(self.store.repo_policy(id)?.unwrap_or(Repo {
+
        Ok(self.store.seed_policy(id)?.unwrap_or(Repo {
            id: *id,
            scope: self.scope,
            policy: self.policy,
@@ -124,7 +124,7 @@ impl<T> Config<T> {
                Scope::All => Ok(Namespaces::All),
                Scope::Followed => {
                    let nodes = self
-
                        .node_policies()
+
                        .follow_policies()
                        .map_err(|err| FailedNodes { rid: *rid, err })?;
                    let mut followed: HashSet<_> = nodes
                        .filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
modified radicle/src/node/policy/store.rs
@@ -32,23 +32,23 @@ pub struct Read;
pub struct Write;

/// Read only config.
-
pub type ConfigReader = Config<Read>;
+
pub type StoreReader = Store<Read>;
/// Read-write config.
-
pub type ConfigWriter = Config<Write>;
+
pub type StoreWriter = Store<Write>;

-
/// Tracking configuration.
-
pub struct Config<T> {
+
/// Policy configuration.
+
pub struct Store<T> {
    db: sql::Connection,
    _marker: PhantomData<T>,
}

-
impl<T> fmt::Debug for Config<T> {
+
impl<T> fmt::Debug for Store<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-
        write!(f, "Config(..)")
+
        write!(f, "Store(..)")
    }
}

-
impl Config<Read> {
+
impl Store<Read> {
    const SCHEMA: &'static str = include_str!("schema.sql");

    /// Same as [`Self::open`], but in read-only mode. This is useful to have multiple
@@ -80,7 +80,7 @@ impl Config<Read> {
    }
}

-
impl Config<Write> {
+
impl Store<Write> {
    const SCHEMA: &'static str = include_str!("schema.sql");

    /// Open a policy store at the given path. Creates a new store if it
@@ -108,15 +108,15 @@ impl Config<Write> {
    }

    /// Get a read-only version of this store.
-
    pub fn read_only(self) -> ConfigReader {
-
        Config {
+
    pub fn read_only(self) -> StoreReader {
+
        Store {
            db: self.db,
            _marker: PhantomData,
        }
    }

-
    /// Track a node.
-
    pub fn track_node(&mut self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> {
+
    /// Follow a node.
+
    pub fn follow(&mut self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> {
        let mut stmt = self.db.prepare(
            "INSERT INTO `following` (id, alias)
             VALUES (?1, ?2)
@@ -131,8 +131,8 @@ impl Config<Write> {
        Ok(self.db.change_count() > 0)
    }

-
    /// Track a repository.
-
    pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result<bool, Error> {
+
    /// Seed a repository.
+
    pub fn seed(&mut self, id: &Id, scope: Scope) -> Result<bool, Error> {
        let mut stmt = self.db.prepare(
            "INSERT INTO `seeding` (id, scope)
             VALUES (?1, ?2)
@@ -147,8 +147,8 @@ impl Config<Write> {
        Ok(self.db.change_count() > 0)
    }

-
    /// Set a node's tracking policy.
-
    pub fn set_node_policy(&mut self, id: &NodeId, policy: Policy) -> Result<bool, Error> {
+
    /// Set a node's follow policy.
+
    pub fn set_follow_policy(&mut self, id: &NodeId, policy: Policy) -> Result<bool, Error> {
        let mut stmt = self.db.prepare(
            "INSERT INTO `following` (id, policy)
             VALUES (?1, ?2)
@@ -163,8 +163,8 @@ impl Config<Write> {
        Ok(self.db.change_count() > 0)
    }

-
    /// Set a repository's tracking policy.
-
    pub fn set_repo_policy(&mut self, id: &Id, policy: Policy) -> Result<bool, Error> {
+
    /// Set a repository's seeding policy.
+
    pub fn set_seed_policy(&mut self, id: &Id, policy: Policy) -> Result<bool, Error> {
        let mut stmt = self.db.prepare(
            "INSERT INTO `seeding` (id, policy)
             VALUES (?1, ?2)
@@ -179,8 +179,8 @@ impl Config<Write> {
        Ok(self.db.change_count() > 0)
    }

-
    /// Untrack a node.
-
    pub fn untrack_node(&mut self, id: &NodeId) -> Result<bool, Error> {
+
    /// Unfollow a node.
+
    pub fn unfollow(&mut self, id: &NodeId) -> Result<bool, Error> {
        let mut stmt = self.db.prepare("DELETE FROM `following` WHERE id = ?")?;

        stmt.bind((1, id))?;
@@ -189,8 +189,8 @@ impl Config<Write> {
        Ok(self.db.change_count() > 0)
    }

-
    /// Untrack a repository.
-
    pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, Error> {
+
    /// Unseed a repository.
+
    pub fn unseed(&mut self, id: &Id) -> Result<bool, Error> {
        let mut stmt = self.db.prepare("DELETE FROM `seeding` WHERE id = ?")?;

        stmt.bind((1, id))?;
@@ -202,11 +202,11 @@ impl Config<Write> {

/// `Read` methods for `Config`. This implies that a
/// `Config<Write>` can access these functions as well.
-
impl<T> Config<T> {
-
    /// Check if a node is tracked.
-
    pub fn is_node_tracked(&self, id: &NodeId) -> Result<bool, Error> {
+
impl<T> Store<T> {
+
    /// Check if a node is followed.
+
    pub fn is_node_followed(&self, id: &NodeId) -> Result<bool, Error> {
        Ok(matches!(
-
            self.node_policy(id)?,
+
            self.follow_policy(id)?,
            Some(Node {
                policy: Policy::Allow,
                ..
@@ -214,10 +214,10 @@ impl<T> Config<T> {
        ))
    }

-
    /// Check if a repository is tracked.
-
    pub fn is_repo_tracked(&self, id: &Id) -> Result<bool, Error> {
+
    /// Check if a repository is seeded.
+
    pub fn is_repo_seeded(&self, id: &Id) -> Result<bool, Error> {
        Ok(matches!(
-
            self.repo_policy(id)?,
+
            self.seed_policy(id)?,
            Some(Repo {
                policy: Policy::Allow,
                ..
@@ -225,8 +225,8 @@ impl<T> Config<T> {
        ))
    }

-
    /// Get a node's tracking policy.
-
    pub fn node_policy(&self, id: &NodeId) -> Result<Option<Node>, Error> {
+
    /// Get a node's follow policy.
+
    pub fn follow_policy(&self, id: &NodeId) -> Result<Option<Node>, Error> {
        let mut stmt = self
            .db
            .prepare("SELECT alias, policy FROM `following` WHERE id = ?")?;
@@ -251,8 +251,8 @@ impl<T> Config<T> {
        Ok(None)
    }

-
    /// Get a repository's tracking policy.
-
    pub fn repo_policy(&self, id: &Id) -> Result<Option<Repo>, Error> {
+
    /// Get a repository's seeding policy.
+
    pub fn seed_policy(&self, id: &Id) -> Result<Option<Repo>, Error> {
        let mut stmt = self
            .db
            .prepare("SELECT scope, policy FROM `seeding` WHERE id = ?")?;
@@ -269,8 +269,8 @@ impl<T> Config<T> {
        Ok(None)
    }

-
    /// Get node tracking policies.
-
    pub fn node_policies(&self) -> Result<Box<dyn Iterator<Item = Node>>, Error> {
+
    /// Get node follow policies.
+
    pub fn follow_policies(&self) -> Result<Box<dyn Iterator<Item = Node>>, Error> {
        let mut stmt = self
            .db
            .prepare("SELECT id, alias, policy FROM `following`")?
@@ -293,8 +293,8 @@ impl<T> Config<T> {
    }

    // TODO: see if sql can return iterator directly
-
    /// Get repository tracking policies.
-
    pub fn repo_policies(&self) -> Result<Box<dyn Iterator<Item = Repo>>, Error> {
+
    /// Get repository seed policies.
+
    pub fn seed_policies(&self) -> Result<Box<dyn Iterator<Item = Repo>>, Error> {
        let mut stmt = self
            .db
            .prepare("SELECT id, scope, policy FROM `seeding`")?
@@ -312,11 +312,11 @@ impl<T> Config<T> {
    }
}

-
impl<T> AliasStore for Config<T> {
+
impl<T> AliasStore for Store<T> {
    /// Retrieve `alias` of given node.
    /// Calls `Self::node_policy` under the hood.
    fn alias(&self, nid: &NodeId) -> Option<Alias> {
-
        self.node_policy(nid)
+
        self.follow_policy(nid)
            .map(|node| node.and_then(|n| n.alias))
            .unwrap_or(None)
    }
@@ -330,38 +330,38 @@ mod test {
    use crate::test::arbitrary;

    #[test]
-
    fn test_track_and_untrack_node() {
+
    fn test_follow_and_unfollow_node() {
        let id = arbitrary::gen::<NodeId>(1);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

-
        assert!(db.track_node(&id, Some("eve")).unwrap());
-
        assert!(db.is_node_tracked(&id).unwrap());
-
        assert!(!db.track_node(&id, Some("eve")).unwrap());
-
        assert!(db.untrack_node(&id).unwrap());
-
        assert!(!db.is_node_tracked(&id).unwrap());
+
        assert!(db.follow(&id, Some("eve")).unwrap());
+
        assert!(db.is_node_followed(&id).unwrap());
+
        assert!(!db.follow(&id, Some("eve")).unwrap());
+
        assert!(db.unfollow(&id).unwrap());
+
        assert!(!db.is_node_followed(&id).unwrap());
    }

    #[test]
-
    fn test_track_and_untrack_repo() {
+
    fn test_seed_and_unseed_repo() {
        let id = arbitrary::gen::<Id>(1);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

-
        assert!(db.track_repo(&id, Scope::All).unwrap());
-
        assert!(db.is_repo_tracked(&id).unwrap());
-
        assert!(!db.track_repo(&id, Scope::All).unwrap());
-
        assert!(db.untrack_repo(&id).unwrap());
-
        assert!(!db.is_repo_tracked(&id).unwrap());
+
        assert!(db.seed(&id, Scope::All).unwrap());
+
        assert!(db.is_repo_seeded(&id).unwrap());
+
        assert!(!db.seed(&id, Scope::All).unwrap());
+
        assert!(db.unseed(&id).unwrap());
+
        assert!(!db.is_repo_seeded(&id).unwrap());
    }

    #[test]
    fn test_node_policies() {
        let ids = arbitrary::vec::<NodeId>(3);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

        for id in &ids {
-
            assert!(db.track_node(id, None).unwrap());
+
            assert!(db.follow(id, None).unwrap());
        }
-
        let mut entries = db.node_policies().unwrap();
+
        let mut entries = db.follow_policies().unwrap();
        assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[0]);
        assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[1]);
        assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[2]);
@@ -370,12 +370,12 @@ mod test {
    #[test]
    fn test_repo_policies() {
        let ids = arbitrary::vec::<Id>(3);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

        for id in &ids {
-
            assert!(db.track_repo(id, Scope::All).unwrap());
+
            assert!(db.seed(id, Scope::All).unwrap());
        }
-
        let mut entries = db.repo_policies().unwrap();
+
        let mut entries = db.seed_policies().unwrap();
        assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[0]);
        assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[1]);
        assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[2]);
@@ -384,19 +384,19 @@ mod test {
    #[test]
    fn test_update_alias() {
        let id = arbitrary::gen::<NodeId>(1);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

-
        assert!(db.track_node(&id, Some("eve")).unwrap());
+
        assert!(db.follow(&id, Some("eve")).unwrap());
        assert_eq!(
-
            db.node_policy(&id).unwrap().unwrap().alias,
+
            db.follow_policy(&id).unwrap().unwrap().alias,
            Some(Alias::from_str("eve").unwrap())
        );
-
        assert!(db.track_node(&id, None).unwrap());
-
        assert_eq!(db.node_policy(&id).unwrap().unwrap().alias, None);
-
        assert!(!db.track_node(&id, None).unwrap());
-
        assert!(db.track_node(&id, Some("alice")).unwrap());
+
        assert!(db.follow(&id, None).unwrap());
+
        assert_eq!(db.follow_policy(&id).unwrap().unwrap().alias, None);
+
        assert!(!db.follow(&id, None).unwrap());
+
        assert!(db.follow(&id, Some("alice")).unwrap());
        assert_eq!(
-
            db.node_policy(&id).unwrap().unwrap().alias,
+
            db.follow_policy(&id).unwrap().unwrap().alias,
            Some(Alias::new("alice"))
        );
    }
@@ -404,33 +404,39 @@ mod test {
    #[test]
    fn test_update_scope() {
        let id = arbitrary::gen::<Id>(1);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

-
        assert!(db.track_repo(&id, Scope::All).unwrap());
-
        assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::All);
-
        assert!(db.track_repo(&id, Scope::Followed).unwrap());
-
        assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::Followed);
+
        assert!(db.seed(&id, Scope::All).unwrap());
+
        assert_eq!(db.seed_policy(&id).unwrap().unwrap().scope, Scope::All);
+
        assert!(db.seed(&id, Scope::Followed).unwrap());
+
        assert_eq!(db.seed_policy(&id).unwrap().unwrap().scope, Scope::Followed);
    }

    #[test]
    fn test_repo_policy() {
        let id = arbitrary::gen::<Id>(1);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

-
        assert!(db.track_repo(&id, Scope::All).unwrap());
-
        assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Allow);
-
        assert!(db.set_repo_policy(&id, Policy::Block).unwrap());
-
        assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Block);
+
        assert!(db.seed(&id, Scope::All).unwrap());
+
        assert_eq!(db.seed_policy(&id).unwrap().unwrap().policy, Policy::Allow);
+
        assert!(db.set_seed_policy(&id, Policy::Block).unwrap());
+
        assert_eq!(db.seed_policy(&id).unwrap().unwrap().policy, Policy::Block);
    }

    #[test]
    fn test_node_policy() {
        let id = arbitrary::gen::<NodeId>(1);
-
        let mut db = Config::open(":memory:").unwrap();
+
        let mut db = Store::open(":memory:").unwrap();

-
        assert!(db.track_node(&id, None).unwrap());
-
        assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Allow);
-
        assert!(db.set_node_policy(&id, Policy::Block).unwrap());
-
        assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Block);
+
        assert!(db.follow(&id, None).unwrap());
+
        assert_eq!(
+
            db.follow_policy(&id).unwrap().unwrap().policy,
+
            Policy::Allow
+
        );
+
        assert!(db.set_follow_policy(&id, Policy::Block).unwrap());
+
        assert_eq!(
+
            db.follow_policy(&id).unwrap().unwrap().policy,
+
            Policy::Block
+
        );
    }
}
modified radicle/src/profile.rs
@@ -146,7 +146,7 @@ pub enum Error {
    #[error("profile key `{0}` is not registered with ssh-agent")]
    KeyNotRegistered(PublicKey),
    #[error(transparent)]
-
    TrackingStore(#[from] node::policy::store::Error),
+
    PolicyStore(#[from] node::policy::store::Error),
    #[error(transparent)]
    DatabaseStore(#[from] node::db::Error),
}
@@ -341,22 +341,6 @@ impl Profile {
        }
    }

-
    /// Return a read-only handle to the tracking configuration of the node.
-
    pub fn tracking(&self) -> Result<policy::store::ConfigReader, policy::store::Error> {
-
        let path = self.home.node().join(node::TRACKING_DB_FILE);
-
        let config = policy::store::Config::reader(path)?;
-

-
        Ok(config)
-
    }
-

-
    /// Return a read-write handle to the tracking configuration of the node.
-
    pub fn tracking_mut(&self) -> Result<policy::store::ConfigWriter, policy::store::Error> {
-
        let path = self.home.node().join(node::TRACKING_DB_FILE);
-
        let config = policy::store::Config::open(path)?;
-

-
        Ok(config)
-
    }
-

    /// Get radicle home.
    pub fn home(&self) -> &Home {
        &self.home
@@ -364,10 +348,10 @@ impl Profile {

    /// Return a multi-source store for aliases.
    pub fn aliases(&self) -> Aliases {
-
        let tracking = self.home.tracking().ok();
+
        let policies = self.home.policies().ok();
        let db = self.home.database().ok();

-
        Aliases { tracking, db }
+
        Aliases { policies, db }
    }
}

@@ -388,15 +372,15 @@ impl std::ops::DerefMut for Profile {
/// Holds multiple alias stores, and will try
/// them one by one when asking for an alias.
pub struct Aliases {
-
    tracking: Option<policy::store::ConfigReader>,
+
    policies: Option<policy::store::StoreReader>,
    db: Option<node::Database>,
}

impl AliasStore for Aliases {
    /// Retrieve `alias` of given node.
-
    /// First looks in `tracking.db` and then `addresses.db`.
+
    /// First looks in `policies.db` and then `addresses.db`.
    fn alias(&self, nid: &NodeId) -> Option<Alias> {
-
        self.tracking
+
        self.policies
            .as_ref()
            .and_then(|db| db.alias(nid))
            .or_else(|| self.db.as_ref().and_then(|db| db.alias(nid)))
@@ -487,18 +471,18 @@ impl Home {
            .unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME))
    }

-
    /// Return a read-only handle to the tracking configuration of the node.
-
    pub fn tracking(&self) -> Result<policy::store::ConfigReader, policy::store::Error> {
-
        let path = self.node().join(node::TRACKING_DB_FILE);
-
        let config = policy::store::Config::reader(path)?;
+
    /// Return a read-only handle to the policies of the node.
+
    pub fn policies(&self) -> Result<policy::store::StoreReader, policy::store::Error> {
+
        let path = self.node().join(node::POLICIES_DB_FILE);
+
        let config = policy::store::Store::reader(path)?;

        Ok(config)
    }

-
    /// Return a read-write handle to the tracking configuration of the node.
-
    pub fn tracking_mut(&self) -> Result<policy::store::ConfigWriter, policy::store::Error> {
-
        let path = self.node().join(node::TRACKING_DB_FILE);
-
        let config = policy::store::Config::open(path)?;
+
    /// Return a read-write handle to the policies of the node.
+
    pub fn policies_mut(&self) -> Result<policy::store::StoreWriter, policy::store::Error> {
+
        let path = self.node().join(node::POLICIES_DB_FILE);
+
        let config = policy::store::Store::open(path)?;

        Ok(config)
    }
modified radicle/src/storage/git.rs
@@ -52,7 +52,7 @@ pub struct RepositoryInfo<V> {
    /// Identity document.
    pub doc: Doc<V>,
    /// Local signed refs, if any.
-
    /// Repositories with this set to `None` are ones that are tracked but not forked.
+
    /// Repositories with this set to `None` are ones that are seeded but not forked.
    pub refs: Option<refs::SignedRefsAt>,
}