radicle-cli: use Did across CLI

The CLI commands and output use PublicKey, ActorId, and NodeId for
peer identifiers.

Instead Did should be standardised for use in CLI arguments and
outputs.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny 2023-02-16 12:40:15 +00:00 committed by Alexis Sellier
parent b57a4606f6
commit 5477a3615f
No known key found for this signature in database
15 changed files with 94 additions and 103 deletions

View File

@ -7,7 +7,7 @@ $ rad auth
Initializing your 🌱 profile and identity
ok Creating your 🌱 Ed25519 keypair...
ok Profile z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi created.
ok Profile did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi created.
Your radicle Node ID is z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. This identifies your device.
@ -18,7 +18,8 @@ You can get the above information at all times using the `self` command:
```
$ rad self
ID z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
ID did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
Node ID z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
Key (hash) SHA256:UIedaL6Cxm6OUErh9GQUzzglSk7VpQlVTI1TAFB/HWA
Key (full) AAAAC3NzaC1lZDI1NTE5AAAAIHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1
Storage (git) [..]/storage

View File

@ -14,7 +14,7 @@ We want to add a new maintainer to the project to help out with the
work.
```
$ rad delegate add z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG --to rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
$ rad delegate add did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG --to rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
Added delegate 'did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG'
ok Update successful!
```
@ -33,7 +33,7 @@ And finally, we no longer want to be part of the project so we pass on
the torch and remove ourselves from the delegate set.
```
$ rad delegate remove z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --to rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
$ rad delegate remove did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --to rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
Removed delegate 'did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi'
ok Update successful!
```

View File

@ -5,7 +5,7 @@ First off, we will create two proposals -- we can imagine two
delegates creating proposals concurrently.
```
$ rad id edit --title "Add Alice" --description "Add Alice as a delegate" --delegates z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
$ rad id edit --title "Add Alice" --description "Add Alice as a delegate" --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
ok Identity proposal '57332790a2eabc0b2fd8c7ff48c3579d5812d405' created 🌱
title: Add Alice
description: Add Alice as a delegate
@ -47,7 +47,7 @@ Quorum Reached
```
```
$ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG --no-confirm
$ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG --no-confirm
ok Identity proposal 'c3698d4e85f9d4c0ee536b34d6122fc7c81f7e2e' created 🌱
title: Add Bob
description: Add Bob as a delegate
@ -294,7 +294,7 @@ Quorum Reached
We can now update the proposal to have both keys in the delegates set:
```
$ rad id update c3698d4e85f9d4c0ee536b34d6122fc7c81f7e2e --rev z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/4 --delegates z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
$ rad id update c3698d4e85f9d4c0ee536b34d6122fc7c81f7e2e --rev z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/4 --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
ok Identity proposal 'c3698d4e85f9d4c0ee536b34d6122fc7c81f7e2e' updated 🌱
ok Revision 'z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/6'
title: Add Bob

View File

@ -9,11 +9,11 @@ delegate` command. For cases where `threshold > 1`, it is necessary to
gather a quorum of signatures to update the Radicle identity. To do
this, we use the `rad id` command.
Let's add Bob as a delegate using their key
`z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn`.
Let's add Bob as a delegate using their DID
`did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn`.
```
$ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
$ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
ok Identity proposal '06d9efa2a9aad06bfdf25a25690e1ec7db2c3c39' created 🌱
title: Add Bob
description: Add Bob as a delegate

View File

@ -22,20 +22,20 @@ others to work on. This is to ensure work is not duplicated.
Let's assign ourselves to this one.
```
$ rad assign 2b4650e3c66d568132034de0d02871a2fbf9c5b5 z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
$ rad assign 2b4650e3c66d568132034de0d02871a2fbf9c5b5 did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```
It will now show in the list of issues assigned to us.
```
$ rad issue list --assigned
2b4650e3c66d568132034de0d02871a2fbf9c5b5 "flux capacitor underpowered" z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
2b4650e3c66d568132034de0d02871a2fbf9c5b5 "flux capacitor underpowered" did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```
Note: this can always be undone with the `unassign` subcommand.
```
$ rad unassign 2b4650e3c66d568132034de0d02871a2fbf9c5b5 z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
$ rad unassign 2b4650e3c66d568132034de0d02871a2fbf9c5b5 did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```
Great, now we have communicated to the world about our car's defect.

View File

@ -2,6 +2,7 @@ use std::ffi::OsString;
use std::str::FromStr;
use anyhow::anyhow;
use radicle::prelude::Did;
use crate::terminal as term;
use crate::terminal::args;
@ -16,7 +17,7 @@ pub const HELP: args::Help = args::Help {
usage: r#"
Usage
rad assign <issue> <peer>
rad assign <issue> <did>
Options
@ -27,7 +28,7 @@ Options
#[derive(Debug)]
pub struct Options {
pub id: issue::IssueId,
pub peer: cob::ActorId,
pub peer: Did,
}
impl args::Args for Options {
@ -36,7 +37,7 @@ impl args::Args for Options {
let mut parser = lexopt::Parser::from_args(args);
let mut id: Option<issue::IssueId> = None;
let mut peer: Option<cob::ActorId> = None;
let mut peer: Option<Did> = None;
while let Some(arg) = parser.next()? {
match arg {
@ -52,12 +53,7 @@ impl args::Args for Options {
id = Some(val);
} else if peer.is_none() {
let val = val.to_string_lossy();
let Ok(val) = cob::ActorId::from_str(&val) else {
return Err(anyhow!("invalid peer ID '{}'", val));
};
peer = Some(val);
peer = Some(term::args::did(val)?);
} else {
return Err(anyhow!(arg.unexpected()));
}
@ -90,7 +86,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
cob::store::Error::NotFound(_, _) => anyhow!("issue not found '{}'", options.id),
_ => err.into(),
})?;
issue.assign(vec![options.peer], &signer)?;
issue.assign(vec![*options.peer], &signer)?;
Ok(())
}

View File

@ -87,7 +87,7 @@ pub fn init(options: Options) -> anyhow::Result<()> {
term::success!(
"Profile {} created.",
term::format::highlight(profile.id().to_string())
term::format::highlight(profile.did().to_string())
);
term::blank();

View File

@ -4,7 +4,7 @@ use std::str::FromStr;
use anyhow::{anyhow, Context as _};
use radicle::identity::Id;
use radicle_crypto::PublicKey;
use radicle::prelude::Did;
use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};
@ -23,7 +23,7 @@ pub const HELP: Help = Help {
usage: r#"
Usage
rad delegate (add|remove) <public key> [--to <id>]
rad delegate (add|remove) <did> [--to <id>]
rad delegate list [<id>]
The `add` and `remove` commands are limited to managing delegates
@ -47,8 +47,8 @@ pub enum OperationName {
#[derive(Debug, Eq, PartialEq)]
pub enum Operation {
Add { id: Option<Id>, key: PublicKey },
Remove { id: Option<Id>, key: PublicKey },
Add { id: Option<Id>, did: Did },
Remove { id: Option<Id>, did: Did },
List { id: Option<Id> },
}
@ -64,7 +64,7 @@ impl Args for Options {
let mut parser = lexopt::Parser::from_args(args);
let mut id: Option<Id> = None;
let mut op: Option<OperationName> = None;
let mut key: Option<PublicKey> = None;
let mut did: Option<Did> = None;
while let Some(arg) = parser.next()? {
match arg {
@ -81,27 +81,21 @@ impl Args for Options {
unknown => anyhow::bail!("unknown operation '{}'", unknown),
},
Value(val) if op.is_some() => {
let val = val.to_string_lossy();
match op {
Some(OperationName::Add) | Some(OperationName::Remove) => {
if let Ok(val) = PublicKey::from_str(&val) {
key = Some(val);
} else {
return Err(anyhow!("invalid Public Key '{}'", val));
}
}
Some(OperationName::List) => {
if let Ok(val) = Id::from_str(&val) {
id = Some(val);
} else {
return Err(anyhow!("invalid Project ID '{}'", val));
}
}
None => continue,
Value(val) if op.is_some() => match op {
Some(OperationName::Add) | Some(OperationName::Remove) => {
did = Some(term::args::did(&val)?);
}
}
Some(OperationName::List) => {
// TODO: create args::project_id function
let val = val.to_string_lossy();
if let Ok(val) = Id::from_str(&val) {
id = Some(val);
} else {
return Err(anyhow!("invalid Project ID '{}'", val));
}
}
None => continue,
},
_ => return Err(anyhow!(arg.unexpected())),
}
}
@ -110,11 +104,11 @@ impl Args for Options {
OperationName::List => Operation::List { id },
OperationName::Add => Operation::Add {
id,
key: key.ok_or_else(|| anyhow!("a delegate key must be provided"))?,
did: did.ok_or_else(|| anyhow!("a delegate DID must be provided"))?,
},
OperationName::Remove => Operation::Remove {
id,
key: key.ok_or_else(|| anyhow!("a delegate key must be provided"))?,
did: did.ok_or_else(|| anyhow!("a delegate DID must be provided"))?,
},
};
@ -127,8 +121,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let storage = &profile.storage;
match options.op {
Operation::Add { id, key } => add::run(&profile, storage, get_id(id)?, key)?,
Operation::Remove { id, key } => remove::run(&profile, storage, get_id(id)?, &key)?,
Operation::Add { id, did } => add::run(&profile, storage, get_id(id)?, *did)?,
Operation::Remove { id, did } => remove::run(&profile, storage, get_id(id)?, &did)?,
Operation::List { id } => list::run(&profile, storage, get_id(id)?)?,
}

View File

@ -4,9 +4,9 @@ use anyhow::{anyhow, Context as _};
use radicle::cob::identity::{self, Proposal, ProposalId, Proposals, Revision, RevisionId};
use radicle::git::Oid;
use radicle::identity::Identity;
use radicle::prelude::Doc;
use radicle::prelude::{Did, Doc};
use radicle::storage::ReadStorage as _;
use radicle_crypto::{PublicKey, Verified};
use radicle_crypto::Verified;
use crate::terminal::args::{Args, Error, Help};
use crate::terminal::{self as term, Interactive};
@ -19,7 +19,7 @@ pub const HELP: Help = Help {
Usage
rad id (update|edit) [--title|-t] [--description|-d]
[--delegates <key>] [--threshold <num>]
[--delegates <did>] [--threshold <num>]
[--no-confirm]
rad id list
rad id (show|rebase) <id> [--rev <revision id>]
@ -61,7 +61,7 @@ pub enum Operation {
Edit {
title: Option<String>,
description: Option<String>,
delegates: Vec<PublicKey>,
delegates: Vec<Did>,
threshold: Option<usize>,
},
Update {
@ -69,7 +69,7 @@ pub enum Operation {
rev: Option<RevisionId>,
title: Option<String>,
description: Option<String>,
delegates: Vec<PublicKey>,
delegates: Vec<Did>,
threshold: Option<usize>,
},
Rebase {
@ -121,7 +121,7 @@ impl Args for Options {
let mut rev: Option<RevisionId> = None;
let mut title: Option<String> = None;
let mut description: Option<String> = None;
let mut delegates: Vec<PublicKey> = Vec::new();
let mut delegates: Vec<Did> = Vec::new();
let mut threshold: Option<usize> = None;
let mut interactive = Interactive::Yes;
let mut show_revisions = false;
@ -161,11 +161,8 @@ impl Args for Options {
);
}
Long("delegates") => {
let val = String::from(parser.value()?.to_string_lossy());
delegates.push(
PublicKey::from_str(&val)
.map_err(|_| anyhow!("invalid Public Key '{}'", val))?,
)
let did = term::args::did(&parser.value()?)?;
delegates.push(did);
}
Long("threshold") => {
threshold = Some(parser.value()?.to_string_lossy().parse()?);
@ -277,9 +274,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let proposed = {
let mut proposed = previous.doc.clone();
proposed.threshold = threshold.unwrap_or(proposed.threshold);
proposed
.delegates
.extend(delegates.into_iter().map(|k| k.into()));
proposed.delegates.extend(delegates);
proposed
};
@ -320,9 +315,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let proposed = {
let mut proposed = revision.proposed.clone();
proposed.threshold = threshold.unwrap_or(revision.proposed.threshold);
proposed
.delegates
.extend(delegates.into_iter().map(|k| k.into()));
proposed.delegates.extend(delegates);
proposed
};

View File

@ -3,6 +3,7 @@ use std::ffi::OsString;
use std::str::FromStr;
use anyhow::{anyhow, Context as _};
use radicle::prelude::Did;
use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};
@ -22,7 +23,7 @@ Usage
rad issue
rad issue delete <id>
rad issue list [--assigned <key>]
rad issue list [--assigned <did>]
rad issue open [--title <title>] [--description <text>]
rad issue react <id> [--emoji <char>]
rad issue show <id>
@ -38,7 +39,7 @@ Options
pub struct Metadata {
title: String,
labels: Vec<Tag>,
assignees: Vec<cob::ActorId>,
assignees: Vec<Did>,
}
#[derive(Default, Debug, PartialEq, Eq)]
@ -57,7 +58,7 @@ pub enum OperationName {
pub enum Assigned {
#[default]
Me,
Peer(cob::ActorId),
Peer(Did),
}
#[derive(Debug, PartialEq, Eq)]
@ -135,10 +136,7 @@ impl Args for Options {
}
Long("assigned") | Short('a') if assigned.is_none() => {
if let Ok(val) = parser.value() {
let val = val.to_string_lossy();
let Ok(peer) = cob::ActorId::from_str(&val) else {
return Err(anyhow!("invalid peer ID '{}'", val));
};
let peer = term::args::did(&val)?;
assigned = Some(Assigned::Peer(peer));
} else {
assigned = Some(Assigned::Me);
@ -265,7 +263,11 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
&meta.title,
description.trim(),
meta.labels.as_slice(),
meta.assignees.as_slice(),
meta.assignees
.into_iter()
.map(cob::ActorId::from)
.collect::<Vec<_>>()
.as_slice(),
&signer,
)?;
}
@ -273,7 +275,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
Operation::List { assigned } => {
let assignee = match assigned {
Some(Assigned::Me) => Some(*profile.id()),
Some(Assigned::Peer(id)) => Some(id),
Some(Assigned::Peer(id)) => Some(id.into()),
None => None,
};
@ -282,7 +284,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let (id, issue, _) = result?;
let assigned: Vec<_> = issue.assigned().collect();
if Some(true) == assignee.map(|a| !assigned.contains(&&a)) {
if Some(true) == assignee.map(|a| !assigned.contains(&Did::from(a))) {
continue;
}

View File

@ -77,8 +77,11 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
fn all(profile: &Profile) -> anyhow::Result<()> {
let mut table = term::Table::default();
let did = profile.did();
table.push(["ID", &term::format::tertiary(did)]);
let node_id = profile.id();
table.push(["ID", &term::format::tertiary(node_id)]);
table.push(["Node ID", &term::format::tertiary(node_id)]);
let ssh_short = ssh::fmt::fingerprint(node_id);
table.push(["Key (hash)", &term::format::tertiary(ssh_short)]);

View File

@ -2,6 +2,7 @@ use std::ffi::OsString;
use std::str::FromStr;
use anyhow::anyhow;
use radicle::prelude::Did;
use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};
@ -27,7 +28,7 @@ Options
#[derive(Debug)]
pub struct Options {
pub id: issue::IssueId,
pub peer: cob::ActorId,
pub peer: Did,
}
impl Args for Options {
@ -36,7 +37,7 @@ impl Args for Options {
let mut parser = lexopt::Parser::from_args(args);
let mut id: Option<issue::IssueId> = None;
let mut peer: Option<cob::ActorId> = None;
let mut peer: Option<Did> = None;
while let Some(arg) = parser.next()? {
match arg {
@ -52,12 +53,7 @@ impl Args for Options {
id = Some(val);
} else if peer.is_none() {
let val = val.to_string_lossy();
let Ok(val) = cob::ActorId::from_str(&val) else {
return Err(anyhow!("invalid peer ID '{}'", val));
};
peer = Some(val);
peer = Some(term::args::did(val)?);
} else {
return Err(anyhow!(arg.unexpected()));
}
@ -90,7 +86,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
cob::store::Error::NotFound(_, _) => anyhow!("issue '{}' not found", options.id),
_ => err.into(),
})?;
issue.unassign(vec![options.peer], &signer)?;
issue.unassign(vec![*options.peer], &signer)?;
Ok(())
}

View File

@ -74,7 +74,7 @@ pub fn finish(unparsed: Vec<OsString>) -> anyhow::Result<()> {
Ok(())
}
pub fn did(val: OsString) -> anyhow::Result<Did> {
pub fn did(val: &OsString) -> anyhow::Result<Did> {
let val = val.to_string_lossy();
let Ok(peer) = Did::from_str(&val) else {
if crypto::PublicKey::from_str(&val).is_ok() {

View File

@ -16,6 +16,7 @@ use crate::cob::thread;
use crate::cob::thread::{CommentId, Thread};
use crate::cob::{store, ActorId, ObjectId, OpId, TypeName};
use crate::crypto::{PublicKey, Signer};
use crate::prelude::Did;
use crate::storage::git as storage;
/// Issue operation.
@ -152,8 +153,8 @@ impl store::FromHistory for Issue {
}
impl Issue {
pub fn assigned(&self) -> impl Iterator<Item = &ActorId> {
self.assignees.iter()
pub fn assigned(&self) -> impl Iterator<Item = Did> + '_ {
self.assignees.iter().map(Did::from)
}
pub fn title(&self) -> &str {
@ -516,21 +517,21 @@ mod test {
let id = issue.id;
let issue = issues.get(&id).unwrap().unwrap();
let assignees: Vec<_> = issue.assigned().cloned().collect::<Vec<_>>();
let assignees: Vec<_> = issue.assigned().collect::<Vec<_>>();
assert_eq!(1, assignees.len());
assert!(assignees.contains(&assignee));
assert!(assignees.contains(&Did::from(assignee)));
let mut issue = issues.get_mut(&id).unwrap();
issue.assign([assignee_two], &signer).unwrap();
let id = issue.id;
let issue = issues.get(&id).unwrap().unwrap();
let assignees: Vec<_> = issue.assigned().cloned().collect::<Vec<_>>();
let assignees: Vec<_> = issue.assigned().collect::<Vec<_>>();
assert_eq!(2, assignees.len());
assert!(assignees.contains(&assignee));
assert!(assignees.contains(&assignee_two));
assert!(assignees.contains(&Did::from(assignee)));
assert!(assignees.contains(&Did::from(assignee_two)));
}
#[test]
@ -556,11 +557,11 @@ mod test {
let id = issue.id;
let issue = issues.get(&id).unwrap().unwrap();
let assignees: Vec<_> = issue.assigned().cloned().collect::<Vec<_>>();
let assignees: Vec<_> = issue.assigned().collect::<Vec<_>>();
assert_eq!(2, assignees.len());
assert!(assignees.contains(&assignee));
assert!(assignees.contains(&assignee_two));
assert!(assignees.contains(&Did::from(assignee)));
assert!(assignees.contains(&Did::from(assignee_two)));
}
#[test]
@ -641,10 +642,10 @@ mod test {
let id = issue.id;
let issue = issues.get(&id).unwrap().unwrap();
let assignees: Vec<_> = issue.assigned().cloned().collect::<Vec<_>>();
let assignees: Vec<_> = issue.assigned().collect::<Vec<_>>();
assert_eq!(1, assignees.len());
assert!(assignees.contains(&assignee_two));
assert!(assignees.contains(&Did::from(assignee_two)));
}
#[test]

View File

@ -19,6 +19,7 @@ use crate::crypto::ssh::agent::Agent;
use crate::crypto::ssh::{keystore, Keystore, Passphrase};
use crate::crypto::{PublicKey, Signer};
use crate::node;
use crate::prelude::Did;
use crate::storage::git::transport;
use crate::storage::git::Storage;
@ -103,6 +104,10 @@ impl Profile {
&self.public_key
}
pub fn did(&self) -> Did {
Did::from(self.public_key)
}
pub fn signer(&self) -> Result<Box<dyn Signer>, Error> {
if let Some(passphrase) = env::read_passphrase() {
let signer = keystore::MemorySigner::load(&self.keystore, passphrase)?;