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