taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

commit 94229c03d8e56dc54cceb24893e0a4a96486810d
parent eda8312b713df9e471d0b8d91a29a4632b90a772
Author: Antoine A <>
Date:   Fri, 18 Sep 2026 16:54:52 +0200

DD104 draft

Diffstat:
Adesign-documents/104-status.rst | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 232 insertions(+), 0 deletions(-)

diff --git a/design-documents/104-status.rst b/design-documents/104-status.rst @@ -0,0 +1,231 @@ +DD 104: Wire Gateway Observability and Administration +##################################################### + +:Design status: Draft +:Implementation status: Not started +:DD shepherd: Antoine d'Aligny +:Historical contributors: Antoine d'Aligny +:First published: 2026-09-18 +:Last substantive change: 2026-09-18 + +Summary +======= + +This design document presents the challenges we currently face when handling +transaction failures in wire gateway adapters and proposes a solution for +addressing them. + +Motivation +========== + +In the current design and implementations, both incoming and outgoing +transactions can fail without administrators being notified or having the +ability to take corrective action. + +Requirements +============ + +* The solution must allow administrators to see failures, understand their + causes, and possibly fix them. +* The solution must be lightweight to implement in wire gateway adapters, as + the goal of this API is to provide a thin wrapper around how each + implementation works. Common logic that can be extracted into another + component should be. + +Proposed Solution +================= + +Terminology +----------- + +* `in_tx`: final incoming transaction +* `out_tx`: final outgoing transaction +* `talerable_in_tx`: talerable final incoming transaction (RESERVE | KYC | MAP) +* `talerable_out_tx`: talerable final outgoing transaction (deposit) +* `transfer`: initiated talerable outgoing transaction +* `initiated`: initiated outgoing transaction +* `bounce`: bounced invalid outgoing transaction + +Current API State +----------------- + +We already have endpoints for querying Talerable transactions: +`talerable_in_tx` with `GET /history/incoming` and `talerable_out_tx` with +`GET /history/outgoing`. + +We also already have endpoints for querying `transfer` with `GET /transfers` +and `GET /transfers/$ROW_ID`. + +What is missing is access to raw (incompled, malformed, etc) transactions, `in_tx` and `out_tx`, as well +as initiated and bounced transactions, `initiated` and `bounce`. + +Generic API Design +------------------ + +Each adapter works differently. In most adapters, `transfer` and `bounce` +produce an `initiated` operation, while other payment systems, such as +Cyclos, provide a proper way to perform a bounce idempotently and have a +specific bounce operation. + +Each adapter also has different internal unique identifiers. For example, +`libeufin_nexus` uses three of them while `depolymerizer-bitcoin` has a +single transaction ID. + +We need an API that can expose enough detail for manual reconciliation while +remaining generic enough to reuse the same logic across all adapters. + +For `transfer`, we already have the paginated `GET /transfers` endpoint and +the single-entry `GET /transfers/$ROW_ID` endpoint. The problem with this +design is that it is not well suited to following the progress of transfers. +Transfers do not progress or reach finality in the order in which they are +created. + +I propose adding a new status-history endpoint, +`GET /transfers/status-history`. This will allow administrators to understand +the lifecycle of a transaction and track the progress of all transfers in +real time. + +We also need new endpoints for `incoming` and `outgoing` transactions, +including paginated and single-entry endpoints, so that administrators can +inspect all transactions, including incomplete or invalid ones. This is +necessary to understand and debug why an incoming transaction never reaches +the Talerable history. + +Finally, we need a way to track both `initiated` and `bounce` transactions +using new endpoints providing paginated listings, status histories and +single-entry views. + +We could potentially remove all `transfer` endpoints, since transfers are a +subset of `initiated` operatuibs. Whether to do so depends on whether the +transfer-specific API remains useful as a higher-level abstraction. + +We could also enforce a single `initiated` operation concept, event if underneath +bounces works differently. As long as doing this it not too messy for the +database layer we should pursue this simplification. + +.. ts:def:: InTx + + interface InTx { + // Opaque identifier of the returned record. + row_id: SafeUint64; + + // Unstructured implementation specific fields that must be shown. + // Contains at least the unique identifiers to be used for manual reconciliation. + details: {string: string}; + + // Date of the transaction. + date: Timestamp; + + // Amount received before credit_fee. + amount?: Amount; + + // Unstructured transaction subject + subject?: string; + + // Fee paid by the creditor. + // If not null, creditor actually received amount - credit_fee + credit_fee?: Amount; + + // Full payto URI to identify the sender of funds. + debit_account?: string; + + // ID of the bounce operation if bounced + bounce_id?: SafeUint64; + } + +.. ts:def:: OutTx + + interface OutTx { + // Opaque identifier of the returned record. + row_id: SafeUint64; + + // Unstructured implementation specific fields that must be shown. + // Contains at least the unique identifiers to be used for manual reconciliation. + details: {string: string}; + + // Date of the transaction. + date: Timestamp; + + // Amount received before credit_fee. + amount?: Amount; + + // Unstructured transaction subject + subject?: string; + + // Fee paid by the debtor. + // If not null, debtor actually paid amount + debit_fee + debit_fee?: Amount; + + // Full payto URI to identify the receiver of funds. + credit_account?: string; + } + +.. ts:def:: InitiatedTx + + interface InitiatedTx { + // Opaque ID of this operation. + operation_id: SafeUint64; + + // Amount to transfer. + amount: Amount; + + // The recipient's account identifier as a full payto:// URI. + credit_account: string; + + // Optional ID of the wire transfer initiation if operation is a transfer + transfer_id?: SafeUint64; + + // Optional ID of the bounced incoming transaction if operation is a bounce + bounced_id?: SafeUint64; + + // Current status + // pending: in progress + // transient_failure: has failed but may succeed later + // permanent_failure: has failed permanently and will never reach finality + // success: has succeeded and reached finality + // late_failure: has failed permanently after reaching the success status + status: "pending" | "transient_failure" | "permanent_failure" | "success" | "late_failure"; + + // Timestamp that indicates when this status was reached. + timestamp: Timestamp; + } + +.. ts:def:: InitiatedStatus + + interface InitiatedStatus { + // Opaque ID of this operation. + operation_id: SafeUint64; + + // Opaque ID of this status, used for pagination. + row_id: SafeUint64; + + // pending: in progress + // transient_failure: has failed but may succeed later + // permanent_failure: has failed permanently and will never reach finality + // success: has succeeded and reached finality + // late_failure: has failed permanently after reaching the success status + status: "pending" | "transient_failure" | "permanent_failure" | "success" | "late_failure"; + + // Optional unstructured messages about the status. Can be used to document the reasons for failure or the state of progress. + status_msg?: string; + + // Timestamp that indicates when this status was reached. + timestamp: Timestamp; + } + +Test Plan +========= + + + +Definition of Done +================== + +Alternatives +============ + +Drawbacks +========= + +Discussion / Q&A +================ +\ No newline at end of file