1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::str::FromStr;

use crate::native::workspace::Workspace;
use anyhow::{anyhow, Result};
use cid::Cid;
use noosphere::{key::KeyStorage, sphere::SphereContextBuilder};
use noosphere_core::{authority::Authorization, data::Did};
use noosphere_sphere::SphereContext;

use ucan::crypto::KeyMaterial;

pub async fn sphere_create(owner_key: &str, workspace: &Workspace) -> Result<()> {
    workspace.ensure_sphere_uninitialized()?;

    let sphere_context_artifacts = SphereContextBuilder::default()
        .create_sphere()
        .at_storage_path(workspace.root_directory())
        .reading_keys_from(workspace.key_storage().clone())
        .using_key(owner_key)
        .build()
        .await?;

    let mnemonic = sphere_context_artifacts.require_mnemonic()?.to_string();
    let sphere_context: SphereContext<_, _> = sphere_context_artifacts.into();
    let sphere_identity = sphere_context.identity();

    println!(
        r#"A new sphere has been created in {:?}
Its identity is {}
Your key {:?} is considered its owner
The owner of a sphere can authorize other keys to write to it

IMPORTANT: Write down the following sequence of words...

{}

...and keep it somewhere safe!
You will be asked to enter them if you ever need to transfer ownership of the sphere to a different key."#,
        workspace.root_directory(),
        sphere_identity,
        owner_key,
        mnemonic
    );

    Ok(())
}

pub async fn sphere_join(
    local_key: &str,
    authorization: Option<String>,
    sphere_identity: &Did,
    workspace: &Workspace,
) -> Result<()> {
    workspace.ensure_sphere_uninitialized()?;
    println!("Joining sphere {}...", sphere_identity);

    let did = {
        let local_key = workspace.key_storage().require_key(local_key).await?;
        local_key.get_did().await?
    };

    let cid_string = match authorization {
        None => {
            println!(
                r#"In order to join the sphere, another client must authorize your local key
This is the local key's ID; share it with an authorized client:

  {0}

Hint: if the authorized client is also using the "orb" CLI, you can use this command from the existing workspace to authorize the new key:

  orb auth add {0}
  
Once authorized, you will get a code.
Type or paste the code here and press enter:"#,
                did
            );

            let mut cid_string = String::new();

            std::io::stdin().read_line(&mut cid_string)?;

            cid_string
        }
        Some(cid_string) => cid_string,
    };

    let cid = Cid::from_str(cid_string.trim())
        .map_err(|_| anyhow!("Could not parse the authorization identity as a CID"))?;

    SphereContextBuilder::default()
        .join_sphere(sphere_identity)
        .at_storage_path(workspace.root_directory())
        .reading_keys_from(workspace.key_storage().clone())
        .using_key(local_key)
        .authorized_by(Some(&Authorization::Cid(cid)))
        .build()
        .await?;

    // TODO(#103): Recovery path if the auth needs to change for some reason

    println!(
        r#"The authorization has been saved.
Make sure that you have configured the gateway's URL:

  orb config set gateway-url <URL>
  
And then you should be able to sync:

  orb sync
  
Happy pondering!"#
    );

    Ok(())
}