Skip to content

Register a node

In this section we'll use a developer certificate to register a node.

We'll start with creating a seed that is used to derive node-secrets from. Each node on the lightning network is identified by a public key and the corresponding private key is one of these secrets. In the next step, we'll connect to the Scheduler using a developer identity and register the node. This requires you to prove that you own the private key mentioned previously.

At the end of this section your node will be registered on Greenlight and you will have a device-identity that can be used to connect the node.

Creating a seed

Let's start with the seed secret: the seed secret is a 32 byte secret that all other secrets and private keys are derived from, as such it is paramount that this secret never leaves your user's device and is only handled by the Signer.

We suggest to derive the seed secret from a BIP 39 seed phrase, so the user can back it up on a physical piece of paper, steel plate, or whatever creative way of storing it they can think of.

Note

The following code-snippets build on each other. By copying each snippet after the other you should get a working example. See the getting started project in examples to view the code in one file.

Install the bip39 and rand crates required for secure randomness and conversion of mnemonics. Add the following lines to your Cargo.toml

[dependencies]
rand = "*"
bip39 = { version = "*", features=["rand_core"] }

Install the bip39 package which we'll use to encode the seed secret as a seed phrase:

pip install bip39

Now we can securely generate some randomness, encode it as BIP 39 phrase and then convert it into a seed secret we can use:

let mut rng = rand::thread_rng();
let m = Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap();

//Show seed phrase to user
let _phrase = m.word_iter().fold("".to_string(), |c, n| c + " " + n);

const EMPTY_PASSPHRASE: &str = "";
let seed = &m.to_seed(EMPTY_PASSPHRASE)[0..32]; // Only need the first 32 bytes

// Store the seed on the filesystem, or secure configuration system
save_to_file("seed", seed.to_vec());
rand = secrets.randbits(256).to_bytes(32, "big")  # 32 bytes of randomness

# Show seed phrase to user
phrase = bip39.encode_bytes(rand)

seed = bip39.phrase_to_seed(phrase)[:32]  # Only need the first 32 bytes

# Store the seed on the filesystem, or secure configuration system
save_to_file("seed", seed)

Important

Remember to store the seed somewhere (file on disk, registry, etc) because without it, you will not have access to the node, and any funds on the node will be lost forever! We mean it when we say you're the only one with access to the seed!

Initializing the signer

To initialize a signer we'll first need to configure Nobody credentials so we can talk to the scheduler using mTLS. Nobody credentials require data from the files downloaded from the Greenlight Developer Console, so the files must be accessible from wherever the node registration program is run. Any connection using the developer_creds object will allow you to register new Greenlight nodes.

let developer_cert = std::fs::read(developer_cert_path).unwrap_or_default();
let developer_key = std::fs::read(developer_key_path).unwrap_or_default();
let developer_creds = Nobody {
    cert: developer_cert,
    key: developer_key,
    ..Nobody::default()
};
developer_cert = Path(developer_cert_path).open(mode="rb").read()
developer_key = Path(developer_key_path).open(mode="rb").read()

developer_creds = Credentials.nobody_with(developer_cert, developer_key)

The next step is to create the Signer which processes incoming signature requests, and is used when registering a node to prove ownership of the private key. The last thing to decide is which network we want the node to run on. You can chose between the following networks:

  • testnet
  • bitcoin

We'll pick bitcoin, because ... reckless 😉

let network = Network::Bitcoin;
let signer = Signer::new(seed, network, developer_creds.clone()).unwrap();
network = "bitcoin"
signer = Signer(seed, network, developer_creds)

Registering a new node

Registering a node with the Scheduler creates the node on the Greenlight service and ensures everything is setup to start the node.

In order to register a node, the client needs to prove it has access to the node's private key. Since the private key is managed exclusively by the Signer we need to pass the Signer to the Scheduler:

let scheduler = Scheduler::new(network, developer_creds).await.unwrap();

// Passing in the signer is required because the client needs to prove
// ownership of the `node_id`
let registration_response = scheduler.register(&signer, None).await.unwrap();

let device_creds = Device::from_bytes(registration_response.creds);
save_to_file("creds", device_creds.to_bytes());
scheduler = Scheduler(network, developer_creds)

# Passing in the signer is required because the client needs to prove
# ownership of the `node_id`
registration_response = scheduler.register(signer, invite_code=None)

device_creds = Credentials.from_bytes(registration_response.creds)
save_to_file("creds", device_creds.to_bytes())

The result of register contains the credentials that can be used going forward to talk to the scheduler and the node itself.

Important

Please make sure to store them somewhere safe, since anyone with these credentials can access your node.

let scheduler = scheduler.authenticate(device_creds).await.unwrap();
let _node: ClnClient = scheduler.node().await.unwrap();
scheduler = scheduler.authenticate(device_creds)
node = scheduler.node()

If you get an error about a certificate verification failure when talking to the node, you most likely are using an unconfigured TlsConfig that doesn't have access to the node. See Security for details on how authentication and authorization work under the hood.