Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
node: Update inventory when unseeding
cloudhead committed 1 year ago
commit b24f8a30d65e194a23d4f233c57973399822197f
parent d9fa83f9da61b770b0798ba9f42301ff84e8c826
30 files changed +510 -432
modified radicle-cli/examples/rad-cob-log.md
@@ -7,7 +7,7 @@ $ rad issue open --title "flux capacitor underpowered" --description "Flux capac
╭─────────────────────────────────────────────────────────╮
│ Title   flux capacitor underpowered                     │
│ Issue   d87dcfe8c2b3200e78b128d9b959cfdf7063fefe        │
-
│ Author  z6MknSL…StBU8Vi (you)                           │
+
│ Author  alice (you)                                     │
│ Status  open                                            │
│                                                         │
│ Flux capacitor power requirements exceed current supply │
@@ -18,11 +18,11 @@ The issue is now listed under our project.

```
$ rad issue list
-
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●   ID        Title                         Author                    Labels   Assignees   Opened │
-
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●   d87dcfe   flux capacitor underpowered   z6MknSL…StBU8Vi   (you)                        now    │
-
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭──────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●   ID        Title                         Author           Labels   Assignees   Opened │
+
├──────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●   d87dcfe   flux capacitor underpowered   alice    (you)                        now    │
+
╰──────────────────────────────────────────────────────────────────────────────────────────╯
```

Let's create a patch, too.
@@ -42,11 +42,11 @@ Patch can be listed.

```
$ rad patch
-
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title                      Author                  Reviews  Head     +   -   Updated │
-
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  aa45913  Define power requirements  z6MknSL…StBU8Vi  (you)  -        3e674d1  +0  -0  now     │
-
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭─────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title                      Author         Reviews  Head     +   -   Updated │
+
├─────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●  aa45913  Define power requirements  alice   (you)  -        3e674d1  +0  -0  now     │
+
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```

Both issue and patch COBs can be listed.
modified radicle-cli/examples/rad-cob-show.md
@@ -10,7 +10,7 @@ $ rad issue open --title "spice harvester broken" --description "Fremen have att
╭──────────────────────────────────────────────────╮
│ Title   spice harvester broken                   │
│ Issue   9de644864342d7a505eb8d58d1ef20e5bb05de2e │
-
│ Author  z6MknSL…StBU8Vi (you)                    │
+
│ Author  alice (you)                              │
│ Status  open                                     │
│                                                  │
│ Fremen have attacked, maybe we went too far?     │
@@ -21,11 +21,11 @@ The issue is now listed under our project.

```
$ rad issue list
-
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●   ID        Title                    Author                    Labels   Assignees   Opened │
-
├──────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●   9de6448   spice harvester broken   z6MknSL…StBU8Vi   (you)                        now    │
-
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭─────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●   ID        Title                    Author           Labels   Assignees   Opened │
+
├─────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●   9de6448   spice harvester broken   alice    (you)                        now    │
+
╰─────────────────────────────────────────────────────────────────────────────────────╯
```

Let's create a patch, too.
@@ -45,11 +45,11 @@ Patch can be listed.

```
$ rad patch
-
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title                        Author                  Reviews  Head     +   -   Updated │
-
├────────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  d1f7f86  Start drafting peace treaty  z6MknSL…StBU8Vi  (you)  -        575ed68  +0  -0  now     │
-
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭───────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title                        Author         Reviews  Head     +   -   Updated │
+
├───────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●  d1f7f86  Start drafting peace treaty  alice   (you)  -        575ed68  +0  -0  now     │
+
╰───────────────────────────────────────────────────────────────────────────────────────────╯
```

Both issue and patch COBs can be listed.
modified radicle-cli/examples/rad-id-update-delete-field.md
@@ -3,18 +3,18 @@ Let's add a payload field and then delete it.
```
$ rad id update --title "Add field" --description "Add a new 'web' field" --payload xyz.radicle.project web '"https://acme.example"'
✓ Identity revision a8a9fee6c4f83578ab132d375f1da0c81282bef3 created
-
╭───────────────────────────────────────────────────────────────────╮
-
│ Title    Add field                                                │
-
│ Revision a8a9fee6c4f83578ab132d375f1da0c81282bef3                 │
-
│ Blob     fbe268d13e60f1f3a1972e0ccd592f3cdecf08b5                 │
-
│ Author   did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
-
│ State    accepted                                                 │
-
│ Quorum   yes                                                      │
-
│                                                                   │
-
│ Add a new 'web' field                                             │
-
├───────────────────────────────────────────────────────────────────┤
-
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi  (you) │
-
╰───────────────────────────────────────────────────────────────────╯
+
╭────────────────────────────────────────────────────────────────────────╮
+
│ Title    Add field                                                     │
+
│ Revision a8a9fee6c4f83578ab132d375f1da0c81282bef3                      │
+
│ Blob     fbe268d13e60f1f3a1972e0ccd592f3cdecf08b5                      │
+
│ Author   did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi      │
+
│ State    accepted                                                      │
+
│ Quorum   yes                                                           │
+
│                                                                        │
+
│ Add a new 'web' field                                                  │
+
├────────────────────────────────────────────────────────────────────────┤
+
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
+
╰────────────────────────────────────────────────────────────────────────╯

@@ -1,13 +1,14 @@
 {
@@ -39,18 +39,18 @@ Now let's delete it by setting it to `null`.
```
$ rad id update --title "Delete field" --description "Delete 'web'" --payload xyz.radicle.project web null
✓ Identity revision d373c35876833105f8aafed8b610660b5737cd67 created
-
╭───────────────────────────────────────────────────────────────────╮
-
│ Title    Delete field                                             │
-
│ Revision d373c35876833105f8aafed8b610660b5737cd67                 │
-
│ Blob     d96f425412c9f8ad5d9a9a05c9831d0728e2338d                 │
-
│ Author   did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
-
│ State    accepted                                                 │
-
│ Quorum   yes                                                      │
-
│                                                                   │
-
│ Delete 'web'                                                      │
-
├───────────────────────────────────────────────────────────────────┤
-
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi  (you) │
-
╰───────────────────────────────────────────────────────────────────╯
+
╭────────────────────────────────────────────────────────────────────────╮
+
│ Title    Delete field                                                  │
+
│ Revision d373c35876833105f8aafed8b610660b5737cd67                      │
+
│ Blob     d96f425412c9f8ad5d9a9a05c9831d0728e2338d                      │
+
│ Author   did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi      │
+
│ State    accepted                                                      │
+
│ Quorum   yes                                                           │
+
│                                                                        │
+
│ Delete 'web'                                                           │
+
├────────────────────────────────────────────────────────────────────────┤
+
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
+
╰────────────────────────────────────────────────────────────────────────╯

@@ -1,14 +1,13 @@
 {
modified radicle-cli/examples/rad-inspect.md
@@ -48,7 +48,7 @@ $ rad inspect --payload
  }
}
$ rad inspect --delegates
-
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
```

Finally, the `--history` flag allows you to examine the identity document's
modified radicle-cli/examples/rad-issue.md
@@ -8,7 +8,7 @@ $ rad issue open --title "flux capacitor underpowered" --description "Flux capac
╭─────────────────────────────────────────────────────────╮
│ Title   flux capacitor underpowered                     │
│ Issue   d87dcfe8c2b3200e78b128d9b959cfdf7063fefe        │
-
│ Author  z6MknSL…StBU8Vi (you)                           │
+
│ Author  alice (you)                                     │
│ Status  open                                            │
│                                                         │
│ Flux capacitor power requirements exceed current supply │
@@ -19,11 +19,11 @@ The issue is now listed under our project.

```
$ rad issue list
-
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●   ID        Title                         Author                    Labels   Assignees   Opened │
-
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●   d87dcfe   flux capacitor underpowered   z6MknSL…StBU8Vi   (you)                        now    │
-
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭──────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●   ID        Title                         Author           Labels   Assignees   Opened │
+
├──────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●   d87dcfe   flux capacitor underpowered   alice    (you)                        now    │
+
╰──────────────────────────────────────────────────────────────────────────────────────────╯
```

Show the issue information issue.
@@ -33,7 +33,7 @@ $ rad issue show d87dcfe
╭─────────────────────────────────────────────────────────╮
│ Title   flux capacitor underpowered                     │
│ Issue   d87dcfe8c2b3200e78b128d9b959cfdf7063fefe        │
-
│ Author  z6MknSL…StBU8Vi (you)                           │
+
│ Author  alice (you)                                     │
│ Status  open                                            │
│                                                         │
│ Flux capacitor power requirements exceed current supply │
@@ -59,11 +59,11 @@ It will now show in the list of issues assigned to us, along with the new label.

```
$ rad issue list --assigned
-
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●   ID        Title                         Author                    Labels             Assignees         Opened │
-
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●   d87dcfe   flux capacitor underpowered   z6MknSL…StBU8Vi   (you)   good-first-issue   z6MknSL…StBU8Vi   now    │
-
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●   ID        Title                         Author           Labels             Assignees   Opened │
+
├────────────────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●   d87dcfe   flux capacitor underpowered   alice    (you)   good-first-issue   alice       now    │
+
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

Note: this can always be undone with the `unassign` subcommand.
@@ -91,16 +91,16 @@ $ rad issue show d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
╭─────────────────────────────────────────────────────────╮
│ Title   flux capacitor underpowered                     │
│ Issue   d87dcfe8c2b3200e78b128d9b959cfdf7063fefe        │
-
│ Author  z6MknSL…StBU8Vi (you)                           │
+
│ Author  alice (you)                                     │
│ Labels  good-first-issue                                │
│ Status  open                                            │
│                                                         │
│ Flux capacitor power requirements exceed current supply │
├─────────────────────────────────────────────────────────┤
-
│ z6MknSL…StBU8Vi (you) now 2193e87                       │
+
│ alice (you) now 2193e87                                 │
│ The flux capacitor needs 1.21 Gigawatts                 │
├─────────────────────────────────────────────────────────┤
-
│ z6MknSL…StBU8Vi (you) now 880fdcd                       │
+
│ alice (you) now 880fdcd                                 │
│ More power!                                             │
╰─────────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-node.md
@@ -34,9 +34,9 @@ $ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias Bo
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (Bob)
```

-
Now, when we use the `rad seed` command we will see
-
information for repositories that we seed -- in this case a
-
repository that was already created:
+
Now, when we use the `rad seed` command we will see information for
+
repositories that we seed -- in this case a repository that was already
+
created:

```
$ rad seed
@@ -87,6 +87,35 @@ $ rad node stop
✗ Stopping node... error: node is not running
```

+
Note that if we unseed a repository, it is no longer part of our inventory:
+

+
```
+
$ rad unseed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+
✓ Seeding policy for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji removed
+
$ rad node routing --nid z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
```
+

+
Likewise, if we seed a repository we don't have locally, it won't show up as
+
part of our inventory:
+
```
+
$ rad seed rad:z3trNYnLWS11cJWC6BbxDs5niGo82
+
[...]
+
$ rad node routing --nid z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
```
+

+
But if we start seeding the repository we have locally again, it'll show
+
up in our inventory:
+
```
+
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+
[...]
+
$ rad node routing --nid z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
╭─────────────────────────────────────────────────────╮
+
│ RID                                 NID             │
+
├─────────────────────────────────────────────────────┤
+
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   z6MknSL…StBU8Vi │
+
╰─────────────────────────────────────────────────────╯
+
```
+

Some commands also give us a hint if the node isn't running:

``` (fail)
modified radicle-cli/examples/rad-patch-ahead-behind.md
@@ -45,11 +45,11 @@ To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkE
When listing, we see that it has one addition:
```
$ rad patch list
-
╭─────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title     Author                  Reviews  Head     +   -   Updated │
-
├─────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  217f050  Add Alan  z6MknSL…StBU8Vi  (you)  -        5c88a79  +1  -0  now     │
-
╰─────────────────────────────────────────────────────────────────────────────────╯
+
╭────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title     Author         Reviews  Head     +   -   Updated │
+
├────────────────────────────────────────────────────────────────────────┤
+
│ ●  217f050  Add Alan  alice   (you)  -        5c88a79  +1  -0  now     │
+
╰────────────────────────────────────────────────────────────────────────╯
```

When showing the patch, we see that it is `ahead 1, behind 1`, since master has
@@ -59,7 +59,7 @@ $ rad patch show -v -p 217f050
╭────────────────────────────────────────────────────╮
│ Title     Add Alan                                 │
│ Patch     217f050f8891def8fb863f7c0b4f85c89f97299d │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7 │
│ Base      f64fb2c8fe28f7c458c72ec8d700373924794943 │
│ Branches  feature/1                                │
@@ -68,7 +68,7 @@ $ rad patch show -v -p 217f050
├────────────────────────────────────────────────────┤
│ 5c88a79 Add Alan                                   │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (5c88a79) now    │
+
│ ● opened by alice (you) (5c88a79) now              │
╰────────────────────────────────────────────────────╯

commit 5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7
@@ -105,7 +105,7 @@ $ rad patch show -v e22ff008e2a0ed47262890d13263031d7555b555
╭────────────────────────────────────────────────────╮
│ Title     Add Mel                                  │
│ Patch     e22ff008e2a0ed47262890d13263031d7555b555 │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      7f63fcbcf23fc39eea784c091ad3d20d7e4bd005 │
│ Base      f64fb2c8fe28f7c458c72ec8d700373924794943 │
│ Branches  feature/2                                │
@@ -115,7 +115,7 @@ $ rad patch show -v e22ff008e2a0ed47262890d13263031d7555b555
│ 7f63fcb Add Mel                                    │
│ 5c88a79 Add Alan                                   │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (7f63fcb) now    │
+
│ ● opened by alice (you) (7f63fcb) now              │
╰────────────────────────────────────────────────────╯
```

@@ -140,7 +140,7 @@ $ rad patch show -v a467ffa260c4fbe355b6fb550ba0c4956078717e
╭────────────────────────────────────────────────────╮
│ Title     Add Mel #2                               │
│ Patch     a467ffa260c4fbe355b6fb550ba0c4956078717e │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      7f63fcbcf23fc39eea784c091ad3d20d7e4bd005 │
│ Base      5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7 │
│ Branches  feature/2                                │
@@ -149,6 +149,6 @@ $ rad patch show -v a467ffa260c4fbe355b6fb550ba0c4956078717e
├────────────────────────────────────────────────────┤
│ 7f63fcb Add Mel                                    │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (7f63fcb) now    │
+
│ ● opened by alice (you) (7f63fcb) now              │
╰────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-patch-change-base.md
@@ -47,7 +47,7 @@ $ rad patch show 183d343ab47d7fe18baf1b24b7209ad033d7fe5c -v
╭────────────────────────────────────────────────────╮
│ Title     Add README, just for the fun             │
│ Patch     183d343ab47d7fe18baf1b24b7209ad033d7fe5c │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
│ Base      f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 │
│ Branches  add-readme                               │
@@ -57,7 +57,7 @@ $ rad patch show 183d343ab47d7fe18baf1b24b7209ad033d7fe5c -v
│ 27857ec Add README, just for the fun               │
│ 3e674d1 Define power requirements                  │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (27857ec) now    │
+
│ ● opened by alice (you) (27857ec) now              │
╰────────────────────────────────────────────────────╯
```

@@ -78,7 +78,7 @@ $ rad patch show 183d343 -v
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Add README, just for the fun                              │
│ Patch     183d343ab47d7fe18baf1b24b7209ad033d7fe5c                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      27857ec9eb04c69cacab516e8bf4b5fd36090f66                  │
│ Base      3e674d1a1df90807e934f9ae5da2591dd6848a33                  │
│ Branches  add-readme                                                │
@@ -87,7 +87,7 @@ $ rad patch show 183d343 -v
├─────────────────────────────────────────────────────────────────────┤
│ 27857ec Add README, just for the fun                                │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (27857ec) now                     │
+
│ ● opened by alice (you) (27857ec) now                               │
│ ↑ updated to ebe76f9c2148eb595d7a745f82275786bf3458c3 (27857ec) now │
╰─────────────────────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-patch-checkout-revision.md
@@ -18,7 +18,7 @@ $ rad patch show aa45913
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Define power requirements                                 │
│ Patch     aa45913e757cacd46972733bddee5472c78fa32a                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      639f44a25145a37f747f3c84265037a9461e44c5                  │
│ Branches  patch/aa45913                                             │
│ Commits   ahead 3, behind 0                                         │
@@ -30,7 +30,7 @@ $ rad patch show aa45913
│ 27857ec Add README, just for the fun                                │
│ 3e674d1 Define power requirements                                   │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now                     │
+
│ ● opened by alice (you) (3e674d1) now                               │
│ ↑ updated to 3156bed9d64d4675d6cf56612d217fc5f4e8a53a (27857ec) now │
│ ↑ updated to 2f5324f61e05cda65b667eeea02570d077a8e724 (639f44a) now │
╰─────────────────────────────────────────────────────────────────────╯
modified radicle-cli/examples/rad-patch-draft.md
@@ -21,7 +21,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
╭────────────────────────────────────────────────────╮
│ Title     Nothing yet                              │
│ Patch     97e18f8598237a396a1c0ac1509c89028e666c97 │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      2a465832b5a76abe25be44a3a5d224bbd7741ba7 │
│ Branches  cloudhead/draft                          │
│ Commits   ahead 1, behind 0                        │
@@ -29,7 +29,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
├────────────────────────────────────────────────────┤
│ 2a46583 Nothing to see here..                      │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (2a46583) [ .. ] │
+
│ ● opened by alice (you) (2a46583) [ .. ]           │
╰────────────────────────────────────────────────────╯
```

@@ -44,7 +44,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
╭────────────────────────────────────────────────────╮
│ Title     Nothing yet                              │
│ Patch     97e18f8598237a396a1c0ac1509c89028e666c97 │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      2a465832b5a76abe25be44a3a5d224bbd7741ba7 │
│ Branches  cloudhead/draft                          │
│ Commits   ahead 1, behind 0                        │
@@ -52,7 +52,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
├────────────────────────────────────────────────────┤
│ 2a46583 Nothing to see here..                      │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (2a46583) [ .. ] │
+
│ ● opened by alice (you) (2a46583) [ .. ]           │
╰────────────────────────────────────────────────────╯
```

@@ -65,7 +65,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
╭────────────────────────────────────────────────────╮
│ Title     Nothing yet                              │
│ Patch     97e18f8598237a396a1c0ac1509c89028e666c97 │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      2a465832b5a76abe25be44a3a5d224bbd7741ba7 │
│ Branches  cloudhead/draft                          │
│ Commits   ahead 1, behind 0                        │
@@ -73,6 +73,6 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
├────────────────────────────────────────────────────┤
│ 2a46583 Nothing to see here..                      │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (2a46583) [ .. ] │
+
│ ● opened by alice (you) (2a46583) [ .. ]           │
╰────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-patch-edit.md
@@ -44,7 +44,7 @@ $ rad patch show 89f7afb
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Add README, just for the fun                              │
│ Patch     89f7afb1511b976482b21f6b2f39aef7f4fb88a2                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      8945f6189adf027892c85ac57f7e9341049c2537                  │
│ Branches  changes                                                   │
│ Commits   ahead 2, behind 0                                         │
@@ -53,7 +53,7 @@ $ rad patch show 89f7afb
│ 8945f61 Define the LICENSE                                          │
│ 03c02af Add README, just for the fun                                │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (03c02af) now                     │
+
│ ● opened by alice (you) (03c02af) now                               │
│ ↑ updated to 5d78dd5376453e25df5988ec86951c99cb73742c (8945f61) now │
╰─────────────────────────────────────────────────────────────────────╯
```
@@ -67,7 +67,7 @@ $ rad patch show 89f7afb
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Add Metadata                                              │
│ Patch     89f7afb1511b976482b21f6b2f39aef7f4fb88a2                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      8945f6189adf027892c85ac57f7e9341049c2537                  │
│ Branches  changes                                                   │
│ Commits   ahead 2, behind 0                                         │
@@ -78,7 +78,7 @@ $ rad patch show 89f7afb
│ 8945f61 Define the LICENSE                                          │
│ 03c02af Add README, just for the fun                                │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (03c02af) now                     │
+
│ ● opened by alice (you) (03c02af) now                               │
│ ↑ updated to 5d78dd5376453e25df5988ec86951c99cb73742c (8945f61) now │
╰─────────────────────────────────────────────────────────────────────╯
```
@@ -95,7 +95,7 @@ $ rad patch show 89f7afb
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Add Metadata                                              │
│ Patch     89f7afb1511b976482b21f6b2f39aef7f4fb88a2                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      8945f6189adf027892c85ac57f7e9341049c2537                  │
│ Branches  changes                                                   │
│ Commits   ahead 2, behind 0                                         │
@@ -106,7 +106,7 @@ $ rad patch show 89f7afb
│ 8945f61 Define the LICENSE                                          │
│ 03c02af Add README, just for the fun                                │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (03c02af) now                     │
+
│ ● opened by alice (you) (03c02af) now                               │
│ ↑ updated to 5d78dd5376453e25df5988ec86951c99cb73742c (8945f61) now │
╰─────────────────────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-patch-update.md
@@ -16,7 +16,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
╭────────────────────────────────────────────────────╮
│ Title     Not a real change                        │
│ Patch     b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5 │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      51b2f0f77b9849bfaa3e9d3ff68ee2f57771d20c │
│ Branches  feature/1                                │
│ Commits   ahead 1, behind 0                        │
@@ -24,7 +24,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
├────────────────────────────────────────────────────┤
│ 51b2f0f Not a real change                          │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (51b2f0f) now    │
+
│ ● opened by alice (you) (51b2f0f) now              │
╰────────────────────────────────────────────────────╯
```

@@ -57,7 +57,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Not a real change                                         │
│ Patch     b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      4d272148458a17620541555b1f0905c01658aa9f                  │
│ Branches  feature/1                                                 │
│ Commits   ahead 2, behind 0                                         │
@@ -66,7 +66,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
│ 4d27214 Rename readme file                                          │
│ 51b2f0f Not a real change                                           │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (51b2f0f) now                     │
+
│ ● opened by alice (you) (51b2f0f) now                               │
│ ↑ updated to ea7def3857f62f404606d7cd6490cd0de4eaebd1 (4d27214) now │
╰─────────────────────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-patch-via-push.md
@@ -23,7 +23,7 @@ $ rad patch show 6035d2f582afbe01ff23ea87528ae523d76875b6
╭────────────────────────────────────────────────────╮
│ Title     Add things #1                            │
│ Patch     6035d2f582afbe01ff23ea87528ae523d76875b6 │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      42d894a83c9c356552a57af09ccdbd5587a99045 │
│ Branches  feature/1                                │
│ Commits   ahead 1, behind 0                        │
@@ -33,7 +33,7 @@ $ rad patch show 6035d2f582afbe01ff23ea87528ae523d76875b6
├────────────────────────────────────────────────────┤
│ 42d894a Add things                                 │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (42d894a) now    │
+
│ ● opened by alice (you) (42d894a) now              │
╰────────────────────────────────────────────────────╯
```

@@ -96,12 +96,12 @@ And both patches:

```
$ rad patch
-
╭────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title            Author                  Reviews  Head     +   -   Updated │
-
├────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  6035d2f  Add things #1    z6MknSL…StBU8Vi  (you)  -        42d894a  +0  -0  now     │
-
│ ●  9580891  Add more things  z6MknSL…StBU8Vi  (you)  -        8b0ea80  +0  -0  now     │
-
╰────────────────────────────────────────────────────────────────────────────────────────╯
+
╭───────────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title            Author         Reviews  Head     +   -   Updated │
+
├───────────────────────────────────────────────────────────────────────────────┤
+
│ ●  6035d2f  Add things #1    alice   (you)  -        42d894a  +0  -0  now     │
+
│ ●  9580891  Add more things  alice   (you)  -        8b0ea80  +0  -0  now     │
+
╰───────────────────────────────────────────────────────────────────────────────╯
```

To update our patch, we simply push commits to the upstream branch:
@@ -136,7 +136,7 @@ $ rad patch show 9580891
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Add more things                                           │
│ Patch     95808913573cead52ad7b42c7b475260ec45c4b2                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      02bef3fac41b2f98bb3c02b868a53ddfecb55b5f                  │
│ Branches  feature/2                                                 │
│ Commits   ahead 2, behind 0                                         │
@@ -145,7 +145,7 @@ $ rad patch show 9580891
│ 02bef3f Improve code                                                │
│ 8b0ea80 Add more things                                             │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (8b0ea80) now                     │
+
│ ● opened by alice (you) (8b0ea80) now                               │
│ ↑ updated to d7040c6c97629c2b94f86fb639bebbff5de39697 (02bef3f) now │
╰─────────────────────────────────────────────────────────────────────╯
```
@@ -212,7 +212,7 @@ $ rad patch show 9580891
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Add more things                                           │
│ Patch     95808913573cead52ad7b42c7b475260ec45c4b2                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Head      9304dbc445925187994a7a93222a3f8bde73b785                  │
│ Branches  feature/2                                                 │
│ Commits   ahead 2, behind 0                                         │
@@ -221,7 +221,7 @@ $ rad patch show 9580891
│ 9304dbc Amended commit                                              │
│ 8b0ea80 Add more things                                             │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (8b0ea80) now                     │
+
│ ● opened by alice (you) (8b0ea80) now                               │
│ ↑ updated to d7040c6c97629c2b94f86fb639bebbff5de39697 (02bef3f) now │
│ ↑ updated to 670d02794aa05afd6e0851f4aa848bc87c4712c7 (9304dbc) now │
╰─────────────────────────────────────────────────────────────────────╯
modified radicle-cli/examples/rad-patch.md
@@ -35,18 +35,18 @@ It will now be listed as one of the project's open patches.

```
$ rad patch
-
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title                      Author                  Reviews  Head     +   -   Updated │
-
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  aa45913  Define power requirements  z6MknSL…StBU8Vi  (you)  -        3e674d1  +0  -0  now     │
-
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭─────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title                      Author         Reviews  Head     +   -   Updated │
+
├─────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●  aa45913  Define power requirements  alice   (you)  -        3e674d1  +0  -0  now     │
+
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```
```
$ rad patch show aa45913e757cacd46972733bddee5472c78fa32a -p
╭────────────────────────────────────────────────────╮
│ Title     Define power requirements                │
│ Patch     aa45913e757cacd46972733bddee5472c78fa32a │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Head      3e674d1a1df90807e934f9ae5da2591dd6848a33 │
│ Branches  flux-capacitor-power                     │
│ Commits   ahead 1, behind 0                        │
@@ -56,7 +56,7 @@ $ rad patch show aa45913e757cacd46972733bddee5472c78fa32a -p
├────────────────────────────────────────────────────┤
│ 3e674d1 Define power requirements                  │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now    │
+
│ ● opened by alice (you) (3e674d1) now              │
╰────────────────────────────────────────────────────╯

commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
@@ -75,11 +75,11 @@ We can also list only patches that we've authored.

```
$ rad patch list --authored
-
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title                      Author                  Reviews  Head     +   -   Updated │
-
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  aa45913  Define power requirements  z6MknSL…StBU8Vi  (you)  -        3e674d1  +0  -0  now     │
-
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭─────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title                      Author         Reviews  Head     +   -   Updated │
+
├─────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●  aa45913  Define power requirements  alice   (you)  -        3e674d1  +0  -0  now     │
+
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```

We can also see that it set an upstream for our patch branch:
@@ -99,7 +99,7 @@ $ rad patch show aa45913
╭────────────────────────────────────────────────────╮
│ Title     Define power requirements                │
│ Patch     aa45913e757cacd46972733bddee5472c78fa32a │
-
│ Author    z6MknSL…StBU8Vi (you)                    │
+
│ Author    alice (you)                              │
│ Labels    fun                                      │
│ Head      3e674d1a1df90807e934f9ae5da2591dd6848a33 │
│ Branches  flux-capacitor-power                     │
@@ -110,7 +110,7 @@ $ rad patch show aa45913
├────────────────────────────────────────────────────┤
│ 3e674d1 Define power requirements                  │
├────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now    │
+
│ ● opened by alice (you) (3e674d1) now              │
╰────────────────────────────────────────────────────╯
```

@@ -136,7 +136,7 @@ And let's leave a quick comment for our team:
```
$ rad patch comment aa45913 --message 'I cannot wait to get back to the 90s!' --no-announce
╭───────────────────────────────────────╮
-
│ z6MknSL…StBU8Vi (you) now 686ec1c     │
+
│ alice (you) now 686ec1c               │
│ I cannot wait to get back to the 90s! │
╰───────────────────────────────────────╯
$ rad patch comment aa45913 --message 'My favorite decade!' --reply-to 686ec1c -q --no-announce
@@ -165,7 +165,7 @@ $ rad patch show aa45913
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Define power requirements                                 │
│ Patch     aa45913e757cacd46972733bddee5472c78fa32a                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Labels    fun                                                       │
│ Head      27857ec9eb04c69cacab516e8bf4b5fd36090f66                  │
│ Branches  flux-capacitor-power, patch/aa45913                       │
@@ -177,16 +177,16 @@ $ rad patch show aa45913
│ 27857ec Add README, just for the fun                                │
│ 3e674d1 Define power requirements                                   │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now                     │
+
│ ● opened by alice (you) (3e674d1) now                               │
│ ↑ updated to 6e5a3b7b2ce27b32e7ccc2f0b3f4594897dde638 (27857ec) now │
-
│   └─ ✓ accepted by z6MknSL…StBU8Vi (you) now                        │
+
│   └─ ✓ accepted by alice (you) now                                  │
╰─────────────────────────────────────────────────────────────────────╯
$ rad patch list
-
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-
│ ●  ID       Title                      Author                  Reviews  Head     +   -   Updated │
-
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
-
│ ●  aa45913  Define power requirements  z6MknSL…StBU8Vi  (you)  ✔        27857ec  +0  -0  now     │
-
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
+
╭─────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●  ID       Title                      Author         Reviews  Head     +   -   Updated │
+
├─────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●  aa45913  Define power requirements  alice   (you)  ✔        27857ec  +0  -0  now     │
+
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```

If you make a mistake on the patch description, you can always change it!
@@ -197,7 +197,7 @@ $ rad patch show aa45913
╭─────────────────────────────────────────────────────────────────────╮
│ Title     Define power requirements                                 │
│ Patch     aa45913e757cacd46972733bddee5472c78fa32a                  │
-
│ Author    z6MknSL…StBU8Vi (you)                                     │
+
│ Author    alice (you)                                               │
│ Labels    fun                                                       │
│ Head      27857ec9eb04c69cacab516e8bf4b5fd36090f66                  │
│ Branches  flux-capacitor-power, patch/aa45913                       │
@@ -209,8 +209,8 @@ $ rad patch show aa45913
│ 27857ec Add README, just for the fun                                │
│ 3e674d1 Define power requirements                                   │
├─────────────────────────────────────────────────────────────────────┤
-
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now                     │
+
│ ● opened by alice (you) (3e674d1) now                               │
│ ↑ updated to 6e5a3b7b2ce27b32e7ccc2f0b3f4594897dde638 (27857ec) now │
-
│   └─ ✓ accepted by z6MknSL…StBU8Vi (you) now                        │
+
│   └─ ✓ accepted by alice (you) now                                  │
╰─────────────────────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-publish.md
@@ -27,18 +27,18 @@ repository private again __will not_ be replicated.
```
$ rad id update --visibility private --title "Privatise" --description "Reverting the rad publish event"
✓ Identity revision 774cc1e72641d97d7dc9377745b7f454a9171747 created
-
╭───────────────────────────────────────────────────────────────────╮
-
│ Title    Privatise                                                │
-
│ Revision 774cc1e72641d97d7dc9377745b7f454a9171747                 │
-
│ Blob     88f759a4d46e9535766fccec0cbfe1fed6160b1a                 │
-
│ Author   did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
-
│ State    accepted                                                 │
-
│ Quorum   yes                                                      │
-
│                                                                   │
-
│ Reverting the rad publish event                                   │
-
├───────────────────────────────────────────────────────────────────┤
-
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi  (you) │
-
╰───────────────────────────────────────────────────────────────────╯
+
╭────────────────────────────────────────────────────────────────────────╮
+
│ Title    Privatise                                                     │
+
│ Revision 774cc1e72641d97d7dc9377745b7f454a9171747                      │
+
│ Blob     88f759a4d46e9535766fccec0cbfe1fed6160b1a                      │
+
│ Author   did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi      │
+
│ State    accepted                                                      │
+
│ Quorum   yes                                                           │
+
│                                                                        │
+
│ Reverting the rad publish event                                        │
+
├────────────────────────────────────────────────────────────────────────┤
+
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
+
╰────────────────────────────────────────────────────────────────────────╯

@@ -1,13 +1,16 @@
 {
modified radicle-cli/src/commands/init.rs
@@ -287,11 +287,14 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
            if options.verbose {
                term::blob(json::to_string_pretty(&proj)?);
            }
-

            // 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.seed {
                cli::project::seed(rid, options.scope, &mut node, profile)?;
+

+
                if doc.visibility.is_public() {
+
                    cli::project::add_inventory(rid, &mut node, profile)?;
+
                }
            }

            if options.set_upstream || git::branch_remote(&repo, proj.default_branch()).is_err() {
@@ -370,7 +373,6 @@ fn sync(
    let events = node.subscribe(DEFAULT_SUBSCRIBE_TIMEOUT)?;
    let sessions = node.sessions()?;

-
    node.update_inventory(rid)?;
    spinner.message("Announcing..");

    if !sessions.iter().any(|s| s.is_connected()) {
modified radicle-cli/src/commands/node/routing.rs
@@ -27,7 +27,7 @@ pub fn run<S: node::routing::Store>(

fn print_table(entries: impl IntoIterator<Item = (RepoId, NodeId)>) {
    let mut t = term::Table::new(term::table::TableOptions::bordered());
-
    t.push([
+
    t.header([
        term::format::default(String::from("RID")),
        term::format::default(String::from("NID")),
    ]);
modified radicle-cli/src/project.rs
@@ -1,9 +1,10 @@
+
use localtime::LocalTime;
use radicle::prelude::*;

use crate::git;
use radicle::git::RefStr;
use radicle::node::policy::Scope;
-
use radicle::node::{Handle, NodeId};
+
use radicle::node::{routing, Handle, NodeId};
use radicle::Node;

/// Setup a repository remote and tracking branch.
@@ -52,8 +53,28 @@ impl<'a> SetupRemote<'a> {
    }
}

-
/// Seed a repository by first trying to seed through the node, and if the node isn't running,
-
/// by updating the policy database directly.
+
/// Add the repo to our inventory.
+
pub fn add_inventory(
+
    rid: RepoId,
+
    node: &mut Node,
+
    profile: &Profile,
+
) -> Result<bool, anyhow::Error> {
+
    match node.update_inventory(rid) {
+
        Ok(updated) => Ok(updated),
+
        Err(e) if e.is_connection_err() => {
+
            let now = LocalTime::now();
+
            let mut db = profile.database_mut()?;
+
            let updates = routing::Store::insert(&mut db, [&rid], *profile.id(), now.into())?;
+

+
            Ok(!updates.is_empty())
+
        }
+
        Err(e) => Err(e.into()),
+
    }
+
}
+

+
/// Seed a repository by first trying to seed through the node, and if the node isn't running, by
+
/// updating the policy database directly. If the repo is available locally, we also add it to our
+
/// inventory.
pub fn seed(
    rid: RepoId,
    scope: Scope,
@@ -64,7 +85,15 @@ pub fn seed(
        Ok(updated) => Ok(updated),
        Err(e) if e.is_connection_err() => {
            let mut config = profile.policies_mut()?;
-
            config.seed(&rid, scope).map_err(|e| e.into())
+
            let result = config.seed(&rid, scope)?;
+

+
            if result && profile.storage.contains(&rid)? {
+
                let now = LocalTime::now();
+
                let mut db = profile.database_mut()?;
+

+
                routing::Store::insert(&mut db, [&rid], *profile.id(), now.into())?;
+
            }
+
            Ok(result)
        }
        Err(e) => Err(e.into()),
    }
@@ -77,7 +106,12 @@ pub fn unseed(rid: RepoId, node: &mut Node, profile: &Profile) -> Result<bool, a
        Ok(updated) => Ok(updated),
        Err(e) if e.is_connection_err() => {
            let mut config = profile.policies_mut()?;
-
            config.unseed(&rid).map_err(|e| e.into())
+
            let result = config.unseed(&rid)?;
+

+
            let mut db = profile.database_mut()?;
+
            radicle::node::routing::Store::remove(&mut db, &rid, profile.id())?;
+

+
            Ok(result)
        }
        Err(e) => Err(e.into()),
    }
modified radicle-cli/tests/commands.rs
@@ -730,6 +730,7 @@ fn rad_node() {
            Address::from(net::SocketAddr::from(([41, 12, 98, 112], 8776))),
            Address::from_str("seed.cloudhead.io:8776").unwrap(),
        ],
+
        seeding_policy: SeedingPolicy::Block,
        ..Config::test(Alias::new("alice"))
    });
    let working = tempfile::tempdir().unwrap();
@@ -1286,6 +1287,8 @@ fn rad_clone_partial_fail() {
    let working = environment.tmp().join("working");
    let carol = NodeId::from_str("z6MksFqXN3Yhqk8pTJdUGLwBTkRfQvwZXPqR2qMEhbS9wzpT").unwrap();

+
    logger::init(log::Level::Debug);
+

    // Setup a test project.
    let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");

@@ -1323,7 +1326,8 @@ fn rad_clone_partial_fail() {
    eve.connect(&alice);
    eve.connect(&bob);
    eve.routes_to(&[(acme, carol), (acme, bob.id), (acme, alice.id)]);
-
    bob.handle.unseed(acme).unwrap(); // Cause the fetch with bob to fail.
+
    bob.storage.repository(acme).unwrap().remove().unwrap(); // Cause the fetch from Bob to fail.
+
    bob.storage.lock_repository(acme).ok(); // Prevent repo from being re-fetched.

    test(
        "examples/rad-clone-partial-fail.md",
modified radicle-node/src/runtime.rs
@@ -125,10 +125,6 @@ impl Runtime {
            log::warn!(target: "node", "Unused or deprecated configuration attribute {:?}", key);
        }
        log::info!(target: "node", "Opening node database..");
-
        let db = home
-
            .database_mut()?
-
            .journal_mode(node::db::JournalMode::default())?;
-
        let mut stores: service::Stores<_> = db.clone().into();

        log::info!(target: "node", "Opening policy database..");
        let policies = home.policies_mut()?;
@@ -175,6 +171,18 @@ impl Runtime {
                .expect("Runtime::init: unable to solve proof-of-work puzzle")
        };

+
        let db = home
+
            .database_mut()?
+
            .journal_mode(node::db::JournalMode::default())?
+
            .init(
+
                &id,
+
                announcement.features,
+
                announcement.alias.clone(),
+
                announcement.timestamp,
+
                announcement.addresses.iter(),
+
            )?;
+
        let mut stores: service::Stores<_> = db.clone().into();
+

        if config.connect.is_empty() && stores.addresses().is_empty()? {
            log::info!(target: "node", "Address book is empty. Adding bootstrap nodes..");

modified radicle-node/src/service.rs
@@ -10,7 +10,7 @@ pub mod message;
pub mod session;

use std::collections::hash_map::Entry;
-
use std::collections::{BTreeSet, HashMap, VecDeque};
+
use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
use std::net::IpAddr;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
@@ -391,6 +391,12 @@ where
    }
}

+
impl<D> AsMut<D> for Stores<D> {
+
    fn as_mut(&mut self) -> &mut D {
+
        &mut self.0
+
    }
+
}
+

impl<D> From<D> for Stores<D> {
    fn from(db: D) -> Self {
        Self(db)
@@ -555,16 +561,23 @@ where
    /// simply not announcing it anymore, it will eventually be pruned by nodes.
    pub fn unseed(&mut self, id: &RepoId) -> 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.policies
-
                .seed_policies()?
-
                .filter_map(|t| (t.policy.is_allow()).then_some(t.rid)),
-
        );
+

+
        if updated {
+
            // 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.policies
+
                    .seed_policies()?
+
                    .filter_map(|t| (t.policy.is_allow()).then_some(t.rid)),
+
            );
+
            // Update and announce new inventory.
+
            if let Err(e) = self.remove_inventory(id) {
+
                error!(target: "service", "Error updating inventory after unseed: {e}");
+
            }
+
        }
        Ok(updated)
    }

@@ -623,12 +636,18 @@ where

    /// Lookup a repository, both locally and in the routing table.
    pub fn lookup(&self, rid: RepoId) -> Result<Lookup, LookupError> {
-
        let remote = self.db.routing().get(&rid)?.iter().cloned().collect();
+
        let this = self.nid();
+
        let local = self.storage.get(rid)?;
+
        let remote = self
+
            .db
+
            .routing()
+
            .get(&rid)?
+
            .iter()
+
            .filter(|nid| nid != &this)
+
            .cloned()
+
            .collect();

-
        Ok(Lookup {
-
            local: self.storage.get(rid)?,
-
            remote,
-
        })
+
        Ok(Lookup { local, remote })
    }

    /// Initialize service with current time. Call this once.
@@ -662,27 +681,13 @@ where
            Err(e) => error!(target: "service", "Error checking refs database: {e}"),
        }

-
        // Ensure that our local node is in our address database.
-
        self.db
-
            .addresses_mut()
-
            .insert(
-
                &nid,
-
                self.node.features,
-
                self.node.alias.clone(),
-
                self.node.work(),
-
                self.node.timestamp,
-
                self.node
-
                    .addresses
-
                    .iter()
-
                    .map(|a| KnownAddress::new(a.clone(), address::Source::Peer)),
-
            )
-
            .expect("Service::initialize: error adding local node to address database");
-

        let announced = self
            .db
            .seeds()
            .seeded_by(&nid)?
            .collect::<Result<HashMap<_, _>, _>>()?;
+
        let mut inventory = BTreeSet::new();
+

        for repo in self.storage.repositories()? {
            let rid = repo.rid;

@@ -693,7 +698,7 @@ where
            }
            // Add public repositories to inventory.
            if repo.doc.visibility.is_public() {
-
                self.storage.insert(rid);
+
                inventory.insert(rid);
            }
            // If we have no owned refs for this repo, then there's nothing to announce.
            let Some(updated_at) = repo.synced_at else {
@@ -724,7 +729,6 @@ where
        }

        {
-
            let inventory = self.storage.inventory()?;
            // 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.
@@ -805,7 +809,7 @@ where
        if now - self.last_sync >= SYNC_INTERVAL {
            trace!(target: "service", "Running 'sync' task...");

-
            if let Err(e) = self.fetch_missing_inventory() {
+
            if let Err(e) = self.fetch_missing_repositories() {
                error!(target: "service", "Error fetching missing inventory: {e}");
            }
            self.outbox.wakeup(SYNC_INTERVAL);
@@ -814,9 +818,7 @@ where
        if now - self.last_announce >= ANNOUNCE_INTERVAL {
            trace!(target: "service", "Running 'announce' task...");

-
            if let Err(err) = self.announce_inventory() {
-
                error!(target: "service", "Error announcing inventory: {err}");
-
            }
+
            self.announce_inventory();
            self.outbox.wakeup(ANNOUNCE_INTERVAL);
        }
        if now - self.last_prune >= PRUNE_INTERVAL {
@@ -939,19 +941,16 @@ where
                }
            }
            Command::AnnounceInventory => {
-
                if let Err(err) = self.announce_inventory() {
-
                    error!(target: "service", "Error announcing inventory: {err}");
-
                }
-
            }
-
            Command::UpdateInventory(rid, resp) => {
-
                self.storage.insert(rid);
-

-
                let synced = self
-
                    .sync_inventory()
-
                    .expect("Service::command: error syncing inventory");
-
                resp.send(synced.added.len() + synced.removed.len() > 0)
-
                    .ok();
+
                self.announce_inventory();
            }
+
            Command::UpdateInventory(rid, resp) => match self.add_inventory(rid) {
+
                Ok(updated) => {
+
                    resp.send(updated).ok();
+
                }
+
                Err(e) => {
+
                    error!(target: "service", "Error adding {rid} to inventory: {e}");
+
                }
+
            },
            Command::QueryState(query, sender) => {
                sender.send(query(self)).ok();
            }
@@ -1171,8 +1170,9 @@ where
                if clone && doc.visibility.is_public() {
                    debug!(target: "service", "Updating and announcing inventory for cloned repository {rid}..");

-
                    self.storage.insert(rid);
-
                    self.sync_and_announce_inventory();
+
                    if let Err(e) = self.add_inventory(rid) {
+
                        error!(target: "service", "Error announcing inventory for {rid}: {e}");
+
                    }
                }

                // It's possible for a fetch to succeed but nothing was updated.
@@ -1539,8 +1539,6 @@ where
                        return Ok(None);
                    }
                }
-

-
                let inventory = self.storage.inventory_ref();
                let mut missing = Vec::new();

                for id in message.inventory.as_slice() {
@@ -1560,14 +1558,20 @@ where
                        ) {
                            // 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.
-
                            if !inventory.contains(id) {
-
                                missing.push(*id);
+
                            match self.db.routing().entry(id, self.nid()) {
+
                                Ok(entry) => {
+
                                    if entry.is_none() {
+
                                        missing.push(*id);
+
                                    }
+
                                }
+
                                Err(e) => error!(
+
                                    target: "service",
+
                                    "Error checking local inventory for {id}: {e}"
+
                                ),
                            }
                        }
                    }
                }
-
                drop(inventory);
-

                for rid in missing {
                    debug!(target: "service", "Missing seeded inventory {rid}; initiating fetch..");
                    self.fetch(rid, *announcer, FETCH_TIMEOUT, None);
@@ -1931,14 +1935,59 @@ where
            > 0
    }

-
    /// Update our routing table with our local node's inventory.
-
    fn sync_inventory(&mut self) -> Result<SyncedRouting, Error> {
-
        let inventory = self.storage.inventory()?;
-
        let result = self.sync_routing(inventory.clone(), self.node_id(), self.clock.into())?;
-
        // Update cached inventory message.
-
        self.inventory = gossip::inventory(self.timestamp(), inventory);
+
    /// Remove a local repository from our inventory.
+
    fn remove_inventory(&mut self, rid: &RepoId) -> Result<bool, Error> {
+
        let node = self.node_id();
+
        let now = self.timestamp();
+

+
        // Remove inventory.
+
        let removed = self.db.routing_mut().remove(rid, &node)?;
+
        if removed {
+
            // Update cached inventory message.
+
            let inventory = self.inventory()?;
+
            self.inventory = gossip::inventory(now, inventory);
+
            self.announce_inventory();
+
        }
+
        Ok(removed)
+
    }
+

+
    /// Add a local repository to our inventory.
+
    fn add_inventory(&mut self, rid: RepoId) -> Result<bool, Error> {
+
        let node = self.node_id();
+
        let now = self.timestamp();
+

+
        if !self.storage.contains(&rid)? {
+
            error!(target: "service", "Attempt to add non-existing inventory {rid}: repository not found in storage");
+
            return Ok(false);
+
        }
+
        // Add to our inventory.
+
        let updates = self.db.routing_mut().insert([&rid], node, now)?;
+
        let updated = !updates.is_empty();
+

+
        if updated {
+
            let inventory = self.inventory()?;

-
        Ok(result)
+
            self.inventory = gossip::inventory(now, inventory);
+
            self.announce_inventory();
+
        }
+
        Ok(updated)
+
    }
+

+
    /// Get our local inventory.
+
    ///
+
    /// A node's inventory is the advertized list of repositories offered by a node.
+
    ///
+
    /// A node's inventory consists of *public* repositories that are seeded and available locally
+
    /// in the node's storage. We use the routing table as the canonical state of all inventories,
+
    /// including the local node's.
+
    ///
+
    /// When a repository is unseeded, it is also removed from the inventory. Private repositories
+
    /// are *not* part of a node's inventory.
+
    fn inventory(&self) -> Result<HashSet<RepoId>, Error> {
+
        self.db
+
            .routing()
+
            .get_resources(self.nid())
+
            .map_err(Error::from)
    }

    /// Process a peer inventory announcement by updating our routing table.
@@ -2093,22 +2142,6 @@ where
        Ok((refs, timestamp))
    }

-
    fn sync_and_announce_inventory(&mut self) {
-
        match self.sync_inventory() {
-
            Ok(synced) => {
-
                // Only announce if our inventory changed.
-
                if synced.added.len() + synced.removed.len() > 0 {
-
                    if let Err(e) = self.announce_inventory() {
-
                        error!(target: "service", "Failed to announce inventory: {e}");
-
                    }
-
                }
-
            }
-
            Err(e) => {
-
                error!(target: "service", "Failed to sync inventory: {e}");
-
            }
-
        }
-
    }
-

    fn reconnect(&mut self, nid: NodeId, addr: Address) -> bool {
        if let Some(sess) = self.sessions.get_mut(&nid) {
            sess.to_initial();
@@ -2263,7 +2296,9 @@ where
    }

    /// Announce our inventory to all connected peers.
-
    fn announce_inventory(&mut self) -> Result<(), storage::Error> {
+
    fn announce_inventory(&mut self) {
+
        // TODO: If we're going to announce soon, skip this.
+
        // TODO: If we just announced this exact inventory, also skip.
        let msg = AnnouncementMessage::from(self.inventory.clone());

        self.outbox.announce(
@@ -2272,8 +2307,6 @@ where
            self.db.gossip_mut(),
        );
        self.last_announce = self.clock;
-

-
        Ok(())
    }

    fn prune_routing_entries(&mut self, now: &LocalTime) -> Result<(), routing::Error> {
@@ -2355,16 +2388,17 @@ where
        }
    }

-
    /// 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
-
            .policies
-
            .seed_policies()?
-
            .filter_map(|t| (t.policy.is_allow()).then_some(t.rid))
-
            .filter(|rid| !inventory.contains(rid));
+
    /// Fetch all repositories that are seeded but missing from storage.
+
    fn fetch_missing_repositories(&mut self) -> Result<(), Error> {
+
        for policy in self.policies.seed_policies()? {
+
            let rid = policy.rid;

-
        for rid in missing {
+
            if !policy.is_allow() {
+
                continue;
+
            }
+
            if self.storage.contains(&rid)? {
+
                continue;
+
            }
            match self.seeds(&rid) {
                Ok(seeds) => {
                    if let Some(connected) = NonEmpty::from_vec(seeds.connected().collect()) {
modified radicle-node/src/test/environment.rs
@@ -9,6 +9,7 @@ use std::{

use crossbeam_channel as chan;

+
use localtime::LocalTime;
use radicle::cob::cache::COBS_DB_FILE;
use radicle::cob::issue;
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
@@ -18,7 +19,6 @@ use radicle::git::refname;
use radicle::identity::{RepoId, Visibility};
use radicle::node::config::ConnectAddress;
use radicle::node::policy::store as policy;
-
use radicle::node::routing::Store;
use radicle::node::seed::Store as _;
use radicle::node::Database;
use radicle::node::{Alias, POLICIES_DB_FILE};
@@ -113,21 +113,32 @@ impl Environment {
        let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
        let policies_db = home.node().join(POLICIES_DB_FILE);
        let cobs_db = home.cobs().join(COBS_DB_FILE);
+
        let now = LocalTime::now();

        config.write(&home.config()).unwrap();

        let storage = Storage::open(
            home.storage(),
            git::UserInfo {
-
                alias,
+
                alias: alias.clone(),
                key: keypair.pk.into(),
            },
        )
        .unwrap();
+
        let public_key = keypair.pk.into();

        policy::Store::open(policies_db).unwrap();
-
        home.database_mut().unwrap(); // Just create the database.
        cob::cache::Store::open(cobs_db).unwrap();
+
        home.database_mut()
+
            .unwrap()
+
            .init(
+
                &public_key,
+
                config.node.features(),
+
                Alias::new(alias),
+
                now.into(),
+
                config.node.external_addresses.iter(),
+
            )
+
            .unwrap();

        transport::local::register(storage.clone());
        keystore.store(keypair.clone(), "radicle", None).unwrap();
@@ -139,7 +150,7 @@ impl Environment {
            home,
            storage,
            keystore,
-
            public_key: keypair.pk.into(),
+
            public_key,
            config,
        }
    }
@@ -248,10 +259,15 @@ impl<G: Signer + cyphernet::Ecdh> NodeHandle<G> {

    /// Get routing table entries.
    pub fn routing(&self) -> impl Iterator<Item = (RepoId, NodeId)> {
-
        Database::reader(self.home.node().join(node::NODE_DB_FILE))
-
            .unwrap()
-
            .entries()
-
            .unwrap()
+
        use node::routing::Store as _;
+

+
        self.home.routing_mut().unwrap().entries().unwrap()
+
    }
+

+
    pub fn inventory(&self) -> impl Iterator<Item = RepoId> + '_ {
+
        self.routing()
+
            .filter(|(_, n)| *n == self.id)
+
            .map(|(r, _)| r)
    }

    /// Get sync status of a repo.
@@ -605,7 +621,7 @@ pub fn converge<'a, G: Signer + cyphernet::Ecdh + 'static>(
            all_routes.insert((rid, seed_id));
        }
        // Routes from the local inventory.
-
        for rid in node.storage.inventory().unwrap() {
+
        for rid in node.inventory() {
            all_routes.insert((rid, node.id));
        }
    }
modified radicle-node/src/test/peer.rs
@@ -1,4 +1,5 @@
#![allow(dead_code)]
+
use std::collections::HashSet;
use std::iter;
use std::net;
use std::ops::{Deref, DerefMut};
@@ -19,6 +20,7 @@ use crate::crypto::test::signer::MockSigner;
use crate::crypto::Signer;
use crate::identity::RepoId;
use crate::node;
+
use crate::node::routing::Store as _;
use crate::prelude::*;
use crate::runtime::Emitter;
use crate::service;
@@ -27,7 +29,6 @@ use crate::service::message::*;
use crate::service::policy::{Scope, SeedingPolicy};
use crate::service::*;
use crate::storage::git::transport::remote;
-
use crate::storage::Inventory;
use crate::storage::{RemoteId, WriteStorage};
use crate::test::storage::MockStorage;
use crate::test::{arbitrary, fixtures, simulator};
@@ -99,7 +100,6 @@ where

pub struct Config<G: Signer + 'static> {
    pub config: service::Config,
-
    pub db: Stores<node::Database>,
    pub local_time: LocalTime,
    pub policy: SeedingPolicy,
    pub signer: G,
@@ -112,13 +112,10 @@ impl Default for Config<MockSigner> {
        let mut rng = fastrand::Rng::new();
        let signer = MockSigner::new(&mut rng);
        let tmp = tempfile::TempDir::new().unwrap();
-
        let db = Database::open(tmp.path().join(node::NODE_DB_FILE))
-
            .unwrap()
-
            .into();
+
        let config = service::Config::test(Alias::from_str("mocky").unwrap());

        Config {
-
            config: service::Config::test(Alias::from_str("mocky").unwrap()),
-
            db,
+
            config,
            local_time: LocalTime::now(),
            policy: SeedingPolicy::default(),
            signer,
@@ -164,19 +161,32 @@ where
        let id = *config.signer.public_key();
        let ip = ip.into();
        let local_addr = net::SocketAddr::new(ip, config.rng.u16(..));
-
        let inventory = storage.inventory().unwrap();
+
        let inventory = storage.repositories().unwrap();

        // Make sure the peer address is advertized.
        config.config.external_addresses.push(local_addr.into());
-
        for rid in &inventory {
-
            policies.seed(rid, Scope::Followed).unwrap();
+
        for repo in &inventory {
+
            policies.seed(&repo.rid, Scope::Followed).unwrap();
        }
+
        // Initialize database.
+
        let db = Database::open(config.tmp.path().join(node::NODE_DB_FILE))
+
            .unwrap()
+
            .init(
+
                &id,
+
                config.config.features(),
+
                config.config.alias.clone(),
+
                config.local_time.into(),
+
                config.config.external_addresses.iter(),
+
            )
+
            .unwrap()
+
            .into();
+

        let announcement =
            service::gossip::node(&config.config, Timestamp::from(config.local_time) + 1);
        let emitter: Emitter<Event> = Default::default();
        let service = Service::new(
            config.config,
-
            config.db,
+
            db,
            storage,
            policies,
            config.signer,
@@ -255,8 +265,12 @@ where
        (*self.clock()).into()
    }

-
    pub fn inventory(&self) -> Inventory {
-
        self.service.storage().inventory().unwrap()
+
    pub fn inventory(&self) -> HashSet<RepoId> {
+
        self.service
+
            .database()
+
            .routing()
+
            .get_resources(self.nid())
+
            .unwrap()
    }

    pub fn git_url(&self, repo: RepoId, namespace: Option<RemoteId>) -> remote::Url {
modified radicle-node/src/tests.rs
@@ -274,21 +274,21 @@ fn test_inventory_sync() {
    let bob_storage = fixtures::storage(tmp.path().join("bob"), &bob_signer).unwrap();
    let bob = Peer::with_storage("bob", [8, 8, 8, 8], bob_storage);
    let now = LocalTime::now().into();
-
    let projs = bob.storage().inventory().unwrap();
+
    let repos = bob.inventory().into_iter().collect::<Vec<_>>();

    alice.connect_to(&bob);
    alice.receive(
        bob.id(),
        Message::inventory(
            InventoryAnnouncement {
-
                inventory: projs.clone().try_into().unwrap(),
+
                inventory: repos.clone().try_into().unwrap(),
                timestamp: now,
            },
            bob.signer(),
        ),
    );

-
    for proj in &projs {
+
    for proj in &repos {
        let seeds = alice.database().routing().get(proj).unwrap();
        assert!(seeds.contains(&bob.node_id()));
    }
@@ -701,12 +701,7 @@ fn test_refs_announcement_relay() {
        )
        .initialized()
    };
-
    let bob_inv = bob
-
        .storage()
-
        .inventory()
-
        .unwrap()
-
        .into_iter()
-
        .collect::<Vec<_>>();
+
    let bob_inv = bob.inventory().into_iter().collect::<Vec<_>>();

    alice.seed(&bob_inv[0], policy::Scope::All).unwrap();
    alice.seed(&bob_inv[1], policy::Scope::All).unwrap();
@@ -779,8 +774,8 @@ fn test_refs_announcement_fetch_trusted_no_inventory() {
        )
        .initialized()
    };
-
    let bob_inv = bob.storage().inventory().unwrap();
-
    let rid = bob_inv.first().unwrap();
+
    let bob_inv = bob.inventory();
+
    let rid = bob_inv.iter().next().unwrap();

    alice.seed(rid, policy::Scope::Followed).unwrap();
    alice.connect_to(&bob);
@@ -887,10 +882,7 @@ fn test_refs_announcement_offline() {
            },
        )
    };
-
    let mut inv = alice.inventory();
-
    let rid = *inv.first().unwrap();
    let mut bob = Peer::new("bob", [8, 8, 8, 8]);
-
    bob.seed(&rid, policy::Scope::All).unwrap();

    // Make sure alice's service wasn't initialized before.
    assert_eq!(*alice.clock(), LocalTime::default());
@@ -899,6 +891,11 @@ fn test_refs_announcement_offline() {
    alice.connect_to(&bob);
    alice.receive(bob.id, Message::Subscribe(Subscribe::all()));

+
    let mut inv = alice.inventory();
+
    let rid = *inv.iter().next().unwrap();
+

+
    bob.seed(&rid, policy::Scope::All).unwrap();
+

    // Alice announces the refs of all projects since she hasn't announced refs for these projects
    // yet.
    for msg in alice.messages(bob.id()) {
@@ -1429,7 +1426,7 @@ fn test_queued_fetch_max_capacity() {

#[test]
fn test_queued_fetch_from_ann_same_rid() {
-
    let storage = arbitrary::nonempty_storage(3);
+
    let storage = arbitrary::nonempty_storage(1); // We're testing both public and private repos.
    let mut repo_keys = storage.repos.keys();
    let rid = *repo_keys.next().unwrap();
    let mut alice = Peer::with_storage("alice", [7, 7, 7, 7], storage);
@@ -1448,8 +1445,6 @@ fn test_queued_fetch_from_ann_same_rid() {
        timestamp: bob.timestamp(),
    };

-
    logger::init(log::Level::Trace);
-

    alice.seed(&rid, policy::Scope::All).unwrap();
    alice.connect_to(&bob);
    alice.connect_to(&eve);
@@ -1713,9 +1708,27 @@ fn test_init_and_seed() {
fn prop_inventory_exchange_dense() {
    fn property(alice_inv: MockStorage, bob_inv: MockStorage, eve_inv: MockStorage) {
        let rng = fastrand::Rng::new();
-
        let alice = Peer::with_storage("alice", [7, 7, 7, 7], alice_inv.clone());
-
        let mut bob = Peer::with_storage("bob", [8, 8, 8, 8], bob_inv.clone());
-
        let mut eve = Peer::with_storage("eve", [9, 9, 9, 9], eve_inv.clone());
+
        let alice = Peer::with_storage(
+
            "alice",
+
            [7, 7, 7, 7],
+
            alice_inv
+
                .clone()
+
                .map(|doc| doc.visibility = Visibility::Public),
+
        );
+
        let mut bob = Peer::with_storage(
+
            "bob",
+
            [8, 8, 8, 8],
+
            bob_inv
+
                .clone()
+
                .map(|doc| doc.visibility = Visibility::Public),
+
        );
+
        let mut eve = Peer::with_storage(
+
            "eve",
+
            [9, 9, 9, 9],
+
            eve_inv
+
                .clone()
+
                .map(|doc| doc.visibility = Visibility::Public),
+
        );
        let mut routing = RandomMap::with_hasher(rng.clone().into());

        for (inv, peer) in &[
@@ -1913,7 +1926,6 @@ fn test_announcement_message_amplification() {
            .repos
            .insert(rid, gen::<MockRepository>(1));
        alice.command(Command::UpdateInventory(rid, tx));
-
        alice.command(Command::AnnounceInventory);

        sim.run_while([&mut alice, &mut bob, &mut eve, &mut zod, &mut tom], |s| {
            s.elapsed() < LocalDuration::from_mins(3)
modified radicle/src/node/db.rs
@@ -15,6 +15,7 @@ use std::{fmt, time};
use sqlite as sql;
use thiserror::Error;

+
use crate::node::{address, Address, Alias, Features, KnownAddress, NodeId, Timestamp};
use crate::sql::transaction;

/// How long to wait for the database lock to be released before failing a read.
@@ -34,6 +35,9 @@ const MIGRATIONS: &[&str] = &[

#[derive(Error, Debug)]
pub enum Error {
+
    /// Initialization error.
+
    #[error("error initializing the database: {0}")]
+
    Init(#[from] address::store::Error),
    /// An Internal error.
    #[error("internal error: {0}")]
    Internal(#[from] sql::Error),
@@ -121,6 +125,30 @@ impl Database {
        Ok(self)
    }

+
    /// Initialize by adding our local node to the database.
+
    pub fn init<'a>(
+
        mut self,
+
        node: &NodeId,
+
        features: Features,
+
        alias: Alias,
+
        timestamp: Timestamp,
+
        addrs: impl IntoIterator<Item = &'a Address>,
+
    ) -> Result<Self, Error> {
+
        address::Store::insert(
+
            &mut self,
+
            node,
+
            features,
+
            alias,
+
            0,
+
            timestamp,
+
            addrs
+
                .into_iter()
+
                .map(|a| KnownAddress::new(a.clone(), address::Source::Imported)),
+
        )?;
+

+
        Ok(self)
+
    }
+

    /// Create a new in-memory database.
    pub fn memory() -> Result<Self, Error> {
        let db = sql::Connection::open_thread_safe(":memory:")?;
modified radicle/src/profile.rs
@@ -14,6 +14,7 @@ use std::io::Write;
use std::path::{Path, PathBuf};
use std::{fs, io};

+
use localtime::LocalTime;
use serde::Serialize;
use serde_json as json;
use thiserror::Error;
@@ -307,8 +308,16 @@ impl Profile {
        )?;
        // Create DBs.
        home.policies_mut()?;
-
        home.database_mut()?;
        home.notifications_mut()?;
+
        home.database_mut()?
+
            .journal_mode(node::db::JournalMode::default())?
+
            .init(
+
                &public_key,
+
                config.node.features(),
+
                config.node.alias.clone(),
+
                LocalTime::now().into(),
+
                config.node.external_addresses.iter(),
+
            )?;

        transport::local::register(storage.clone());

@@ -576,6 +585,16 @@ impl Home {
        Ok(db)
    }

+
    /// Returns the address store.
+
    pub fn addresses(&self) -> Result<impl node::address::Store, node::db::Error> {
+
        self.database_mut()
+
    }
+

+
    /// Returns the routing store.
+
    pub fn routing_mut(&self) -> Result<impl node::routing::Store, node::db::Error> {
+
        self.database_mut()
+
    }
+

    /// Return a read-only handle for the issues cache.
    pub fn issues<'a, R>(
        &self,
modified radicle/src/rad.rs
@@ -73,9 +73,6 @@ pub fn init<G: Signer, S: WriteStorage>(
    let (project, _) = Repository::init(&doc, &storage, signer)?;
    let url = git::Url::from(project.id);

-
    // Update inventory cache for this storage instance.
-
    storage.insert(project.id);
-

    match init_configure(repo, &project, pk, &default_branch, &url, signer) {
        Ok(signed) => Ok((project.id, doc, signed)),
        Err(err) => {
modified radicle/src/storage.rs
@@ -1,7 +1,7 @@
pub mod git;
pub mod refs;

-
use std::collections::{hash_map, BTreeSet, HashSet};
+
use std::collections::{hash_map, HashSet};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::{fmt, io};
@@ -29,7 +29,6 @@ use self::git::UserInfo;
use self::refs::{RefsAt, SignedRefs};

pub type BranchName = git::RefString;
-
pub type Inventory = BTreeSet<RepoId>;

/// Basic repository information.
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -150,8 +149,6 @@ pub enum Error {
    Ext(#[from] git::ext::Error),
    #[error("invalid repository identifier {0:?}")]
    InvalidId(std::ffi::OsString),
-
    #[error("inventory: {0}")]
-
    Inventory(io::Error),
    #[error("i/o: {0}")]
    Io(#[from] io::Error),
}
@@ -405,9 +402,6 @@ impl<V> Deref for Remote<V> {
/// Read-only operations on a storage instance.
pub trait ReadStorage {
    type Repository: ReadRepository;
-
    type InventoryRef<'a>: Deref<Target = Inventory>
-
    where
-
        Self: 'a;

    /// Get user info for this storage.
    fn info(&self) -> &UserInfo;
@@ -417,16 +411,8 @@ pub trait ReadStorage {
    fn path_of(&self, rid: &RepoId) -> PathBuf;
    /// Check whether storage contains a repository.
    fn contains(&self, rid: &RepoId) -> Result<bool, RepositoryError>;
-
    /// Get the inventory of repositories hosted under this storage.
-
    /// This function should typically only return public repositories.
-
    fn inventory(&self) -> Result<Inventory, Error>;
-
    /// Return a reference to the inventory. Note that this function may hold a
-
    /// lock to the inventory.
-
    fn inventory_ref<'a, 's: 'a>(&'s self) -> Self::InventoryRef<'a>;
    /// Return all repositories (public and private).
    fn repositories(&self) -> Result<Vec<RepositoryInfo<Verified>>, Error>;
-
    /// Insert this repository into the inventory.
-
    fn insert(&self, rid: RepoId);
    /// Open or create a read-only repository.
    fn repository(&self, rid: RepoId) -> Result<Self::Repository, RepositoryError>;
    /// Get a repository's identity if it exists.
@@ -667,7 +653,6 @@ where
    S: ReadStorage + 'static,
{
    type Repository = S::Repository;
-
    type InventoryRef<'a> = S::InventoryRef<'a> where T: 'a;

    fn info(&self) -> &UserInfo {
        self.deref().info()
@@ -681,25 +666,10 @@ where
        self.deref().path_of(rid)
    }

-
    fn insert(&self, rid: RepoId) {
-
        self.deref().insert(rid)
-
    }
-

    fn contains(&self, rid: &RepoId) -> Result<bool, RepositoryError> {
        self.deref().contains(rid)
    }

-
    fn inventory(&self) -> Result<Inventory, Error> {
-
        self.deref().inventory()
-
    }
-

-
    fn inventory_ref<'a, 's: 'a>(&'s self) -> Self::InventoryRef<'a>
-
    where
-
        T: 'a,
-
    {
-
        self.deref().inventory_ref()
-
    }
-

    fn get(&self, rid: RepoId) -> Result<Option<Doc<Verified>>, RepositoryError> {
        self.deref().get(rid)
    }
modified radicle/src/storage/git.rs
@@ -5,7 +5,6 @@ pub mod transport;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
-
use std::sync::{Arc, Mutex, MutexGuard};
use std::{fs, io};

use crypto::{Signer, Verified};
@@ -20,8 +19,8 @@ use crate::node::SyncedAt;
use crate::storage::refs;
use crate::storage::refs::{Refs, SignedRefs, SignedRefsAt};
use crate::storage::{
-
    Inventory, ReadRepository, ReadStorage, Remote, Remotes, RepositoryError, RepositoryInfo,
-
    SetHead, SignRepository, WriteRepository, WriteStorage,
+
    ReadRepository, ReadStorage, Remote, Remotes, RepositoryError, RepositoryInfo, SetHead,
+
    SignRepository, WriteRepository, WriteStorage,
};
use crate::{git, node};

@@ -73,34 +72,14 @@ impl<'a> TryFrom<git2::Reference<'a>> for Ref {
    }
}

-
/// A reference to a locked inventory.
-
pub struct InventoryLock<'a> {
-
    guard: MutexGuard<'a, Option<BTreeSet<RepoId>>>,
-
}
-

-
impl<'a> Deref for InventoryLock<'a> {
-
    type Target = BTreeSet<RepoId>;
-

-
    #[track_caller]
-
    fn deref(&self) -> &Self::Target {
-
        match *self.guard {
-
            Some(ref cache) => cache,
-
            None => panic!("InventoryLock::deref: inventory cache was not initialized"),
-
        }
-
    }
-
}
-

#[derive(Debug, Clone)]
pub struct Storage {
    path: PathBuf,
    info: UserInfo,
-
    /// Inventory cache. Set to `None` until the cache is populated.
-
    inventory: Arc<Mutex<Option<BTreeSet<RepoId>>>>,
}

impl ReadStorage for Storage {
    type Repository = Repository;
-
    type InventoryRef<'a> = InventoryLock<'a>;

    fn info(&self) -> &UserInfo {
        &self.info
@@ -122,44 +101,6 @@ impl ReadStorage for Storage {
        Ok(false)
    }

-
    fn inventory_ref<'a, 's: 'a>(&'s self) -> InventoryLock<'a> {
-
        let guard = self
-
            .inventory
-
            .lock()
-
            .unwrap_or_else(|poisoned| poisoned.into_inner());
-

-
        InventoryLock { guard }
-
    }
-

-
    fn inventory(&self) -> Result<Inventory, Error> {
-
        let mut cache = self
-
            .inventory
-
            .lock()
-
            .unwrap_or_else(|poisoned| poisoned.into_inner());
-

-
        match *cache {
-
            Some(ref cache) => Ok(cache.clone()),
-
            None => {
-
                let repos: BTreeSet<_> = self.public_repositories()?.collect();
-
                *cache = Some(repos.clone());
-
                Ok(repos)
-
            }
-
        }
-
    }
-

-
    fn insert(&self, rid: RepoId) {
-
        let mut repos = self
-
            .inventory
-
            .lock()
-
            .unwrap_or_else(|poisoned| poisoned.into_inner());
-

-
        if let Some(ref mut repos) = *repos {
-
            repos.insert(rid);
-
        } else {
-
            *repos = Some(BTreeSet::from_iter([rid]));
-
        }
-
    }
-

    fn repository(&self, rid: RepoId) -> Result<Self::Repository, RepositoryError> {
        Repository::open(paths::repository(self, &rid), rid)
    }
@@ -265,11 +206,7 @@ impl Storage {
            Err(err) => return Err(Error::Io(err)),
            Ok(()) => {}
        }
-
        Ok(Self {
-
            path,
-
            info,
-
            inventory: Default::default(),
-
        })
+
        Ok(Self { path, info })
    }

    /// Create a [`Repository`] in a temporary directory.
@@ -336,14 +273,6 @@ impl Storage {
        }
        Ok(())
    }
-

-
    fn public_repositories(&self) -> Result<impl Iterator<Item = RepoId>, Error> {
-
        let repos = self.repositories()?;
-
        Ok(repos
-
            .into_iter()
-
            .filter(|r| r.doc.visibility.is_public())
-
            .map(|r| r.rid))
-
    }
}

/// Git implementation of [`WriteRepository`] using the `git2` crate.
modified radicle/src/test/storage.rs
@@ -1,4 +1,4 @@
-
use std::collections::{BTreeSet, HashMap};
+
use std::collections::HashMap;
use std::convert::Infallible;
use std::io;
use std::path::{Path, PathBuf};
@@ -51,26 +51,20 @@ impl MockStorage {
            .expect("MockStorage::repo_mut: repository does not exist")
    }

-
    pub fn empty() -> Self {
-
        Self::new(Vec::new())
+
    pub fn map(mut self, f: impl Fn(&mut Doc<Verified>)) -> Self {
+
        for repo in self.repos.values_mut() {
+
            f(&mut repo.doc.doc);
+
        }
+
        self
    }
-
}

-
pub struct InventoryLock {
-
    inner: Inventory,
-
}
-

-
impl std::ops::Deref for InventoryLock {
-
    type Target = BTreeSet<RepoId>;
-

-
    fn deref(&self) -> &Self::Target {
-
        &self.inner
+
    pub fn empty() -> Self {
+
        Self::new(Vec::new())
    }
}

impl ReadStorage for MockStorage {
    type Repository = MockRepository;
-
    type InventoryRef<'a> = InventoryLock;

    fn info(&self) -> &git::UserInfo {
        &self.info
@@ -88,18 +82,6 @@ impl ReadStorage for MockStorage {
        Ok(self.repos.contains_key(rid))
    }

-
    fn inventory(&self) -> Result<Inventory, Error> {
-
        Ok(self.repos.keys().cloned().collect::<BTreeSet<_>>())
-
    }
-

-
    fn inventory_ref<'a, 's: 'a>(&'s self) -> Self::InventoryRef<'a> {
-
        InventoryLock {
-
            inner: self.inventory().unwrap(),
-
        }
-
    }
-

-
    fn insert(&self, _rid: RepoId) {}
-

    fn repository(&self, rid: RepoId) -> Result<Self::Repository, RepositoryError> {
        self.repos
            .get(&rid)