lexe_api_core/def.rs
1//! # API Definitions
2//!
3//! This module, as closely as possible, defines the various APIs exposed by
4//! different services to different clients. Although we do not have
5//! compile-time guarantees that the services exposed exactly match the
6//! definitions below, it is straightforward to compare the Axum routers and
7//! handlers with the definitions below to ensure consistency.
8//!
9//! ## Guidelines
10//!
11//! All API requests and responses should return structs for upgradeability,
12//! e.g. [`UserPkStruct`] instead of [`UserPk`].
13//!
14//! If an API method takes or returns nothing, make the type [`Empty`] and NOT
15//! `()` (unit type). Using `()` makes it impossible to add optional fields in a
16//! backwards-compatible way.
17//!
18//! Each endpoint should be documented with:
19//! - 1) HTTP method e.g. `GET`
20//! - 2) Endpoint e.g. `/v1/file`
21//! - 3) Data used to make the request e.g. `VfsFileId`
22//! - 4) The return type e.g. `MaybeVfsFile`
23//!
24//! The methods below should resemble the data actually sent across the wire.
25//!
26//! [`Empty`]: crate::types::Empty
27//! [`UserPk`]: lexe_common::api::user::UserPk
28//! [`UserPkStruct`]: lexe_common::api::user::UserPkStruct
29
30#![deny(missing_docs)]
31// We don't export our traits currently so auto trait stability is not relevant.
32#![allow(async_fn_in_trait)]
33
34use std::collections::HashSet;
35
36use async_trait::async_trait;
37use bytes::Bytes;
38use lexe_common::api::{
39 MegaId,
40 auth::{
41 BearerAuthRequestWire, BearerAuthResponse, BearerAuthToken,
42 UserSignupRequestWire, UserSignupRequestWireV1,
43 },
44 fiat_rates::FiatRates,
45 models::{
46 SignMsgRequest, SignMsgResponse, Status, VerifyMsgRequest,
47 VerifyMsgResponse,
48 },
49 provision::NodeProvisionRequest,
50 revocable_clients::{
51 CreateRevocableClientRequest, CreateRevocableClientResponse,
52 GetRevocableClients, RevocableClients, UpdateClientRequest,
53 UpdateClientResponse,
54 },
55 test_event::TestEventOp,
56 user::{GetNewScidsRequest, MaybeScid, MaybeUser, Scids, UserPk},
57 version::{CurrentEnclaves, EnclavesToProvision, NodeEnclave},
58};
59#[cfg(doc)]
60use lexe_common::{
61 api::MegaIdStruct,
62 api::models::BroadcastedTxInfo,
63 api::user::NodePkStruct,
64 api::user::{UserPkSet, UserPkStruct},
65 api::version::MeasurementStruct,
66};
67use lexe_crypto::ed25519;
68use lexe_enclave::enclave::Measurement;
69use lightning::events::Event;
70
71#[cfg(doc)]
72use crate::types::payments::{PaymentCreatedIndex, PaymentId};
73use crate::{
74 error::{
75 BackendApiError, GatewayApiError, LspApiError, MegaApiError,
76 NodeApiError, RunnerApiError,
77 },
78 models::{
79 command::{
80 BackupInfo, ClaimGeneratedHumanBitcoinAddress, CloseChannelRequest,
81 CreateInvoiceRequest, CreateInvoiceResponse, CreateOfferRequest,
82 CreateOfferResponse, DebugInfo, EnclavesToProvisionRequest,
83 GetAddressResponse, GetGeneratedUsernameResponse, GetNewPayments,
84 GetUpdatedPaymentMetadata, GetUpdatedPayments, HumanBitcoinAddress,
85 ListChannelsResponse, NodeInfo, NodeInfoV1, OpenChannelRequest,
86 OpenChannelResponse, PayInvoiceRequest, PayInvoiceResponse,
87 PayOfferRequest, PayOfferResponse, PayOnchainRequest,
88 PayOnchainResponse, PaymentCreatedIndexStruct,
89 PaymentCreatedIndexes, PaymentIdStruct,
90 PreflightCloseChannelRequest, PreflightCloseChannelResponse,
91 PreflightOpenChannelRequest, PreflightOpenChannelResponse,
92 PreflightPayInvoiceRequest, PreflightPayInvoiceResponse,
93 PreflightPayOfferRequest, PreflightPayOfferResponse,
94 PreflightPayOnchainRequest, PreflightPayOnchainResponse,
95 ResyncRequest, SetupGDrive, UpdateHumanBitcoinAddress,
96 UpdatePaymentNote, VecPaymentId,
97 },
98 nwc::{
99 CreateNwcClientRequest, CreateNwcClientResponse, DbNwcClient,
100 DbNwcClientFields, GetNwcClients, ListNwcClientResponse,
101 NostrPkStruct, NostrSignedEvent, NwcRequest,
102 UpdateNwcClientRequest, UpdateNwcClientResponse, VecDbNwcClient,
103 },
104 runner::{
105 MegaNodeApiUserEvictRequest, MegaNodeApiUserRunRequest,
106 MegaNodeApiUserRunResponse, UserFinishedRequest,
107 UserLeaseRenewalRequest,
108 },
109 },
110 types::{
111 Empty,
112 lnurl::{
113 LnurlCallbackRequest, LnurlCallbackResponse, LnurlError,
114 LnurlPayRequestWire,
115 },
116 payments::{
117 DbPaymentMetadata, DbPaymentV1, DbPaymentV2, MaybeBasicPaymentV2,
118 MaybeDbPaymentMetadata, MaybeDbPaymentV1, MaybeDbPaymentV2,
119 VecBasicPaymentV1, VecBasicPaymentV2, VecDbPaymentMetadata,
120 VecDbPaymentV1, VecDbPaymentV2,
121 },
122 ports::MegaPorts,
123 sealed_seed::{MaybeSealedSeed, SealedSeed, SealedSeedId},
124 username::UsernameStruct,
125 },
126 vfs::{
127 MaybeVfsFile, VecVfsFile, VfsDirectory, VfsDirectoryList, VfsFile,
128 VfsFileId,
129 },
130};
131
132// TODO(max): To make clear that only upgradeable structs are being serialized,
133// these methods should take e.g. `&UserPkStruct` instead of `UserPk`.
134
135/// Defines the api that the backend exposes to the app (via the gateway).
136pub trait AppBackendApi {
137 /// POST /app/v2/signup [`ed25519::Signed<UserSignupRequestWire>`] ->
138 /// [`Empty`]
139 async fn signup_v2(
140 &self,
141 signed_req: &ed25519::Signed<&UserSignupRequestWire>,
142 ) -> Result<Empty, BackendApiError>;
143
144 /// POST /app/v1/signup [`ed25519::Signed<UserSignupRequestWireV1>`] ->
145 /// [`Empty`]
146 // TODO(phlip9): remove once all installed mobile clients above `app-v0.7.6`
147 #[deprecated = "Use the `signup_v2` API instead"]
148 async fn signup_v1(
149 &self,
150 signed_req: &ed25519::Signed<&UserSignupRequestWireV1>,
151 ) -> Result<Empty, BackendApiError>;
152
153 /// Query which node enclaves the user needs to provision to.
154 ///
155 /// POST /app/v1/enclaves_to_provision
156 /// [`EnclavesToProvisionRequest`] -> [`EnclavesToProvision`]
157 async fn enclaves_to_provision(
158 &self,
159 req: &EnclavesToProvisionRequest,
160 auth: BearerAuthToken,
161 ) -> Result<EnclavesToProvision, BackendApiError>;
162}
163
164/// Defines the api that the gateway directly exposes to the app.
165pub trait AppGatewayApi {
166 /// GET /app/v1/fiat_rates [`Empty`] -> [`FiatRates`]
167 async fn get_fiat_rates(&self) -> Result<FiatRates, GatewayApiError>;
168
169 /// Get the measurement and semver version of the latest node release.
170 ///
171 /// GET /app/v1/latest_release [`Empty`] -> [`NodeEnclave`]
172 #[deprecated(note = "since app-v0.8.1: Use current_releases() instead")]
173 async fn latest_release(&self) -> Result<NodeEnclave, GatewayApiError>;
174
175 /// Get the measurements, enclave machine id and versions of all
176 /// current node enclaves.
177 ///
178 /// GET /app/v1/current_releases [`Empty`] -> [`CurrentEnclaves`]
179 #[deprecated(note = "since app-v0.8.8: Use current_enclaves() instead")]
180 async fn current_releases(
181 &self,
182 ) -> Result<CurrentEnclaves, GatewayApiError>;
183
184 /// Get the measurements, enclave machine id and versions of all
185 /// current node enclaves.
186 ///
187 /// GET /app/v1/current_enclaves [`Empty`] -> [`CurrentEnclaves`]
188 async fn current_enclaves(
189 &self,
190 ) -> Result<CurrentEnclaves, GatewayApiError>;
191}
192
193/// Defines the api that the node exposes to the app during provisioning.
194pub trait AppNodeProvisionApi {
195 /// Provision a node with the given [`Measurement`]. The provisioning node's
196 /// remote attestation will be checked against the given [`Measurement`].
197 ///
198 /// POST /app/provision [`NodeProvisionRequest`] -> [`Empty`]
199 async fn provision(
200 &self,
201 measurement: Measurement,
202 data: NodeProvisionRequest,
203 ) -> Result<Empty, NodeApiError>;
204}
205
206/// Defines the api that the node exposes to the app during normal operation.
207pub trait AppNodeRunApi {
208 /// GET /app/v2/node_info [`Empty`] -> [`NodeInfo`]
209 async fn node_info(&self) -> Result<NodeInfo, NodeApiError>;
210
211 /// GET /app/node_info [`Empty`] -> [`NodeInfoV1`]
212 #[deprecated(note = "since node-v0.9.4: Use node_info instead")]
213 async fn node_info_v1(&self) -> Result<NodeInfoV1, NodeApiError>;
214
215 /// GET /app/debug_info [`Empty`] -> [`DebugInfo`]
216 async fn debug_info(&self) -> Result<DebugInfo, NodeApiError>;
217
218 /// GET /app/list_channels [`Empty`] -> [`ListChannelsResponse`]
219 async fn list_channels(&self)
220 -> Result<ListChannelsResponse, NodeApiError>;
221
222 /// POST /app/sign_message [`SignMsgRequest`] -> [`SignMsgResponse`]
223 ///
224 /// Introduced in `node-v0.6.5`.
225 async fn sign_message(
226 &self,
227 req: SignMsgRequest,
228 ) -> Result<SignMsgResponse, NodeApiError>;
229
230 /// POST /app/verify_message [`VerifyMsgRequest`] -> [`VerifyMsgResponse`]
231 ///
232 /// Introduced in `node-v0.6.5`.
233 async fn verify_message(
234 &self,
235 req: VerifyMsgRequest,
236 ) -> Result<VerifyMsgResponse, NodeApiError>;
237
238 /// POST /app/open_channel [`OpenChannelRequest`] -> [`OpenChannelResponse`]
239 ///
240 /// Opens a channel to the LSP.
241 async fn open_channel(
242 &self,
243 req: OpenChannelRequest,
244 ) -> Result<OpenChannelResponse, NodeApiError>;
245
246 /// POST /app/preflight_open_channel [`PreflightOpenChannelRequest`]
247 /// -> [`PreflightOpenChannelResponse`]
248 ///
249 /// Estimate on-chain fees required for an [`open_channel`] to the LSP.
250 ///
251 /// [`open_channel`]: AppNodeRunApi::open_channel
252 async fn preflight_open_channel(
253 &self,
254 req: PreflightOpenChannelRequest,
255 ) -> Result<PreflightOpenChannelResponse, NodeApiError>;
256
257 /// POST /app/close_channel [`CloseChannelRequest`] -> [`Empty`]
258 ///
259 /// Closes a channel to the LSP.
260 async fn close_channel(
261 &self,
262 req: CloseChannelRequest,
263 ) -> Result<Empty, NodeApiError>;
264
265 /// POST /app/preflight_close_channel [`PreflightCloseChannelRequest`]
266 /// -> [`PreflightCloseChannelResponse`]
267 ///
268 /// Estimate the on-chain fees required for a [`close_channel`].
269 ///
270 /// [`close_channel`]: AppNodeRunApi::close_channel
271 async fn preflight_close_channel(
272 &self,
273 req: PreflightCloseChannelRequest,
274 ) -> Result<PreflightCloseChannelResponse, NodeApiError>;
275
276 /// POST /app/create_invoice [`CreateInvoiceRequest`]
277 /// -> [`CreateInvoiceResponse`]
278 async fn create_invoice(
279 &self,
280 req: CreateInvoiceRequest,
281 ) -> Result<CreateInvoiceResponse, NodeApiError>;
282
283 /// POST /app/pay_invoice [`PayInvoiceRequest`] -> [`PayInvoiceResponse`]
284 async fn pay_invoice(
285 &self,
286 req: PayInvoiceRequest,
287 ) -> Result<PayInvoiceResponse, NodeApiError>;
288
289 /// POST /app/preflight_pay_invoice [`PreflightPayInvoiceRequest`]
290 /// -> [`PreflightPayInvoiceResponse`]
291 ///
292 /// This endpoint lets the app ask its node to "pre-flight" a BOLT11 invoice
293 /// payment without going through with the actual payment. We verify as much
294 /// as we can, find a route, and get the fee estimates.
295 async fn preflight_pay_invoice(
296 &self,
297 req: PreflightPayInvoiceRequest,
298 ) -> Result<PreflightPayInvoiceResponse, NodeApiError>;
299
300 /// POST /app/create_offer [`CreateOfferRequest`] -> [`CreateOfferResponse`]
301 ///
302 /// Create a new Lightning offer (BOLT12).
303 //
304 // Added in `node-v0.7.3`.
305 async fn create_offer(
306 &self,
307 req: CreateOfferRequest,
308 ) -> Result<CreateOfferResponse, NodeApiError>;
309
310 /// POST /app/pay_offer [`PayOfferRequest`] -> [`PayOfferResponse`]
311 ///
312 /// Pay a Lightning offer (BOLT12).
313 //
314 // Added in `node-v0.7.4`.
315 async fn pay_offer(
316 &self,
317 req: PayOfferRequest,
318 ) -> Result<PayOfferResponse, NodeApiError>;
319
320 /// POST /app/preflight_pay_offer [`PreflightPayOfferRequest`]
321 /// -> [`PreflightPayOfferResponse`]
322 ///
323 /// This endpoint lets the app ask its node to "pre-flight" a Lightning
324 /// offer (BOLT12) payment without going through with the actual payment. We
325 /// verify as much as we can, find a route, and get the fee estimates.
326 //
327 // Added in `node-v0.7.4`.
328 async fn preflight_pay_offer(
329 &self,
330 req: PreflightPayOfferRequest,
331 ) -> Result<PreflightPayOfferResponse, NodeApiError>;
332
333 // TODO(phlip9): BOLT12: /app/request_refund
334
335 /// POST /app/get_address [`Empty`] -> [`GetAddressResponse`]
336 ///
337 /// Returns an address which can be used to receive funds. It is unused
338 /// unless there is an incoming tx and BDK hasn't detected it yet.
339 async fn get_address(&self) -> Result<GetAddressResponse, NodeApiError>;
340
341 /// POST /app/pay_onchain [`PayOnchainRequest`] -> [`PayOnchainResponse`]
342 ///
343 /// Pay bitcoin onchain. If the address is valid and we have sufficient
344 /// onchain funds, this will broadcast a new transaction to the bitcoin
345 /// mempool.
346 async fn pay_onchain(
347 &self,
348 req: PayOnchainRequest,
349 ) -> Result<PayOnchainResponse, NodeApiError>;
350
351 /// POST /app/preflight_pay_onchain [`PreflightPayOnchainRequest`]
352 /// -> [`PreflightPayOnchainResponse`]
353 ///
354 /// Returns estimated network fees for a potential onchain payment.
355 async fn preflight_pay_onchain(
356 &self,
357 req: PreflightPayOnchainRequest,
358 ) -> Result<PreflightPayOnchainResponse, NodeApiError>;
359
360 /// GET /app/v1/payments/id [`PaymentIdStruct`] -> [`MaybeBasicPaymentV2`]
361 //
362 // Added in `node-v0.8.10`.
363 async fn get_payment_by_id(
364 &self,
365 req: PaymentIdStruct,
366 ) -> Result<MaybeBasicPaymentV2, NodeApiError>;
367
368 /// POST /app/payments/indexes [`PaymentCreatedIndexes`]
369 /// -> [`VecDbPaymentV1`]
370 ///
371 /// Fetch a batch of payments by their [`PaymentCreatedIndex`]s. This is
372 /// typically used by a mobile client to poll for updates on payments
373 /// which it currently has stored locally as "pending"; the intention is
374 /// to check if any of these payments have been updated.
375 //
376 // We use POST because there may be a lot of idxs, which might be too large
377 // to fit inside query parameters.
378 #[deprecated(note = "since app-v0.8.9+29 and sdk-sidecar-v0.3.1: \
379 Use get_payments_by_ids instead")]
380 async fn get_payments_by_indexes(
381 &self,
382 req: PaymentCreatedIndexes,
383 ) -> Result<VecBasicPaymentV1, NodeApiError>;
384
385 /// GET /app/payments/new [`GetNewPayments`] -> [`VecBasicPaymentV1`]
386 #[deprecated(note = "since app-v0.8.9+29 and sdk-sidecar-v0.3.1: \
387 Use get_updated_payments instead")]
388 async fn get_new_payments(
389 &self,
390 req: GetNewPayments,
391 ) -> Result<VecBasicPaymentV1, NodeApiError>;
392
393 /// GET /app/payments/updated [`GetUpdatedPayments`]
394 /// -> [`VecBasicPaymentV2`]
395 async fn get_updated_payments(
396 &self,
397 req: GetUpdatedPayments,
398 ) -> Result<VecBasicPaymentV2, NodeApiError>;
399
400 /// PUT /app/payments/note [`UpdatePaymentNote`] -> [`Empty`]
401 async fn update_payment_note(
402 &self,
403 req: UpdatePaymentNote,
404 ) -> Result<Empty, NodeApiError>;
405
406 /// Lists all revocable clients.
407 ///
408 /// GET /app/clients [`GetRevocableClients`] -> [`RevocableClients`]
409 // Added in `node-0.7.9`
410 async fn get_revocable_clients(
411 &self,
412 req: GetRevocableClients,
413 ) -> Result<RevocableClients, NodeApiError>;
414
415 /// Creates a new revocable client. Returns the newly issued client cert.
416 ///
417 /// POST /app/clients [`CreateRevocableClientRequest`]
418 /// -> [`CreateRevocableClientResponse`]
419 // Added in `node-0.7.9`
420 async fn create_revocable_client(
421 &self,
422 req: CreateRevocableClientRequest,
423 ) -> Result<CreateRevocableClientResponse, NodeApiError>;
424
425 /// Updates this revocable client. Returns the updated client.
426 ///
427 /// PUT /app/clients [`UpdateClientRequest`] -> [`UpdateClientResponse`]
428 // Added in `node-0.7.9`
429 async fn update_revocable_client(
430 &self,
431 req: UpdateClientRequest,
432 ) -> Result<UpdateClientResponse, NodeApiError>;
433
434 /// List all broadcasted transactions.
435 ///
436 /// GET /app/list_broadcasted_txs [`Empty`] -> [`Vec<BroadcastedTxInfo>`]
437 async fn list_broadcasted_txs(
438 &self,
439 ) -> Result<serde_json::Value, NodeApiError>;
440
441 /// Get the current status of Node backup.
442 ///
443 /// GET /app/backup [`Empty`] -> [`BackupInfo`]
444 async fn backup_info(&self) -> Result<BackupInfo, NodeApiError>;
445
446 /// Setup GDrive backup.
447 ///
448 /// POST /app/backup/gdrive [`SetupGDrive`] -> [`Empty`]
449 async fn setup_gdrive(
450 &self,
451 req: SetupGDrive,
452 ) -> Result<Empty, NodeApiError>;
453
454 /// Get current user's human Bitcoin address.
455 ///
456 /// GET /app/human_bitcoin_address [`Empty`] -> [`HumanBitcoinAddress`]
457 async fn get_human_bitcoin_address(
458 &self,
459 ) -> Result<HumanBitcoinAddress, NodeApiError>;
460
461 /// Update current user's human Bitcoin address.
462 ///
463 /// PUT /app/human_bitcoin_address [`UsernameStruct`] ->
464 /// [`HumanBitcoinAddress`]
465 async fn update_human_bitcoin_address(
466 &self,
467 req: UsernameStruct,
468 ) -> Result<HumanBitcoinAddress, NodeApiError>;
469
470 /// GET /app/payment_address [`Empty`] -> [`HumanBitcoinAddress`]
471 #[deprecated(note = "since app-v0.9.3 and sdk-sidecar-v0.4.2: \
472 Use get_human_bitcoin_address instead")]
473 async fn get_payment_address(
474 &self,
475 ) -> Result<HumanBitcoinAddress, NodeApiError> {
476 self.get_human_bitcoin_address().await
477 }
478
479 /// PUT /app/payment_address [`UsernameStruct`] -> [`HumanBitcoinAddress`]
480 #[deprecated(note = "since app-v0.9.3 and sdk-sidecar-v0.4.2: \
481 Use update_human_bitcoin_address instead")]
482 async fn update_payment_address(
483 &self,
484 req: UsernameStruct,
485 ) -> Result<HumanBitcoinAddress, NodeApiError> {
486 self.update_human_bitcoin_address(req).await
487 }
488
489 /// List NWC clients for the current user.
490 /// Returns client info without sensitive data (no connection strings).
491 ///
492 /// GET /app/nwc_clients [`Empty`] -> [`ListNwcClientResponse`]
493 async fn list_nwc_clients(
494 &self,
495 ) -> Result<ListNwcClientResponse, NodeApiError>;
496
497 /// Create a new NWC client.
498 /// Generates new keys and returns the connection string.
499 ///
500 /// POST /app/nwc_clients [`CreateNwcClientRequest`]
501 /// -> [`CreateNwcClientResponse`]
502 async fn create_nwc_client(
503 &self,
504 req: CreateNwcClientRequest,
505 ) -> Result<CreateNwcClientResponse, NodeApiError>;
506
507 /// Update an existing NWC client's label.
508 ///
509 /// PUT /app/nwc_clients [`UpdateNwcClientRequest`]
510 /// -> [`UpdateNwcClientResponse`]
511 async fn update_nwc_client(
512 &self,
513 req: UpdateNwcClientRequest,
514 ) -> Result<UpdateNwcClientResponse, NodeApiError>;
515
516 /// Delete an NWC client given its nostr client public key.
517 ///
518 /// DELETE /app/nwc_clients [`NostrPkStruct`] -> [`Empty`]
519 async fn delete_nwc_client(
520 &self,
521 req: NostrPkStruct,
522 ) -> Result<Empty, NodeApiError>;
523}
524
525/// The bearer auth API exposed by the backend (sometimes via the gateway) to
526/// various consumers. This trait is defined separately from the
527/// usual `ConsumerServiceApi` traits because `BearerAuthenticator` needs to
528/// abstract over a generic implementor of [`BearerAuthBackendApi`].
529#[async_trait]
530pub trait BearerAuthBackendApi {
531 /// POST /CONSUMER/bearer_auth [`ed25519::Signed<BearerAuthRequest>`]
532 /// -> [`BearerAuthResponse`]
533 ///
534 /// Valid values for `CONSUMER` are: "app", "node" and "lsp".
535 async fn bearer_auth(
536 &self,
537 signed_req: &ed25519::Signed<&BearerAuthRequestWire>,
538 ) -> Result<BearerAuthResponse, BackendApiError>;
539}
540
541/// Defines the API the mega node exposes to the Lexe operators.
542///
543/// NOTE: For performance, this API does not use TLS! This API should only
544/// contain methods for limited operational and lifecycle management endpoints.
545pub trait LexeMegaApi {
546 /// POST /lexe/run_user [`MegaNodeApiUserRunRequest`]
547 /// -> [`MegaNodeApiUserRunResponse`]
548 async fn run_user(
549 &self,
550 req: MegaNodeApiUserRunRequest,
551 ) -> Result<MegaNodeApiUserRunResponse, MegaApiError>;
552
553 /// POST /lexe/evict_user [`MegaNodeApiUserEvictRequest`] -> [`Empty`]
554 async fn evict_user(
555 &self,
556 req: MegaNodeApiUserEvictRequest,
557 ) -> Result<Empty, MegaApiError>;
558
559 /// GET /lexe/status [`MegaIdStruct`] -> [`Status`]
560 async fn status_mega(
561 &self,
562 mega_id: MegaId,
563 ) -> Result<Status, MegaApiError>;
564
565 /// POST /lexe/shutdown [`Empty`] -> [`Empty`]
566 async fn shutdown_mega(&self) -> Result<Empty, MegaApiError>;
567}
568
569/// Defines the API the node exposes to the Lexe operators at run time.
570///
571/// NOTE: For performance, this API does not use TLS! This API should only
572/// contain methods for limited operational and lifecycle management endpoints.
573pub trait LexeNodeRunApi {
574 /// GET /lexe/status [`UserPkStruct`] -> [`Status`]
575 async fn status_run(&self, user_pk: UserPk)
576 -> Result<Status, NodeApiError>;
577
578 /// POST /lexe/resync [`ResyncRequest`] -> [`Empty`]
579 ///
580 /// Triggers an immediate resync of BDK and LDK. Optionally full sync the
581 /// BDK wallet. Returns only once sync has either completed or timed out.
582 async fn resync(&self, req: ResyncRequest) -> Result<Empty, NodeApiError>;
583
584 /// POST /lexe/test_event [`TestEventOp`] -> [`Empty`]
585 ///
586 /// Calls the corresponding `TestEventReceiver` method.
587 /// This endpoint can only be called by one caller at any one time.
588 /// Does nothing and returns an error if called in prod.
589 async fn test_event(&self, op: &TestEventOp)
590 -> Result<Empty, NodeApiError>;
591
592 /// GET /lexe/shutdown [`UserPkStruct`] -> [`Empty`]
593 ///
594 /// Not to be confused with [`LexeMegaApi::shutdown_mega`].
595 async fn shutdown_run(
596 &self,
597 user_pk: UserPk,
598 ) -> Result<Empty, NodeApiError>;
599
600 /// POST /lexe/create_invoice [`CreateInvoiceRequest`] ->
601 /// [`CreateInvoiceResponse`]
602 async fn create_invoice(
603 &self,
604 req: CreateInvoiceRequest,
605 ) -> Result<CreateInvoiceResponse, NodeApiError>;
606
607 /// POST /lexe/nwc_request [`NwcRequest`] -> [`NostrSignedEvent`]
608 ///
609 /// Processes an encrypted NWC request from the nostr-bridge.
610 /// The node will decrypt the request, execute the command, and return
611 /// an encrypted response.
612 async fn nwc_request(
613 &self,
614 req: NwcRequest,
615 ) -> Result<NostrSignedEvent, NodeApiError>;
616}
617
618/// Defines the API the runner exposes to mega nodes.
619pub trait MegaRunnerApi {
620 /// POST /mega/ready [`MegaPorts`] -> [`Empty`]
621 ///
622 /// Indicates this mega node is initialized and ready to load user nodes.
623 async fn mega_ready(
624 &self,
625 ports: &MegaPorts,
626 ) -> Result<Empty, RunnerApiError>;
627
628 /// POST /mega/activity [`UserPkSet`] -> [`Empty`]
629 ///
630 /// Indicates the meganode received some activity from its users.
631 async fn activity(
632 &self,
633 user_pks: HashSet<UserPk>,
634 ) -> Result<Empty, RunnerApiError>;
635
636 /// POST /mega/user_finished [`UserFinishedRequest`] -> [`Empty`]
637 ///
638 /// Notifies the runner that a user has shut down,
639 /// and that the user's lease can be terminated.
640 async fn user_finished(
641 &self,
642 req: &UserFinishedRequest,
643 ) -> Result<Empty, RunnerApiError>;
644}
645
646/// Defines the api that the backend exposes to the node.
647pub trait NodeBackendApi {
648 // --- Unauthenticated --- //
649
650 /// GET /node/v1/user [`UserPkStruct`] -> [`MaybeUser`]
651 async fn get_user(
652 &self,
653 user_pk: UserPk,
654 ) -> Result<MaybeUser, BackendApiError>;
655
656 /// GET /node/v1/sealed_seed [`SealedSeedId`] -> [`MaybeSealedSeed`]
657 async fn get_sealed_seed(
658 &self,
659 data: &SealedSeedId,
660 ) -> Result<MaybeSealedSeed, BackendApiError>;
661
662 // --- Bearer authentication required --- //
663
664 /// PUT /node/v1/sealed_seed [`SealedSeed`] -> [`Empty`]
665 ///
666 /// Idempotent: does nothing if the [`SealedSeedId`] already exists.
667 async fn create_sealed_seed(
668 &self,
669 data: &SealedSeed,
670 auth: BearerAuthToken,
671 ) -> Result<Empty, BackendApiError>;
672
673 /// Delete all sealed seeds which have the given measurement and the user_pk
674 /// of the authenticated user.
675 ///
676 /// DELETE /node/v1/sealed_seed [`MeasurementStruct`] -> [`Empty`]
677 async fn delete_sealed_seeds(
678 &self,
679 measurement: Measurement,
680 auth: BearerAuthToken,
681 ) -> Result<Empty, BackendApiError>;
682
683 /// GET /node/v1/scids [`Empty`] -> [`Scids`]
684 async fn get_scids(
685 &self,
686 auth: BearerAuthToken,
687 ) -> Result<Scids, BackendApiError>;
688
689 /// GET /node/v1/scid [`Empty`] -> [`MaybeScid`]
690 // NOTE: Keep this def around until we can remove the backend handler.
691 #[deprecated(note = "since lsp-v0.7.3: Use multi scid version instead")]
692 async fn get_scid(
693 &self,
694 auth: BearerAuthToken,
695 ) -> Result<MaybeScid, BackendApiError>;
696
697 /// GET /node/v1/file [`VfsFileId`] -> [`MaybeVfsFile`]
698 #[deprecated(note = "since node-v0.8.5: Use get_file instead")]
699 async fn get_file_v1(
700 &self,
701 file_id: &VfsFileId,
702 auth: BearerAuthToken,
703 ) -> Result<MaybeVfsFile, BackendApiError>;
704
705 /// GET /node/v2/file [`VfsFileId`] -> [`Bytes`] ([`VfsFile::data`])
706 async fn get_file(
707 &self,
708 file_id: &VfsFileId,
709 token: BearerAuthToken,
710 ) -> Result<Bytes, BackendApiError>;
711
712 /// POST /node/v1/file [`VfsFile`] -> [`Empty`]
713 #[deprecated(note = "since node-v0.8.5: Use create_file instead")]
714 async fn create_file_v1(
715 &self,
716 file: &VfsFile,
717 auth: BearerAuthToken,
718 ) -> Result<Empty, BackendApiError>;
719
720 /// POST /node/v2/file [`VfsFileId`] (query) + [`Bytes`] (body) -> [`Empty`]
721 async fn create_file(
722 &self,
723 file_id: &VfsFileId,
724 data: bytes::Bytes,
725 auth: BearerAuthToken,
726 ) -> Result<Empty, BackendApiError>;
727
728 /// PUT /node/v1/file [`VfsFile`] -> [`Empty`]
729 #[deprecated(note = "since node-v0.8.5: Use upsert_file instead")]
730 async fn upsert_file_v1(
731 &self,
732 file: &VfsFile,
733 auth: BearerAuthToken,
734 ) -> Result<Empty, BackendApiError>;
735
736 /// PUT /node/v2/file [`VfsFileId`] (query) + [`Bytes`] (body) -> [`Empty`]
737 async fn upsert_file(
738 &self,
739 file_id: &VfsFileId,
740 data: bytes::Bytes,
741 auth: BearerAuthToken,
742 ) -> Result<Empty, BackendApiError>;
743
744 /// DELETE /node/v1/file [`VfsFileId`] -> [`Empty`]
745 ///
746 /// Returns [`Ok`] only if exactly one row was deleted.
747 async fn delete_file(
748 &self,
749 file_id: &VfsFileId,
750 auth: BearerAuthToken,
751 ) -> Result<Empty, BackendApiError>;
752
753 /// GET /node/v1/directory [`VfsDirectory`] -> [`VecVfsFile`]
754 #[deprecated(note = "since node-v0.8.5: Use list_directory instead")]
755 async fn get_directory_v1(
756 &self,
757 dir: &VfsDirectory,
758 auth: BearerAuthToken,
759 ) -> Result<VecVfsFile, BackendApiError>;
760
761 /// GET /node/v2/directory [`VfsDirectory`] -> [`VecVfsFile`]
762 async fn list_directory(
763 &self,
764 dir: &VfsDirectory,
765 auth: BearerAuthToken,
766 ) -> Result<VfsDirectoryList, BackendApiError>;
767
768 /// GET /node/v1/payments [`PaymentCreatedIndexStruct`]
769 /// -> [`MaybeDbPaymentV1`]
770 #[deprecated(note = "since node-v0.8.8: Use get_payment_by_index instead")]
771 async fn get_payment_by_index_v1(
772 &self,
773 req: PaymentCreatedIndexStruct,
774 auth: BearerAuthToken,
775 ) -> Result<MaybeDbPaymentV1, BackendApiError>;
776
777 /// POST /node/v1/payments [`DbPaymentV1`] -> [`Empty`]
778 #[deprecated(note = "since node-v0.8.10: Use upsert_payment instead")]
779 async fn create_payment(
780 &self,
781 payment: DbPaymentV1,
782 auth: BearerAuthToken,
783 ) -> Result<Empty, BackendApiError>;
784
785 /// PUT /node/v1/payments [`DbPaymentV1`] -> [`Empty`]
786 #[deprecated(note = "since node-v0.8.8: Use upsert_payment instead")]
787 async fn upsert_payment_v1(
788 &self,
789 payment: DbPaymentV1,
790 auth: BearerAuthToken,
791 ) -> Result<Empty, BackendApiError>;
792
793 /// PUT /node/v2/payments [`DbPaymentV2`] -> [`Empty`]
794 async fn upsert_payment(
795 &self,
796 payment: DbPaymentV2,
797 auth: BearerAuthToken,
798 ) -> Result<Empty, BackendApiError>;
799
800 /// GET /node/v1/payments/id [`PaymentIdStruct`] -> [`MaybeDbPaymentV1`]
801 #[deprecated(note = "since node-v0.8.10: Use get_payment_by_id instead")]
802 async fn get_payment_by_id_v1(
803 &self,
804 req: PaymentIdStruct,
805 auth: BearerAuthToken,
806 ) -> Result<MaybeDbPaymentV1, BackendApiError>;
807
808 /// GET /node/v2/payments/id [`PaymentIdStruct`] -> [`MaybeDbPaymentV2`]
809 async fn get_payment_by_id(
810 &self,
811 req: PaymentIdStruct,
812 auth: BearerAuthToken,
813 ) -> Result<MaybeDbPaymentV2, BackendApiError>;
814
815 /// GET /node/v1/payments/index [`PaymentCreatedIndexStruct`]
816 /// -> [`MaybeDbPaymentV1`]
817 #[deprecated(note = "since node-v0.8.10: Use get_payment_by_id instead")]
818 async fn get_payment_by_index(
819 &self,
820 req: PaymentCreatedIndexStruct,
821 auth: BearerAuthToken,
822 ) -> Result<MaybeDbPaymentV1, BackendApiError>;
823
824 /// PUT /node/v1/payments/batch [`VecDbPaymentV1`] -> [`Empty`]
825 ///
826 /// ACID endpoint for upserting a batch of payments.
827 #[deprecated(note = "since node-v0.8.8: Use upsert_payment_batch instead")]
828 async fn upsert_payment_batch_v1(
829 &self,
830 payments: VecDbPaymentV1,
831 auth: BearerAuthToken,
832 ) -> Result<Empty, BackendApiError>;
833
834 /// PUT /node/v2/payments/batch [`VecDbPaymentV2`] -> [`Empty`]
835 ///
836 /// ACID endpoint for upserting a batch of payments.
837 async fn upsert_payment_batch(
838 &self,
839 payments: VecDbPaymentV2,
840 auth: BearerAuthToken,
841 ) -> Result<Empty, BackendApiError>;
842
843 /// POST /node/v1/payments/indexes [`PaymentCreatedIndexes`]
844 /// -> [`VecDbPaymentV1`]
845 ///
846 /// Fetch a batch of payments by their [`PaymentCreatedIndex`]s. This is
847 /// typically used by a mobile client to poll for updates on payments
848 /// which it currently has stored locally as "pending"; the intention is
849 /// to check if any of these payments have been updated.
850 //
851 // We use POST because there may be a lot of idxs, which might be too large
852 // to fit inside query parameters.
853 #[deprecated(note = "since node-v0.8.10: Use get_payments_by_ids instead")]
854 async fn get_payments_by_indexes(
855 &self,
856 req: PaymentCreatedIndexes,
857 auth: BearerAuthToken,
858 ) -> Result<VecDbPaymentV1, BackendApiError>;
859
860 /// POST /node/v1/payments/ids [`VecPaymentId`]
861 /// -> [`VecDbPaymentV2`]
862 ///
863 /// Fetch a batch of payments by their [`PaymentId`]s.
864 //
865 // We use POST because there may be a lot of ids,
866 // which might be too large to fit inside query params.
867 async fn get_payments_by_ids(
868 &self,
869 req: VecPaymentId,
870 auth: BearerAuthToken,
871 ) -> Result<VecDbPaymentV2, BackendApiError>;
872
873 /// GET /node/v1/payments/new [`GetNewPayments`] -> [`VecDbPaymentV1`]
874 ///
875 /// Sync a batch of new payments to local storage, optionally starting from
876 /// a known [`PaymentCreatedIndex`] (exclusive).
877 /// Results are in ascending order, by `(created_at, payment_id)`.
878 /// See [`GetNewPayments`] for more info.
879 #[deprecated(note = "since app-v0.8.9+29 and sdk-sidecar-v0.3.1: \
880 Use get_updated_payments instead")]
881 // NOTE: This fn is used in the app->node handler for /app/payments/new, so
882 // the node->backend client code must remain until all app and sdk-sidecar
883 // clients have been updated.
884 async fn get_new_payments(
885 &self,
886 req: GetNewPayments,
887 auth: BearerAuthToken,
888 ) -> Result<VecDbPaymentV1, BackendApiError>;
889
890 /// GET /node/v1/payments/updated [`GetUpdatedPayments`]
891 /// -> [`VecDbPaymentV2`]
892 async fn get_updated_payments(
893 &self,
894 req: GetUpdatedPayments,
895 auth: BearerAuthToken,
896 ) -> Result<VecDbPaymentV2, BackendApiError>;
897
898 /// GET /node/v1/payments/pending -> [`VecDbPaymentV1`]
899 ///
900 /// Fetches all pending payments.
901 #[deprecated(note = "since node-v0.8.10: Use get_pending_payments instead")]
902 async fn get_pending_payments_v1(
903 &self,
904 auth: BearerAuthToken,
905 ) -> Result<VecDbPaymentV1, BackendApiError>;
906
907 /// GET /node/v2/payments/pending -> [`VecDbPaymentV2`]
908 ///
909 /// Fetches all pending payments.
910 async fn get_pending_payments(
911 &self,
912 auth: BearerAuthToken,
913 ) -> Result<VecDbPaymentV2, BackendApiError>;
914
915 /// GET /node/v1/payments/final -> [`VecPaymentId`]
916 ///
917 /// Fetches the IDs of all finalized payments.
918 #[deprecated(note = "since node-v0.8.8")]
919 async fn get_finalized_payment_ids(
920 &self,
921 auth: BearerAuthToken,
922 ) -> Result<VecPaymentId, BackendApiError>;
923
924 /// PUT /node/v1/payments/metadata [`DbPaymentMetadata`] -> [`Empty`]
925 async fn upsert_payment_metadata(
926 &self,
927 metadata: DbPaymentMetadata,
928 auth: BearerAuthToken,
929 ) -> Result<Empty, BackendApiError>;
930
931 /// PUT /node/v1/payments/metadata/batch [`VecDbPaymentMetadata`]
932 /// -> [`Empty`]
933 ///
934 /// ACID endpoint for upserting a batch of payments metadata.
935 async fn upsert_payment_metadata_batch(
936 &self,
937 payments: VecDbPaymentMetadata,
938 auth: BearerAuthToken,
939 ) -> Result<Empty, BackendApiError>;
940
941 /// GET /node/v1/payments/metadata/id [`PaymentIdStruct`]
942 /// -> [`MaybeDbPaymentMetadata`]
943 ///
944 /// Fetch a payment metadata by its [`PaymentId`].
945 async fn get_payment_metadata_by_id(
946 &self,
947 req: PaymentIdStruct,
948 token: BearerAuthToken,
949 ) -> Result<MaybeDbPaymentMetadata, BackendApiError>;
950
951 /// POST /node/v1/payments/metadata/ids [`VecPaymentId`]
952 /// -> [`VecDbPaymentMetadata`]
953 ///
954 /// Fetch a batch of payment metadata by their [`PaymentId`]s.
955 //
956 // We use POST because there may be a lot of ids,
957 // which might be too large to fit inside query params.
958 async fn get_payment_metadata_by_ids(
959 &self,
960 req: VecPaymentId,
961 token: BearerAuthToken,
962 ) -> Result<VecDbPaymentMetadata, BackendApiError>;
963
964 /// GET /node/v1/payments/metadata/updated [`GetUpdatedPaymentMetadata`]
965 /// -> [`VecDbPaymentMetadata`]
966 ///
967 /// Get a batch of payment metadata in asc `(updated_at, payment_id)` order.
968 async fn get_updated_payment_metadata(
969 &self,
970 req: GetUpdatedPaymentMetadata,
971 auth: BearerAuthToken,
972 ) -> Result<VecDbPaymentMetadata, BackendApiError>;
973
974 /// PUT /node/v1/human_bitcoin_address [`UpdateHumanBitcoinAddress`]
975 /// -> [`HumanBitcoinAddress`]
976 ///
977 /// Updates the human Bitcoin address (username and offer) of the given
978 /// node.
979 async fn update_human_bitcoin_address(
980 &self,
981 req: UpdateHumanBitcoinAddress,
982 auth: BearerAuthToken,
983 ) -> Result<HumanBitcoinAddress, BackendApiError>;
984
985 /// PUT /node/v1/payment_address [`UpdateHumanBitcoinAddress`]
986 /// -> [`HumanBitcoinAddress`]
987 #[deprecated(note = "since node-v0.9.3: \
988 Use update_human_bitcoin_address instead")]
989 async fn update_payment_address(
990 &self,
991 req: UpdateHumanBitcoinAddress,
992 auth: BearerAuthToken,
993 ) -> Result<HumanBitcoinAddress, BackendApiError> {
994 self.update_human_bitcoin_address(req, auth).await
995 }
996
997 /// GET /node/v1/human_bitcoin_address [`Empty`] -> [`HumanBitcoinAddress`]
998 ///
999 /// Fetches the node's primary human Bitcoin address (username and offer).
1000 async fn get_human_bitcoin_address(
1001 &self,
1002 auth: BearerAuthToken,
1003 ) -> Result<HumanBitcoinAddress, BackendApiError>;
1004
1005 /// GET /node/v1/payment_address [`Empty`] -> [`HumanBitcoinAddress`]
1006 #[deprecated(note = "since node-v0.9.3: \
1007 Use get_human_bitcoin_address instead")]
1008 async fn get_payment_address(
1009 &self,
1010 auth: BearerAuthToken,
1011 ) -> Result<HumanBitcoinAddress, BackendApiError> {
1012 self.get_human_bitcoin_address(auth).await
1013 }
1014
1015 /// POST /node/v1/claim_generated_human_bitcoin_address
1016 /// [`ClaimGeneratedHumanBitcoinAddress`] -> [`Empty`]
1017 ///
1018 /// Claims a generated human Bitcoin address for the given node.
1019 async fn claim_generated_human_bitcoin_address(
1020 &self,
1021 req: ClaimGeneratedHumanBitcoinAddress,
1022 auth: BearerAuthToken,
1023 ) -> Result<Empty, BackendApiError>;
1024
1025 /// POST /node/v1/claim_generated_payment_address
1026 /// [`ClaimGeneratedHumanBitcoinAddress`] -> [`Empty`]
1027 #[deprecated(note = "since node-v0.9.3: \
1028 Use claim_generated_human_bitcoin_address instead")]
1029 async fn claim_generated_payment_address(
1030 &self,
1031 req: ClaimGeneratedHumanBitcoinAddress,
1032 auth: BearerAuthToken,
1033 ) -> Result<Empty, BackendApiError> {
1034 self.claim_generated_human_bitcoin_address(req, auth).await
1035 }
1036
1037 /// GET /node/v1/generated_username [`Empty`]
1038 /// -> [`GetGeneratedUsernameResponse`]
1039 ///
1040 /// Generates an available username for the authenticated user without
1041 /// storing it. The username is derived deterministically from the
1042 /// user's `user_pk`, with collision handling via numeric suffixes.
1043 async fn get_generated_username(
1044 &self,
1045 auth: BearerAuthToken,
1046 ) -> Result<GetGeneratedUsernameResponse, BackendApiError>;
1047
1048 /// GET /node/v1/nwc_clients [`Empty`] -> [`VecDbNwcClient`]
1049 ///
1050 /// Fetches the node's NWC clients and optionally filters by
1051 /// client_nostr_pk.
1052 async fn get_nwc_clients(
1053 &self,
1054 req: GetNwcClients,
1055 auth: BearerAuthToken,
1056 ) -> Result<VecDbNwcClient, BackendApiError>;
1057
1058 /// PUT /node/v1/nwc_clients [`DbNwcClientFields`] -> [`DbNwcClient`]
1059 ///
1060 /// Upserts a NWC client in the database.
1061 async fn upsert_nwc_client(
1062 &self,
1063 req: DbNwcClientFields,
1064 auth: BearerAuthToken,
1065 ) -> Result<DbNwcClient, BackendApiError>;
1066
1067 /// DELETE /node/v1/nwc_clients [`NostrPkStruct`] -> [`Empty`]
1068 ///
1069 /// Deletes a NWC client given its nostr client public key.
1070 async fn delete_nwc_client(
1071 &self,
1072 req: NostrPkStruct,
1073 auth: BearerAuthToken,
1074 ) -> Result<Empty, BackendApiError>;
1075}
1076
1077/// Defines the api that the LSP exposes to user nodes.
1078pub trait NodeLspApi {
1079 /// GET /node/v1/scids [`GetNewScidsRequest`] -> [`Scids`]
1080 async fn get_new_scids(
1081 &self,
1082 req: &GetNewScidsRequest,
1083 ) -> Result<Scids, LspApiError>;
1084
1085 /// GET /node/v1/network_graph [`Empty`] -> [`Bytes`] (LDK-serialized graph)
1086 ///
1087 /// Introduced in node-v0.6.8 and lsp-v0.6.29.
1088 async fn get_network_graph(&self) -> Result<Bytes, LspApiError>;
1089
1090 /// GET /node/v1/prob_scorer [`Empty`]
1091 /// -> [`Bytes`] (LDK-serialized probabilistic scorer)
1092 ///
1093 /// Introduced in node-v0.6.17 and lsp-v0.6.33.
1094 async fn get_prob_scorer(&self) -> Result<Bytes, LspApiError>;
1095
1096 /// POST /node/v1/payment_path [`Bytes`] (LDK-serialized [`Event`])
1097 /// -> [`Empty`]
1098 ///
1099 /// Sends an anonymized successful or failed payment path to the LSP to
1100 /// update Lexe's shared network graph and improve payment reliability.
1101 ///
1102 /// Introduced in node-v0.6.17 and lsp-v0.6.33.
1103 async fn payment_path(&self, event: &Event) -> Result<Empty, LspApiError>;
1104}
1105
1106/// Defines the api that the runner exposes to the user node.
1107pub trait NodeRunnerApi {
1108 /// POST /node/renew_lease [`UserLeaseRenewalRequest`] -> [`Empty`]
1109 ///
1110 /// Renew's a user node's lease with the megarunner.
1111 async fn renew_lease(
1112 &self,
1113 req: &UserLeaseRenewalRequest,
1114 ) -> Result<Empty, RunnerApiError>;
1115
1116 /// POST /node/sync_success [`UserPkStruct`] -> [`Empty`]
1117 ///
1118 /// Indicates the node successfully completed sync.
1119 async fn sync_succ(&self, user_pk: UserPk)
1120 -> Result<Empty, RunnerApiError>;
1121}
1122
1123/// Defines the api that the gateway exposes to the internet.
1124pub trait PublicGatewayApi {
1125 /// Get the LNURL pay request message, if any username has been set.
1126 /// Uses lnurl_callback endpoint as the callback.
1127 ///
1128 /// GET /.well-known/lnurlp/{username}
1129 async fn get_lnurl_pay_request(
1130 &self,
1131 ) -> Result<LnurlPayRequestWire, LnurlError>;
1132
1133 /// Resolves the invoice given a LNURL pay request previously generated.
1134 /// Ciphertext is a base64 encoded string of the username and
1135 /// other metadata information.
1136 ///
1137 /// GET /public/v1/lnurl_callback/{encoded_params}
1138 async fn lnurl_callback(
1139 &self,
1140 req: LnurlCallbackRequest,
1141 ) -> Result<LnurlCallbackResponse, LnurlError>;
1142}