From 1654a3e3fd923b28d48375944bd9dbc2cf85152b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 30 Sep 2024 11:18:38 +0200 Subject: [PATCH 1/2] signer: Have the signer report its node_id itself --- libs/gl-client/.resources/proto/glclient/scheduler.proto | 3 ++- libs/gl-client/src/signer/mod.rs | 2 ++ libs/proto/glclient/scheduler.proto | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/gl-client/.resources/proto/glclient/scheduler.proto b/libs/gl-client/.resources/proto/glclient/scheduler.proto index 07eb4e11f..3bb15f32e 100644 --- a/libs/gl-client/.resources/proto/glclient/scheduler.proto +++ b/libs/gl-client/.resources/proto/glclient/scheduler.proto @@ -423,6 +423,7 @@ message SignerRejection { string msg = 1; greenlight.HsmRequest request = 2; string git_version = 3; + bytes node_id = 4; } message PairDeviceRequest { @@ -545,4 +546,4 @@ message SignerResponse { greenlight.Empty empty = 2; ApprovePairingResponse approve_pairing = 3; } -} \ No newline at end of file +} diff --git a/libs/gl-client/src/signer/mod.rs b/libs/gl-client/src/signer/mod.rs index d1a2f771a..a4fd1995c 100644 --- a/libs/gl-client/src/signer/mod.rs +++ b/libs/gl-client/src/signer/mod.rs @@ -504,6 +504,7 @@ impl Signer { msg: e.to_string(), request: Some(req.clone()), git_version: GITHASH.to_string(), + node_id: self.node_id(), }) .await; #[cfg(not(feature = "permissive"))] @@ -568,6 +569,7 @@ impl Signer { msg: format!("{:?}", e), request: Some(req.clone()), git_version: GITHASH.to_string(), + node_id: self.node_id(), }) .await; return Err(Error::Other(anyhow!("processing request: {e:?}"))); diff --git a/libs/proto/glclient/scheduler.proto b/libs/proto/glclient/scheduler.proto index 07eb4e11f..3bb15f32e 100644 --- a/libs/proto/glclient/scheduler.proto +++ b/libs/proto/glclient/scheduler.proto @@ -423,6 +423,7 @@ message SignerRejection { string msg = 1; greenlight.HsmRequest request = 2; string git_version = 3; + bytes node_id = 4; } message PairDeviceRequest { @@ -545,4 +546,4 @@ message SignerResponse { greenlight.Empty empty = 2; ApprovePairingResponse approve_pairing = 3; } -} \ No newline at end of file +} From 8f603e1d56b47fc797aa1ec79de9dd20e370ab67 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 30 Sep 2024 11:53:24 +0200 Subject: [PATCH 2/2] py: Let the python bindings have a copy of the protos --- libs/gl-client-py/Makefile | 7 +- libs/gl-client-py/glclient/greenlight.proto | 574 +++++++++++++++++++ libs/gl-client-py/glclient/scheduler.proto | 549 ++++++++++++++++++ libs/gl-client-py/glclient/scheduler_pb2.py | 52 +- libs/gl-client-py/glclient/scheduler_pb2.pyi | 5 +- 5 files changed, 1158 insertions(+), 29 deletions(-) create mode 100644 libs/gl-client-py/glclient/greenlight.proto create mode 100644 libs/gl-client-py/glclient/scheduler.proto diff --git a/libs/gl-client-py/Makefile b/libs/gl-client-py/Makefile index 38c09dde7..79ab98382 100644 --- a/libs/gl-client-py/Makefile +++ b/libs/gl-client-py/Makefile @@ -33,8 +33,11 @@ GENALL += ${PYPROTOS} ${PYPROTOS}: pygrpc pygrpc: ${PROTOSRC} - python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/scheduler.proto - python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/greenlight.proto + cp ${PYDIR}/../proto/glclient/scheduler.proto ${PYDIR}/glclient/ + cp ${PYDIR}/../proto/glclient/greenlight.proto ${PYDIR}/glclient/ + cd ${PYDIR}; poetry install + cd ${PYDIR}; poetry run python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/scheduler.proto + cd ${PYDIR}; poetry run python -m grpc_tools.protoc ${PYPROTOC_OPTS} glclient/greenlight.proto check-py: cd ${PYDIR}; mypy glclient diff --git a/libs/gl-client-py/glclient/greenlight.proto b/libs/gl-client-py/glclient/greenlight.proto new file mode 100644 index 000000000..4730e18b2 --- /dev/null +++ b/libs/gl-client-py/glclient/greenlight.proto @@ -0,0 +1,574 @@ +syntax = "proto3"; +package greenlight; + +// The node service represents your node running on greenlight's +// infrastructure. You can use the exposed RPC methods to interact +// with your node. The URI used to connect to the node depends on +// where the node is being scheduled and is returned by the +// `Scheduler.Schedule()` RPC call. +// +// Notice that in order to connect to the node the caller must use the +// node-specific mTLS keypair returned by `Scheduler.Register()` or +// `Scheduler.Recover()`. In particular the anonymous mTLS keypair is +// rejected by the node. +// +// Deprecated methods are being replaced by the standardized and +// automatically managed cln-grpc protocol you can find in +// `node.proto` +service Node { + // Stream incoming payments + // + // Currently includes off-chain payments received matching an + // invoice or spontaneus paymens through keysend. + rpc StreamIncoming(StreamIncomingFilter) returns (stream IncomingPayment) {} + + // Stream the logs as they are produced by the node + // + // Mainly intended for debugging clients by tailing the log as + // they are written on the node. The logs start streaming from + // the first beginning, in order to allow inspection of events + // after an error occurred, That also means that the logs can + // be rather large, and should not be streamed onto + // resource-constrained devices. + rpc StreamLog(StreamLogRequest) returns (stream LogEntry) {} + + // Listen for incoming `custommsg` messages from peers. + // + // The messages are forwarded as they come in, and will not be + // replayed if the stream is interrupted. + rpc StreamCustommsg(StreamCustommsgRequest) returns (stream Custommsg) {} + + //////////////////////////////// HSM Messages //////////////////////// + // + // The following messages are related to communicating HSM + // requests back and forth. Chances are you won't need to + // interact with these at all, unless you want to embed the + // hsmd into your client. We recommend using a standalone hsmd + // such as hagrid, keeper of keys, to get started. + // + // Stream requests from the node to any key device that can + // respond to them. + rpc StreamHsmRequests(Empty) returns (stream HsmRequest) {} + + rpc RespondHsmRequest(HsmResponse) returns (Empty) {} + + rpc Configure(GlConfig) returns (Empty) {} + + rpc TrampolinePay(TrampolinePayRequest) returns (TrampolinePayResponse) {} +} + +message HsmRequestContext { + bytes node_id = 1; + uint64 dbid = 2; + uint64 capabilities = 3; +} + +message HsmResponse { + uint32 request_id = 1; + bytes raw = 2; + + // A list of updated key-value-version tuples that is to be + // merged into the state tracked by the plugin. + repeated SignerStateEntry signer_state = 5; + + // If the signer reported an error, and did therefore not include + // `raw`, this is the stringified error, so we can print it in the + // logs. This should help us collate policy errors with the changes + // proposed by CLN + string error = 6; +} + +message HsmRequest { + uint32 request_id = 1; + HsmRequestContext context = 2; + bytes raw = 3; + + // A list of key-value-version tuples that the signer should + // use to update its internal state. + repeated SignerStateEntry signer_state = 4; + + // Currently active requests that are used to justify changes + // in state. + repeated PendingRequest requests = 5; +} + +message Empty {} +service Hsm { + rpc Request(HsmRequest) returns (HsmResponse) {} + rpc Ping(Empty) returns (Empty) {} +} + +enum NetAddressType { + Ipv4 = 0; + Ipv6 = 1; + TorV2 = 2; + TorV3 = 3; +} +message Address { + NetAddressType type = 1; + string addr = 2; + uint32 port = 3; +} + +message GetInfoRequest {} + +message GetInfoResponse { + bytes node_id = 1; + string alias = 2; + bytes color = 3; + uint32 num_peers = 4; + repeated Address addresses = 5; + string version = 6; + uint32 blockheight = 7; + string network = 8; +} + +message StopRequest {} +message StopResponse {} + +message ConnectRequest { + string node_id = 1; + string addr = 2; +} + +message ConnectResponse { + string node_id = 1; + string features = 2; +} + + +message ListPeersRequest { + string node_id = 1; +} + +message Htlc { + string direction = 1; + uint64 id = 2; + string amount = 3; + uint64 expiry = 4; + string payment_hash = 5; + string state = 6; + bool local_trimmed = 7; +} + +message Aliases { + string local = 1; + string remote = 2; +} + +message Channel { + string state = 1; + string owner = 2; + Aliases alias = 18; + string short_channel_id = 3; + uint32 direction = 4; + string channel_id = 5; + string funding_txid = 6; + string close_to_addr = 7; + string close_to = 8; + bool private = 9; + string total = 10; + string dust_limit = 11; + string spendable = 12; + string receivable = 13; + uint32 their_to_self_delay = 14; + uint32 our_to_self_delay = 15; + repeated string status = 16; + repeated Htlc htlcs = 17; +} + +message Peer { + bytes id = 1; + bool connected = 2; + repeated Address addresses = 3; + string features = 4; + repeated Channel channels = 5; +} + +message ListPeersResponse { + repeated Peer peers = 1; +} + +message DisconnectRequest { + string node_id = 1; + bool force = 2; +} + +message DisconnectResponse {} + +enum BtcAddressType { + BECH32 = 0; // Default + P2SH_SEGWIT = 1; +} + +message NewAddrRequest { + BtcAddressType address_type = 1; +} +message NewAddrResponse { + BtcAddressType address_type = 1; + string address = 2; +} + +message ListFundsRequest { + Confirmation minconf = 1; +} + +enum OutputStatus { + CONFIRMED = 0; + UNCONFIRMED = 1; +} + +message ListFundsOutput { + Outpoint output = 1; + Amount amount = 2; + string address = 4; + OutputStatus status = 5; + bool reserved = 6; + uint32 reserved_to_block = 7; +} + +message ListFundsChannel { + bytes peer_id = 1; + bool connected = 2; + uint64 short_channel_id = 3; + uint64 our_amount_msat = 4; + uint64 amount_msat = 5; + bytes funding_txid = 6; + uint32 funding_output = 7; +} + +message ListFundsResponse { + repeated ListFundsOutput outputs = 1; + repeated ListFundsChannel channels = 2; +} + +// Let the node decide what feerate to apply based on its internal +// view of the network's current feerate. +enum FeeratePreset { + NORMAL = 0; + SLOW = 1; + URGENT = 2; +} + +message Feerate { + oneof value { + FeeratePreset preset = 1; + uint64 perkw = 5; + uint64 perkb = 6; + } +} + +message Confirmation { + uint32 blocks = 1; +} + +message WithdrawRequest { + string destination = 1; + Amount amount = 2; + Feerate feerate = 3; + Confirmation minconf = 7; + repeated Outpoint utxos = 8; +} + +message WithdrawResponse { + bytes tx = 1; + bytes txid = 2; +} + +// TODO: Extract AmountOrAll into its own message +// TODO: Extract Feerate into its own message + +message FundChannelRequest { + bytes node_id = 1; + Amount amount = 2; + Feerate feerate = 3; + bool announce = 7; + Confirmation minconf = 8; + //TODO Maybe add UTXOS + string close_to = 10; +} + +message Outpoint { + bytes txid = 1; + uint32 outnum = 2; +} + +message FundChannelResponse { + bytes tx = 1; + Outpoint outpoint = 2; + bytes channel_id = 3; + string close_to = 4; +} + +message Timeout { + uint32 seconds = 1; +} + +message BitcoinAddress { + string address = 1; +} + +message CloseChannelRequest { + bytes node_id = 1; + Timeout unilateraltimeout = 2; + BitcoinAddress destination = 3; +} + +enum CloseChannelType { + MUTUAL = 0; + UNILATERAL = 1; +} + +message CloseChannelResponse { + CloseChannelType close_type = 1; + bytes tx = 2; + bytes txid = 3; +} + +message Amount { + oneof unit { + uint64 millisatoshi = 1; + uint64 satoshi = 2; + uint64 bitcoin = 3; + bool all = 4; + bool any = 5; + } +} + +message InvoiceRequest { + Amount amount = 1; + string label = 2; + string description = 3; + bytes preimage = 4; +} + +enum InvoiceStatus { + UNPAID = 0; + PAID = 1; + EXPIRED = 2; +} + +message Invoice { + string label = 1; + string description = 2; + Amount amount = 3; + Amount received = 4; + InvoiceStatus status = 5; + uint32 payment_time = 6; + uint32 expiry_time = 7; + string bolt11 = 8; + bytes payment_hash = 9; + bytes payment_preimage = 10; +} + +message PayRequest { + string bolt11 = 1; + + // Only needed when the invoice does not specify an amount. + Amount amount = 2; + + // Non-zero number of seconds before we should stop retrying + // the payment and return an error. + uint32 timeout = 3; + + double maxfeepercent = 4; + + Amount maxfee = 5; +} + +enum PayStatus { + PENDING = 0; + COMPLETE = 1; + FAILED = 2; +} + +message Payment { + bytes destination = 1; + bytes payment_hash = 2; + bytes payment_preimage = 3; + PayStatus status = 4; + Amount amount = 5; + Amount amount_sent = 6; + string bolt11 = 7; + + // UTC Unix timestamp of the time the invoice was created. + double created_at = 8; + // UTC Unix timestamp of the time the payment was completed + // (successfully or failed). 0 if not completed yet. + uint64 completed_at = 9; +} + +// A payment identifier is a way to reference a unique payment, either +// by its bolt11 string or just the payment hash. Only one of the two +// may be provided at once, since having multiple ones may conflict +// with each other. +message PaymentIdentifier { + oneof id { + string bolt11 = 1; + bytes payment_hash = 2; + } +} + +// Request a list of payments that this node has performed. Optionally +// the query can be narrowed to a single payment by providing an +// identifier. +message ListPaymentsRequest { + PaymentIdentifier identifier = 1; +} + +// The response matching `ListPaymentRequest`. It returns a list of +// PayResponses, i.e., the same format as `Pay` returned its result. +message ListPaymentsResponse { + repeated Payment payments = 1; +} + +message InvoiceIdentifier { + oneof id { + string label = 1; + string invstring = 2; + bytes payment_hash = 3; + } +} + +message ListInvoicesRequest { + InvoiceIdentifier identifier = 1; +} + +// Options to stream_incoming to specify what to stream. +message StreamIncomingFilter { +} + +message ListInvoicesResponse { + repeated Invoice invoices = 1; +} + +message TlvField { + uint64 type = 1; + // length is implied since the value field carries its own + // length here. + bytes value = 2; +} + +message OffChainPayment { + string label = 1; + bytes preimage = 2; + Amount amount = 3; + repeated TlvField extratlvs = 4; + bytes payment_hash = 5; + string bolt11 = 6; +} + +message IncomingPayment { + oneof details { + OffChainPayment offchain = 1; + } +} + +// A single hop in a Routehint +message RoutehintHop { + bytes node_id = 1; + string short_channel_id = 2; + uint64 fee_base = 3; + uint32 fee_prop = 4; + uint32 cltv_expiry_delta = 5; +} + +message Routehint { + repeated RoutehintHop hops = 1; +} + +message KeysendRequest { + bytes node_id = 1; + Amount amount = 2; + string label = 3; + repeated Routehint routehints = 4; + repeated TlvField extratlvs = 5; +} + +message StreamLogRequest {}; +message LogEntry { + string line = 1; +} + +message SignerStateEntry { + uint64 version = 1; + string key = 2; + bytes value = 3; +} + +// This represents a grpc request that is currently pending, along +// with the pubkey of the client issuing the request and a matching +// signature. This allows the signer to verify that the state changes +// implied in a signature request correspond to authentic grpc +// commands, and were not injected somewhere along the way. +message PendingRequest { + bytes request = 1; + string uri = 2; + bytes signature = 3; + bytes pubkey = 4; + uint64 timestamp = 5; + bytes rune = 6; +} + + +// The `NodeConfig` is used to pass startup parameters to the +// node. The `gl-plugin` will look for a file in its directory to load +// these values from. Please refer to the individual fields to learn +// what they do. +message NodeConfig { + // In order to start without a signer attached we need to + // stash a couple of canned messages that we'd otherwise ask + // from the signer. These are just request-response tuples + // that we use to match and reply against incoming requests. + repeated StartupMessage startupmsgs = 1; +} + + +// The `GlConfig` is used to pass greenlight-specific startup parameters +// to the node. The `gl-plugin` will look for a serialized config object in +// the node's datastore to load these values from. Please refer to the +// individual fields to learn what they do. +message GlConfig { + string close_to_addr = 1; +} + +// A message that we know will be requested by `lightningd` at +// startup, and that we stash a response to on the scheduler. This +// allows the scheduler to start a node without requiring the signer +// to connect first. Messages are stored in full, including type +// prefix, but without the length prefix. +message StartupMessage { + bytes request = 1; + bytes response = 2; +} + +message StreamCustommsgRequest {} + +message Custommsg { + bytes peer_id = 1; + bytes payload = 2; +} + +message TrampolinePayRequest { + string bolt11 = 1; + bytes trampoline_node_id = 2; + uint64 amount_msat = 3; + string label = 4; + float maxfeepercent = 5; + uint32 maxdelay = 6; + string description = 7; +} + +message TrampolinePayResponse { + enum PayStatus { + COMPLETE = 0; + FAILED = 2; + } + bytes payment_preimage = 1; + bytes payment_hash = 2; + double created_at = 3; + uint32 parts = 4; + uint64 amount_msat = 5; + uint64 amount_sent_msat = 6; + bytes destination = 7; +} \ No newline at end of file diff --git a/libs/gl-client-py/glclient/scheduler.proto b/libs/gl-client-py/glclient/scheduler.proto new file mode 100644 index 000000000..3bb15f32e --- /dev/null +++ b/libs/gl-client-py/glclient/scheduler.proto @@ -0,0 +1,549 @@ +syntax = "proto3"; +package scheduler; + +import "glclient/greenlight.proto"; + +// The scheduler service is the endpoint which allows users to +// register a new node with greenlight, recover access to an existing +// node if the owner lost its credentials, schedule the node to be run +// on greenlight's infrastructure, and retrieve metadata about the +// node. +// +// Node +// ==== +// +// A node is the basic object representing an account on +// greenlight. Each node corresponds to a c-lightning instance bound +// to a specific network that can be scheduled on greenlight, and must +// have a unique `node_id`. +// +// Nodes are scheduled on-demand onto the infrastructure, but the time +// to schedule a node is almost instantaneous. +// +// Authentication +// ============== +// +// Users are authenticated using mTLS authentication. Applications are +// provisioned with an anonymous keypair that is not bound to a node, +// allowing access only to the unauthenticated endpoints +// `Scheduler.GetChallenge`, `Scheduler.Register` and +// `Scheduler.Recover`. This allows them to register or recover a +// node, but doesn't give access to the node itself. Upon registering +// or recovering an account the user receives a keypair that is bound +// to the specific node. Once the user receives their personal mTLS +// keypair they may use it to connect to greenlight, and thus get +// access to the node-specific functionality. Please refer to the +// documentation of your grpc library to learn how to configure grpc +// to use the node-specific mTLS keypair. +// +service Scheduler { + // A user may register a new node with greenlight by providing + // some basic metadata and proving that they have access to + // the corresponding private key (see challenge-response + // mechanism below). This means that in order to register a + // new node the user must have access to the corresponding + // private keys to prove ownership, and prevent users from + // just registering arbitrary node_ids without actually + // knowing the corresponding secrets. + // + // Upon successful registration an mTLS certificate and + // private key are returned. These can be used to authenticate + // future connections to the scheduler or the node. + // + // Each node may be registered once, any later attempt will + // result in an error being returned. If the user lost its + // credentials it can make use of the Recover RPC method to + // recover the credentials. Notice that this also means that + // the same node_id cannot be reused for different networks. + rpc Register(RegistrationRequest) returns (RegistrationResponse) {} + + // Should a user have lost its credentials (mTLS keypair) for + // any reason, they may regain access to their node using the + // Recover RPC method. Similar to the initial registration the + // caller needs to authenticate the call by proving access to + // the node's secret. This also uses the challenge-response + // mechanism. + // + // Upon success a newly generated mTLS certificate and private + // key are returned, allowing the user to authenticate going + // forward. Existing keypairs are not revoked, in order to + // avoid locking out other authenticated applications. + rpc Recover(RecoveryRequest) returns (RecoveryResponse) {} + + // Challenges are one-time values issued by the server, used + // to authenticate a user/device against the server. A user or + // device can authenticate to the server by signing the + // challenge and returning the signed message as part of the + // request that is to be authenticated. + // + // Challenges may not be reused, and are bound to the scope + // they have been issued for. Attempting to reuse a challenge + // or use a challenge with a different scope will result in an + // error being returned. + rpc GetChallenge(ChallengeRequest) returns (ChallengeResponse) {} + + // Scheduling takes a previously registered node, locates a + // free slot in greenlight's infrastructure and allocates it + // to run the node. The node then goes through the startup + // sequence, synchronizing with the blockchain, and finally + // binding its grpc interface (see Node service below) to a + // public IP address and port. Access is authenticated via the + // mTLS keypair the user received from registering or + // recovering the node. + // + // Upon success a NodeInfoResponse containing the grpc + // connection details and some metadata is returned. The + // application must use the grpc details and its node-specific + // mTLS keypair to interact with the node directly. + rpc Schedule(ScheduleRequest) returns (NodeInfoResponse) {} + + // Much like `Schedule` this call is used to retrieve the + // metadata and grpc details of a node. Unlike the other call + // however it is passive, and will not result in the node + // being scheduled if it isn't already running. This can be + // used to check if a node is already scheduled, or to wait + // for it to be scheduled (e.g., if the caller is an `hsmd` + // that signs off on changes, but doesn't want to keep the + // node itself scheduled). + rpc GetNodeInfo(NodeInfoRequest) returns (NodeInfoResponse) {} + + // The signer may want to trigger an upgrade of the node + // before waiting for the node to be scheduled. This ensures + // that the signer version is in sync with the node + // version. The scheduler may decide to defer upgrading if the + // protocols are compatible. Please do not use this directly, + // rather use the Signer in the client library to trigger this + // automatically when required. Posting an incomplete or + // non-functional UpgradeRequest may result in unschedulable + // nodes. + rpc MaybeUpgrade(UpgradeRequest) returns (UpgradeResponse) {} + + // This call is used to fetch a list of invite codes associated + // with the node id of the client. These invite codes can be used + // for further registration of new nodes. + rpc ListInviteCodes(ListInviteCodesRequest) returns (ListInviteCodesResponse) {} + + // Exporting a node allows users to take control of their node + // + // This method initiates the node export on Greenlight, + // allowing users to offboard from GL into their own + // infrastructure. After calling this method the node will no + // longer be schedulable on Greenlight, since it is never safe + // to assume there haven't been changes in the node's state + // (see CLN Backups documentation for details). `ExportNode` + // marks the node as `Exporting`, then generates an encryption + // secret which is then used to encrypt a database + // backup. This encrypted database backup is then made + // accessible through an HTTP server, and a link to it is + // returned as a response to `ExportNode`. After the export + // completes the node is marked as `Exported`. The encryption + // key can then be derived using the signer, using ECDH, + // allowing only users with the node secret to decrypt it. + // + // `ExportNode` is idempotent and may be called multiple + // times, without causing the node to be re-exported multiple + // times, should the call or the download be interrupted. DO + // NOT import the backup multiple times into your + // infrastructure, as that can lead to dataloss (old state + // being replayed) and loss of funds (see CLN Backups + // documentation for more information) + rpc ExportNode(ExportNodeRequest) returns (ExportNodeResponse) {} + + rpc AddOutgoingWebhook(AddOutgoingWebhookRequest) returns (AddOutgoingWebhookResponse) {} + + rpc ListOutgoingWebhooks(ListOutgoingWebhooksRequest) returns (ListOutgoingWebhooksResponse) {} + + rpc DeleteWebhooks(DeleteOutgoingWebhooksRequest) returns (greenlight.Empty) {} + + rpc RotateOutgoingWebhookSecret(RotateOutgoingWebhookSecretRequest) returns (WebhookSecretResponse) {} + + // Attaches a Signer via a bidirectional stream to the scheduler. + // This is a communication channel between greenlight and the signing + // device that is used for requests that are not part of the node api. + // + // The stream is used to hand out the ApprovePairingRequests that + // the signer answers with a ApprovePairingResponse. + rpc SignerRequestsStream(stream SignerResponse) returns (stream SignerRequest) {}; +}; + +message AddOutgoingWebhookRequest { + bytes node_id = 1; + string uri = 2; +} + +message AddOutgoingWebhookResponse { + int64 id = 1; + string secret = 2; +} + +message ListOutgoingWebhooksRequest { + bytes node_id = 1; +} + +message Webhook { + int64 id = 1; + string uri = 2; +} + +message ListOutgoingWebhooksResponse { + repeated Webhook outgoing_webhooks = 1; +} + +message DeleteOutgoingWebhooksRequest { + bytes node_id = 1; + repeated int64 ids = 2; +} + +message RotateOutgoingWebhookSecretRequest { + bytes node_id = 1; + int64 webhook_id = 2; +} + +message WebhookSecretResponse { + string secret = 1; +} + +// A service to collect debugging information from clients. +service Debug { + // The signer is designed to fail closed, i.e., we reject requests + // that do not resolve or that go against one of its policies. This + // comes with some issues, such as false negatives, where we reject + // despite the request being valid. As more apps use the API we need + // to debug these false negatives, hence why we report rejections, + // so we can investigate the validity of the rejection, and to + // fine-tine the signer's ruleset. + rpc ReportSignerRejection(SignerRejection) returns (greenlight.Empty) {} +} + +// A service to pair signer-less clients with an existing signer. +service Pairing { + // Initiates a new Pairing Sessions. This is called by the new + // device that wants to request a pairing from an existing device. + // The session lifetime is bound to the stream so closing the + // stream destroys the session. + rpc PairDevice(PairDeviceRequest) returns (PairDeviceResponse) {} + + // Returns the pairing related data that belongs to a pairing + // session. This is meant to be called from a device that can + // approve a pairing request, we sometimes call it "old device". + rpc GetPairingData(GetPairingDataRequest) returns (GetPairingDataResponse) {} + + // Approves a pairing request. The ApprovePairingRequest is + // forwarded to a signer for further processing. + rpc ApprovePairing(ApprovePairingRequest) returns (greenlight.Empty) {} +} + +message ChallengeRequest { + ChallengeScope scope = 1; + bytes node_id = 2; +}; + +message ChallengeResponse { + bytes challenge = 1; +}; + +// Operation is the challenge associated with? +enum ChallengeScope { + REGISTER = 0; + RECOVER = 1; +} + +message RegistrationRequest { + // 33 bytes node public key. + bytes node_id = 1; + + // DEPRECATED: The `init_msg` subsumes this field + bytes bip32_key = 2; + + // Which network is this node going to run on? Options are + // bitcoin, testnet, and regtest. + string network = 4; + + // An previously unused challenge as retrieved from + // `Scheduler.GetChallenge() with `scope=REGISTER`. In + // combination with the `signature` below this is used to + // authenticate the caller and ensure the caller has access to + // the secret keys corresponding to the `node_id`. + bytes challenge = 5; + + // A signature for the `challenge` signed by the secret key + // corresponding to the `node_id`. Please refer to the + // documentation of `Scheduler.GetChallenge()` for details on + // how to create this signature. + bytes signature = 6; + + // The signer_proto is required in order to determine which + // version the node should run. If these don't match the + // signer may not be able to sign incoming requests. + string signer_proto = 7; + + // The fuil init message returned by the `libhsmd`, this + // supersedes the bip32_key field which was a misnomer. Notice + // that this includes the prefix 0x006F which is the message + // type. + bytes init_msg = 8; + + // The certificate signing request that will be signed by the + // greenlight backend. Notice that this must have the valid + // CN corresponding to the node_id e.g. /users/{node_id} set. + bytes csr = 9; + + // An optional invite code. We may want to throttle the + // registration rate. Therefore we might check that a registration + // request has a valid invite code. + string invite_code = 10; + + // Messages stashed at the scheduler to allow signerless + // startups. + repeated StartupMessage startupmsgs = 3; +}; + +message RegistrationResponse { + // Upon registering the user receives back the signed certificate that + // belongs to the certificate signing request the that was sent in the + // registration request, so they can authenticate themselves in the future. + string device_cert = 1; + + // The private key that was used to create the certificate with. This key + // is used to sign the requests to the node. + string device_key = 2; + + // A master rune that is returned if the device that is registered has its + // own signer. The signer is necessary as the response is intercepted on the + // client side and appends the rune to the registratrion response. + string rune = 3; + + // Creds contains a serialized version of the device_cert, the device_key + // and the rune that are used to authenticate a device at the backend, + // and to authorize a request at the signer. + bytes creds = 4; +}; + +// Ask the scheduler to schedule the node to be run on an available nodelet. +// +// This will always cause the scheduler to kick into action. If you'd +// like to see if a nodelet is currently taking care of this node, or +// wait for one to start please use the +message ScheduleRequest { + bytes node_id = 1; +}; + +// Discovery request asking the scheduler if a nodelet is currently assigned +// the specified node_id, or wait for one to be assigned. If `wait` is set to +// `true` the scheduler will keep the request pending until a nodelet is +// assigned. +message NodeInfoRequest { + bytes node_id = 1; + bool wait = 2; +}; + +message NodeInfoResponse { + bytes node_id = 1; + string grpc_uri = 2; + uint64 session_id = 3; +}; + + +message RecoveryRequest { + bytes challenge = 1; + bytes signature = 2; + bytes node_id = 3; + // The certificate signing request that will be signed by the + // greenlight backend. Notice that this must have the valid + // CN corresponding to the node_id e.g. /users/{node_id} set. + bytes csr = 9; +}; + +message RecoveryResponse { + string device_cert = 1; + string device_key = 2; + + // A master rune that is returned if the device that is registered has its + // own signer. The signer is necessary as the response is intercepted on the + // client side and appends the rune to the registratrion response. + string rune = 3; + + // Creds contains a serialized version of the device_cert, the device_key + // and the rune that are used to authenticate a device at the backend, + // and to authorize a request at the signer. + bytes creds = 4; +}; + +message UpgradeRequest { + // The version of the signer, i.e., the maximum version of the + // protocol that this signer can understand. + string signer_version = 1; + + // The new initmsg matching the above version. Necessary to + // schedule the node without a signer present. + // Deprecated: Replaced by the more generic `startupmsgs` + bytes initmsg = 2 [deprecated = true]; + + // Messages stashed at the scheduler to allow signerless startups. + repeated StartupMessage startupmsgs = 3; +}; +message UpgradeResponse { + // The version of the node before the upgrade request has been + // processed. + string old_version = 1; +}; + +// A message that we know will be requested by `lightningd` at +// startup, and that we stash a response to on the scheduler. This +// allows the scheduler to start a node without requiring the signer +// to connect first. Messages are stored in full, including type +// prefix, but without the length prefix. +message StartupMessage { + bytes request = 1; + bytes response = 2; +} + +message ListInviteCodesRequest {}; + +message ListInviteCodesResponse { + repeated InviteCode invite_code_list = 1; +}; + +message InviteCode { + string code = 1; + bool is_redeemed = 2; +}; + +// Empty message for now, node identity is extracted from the mTLS +// certificate used to authenticate against the Scheduler. +message ExportNodeRequest {} + +message ExportNodeResponse { + // URL where the encrypted backup can be retrieved from. + string url = 1; +} + +message SignerRejection { + // A human-readable description of what went wrong + string msg = 1; + greenlight.HsmRequest request = 2; + string git_version = 3; + bytes node_id = 4; +} + +message PairDeviceRequest { + // The tls public key of the new device. + string device_id = 1; + + // The certificate signing request that will be signed by the + // greenlight backend if the pairing succeeds. Notice that the CN + // here is irrelevant. + bytes csr = 2; + + // The name of the device that will be part of the tls certificate + // subjects CN field: CN=/users//. + string device_name = 3; + + // A human readable description of the device, this can be a + // purpose or something similar. Can be used to display to the + // user on the old device. + string description = 4; + + // A set of restrictions that the new devices requests for the + // rune. This might in the future get upgraded for easier naming. + string restrictions = 5; +} + +message PairDeviceResponse { + // device_id is the public key of the new device used for the + // tls cert. + string device_id = 1; + + // Upon a pairing request, the device receives back the signed certificate + // that belongs to the certificate signing request the that was sent with + // the pairing request, so they can authenticate themselves in the future. + string device_cert = 2; + + // The private key that was used to create the certificate with. This key + // is used to sign the requests to the node. + string device_key = 3; + + // A rune that is returned if the device that is created by the signer that + // the device pairs to. + string rune = 4; + + // Creds contains a serialized version of the device certificate, the device + // key and the rune that are used to authenticate a device at the backend, + // and to authorize a request at the signer. + bytes creds = 5; +} + +message GetPairingDataRequest { + // The device_id that the client got from the qr-code. + string device_id = 1; +} + +message GetPairingDataResponse { + string device_id = 1; + + // The certificate signing request that will be signed by the + // greenlight backend if the pairing succeeds. Notice that the CN + // here is irrelevant. + bytes csr = 2; + + // The name of the device that will be part of the tls certificate + // subjects CN field: CN=/users//. + string device_name = 3; + + // A human readable description of the device, this can be a + // purpose or something similar. Can be used to display to the + // user on the old device. + string description = 4; + + // A set of restrictions that the new devices requests for the + // rune. This might in the future get upgraded for easier naming. + string restrictions = 5; +} + +message ApprovePairingRequest { + string device_id = 1; + + // The time that the old device approved the pairing request. This + // is used by the signer to restrict the duration an approval + // request is valid. + uint64 timestamp = 2; + + // The name of the device that will be part of the tls certificate + // subjects CN field: CN=/users//. + string device_name = 3; + + // The restrictions need a pubkey set. + string restrictions = 4; + + // The signature of the above to ensure data integrity. + bytes sig = 5; + + // The public key corresponding to the private key that was used + // to sign the request and that is part of the rune; + bytes pubkey = 6; + + // The rune of the old device with a pubkey field corresponding to + // the signature above. Used to authorize the approval request. + string rune = 7; +} + +message ApprovePairingResponse { + string device_id = 1; + bytes node_id = 2; + string rune =3; +} + +message SignerRequest { + uint32 request_id = 1; + oneof request { + ApprovePairingRequest approve_pairing = 2; + } +} + +message SignerResponse { + uint32 request_id = 1; + oneof response { + greenlight.Empty empty = 2; + ApprovePairingResponse approve_pairing = 3; + } +} diff --git a/libs/gl-client-py/glclient/scheduler_pb2.py b/libs/gl-client-py/glclient/scheduler_pb2.py index d81f633f9..1b05456f6 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2.py +++ b/libs/gl-client-py/glclient/scheduler_pb2.py @@ -15,7 +15,7 @@ from glclient import greenlight_pb2 as glclient_dot_greenlight__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18glclient/scheduler.proto\x12\tscheduler\x1a\x19glclient/greenlight.proto\"9\n\x19\x41\x64\x64OutgoingWebhookRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\"8\n\x1a\x41\x64\x64OutgoingWebhookResponse\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0e\n\x06secret\x18\x02 \x01(\t\".\n\x1bListOutgoingWebhooksRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"\"\n\x07Webhook\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0b\n\x03uri\x18\x02 \x01(\t\"M\n\x1cListOutgoingWebhooksResponse\x12-\n\x11outgoing_webhooks\x18\x01 \x03(\x0b\x32\x12.scheduler.Webhook\"=\n\x1d\x44\x65leteOutgoingWebhooksRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03ids\x18\x02 \x03(\x03\"I\n\"RotateOutgoingWebhookSecretRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x12\n\nwebhook_id\x18\x02 \x01(\x03\"\'\n\x15WebhookSecretResponse\x12\x0e\n\x06secret\x18\x01 \x01(\t\"M\n\x10\x43hallengeRequest\x12(\n\x05scope\x18\x01 \x01(\x0e\x32\x19.scheduler.ChallengeScope\x12\x0f\n\x07node_id\x18\x02 \x01(\x0c\"&\n\x11\x43hallengeResponse\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\"\xea\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x11\n\tbip32_key\x18\x02 \x01(\x0c\x12\x0f\n\x07network\x18\x04 \x01(\t\x12\x11\n\tchallenge\x18\x05 \x01(\x0c\x12\x11\n\tsignature\x18\x06 \x01(\x0c\x12\x14\n\x0csigner_proto\x18\x07 \x01(\t\x12\x10\n\x08init_msg\x18\x08 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\x12\x13\n\x0binvite_code\x18\n \x01(\t\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"\\\n\x14RegistrationResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\x12\x0c\n\x04rune\x18\x03 \x01(\t\x12\r\n\x05\x63reds\x18\x04 \x01(\x0c\"\"\n\x0fScheduleRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"0\n\x0fNodeInfoRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0c\n\x04wait\x18\x02 \x01(\x08\"I\n\x10NodeInfoResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x10\n\x08grpc_uri\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\x04\"U\n\x0fRecoveryRequest\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07node_id\x18\x03 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\"X\n\x10RecoveryResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\x12\x0c\n\x04rune\x18\x03 \x01(\t\x12\r\n\x05\x63reds\x18\x04 \x01(\x0c\"m\n\x0eUpgradeRequest\x12\x16\n\x0esigner_version\x18\x01 \x01(\t\x12\x13\n\x07initmsg\x18\x02 \x01(\x0c\x42\x02\x18\x01\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"&\n\x0fUpgradeResponse\x12\x13\n\x0bold_version\x18\x01 \x01(\t\"3\n\x0eStartupMessage\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x10\n\x08response\x18\x02 \x01(\x0c\"\x18\n\x16ListInviteCodesRequest\"J\n\x17ListInviteCodesResponse\x12/\n\x10invite_code_list\x18\x01 \x03(\x0b\x32\x15.scheduler.InviteCode\"/\n\nInviteCode\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x13\n\x0bis_redeemed\x18\x02 \x01(\x08\"\x13\n\x11\x45xportNodeRequest\"!\n\x12\x45xportNodeResponse\x12\x0b\n\x03url\x18\x01 \x01(\t\"\\\n\x0fSignerRejection\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\'\n\x07request\x18\x02 \x01(\x0b\x32\x16.greenlight.HsmRequest\x12\x13\n\x0bgit_version\x18\x03 \x01(\t\"s\n\x11PairDeviceRequest\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x0b\n\x03\x63sr\x18\x02 \x01(\x0c\x12\x13\n\x0b\x64\x65vice_name\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x14\n\x0crestrictions\x18\x05 \x01(\t\"m\n\x12PairDeviceResponse\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65vice_cert\x18\x02 \x01(\t\x12\x12\n\ndevice_key\x18\x03 \x01(\t\x12\x0c\n\x04rune\x18\x04 \x01(\t\x12\r\n\x05\x63reds\x18\x05 \x01(\x0c\"*\n\x15GetPairingDataRequest\x12\x11\n\tdevice_id\x18\x01 \x01(\t\"x\n\x16GetPairingDataResponse\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x0b\n\x03\x63sr\x18\x02 \x01(\x0c\x12\x13\n\x0b\x64\x65vice_name\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x14\n\x0crestrictions\x18\x05 \x01(\t\"\x93\x01\n\x15\x41pprovePairingRequest\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x65vice_name\x18\x03 \x01(\t\x12\x14\n\x0crestrictions\x18\x04 \x01(\t\x12\x0b\n\x03sig\x18\x05 \x01(\x0c\x12\x0e\n\x06pubkey\x18\x06 \x01(\x0c\x12\x0c\n\x04rune\x18\x07 \x01(\t\"J\n\x16\x41pprovePairingResponse\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x0c\x12\x0c\n\x04rune\x18\x03 \x01(\t\"k\n\rSignerRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12;\n\x0f\x61pprove_pairing\x18\x02 \x01(\x0b\x32 .scheduler.ApprovePairingRequestH\x00\x42\t\n\x07request\"\x92\x01\n\x0eSignerResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12\"\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\x11.greenlight.EmptyH\x00\x12<\n\x0f\x61pprove_pairing\x18\x03 \x01(\x0b\x32!.scheduler.ApprovePairingResponseH\x00\x42\n\n\x08response*+\n\x0e\x43hallengeScope\x12\x0c\n\x08REGISTER\x10\x00\x12\x0b\n\x07RECOVER\x10\x01\x32\xd6\x08\n\tScheduler\x12M\n\x08Register\x12\x1e.scheduler.RegistrationRequest\x1a\x1f.scheduler.RegistrationResponse\"\x00\x12\x44\n\x07Recover\x12\x1a.scheduler.RecoveryRequest\x1a\x1b.scheduler.RecoveryResponse\"\x00\x12K\n\x0cGetChallenge\x12\x1b.scheduler.ChallengeRequest\x1a\x1c.scheduler.ChallengeResponse\"\x00\x12\x45\n\x08Schedule\x12\x1a.scheduler.ScheduleRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12H\n\x0bGetNodeInfo\x12\x1a.scheduler.NodeInfoRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12G\n\x0cMaybeUpgrade\x12\x19.scheduler.UpgradeRequest\x1a\x1a.scheduler.UpgradeResponse\"\x00\x12Z\n\x0fListInviteCodes\x12!.scheduler.ListInviteCodesRequest\x1a\".scheduler.ListInviteCodesResponse\"\x00\x12K\n\nExportNode\x12\x1c.scheduler.ExportNodeRequest\x1a\x1d.scheduler.ExportNodeResponse\"\x00\x12\x63\n\x12\x41\x64\x64OutgoingWebhook\x12$.scheduler.AddOutgoingWebhookRequest\x1a%.scheduler.AddOutgoingWebhookResponse\"\x00\x12i\n\x14ListOutgoingWebhooks\x12&.scheduler.ListOutgoingWebhooksRequest\x1a\'.scheduler.ListOutgoingWebhooksResponse\"\x00\x12O\n\x0e\x44\x65leteWebhooks\x12(.scheduler.DeleteOutgoingWebhooksRequest\x1a\x11.greenlight.Empty\"\x00\x12p\n\x1bRotateOutgoingWebhookSecret\x12-.scheduler.RotateOutgoingWebhookSecretRequest\x1a .scheduler.WebhookSecretResponse\"\x00\x12Q\n\x14SignerRequestsStream\x12\x19.scheduler.SignerResponse\x1a\x18.scheduler.SignerRequest\"\x00(\x01\x30\x01\x32Q\n\x05\x44\x65\x62ug\x12H\n\x15ReportSignerRejection\x12\x1a.scheduler.SignerRejection\x1a\x11.greenlight.Empty\"\x00\x32\xf8\x01\n\x07Pairing\x12K\n\nPairDevice\x12\x1c.scheduler.PairDeviceRequest\x1a\x1d.scheduler.PairDeviceResponse\"\x00\x12W\n\x0eGetPairingData\x12 .scheduler.GetPairingDataRequest\x1a!.scheduler.GetPairingDataResponse\"\x00\x12G\n\x0e\x41pprovePairing\x12 .scheduler.ApprovePairingRequest\x1a\x11.greenlight.Empty\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18glclient/scheduler.proto\x12\tscheduler\x1a\x19glclient/greenlight.proto\"9\n\x19\x41\x64\x64OutgoingWebhookRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\"8\n\x1a\x41\x64\x64OutgoingWebhookResponse\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0e\n\x06secret\x18\x02 \x01(\t\".\n\x1bListOutgoingWebhooksRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"\"\n\x07Webhook\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0b\n\x03uri\x18\x02 \x01(\t\"M\n\x1cListOutgoingWebhooksResponse\x12-\n\x11outgoing_webhooks\x18\x01 \x03(\x0b\x32\x12.scheduler.Webhook\"=\n\x1d\x44\x65leteOutgoingWebhooksRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03ids\x18\x02 \x03(\x03\"I\n\"RotateOutgoingWebhookSecretRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x12\n\nwebhook_id\x18\x02 \x01(\x03\"\'\n\x15WebhookSecretResponse\x12\x0e\n\x06secret\x18\x01 \x01(\t\"M\n\x10\x43hallengeRequest\x12(\n\x05scope\x18\x01 \x01(\x0e\x32\x19.scheduler.ChallengeScope\x12\x0f\n\x07node_id\x18\x02 \x01(\x0c\"&\n\x11\x43hallengeResponse\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\"\xea\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x11\n\tbip32_key\x18\x02 \x01(\x0c\x12\x0f\n\x07network\x18\x04 \x01(\t\x12\x11\n\tchallenge\x18\x05 \x01(\x0c\x12\x11\n\tsignature\x18\x06 \x01(\x0c\x12\x14\n\x0csigner_proto\x18\x07 \x01(\t\x12\x10\n\x08init_msg\x18\x08 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\x12\x13\n\x0binvite_code\x18\n \x01(\t\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"\\\n\x14RegistrationResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\x12\x0c\n\x04rune\x18\x03 \x01(\t\x12\r\n\x05\x63reds\x18\x04 \x01(\x0c\"\"\n\x0fScheduleRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"0\n\x0fNodeInfoRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0c\n\x04wait\x18\x02 \x01(\x08\"I\n\x10NodeInfoResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x10\n\x08grpc_uri\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\x04\"U\n\x0fRecoveryRequest\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07node_id\x18\x03 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\"X\n\x10RecoveryResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\x12\x0c\n\x04rune\x18\x03 \x01(\t\x12\r\n\x05\x63reds\x18\x04 \x01(\x0c\"m\n\x0eUpgradeRequest\x12\x16\n\x0esigner_version\x18\x01 \x01(\t\x12\x13\n\x07initmsg\x18\x02 \x01(\x0c\x42\x02\x18\x01\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"&\n\x0fUpgradeResponse\x12\x13\n\x0bold_version\x18\x01 \x01(\t\"3\n\x0eStartupMessage\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x10\n\x08response\x18\x02 \x01(\x0c\"\x18\n\x16ListInviteCodesRequest\"J\n\x17ListInviteCodesResponse\x12/\n\x10invite_code_list\x18\x01 \x03(\x0b\x32\x15.scheduler.InviteCode\"/\n\nInviteCode\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x13\n\x0bis_redeemed\x18\x02 \x01(\x08\"\x13\n\x11\x45xportNodeRequest\"!\n\x12\x45xportNodeResponse\x12\x0b\n\x03url\x18\x01 \x01(\t\"m\n\x0fSignerRejection\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\'\n\x07request\x18\x02 \x01(\x0b\x32\x16.greenlight.HsmRequest\x12\x13\n\x0bgit_version\x18\x03 \x01(\t\x12\x0f\n\x07node_id\x18\x04 \x01(\x0c\"s\n\x11PairDeviceRequest\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x0b\n\x03\x63sr\x18\x02 \x01(\x0c\x12\x13\n\x0b\x64\x65vice_name\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x14\n\x0crestrictions\x18\x05 \x01(\t\"m\n\x12PairDeviceResponse\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65vice_cert\x18\x02 \x01(\t\x12\x12\n\ndevice_key\x18\x03 \x01(\t\x12\x0c\n\x04rune\x18\x04 \x01(\t\x12\r\n\x05\x63reds\x18\x05 \x01(\x0c\"*\n\x15GetPairingDataRequest\x12\x11\n\tdevice_id\x18\x01 \x01(\t\"x\n\x16GetPairingDataResponse\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x0b\n\x03\x63sr\x18\x02 \x01(\x0c\x12\x13\n\x0b\x64\x65vice_name\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x14\n\x0crestrictions\x18\x05 \x01(\t\"\x93\x01\n\x15\x41pprovePairingRequest\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x65vice_name\x18\x03 \x01(\t\x12\x14\n\x0crestrictions\x18\x04 \x01(\t\x12\x0b\n\x03sig\x18\x05 \x01(\x0c\x12\x0e\n\x06pubkey\x18\x06 \x01(\x0c\x12\x0c\n\x04rune\x18\x07 \x01(\t\"J\n\x16\x41pprovePairingResponse\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x0c\x12\x0c\n\x04rune\x18\x03 \x01(\t\"k\n\rSignerRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12;\n\x0f\x61pprove_pairing\x18\x02 \x01(\x0b\x32 .scheduler.ApprovePairingRequestH\x00\x42\t\n\x07request\"\x92\x01\n\x0eSignerResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\r\x12\"\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\x11.greenlight.EmptyH\x00\x12<\n\x0f\x61pprove_pairing\x18\x03 \x01(\x0b\x32!.scheduler.ApprovePairingResponseH\x00\x42\n\n\x08response*+\n\x0e\x43hallengeScope\x12\x0c\n\x08REGISTER\x10\x00\x12\x0b\n\x07RECOVER\x10\x01\x32\xd6\x08\n\tScheduler\x12M\n\x08Register\x12\x1e.scheduler.RegistrationRequest\x1a\x1f.scheduler.RegistrationResponse\"\x00\x12\x44\n\x07Recover\x12\x1a.scheduler.RecoveryRequest\x1a\x1b.scheduler.RecoveryResponse\"\x00\x12K\n\x0cGetChallenge\x12\x1b.scheduler.ChallengeRequest\x1a\x1c.scheduler.ChallengeResponse\"\x00\x12\x45\n\x08Schedule\x12\x1a.scheduler.ScheduleRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12H\n\x0bGetNodeInfo\x12\x1a.scheduler.NodeInfoRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12G\n\x0cMaybeUpgrade\x12\x19.scheduler.UpgradeRequest\x1a\x1a.scheduler.UpgradeResponse\"\x00\x12Z\n\x0fListInviteCodes\x12!.scheduler.ListInviteCodesRequest\x1a\".scheduler.ListInviteCodesResponse\"\x00\x12K\n\nExportNode\x12\x1c.scheduler.ExportNodeRequest\x1a\x1d.scheduler.ExportNodeResponse\"\x00\x12\x63\n\x12\x41\x64\x64OutgoingWebhook\x12$.scheduler.AddOutgoingWebhookRequest\x1a%.scheduler.AddOutgoingWebhookResponse\"\x00\x12i\n\x14ListOutgoingWebhooks\x12&.scheduler.ListOutgoingWebhooksRequest\x1a\'.scheduler.ListOutgoingWebhooksResponse\"\x00\x12O\n\x0e\x44\x65leteWebhooks\x12(.scheduler.DeleteOutgoingWebhooksRequest\x1a\x11.greenlight.Empty\"\x00\x12p\n\x1bRotateOutgoingWebhookSecret\x12-.scheduler.RotateOutgoingWebhookSecretRequest\x1a .scheduler.WebhookSecretResponse\"\x00\x12Q\n\x14SignerRequestsStream\x12\x19.scheduler.SignerResponse\x1a\x18.scheduler.SignerRequest\"\x00(\x01\x30\x01\x32Q\n\x05\x44\x65\x62ug\x12H\n\x15ReportSignerRejection\x12\x1a.scheduler.SignerRejection\x1a\x11.greenlight.Empty\"\x00\x32\xf8\x01\n\x07Pairing\x12K\n\nPairDevice\x12\x1c.scheduler.PairDeviceRequest\x1a\x1d.scheduler.PairDeviceResponse\"\x00\x12W\n\x0eGetPairingData\x12 .scheduler.GetPairingDataRequest\x1a!.scheduler.GetPairingDataResponse\"\x00\x12G\n\x0e\x41pprovePairing\x12 .scheduler.ApprovePairingRequest\x1a\x11.greenlight.Empty\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -24,8 +24,8 @@ DESCRIPTOR._loaded_options = None _globals['_UPGRADEREQUEST'].fields_by_name['initmsg']._loaded_options = None _globals['_UPGRADEREQUEST'].fields_by_name['initmsg']._serialized_options = b'\030\001' - _globals['_CHALLENGESCOPE']._serialized_start=2696 - _globals['_CHALLENGESCOPE']._serialized_end=2739 + _globals['_CHALLENGESCOPE']._serialized_start=2713 + _globals['_CHALLENGESCOPE']._serialized_end=2756 _globals['_ADDOUTGOINGWEBHOOKREQUEST']._serialized_start=66 _globals['_ADDOUTGOINGWEBHOOKREQUEST']._serialized_end=123 _globals['_ADDOUTGOINGWEBHOOKRESPONSE']._serialized_start=125 @@ -77,27 +77,27 @@ _globals['_EXPORTNODERESPONSE']._serialized_start=1689 _globals['_EXPORTNODERESPONSE']._serialized_end=1722 _globals['_SIGNERREJECTION']._serialized_start=1724 - _globals['_SIGNERREJECTION']._serialized_end=1816 - _globals['_PAIRDEVICEREQUEST']._serialized_start=1818 - _globals['_PAIRDEVICEREQUEST']._serialized_end=1933 - _globals['_PAIRDEVICERESPONSE']._serialized_start=1935 - _globals['_PAIRDEVICERESPONSE']._serialized_end=2044 - _globals['_GETPAIRINGDATAREQUEST']._serialized_start=2046 - _globals['_GETPAIRINGDATAREQUEST']._serialized_end=2088 - _globals['_GETPAIRINGDATARESPONSE']._serialized_start=2090 - _globals['_GETPAIRINGDATARESPONSE']._serialized_end=2210 - _globals['_APPROVEPAIRINGREQUEST']._serialized_start=2213 - _globals['_APPROVEPAIRINGREQUEST']._serialized_end=2360 - _globals['_APPROVEPAIRINGRESPONSE']._serialized_start=2362 - _globals['_APPROVEPAIRINGRESPONSE']._serialized_end=2436 - _globals['_SIGNERREQUEST']._serialized_start=2438 - _globals['_SIGNERREQUEST']._serialized_end=2545 - _globals['_SIGNERRESPONSE']._serialized_start=2548 - _globals['_SIGNERRESPONSE']._serialized_end=2694 - _globals['_SCHEDULER']._serialized_start=2742 - _globals['_SCHEDULER']._serialized_end=3852 - _globals['_DEBUG']._serialized_start=3854 - _globals['_DEBUG']._serialized_end=3935 - _globals['_PAIRING']._serialized_start=3938 - _globals['_PAIRING']._serialized_end=4186 + _globals['_SIGNERREJECTION']._serialized_end=1833 + _globals['_PAIRDEVICEREQUEST']._serialized_start=1835 + _globals['_PAIRDEVICEREQUEST']._serialized_end=1950 + _globals['_PAIRDEVICERESPONSE']._serialized_start=1952 + _globals['_PAIRDEVICERESPONSE']._serialized_end=2061 + _globals['_GETPAIRINGDATAREQUEST']._serialized_start=2063 + _globals['_GETPAIRINGDATAREQUEST']._serialized_end=2105 + _globals['_GETPAIRINGDATARESPONSE']._serialized_start=2107 + _globals['_GETPAIRINGDATARESPONSE']._serialized_end=2227 + _globals['_APPROVEPAIRINGREQUEST']._serialized_start=2230 + _globals['_APPROVEPAIRINGREQUEST']._serialized_end=2377 + _globals['_APPROVEPAIRINGRESPONSE']._serialized_start=2379 + _globals['_APPROVEPAIRINGRESPONSE']._serialized_end=2453 + _globals['_SIGNERREQUEST']._serialized_start=2455 + _globals['_SIGNERREQUEST']._serialized_end=2562 + _globals['_SIGNERRESPONSE']._serialized_start=2565 + _globals['_SIGNERRESPONSE']._serialized_end=2711 + _globals['_SCHEDULER']._serialized_start=2759 + _globals['_SCHEDULER']._serialized_end=3869 + _globals['_DEBUG']._serialized_start=3871 + _globals['_DEBUG']._serialized_end=3952 + _globals['_PAIRING']._serialized_start=3955 + _globals['_PAIRING']._serialized_end=4203 # @@protoc_insertion_point(module_scope) diff --git a/libs/gl-client-py/glclient/scheduler_pb2.pyi b/libs/gl-client-py/glclient/scheduler_pb2.pyi index 49734f515..aac68c5d0 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2.pyi +++ b/libs/gl-client-py/glclient/scheduler_pb2.pyi @@ -607,9 +607,11 @@ class SignerRejection(google.protobuf.message.Message): MSG_FIELD_NUMBER: builtins.int REQUEST_FIELD_NUMBER: builtins.int GIT_VERSION_FIELD_NUMBER: builtins.int + NODE_ID_FIELD_NUMBER: builtins.int msg: builtins.str """A human-readable description of what went wrong""" git_version: builtins.str + node_id: builtins.bytes @property def request(self) -> glclient.greenlight_pb2.HsmRequest: ... def __init__( @@ -618,9 +620,10 @@ class SignerRejection(google.protobuf.message.Message): msg: builtins.str = ..., request: glclient.greenlight_pb2.HsmRequest | None = ..., git_version: builtins.str = ..., + node_id: builtins.bytes = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["request", b"request"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["git_version", b"git_version", "msg", b"msg", "request", b"request"]) -> None: ... + def ClearField(self, field_name: typing.Literal["git_version", b"git_version", "msg", b"msg", "node_id", b"node_id", "request", b"request"]) -> None: ... global___SignerRejection = SignerRejection