023-taler-kyc.rst (74122B)
1 DD 23: Taler KYC 2 ################ 3 4 :Design status: Accepted 5 :Implementation status: Implemented 6 :DD shepherd: TBD 7 :Historical contributors: Florian Dold, Christian Grothoff, Özgür Kesim 8 :First published: 2021-09-12 9 :Last substantive change: 2026-05-03 10 :Implementation evidence: exchange (2024-04-22) 11 :Normative references: :doc:`../core/api-exchange`, :doc:`../taler-kyc-manual` 12 13 .. note:: 14 15 This is a living design rationale. The core exchange API and KYC manual 16 are authoritative for implemented endpoints and operations. 17 18 Summary 19 ======= 20 21 This document discusses the Know-your-customer (KYC) and Anti-Money Laundering 22 (AML) processes supported by Taler. 23 24 For the current wallet-core transaction states, KYC continuation data, and 25 available actions, see :ref:`dd37-kyc` in 26 :doc:`037-wallet-transactions-lifecycle`. 27 28 29 Motivation 30 ========== 31 32 To legally operate, Taler has to comply with KYC/AML regulation that requires 33 banks to identify parties involved in transactions at certain points. 34 35 36 Requirements 37 ============ 38 39 Taler needs to take *measures* based on the following primary *triggers*: 40 41 * Customer withdraws money over a monthly threshold 42 43 * exchange triggers KYC 44 * key: IBAN (encoded as payto:// URI) 45 46 * Wallet receives (via refunds) money resulting in a balance over a threshold 47 48 * this is a client-side restriction 49 * key: reserve (=KYC account) long term public key per wallet (encoded as payto:// URI) 50 51 * Wallet receives money via P2P payments 52 53 * there are two sub-cases: PUSH and PULL payments 54 * key: reserve (=KYC account) long term public key per wallet (encoded as payto:// URI) 55 56 * Merchant receives money (Q: any money, or above a monthly threshold?) 57 58 * key: IBAN (encoded as payto:// URI) 59 60 * Reserve is "opened" for invoicing. 61 62 * key: reserve (=KYC account) long term public key per wallet (encoded as payto:// URI) 63 64 * Import of new sanctions lists and triggering of measures against matches of existing 65 customer records against the list 66 67 For the different operation types, there can be both soft and hard 68 limits. Soft limits are those that the customer may raise by providing data 69 and passing KYC checks. Hard limits cannot be lifted, for example because an 70 exchange forbids crossing those limits in its terms of service for all 71 customers. 72 73 74 Process requirements 75 ^^^^^^^^^^^^^^^^^^^^ 76 77 The key consideration here is *plausibilization*: staff needs to 78 check that the client-provided information is plausible. As this 79 is highly case-dependent, this cannot be automated. 80 81 For the different *measures*, there are various different possible KYC/AML 82 *checks* that could happen: 83 84 * In-person validation by AML staff 85 * Various forms to be filled by AML staff 86 * Validation involving local authorities and post-office 87 * Online validation, sometimes with multiple options (like KYC for multiple people): 88 89 * Forms to be supplied by user (different types of ID) 90 * Interactive video 91 * Documents to be supplied (business register) 92 * Address validation (e-mail or phone or postal) 93 94 Additionally, the process is dynamic and conditional upon various decisions: 95 96 * Individual vs. business 97 * PEP or non-PEP 98 * Hit on sanctions list 99 * Type of business (trust, foundation, listed on stock market, etc.) 100 * Need for plausibilization (via documents by user or staff research) 101 * Periodic updates (of customer data, of sanction lists) and re-assessment 102 103 There are also various *outcomes*: 104 105 * normal operation (with expiration date) 106 * normal operation but with AML staff investigating (new measure) 107 * held, requesting customer documentation (new measure) 108 * held, AML staff reviewing evidence for plausibilization (new measure) 109 * automatically frozen until certain day (due to sanctions) 110 * institutionally frozen until certain day (due to order by state authority) 111 * operation is categorically not allowed (at least above certain limits) 112 113 Outcomes may also be (partially) public, that is exposed to the client. For 114 example, we may want to tell a wallet that it has hit a hard withdraw limit, 115 but might succeed at withdrawing a smaller amount. 116 117 The outcome of a *check* can set new rules or trigger another *measure* (the 118 latter is conditional on reaching the expiration time of the outcome). 119 120 As a result, we largely end up in a large state machine where the AML staff has 121 serious flexibility while the user needs guidance as to the possible next moves 122 and/or to the current state of their account (where some information must not be 123 disclosed). 124 125 126 Documentation requirements 127 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 128 129 For each account we must: 130 131 * define risk-profile (902.4, 905.1) 132 * document the specific setup, likely not just the INI file 133 * should have some key Anti-Money-Laundering Act (AMLA) 134 file attributes, such as: 135 136 * File opened, file closed (keep data for X years afterwards!) 137 * low-risk or high-risk business relationship 138 * PEP status 139 * business domain 140 * authority notification dates (possibly multiple) with 141 voluntary or mandatory notification classification 142 143 Finally, we need to produce statistics: 144 145 * There must be a page with an overview of AMLA files with opening 146 and closing dates and an easy way to determine for any day the 147 number of open AMLA files 148 * Technically, we also need a list of at-risk transactions and of 149 frozen transactions, but given that we can really only freeze 150 on an account-basis, I think there is nothing to do here 151 * number of incidents reported (voluntarily, required) 152 * number of business relationships at any point in time 153 * number of risky business relationships (PEP, etc.) 154 * number of frozen transactions (authority vs. sanction) with start-date and end-date 155 * start-data and end-date of relationships (data retained for X years after end of relationship) 156 157 For this high-level monitoring, we need certain designated critical events to 158 be tracked in the system statistics: 159 160 * account opened 161 * set to high risk 162 * set to low risk 163 * suspicious activity report filed with authority 164 * account frozen 165 * account unfrozen 166 * account closed 167 * sanction list import / update 168 169 170 Sanction lists 171 ^^^^^^^^^^^^^^ 172 173 .. note:: 174 175 The implementation described in the current KYC manual supersedes this 176 historical design sketch. 177 178 We need to be able to import new sanction lists (whenever they are published) 179 and then check existing AMLA files against those lists. Additionally, newly 180 created AMLA files must be checked against the current list and some "measure" 181 applied in case of a match. 182 183 This will primarily require us to define an endpoint to upload a sanction list 184 and to define a new table to track the list of sanctioned entities. As it is 185 expected that sanction lists will not permit fully automated determinations in 186 all cases, an external "sanction check" program should be configured which 187 compares records against the current list and determines the correct measure, 188 such as no change, further manual review by AML staff, or even automatic 189 freeze (and report) depending on how well the records match. 190 191 Basically, the "sanction check" program takes the sanction list and an 192 attribute set to compute the same kind of `AmlOutcome` that an AML program 193 outputs given a context and an attribute set. 194 195 196 Security requirements 197 ^^^^^^^^^^^^^^^^^^^^^ 198 199 IBANs are predictable. We (probably) do not want random people to be able to 200 initate KYC processes for other parties. Similarly, the attestation API 201 requires us to somehow *authenticate* the user to ensure we only give out 202 attestation data to the data subject themselves. For P2P payments and 203 withdrawals, we have the reserve public key that is only known to the data 204 subject and thus can be used to authenticate the client via a signature. Only 205 pure deposits (by merchants or directly from a wallet) are a problem as the 206 only thing we know about the receiver is the IBAN at that time, and literally 207 any user could just deposit money into some bank account, so knowledge of the 208 IBAN is insufficient to determine that we actually are communicating with the 209 owner of the bank account. 210 211 212 Further considerations 213 ^^^^^^^^^^^^^^^^^^^^^^ 214 215 On top of all of this, we need to plan some *diagnostics* to determine when 216 components fail (such as scripts or external services providing malformed 217 results). 218 219 Optionally, in the future, the solution should support fees to be paid by the 220 user for *voluntary* KYC processes related to attestation (#7365). 221 222 223 Proposed Solution 224 ================= 225 226 The main state of an account is represented by a set of `KYC rules <KycRule>` (the 227 `LegitimizationRuleSet`) which specify the current *rules* to apply to 228 transactions involving the account. Rules can *exposed* to the account owner, 229 or can be secret. Each *rule* specifies certain *conditions* which, if met, 230 *trigger* a set of *measures*. After a *rule* was *triggered* and 231 before the *outcome* of a respective *measure* has been produced (say 232 because the user did not yet enter their data or the AML officer is still 233 reviewing the case), the existing rules remain in force. Rules have a display 234 priority, and if a second rule with a higher display priority is also 235 triggered, the *measures* of the higher-priority rule become the active 236 *measures*. Except for the default rule set, every legitimization rule set 237 also has an *expiration* time after which a successor *measure* (or the 238 default rule set) is automatically triggered. 239 240 For any possible *measures*, we define: 241 242 * Contextual input data to be provided (with dynamic inputs, 243 e.g. amount set dynamically based on the *trigger* could be 244 in the context) 245 * A *check* to be performed (checks can be user-interactive (LINK, FORM) 246 or staff-interactive (INFO)) 247 * A fallback *measure* to take on failure of a user-interactive check 248 (if the check fails, we cannot run the AML *program* as required inputs 249 might be missing!) 250 * An (AML) *program* that uses *attribtes* from the *check* as well as 251 *context* data to determine an *outcome* represented as the 252 `AmlOutcome`. 253 254 "verboten" is the name of a special *measure*, which means that crossing the 255 respective transaction threshold is categorically not allowed (for this 256 account). "verboten" with a threshold of zero can be used to freeze funds. 257 258 Possible *outcomes* of a measure include: 259 260 * The next operational state (normal, AML investigation) of the account 261 (basically, whether to add it to the work list of AML staff). 262 * A new set of *rules* in the form of a `LegitimizationRuleSet` that 263 determines custom rules to apply to transactions involving the account; 264 such rules may be used to block certain transactions by using the 265 "verboten" measure. The `LegitimizationRuleSet` also must specify 266 an *expiration* time by which we fall back to a successor measure 267 *or* to the default rules. 268 * A (largely) free-form set of `AccountProperties` that AML staff can 269 use to tag accounts with. Some default properties are defined, but 270 the exchange does not do anything with these and AML SPAs are free to 271 use any properties they like. Account properties are only exposed 272 to AML staff and never to the customer. 273 * A set of *events* that are to be added to the timeline of the 274 operator for statistical purposes. 275 276 For the user-interactive *checks* we need a KYC SPA that is given: 277 278 * instructions to render (with either a form to fill or links to external checks); 279 here the context could provide an array of choices! 280 * possibly an external check that was set up (if any); for cost-reasons, we 281 should only do one at a time, and probably should then always redirect the 282 browser to that check. 283 284 For the staff-interactive *checks* we need an AML SPA: 285 286 * to file forms and upload documentation (without state transition) 287 * to decide on next measure (providing context); here, the exchange needs 288 to expose the list of available *measures* and required *context* for each 289 290 We need some customer-driven interactivity in KYB/KYC process, for example the 291 user may need to be given choices (address vs. phone, individual vs. business, 292 order in which to provide KYC data of beneficiaries). As a result, the 293 exchange needs to serve some SPA for *measures* where the user is shown the 294 next step(s) or choices (which person to collect KYC data on, whether to run 295 challenger on phone number of physical address, etc.). The SPA should also 296 potentially contain a form to allow the customer to directly upload documents 297 to us (like business registration) instead of to some KYC provider. This is 298 because KYC providers may not be flexible enough. The SPA should also allow 299 the customer to perform KYC checks voluntarily. 300 301 Similarly, the AML staff will need to be able to trigger rather complex 302 KYB/KYC processes, like "need KYC on X and Y and Z" or "phone number or 303 mailing address" or "please upload form A/T/S". Here in particular it 304 should be possible to request not only filled forms, but arbitrary 305 documents. 306 307 308 Terminology 309 ^^^^^^^^^^^ 310 311 * **Attributes**: Attributes are used to represent KYC data obtained about 312 an account holder. Attributes include passport images, address data, 313 business registration documents, and indeed arbitrary forms filed by 314 AML staff or the customer themselves. Attribute data is considered 315 sensitive private information and is thus stored encrypted within the 316 exchange database. 317 318 * **Check**: A check establishes a particular attribute of a user, such as 319 their name based on an ID document and lifeness, mailing address, phone 320 number, taxpayer identity, etc. Checks may be given *context* (such as 321 whether a customer is an individual or a business) to run correctly. Checks 322 can also be AML staff inserting information for plausibilization. Checks 323 result in *attributes* about the account's owner which are given to an 324 external AML *program* together with the *context* to determine an *outcome*. 325 KYC checks are always specified with a fallback *measure* to be taken if 326 the check fails. 327 328 * **Condition**: A condition specifies when KYC is required. Conditions 329 include the *type of operation*, a threshold amount (e.g. above EUR:1000) 330 and possibly a time period (e.g. over the last month). 331 332 * **Configuration**: The configuration determines the *legitimization rules*, 333 and specifies which providers offer which *checks*. 334 335 * **Context**: Context is information provided as input into a *check* and 336 *program* to customize their execution. The context is initially set by the 337 *measure* (possibly including data from the *trigger*). Naturally, the 338 *program* may use its `AmlProgramInput` which includes *context* and 339 *attribute* data to compute an update *context* for the next set of 340 *measures* that it specifies in the `LegitimizationRuleSet` as part 341 of the `AmlOutcome`. Thus, *context* is something that typically 342 evolves as the *account* undergoes *measures*. Context is lost if 343 an account transitions to default *legitimization rules* due to 344 *expiration*. 345 346 * **Display priority**: Every rule has a *display priority*. If a second 347 *rule* is *triggered* before the *outcome* of a *rule* could be determined, 348 the *rule* with the larger *display priority* becomes the requirement that 349 the account owner has to satisfy (and that thus will be displayed by the 350 KYC SPA). 351 352 * **Expiration**: Except for the default rules, any set of KYC rules is 353 subject to *expiration*. This can be because *attributes* become outdated or 354 because sanctions have a time limit. The expiration time thus determines 355 when a new *measure* is triggered in the absence of a transaction crossing 356 thresholds in the current set of *legtimization rules*. 357 358 * **Legitimization rules**: The *legitimization rules* determine under which 359 *conditions* which *measures* will be taken. A `LegitimizationRuleSet` 360 always also includes an *expiration* time period for (custom, non-default) 361 *legitimization rules* after which a fallback measure* will automatically 362 apply. Legitimization rules may be *exposed* to the client (for example, 363 to allow a wallet to stay below hard withdraw thresholds) or could be secret. 364 365 * **Logic**: Logic refers to a specific bit of code (realized as an exchange 366 plugin) that enables the interaction with a specific *provider*. Logic 367 typically requires *configuration* for access control (such as an 368 authorization token) and possibly the endpoint of the specific *provider* 369 implementing the respective API. 370 371 * **Measure**: Describes the possible outgoing edges from one state in the 372 state machine (including how to show the current state). Each edge is given 373 some *context* and a *check* to be performed as well as an AML *program* 374 which determines the *outcome*. We generally distinguish between 375 "original" measures (defined globally in the exchange configuration) and 376 "custom" measures (defined specifically for an account by AML staff). 377 378 * **Outcome**: An `AmlOutcome` describes the account state that an account 379 ends up in due to either an AML staff action or an AML *program* doing some 380 computation over the attributes resulting from a *check*. Outcomes can be 381 that certain types of transactions are "verboten", that the account is (or 382 remains) under investigation by AML staff, that the account is given certain 383 properties, and/or that certain events are to be logged. Outcomes also 384 include a new set of *legitimization rules* to apply (and an *expiration* 385 time at which point a successor *measure* will be automatically taken). 386 387 * **Provider**: A provider performs a specific set of *checks* at a certain 388 *cost*. Interaction with a provider is performed by provider-specific 389 *logic*. 390 391 * **Program**: An AML helper *program* is given *context* about the current 392 state of an account and the attribute data from a *check* to compute the 393 *outcome*. For example, a *program* may look at the "PEP" field of a KYC 394 check and decide if the outcome is to put the account into ``normal`` or 395 ``held-for-manual-review`` state. AML programs are always specified 396 with a fallback *measure* to be taken if the program fails. 397 398 * **Trigger**: A specific transaction that satisfies a **Condition**. 399 400 * **Type of operation**: The operation type determines which Taler-specific 401 operation has triggered the KYC requirement. We support four types of 402 operation: withdraw (by customer), deposit (by merchant), P2P receive (by 403 wallet) and (high) wallet balance. 404 405 406 Account owner authentication 407 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 408 409 Access to the KYC SPA (or rather, its account-specific state) is controlled by 410 a *target token* (which is effectively like a bearer token, except passed 411 inside the URL). The *target token* ensures that only the account owner has 412 access to the KYC processes. It can be obtained by authenticating using 413 either the merchant private key or reserve private key, depending on the type 414 of the account (IBAN or wallet-reserve respectively). 415 416 When we need to authenticate a bank account owner, we will simply require them 417 to make an outgoing wire transfer into the exchange bank account with a public 418 key in the wire transfer subject (just like when withdrawing), but augmented 419 with the string "KYC" so we can distinguish the wire transfer from a regular 420 withdrawal. Typically, we would put the merchant public key into the wire 421 transfer subject; wallets MAY put their long-term reserve public key instead. 422 The amount to be transferred is the *KYC fee*. 423 424 This has several advantages: 425 426 * Only the account owner can provide us with the public key, so we already 427 have also one super-hard piece of KYC evidence. 428 * If the account owner looses their public key, it's not a problem: they 429 would just have to do the transfer again with a new key. No need for 430 us to do any kind of intervention for key management. 431 * We could theoretically get paid to do the KYC process, or just "charge" a 432 nominal amount. 433 * This also somewhat addresses the payment for voluntary KYC processes where 434 a merchant wants to do KYC to get us to attest their identity for their 435 customers even if we do not yet have a legal need. The only issue here 436 is that this does not work if voluntary KYC is invoiced while mandatory 437 KYC is gratis. But, that kind of configuration is a business decision 438 and there is no hard need to support it immediately. 439 * This definitively addresses the need for authentication to access the 440 attestation API, which so far was only available for P2P payments as 441 we could not authenticate merchants. 442 * The "KYC" string allows us to distinguish the authentication transfers from 443 withdrawal transfers; by keeping the KYC fee at or below the closing fee, 444 we can even deploy this without fully updating the logic everywhere to 445 distinguish KYC transfers 446 447 448 451 Response 449 ^^^^^^^^^^^^ 450 451 When KYC operations are required, various endpoints may respond with a 452 ``451 Unavailable for Legal Reasons`` status code and a `LegitimizationNeededResponse` 453 body. 454 455 New endpoints 456 ^^^^^^^^^^^^^ 457 458 .. http:get:: /kyc-check/$H_PAYTO 459 460 Checks the KYC status of a particular payment target and possibly begins a 461 KYC process by allowing the customer to choose the next KYC measure to 462 satisfy. This endpoint is typically used by wallets or merchants that 463 have been told that a transaction is not happening because it triggered 464 some KYC/AML measure and now want to check how the KYC/AML 465 requirement could be fulfilled (or whether it already has been 466 satisfied and the operation can now proceed). Long-polling may be used 467 to instantly observe a change in the KYC requirement status. 468 469 The payto hash of the ``/kyc-check/`` endpoint encodes the 470 account (or wallet) for which legitimization measures are determined. 471 It is returned in `LegitimizationNeededResponse` responses as in 472 the case of withdraw, the wallet may not know the debited bank account. 473 474 Given a valid pair of payto hash and account owner signature, the 475 ``/kyc-check/`` endpoint returns either just the KYC status or redirects the 476 client (202) to the next required stage of the KYC process. The redirection 477 must be for an HTTP(S) endpoint to be triggered via a simple HTTP GET. It 478 must always be the same endpoint for the same client, as the wallet/merchant 479 backend are not required to check for changes to this endpoint. Clients 480 that received a 202 status code may repeat the request and use long-polling 481 to detect a change of the HTTP status. 482 483 **Request:** 484 485 *Account-Owner-Signature*: 486 487 The client must provide Base-32 encoded EdDSA signature with 488 ``$ACCOUNT_PRIV``, affirming the desire to obtain KYC data. Note that 489 this is merely a simple authentication mechanism, the details of the 490 request are not protected by the signature. The ``$ACCOUNT_PRIV`` is 491 either the (wallet long-term) reserve private key or the merchant instance 492 private key. 493 494 :query timeout_ms=NUMBER: *Optional.* If specified, the exchange will 495 wait up to ``timeout_ms`` milliseconds if the requirement continues 496 to be mandatory provisioning of KYC data by the client. 497 Ignored if the HTTP status code is already ``200 Ok``. Note that 498 clients cannot long-poll for AML staff actions, so status information 499 about an account being under AML review needs to be requested 500 periodically. 501 502 **Response:** 503 504 :http:statuscode:`200 Ok`: 505 No mandatory KYC actions are required by the client at this time. 506 The client *may* still visit the KYC URL to initiate voluntary checks. 507 The response will be an `AccountKycStatus` object which specifies 508 restrictions that currently apply to the account. If the 509 client attempts to exceed *soft* limits, the status may change 510 to a ``202 Accepted``. Hard limits cannot be lifted by passing KYC checks. 511 :http:statuscode:`202 Accepted`: 512 The account holder performed an operation that would have crossed 513 *soft* limits and must be redirected to the provided location to perform 514 the required KYC checks to satisfy the legal requirements. Afterwards, the 515 ``/kyc-check/`` request should be repeated to check whether the 516 user has completed the process. 517 The response will be an `AccountKycStatus` object. 518 :http:statuscode:`204 No content`: 519 The exchange is not configured to perform KYC and thus 520 the legal requirements are already satisfied. 521 :http:statuscode:`403 Forbidden`: 522 The provided signature is not acceptable for the payto hash. 523 :http:statuscode:`404 Not found`: 524 The requirement row is unknown. 525 526 .. http:get:: /aml-spa/$FILENAME 527 528 Serves the resources of the AML SPA. 529 530 .. http:get:: /kyc-spa/$FILENAME 531 532 Serves the resources of the KYC SPA. Note that 533 ``$FILENAME`` must be syntactically distinct from 534 the access token. Any ``$ACCESS_TOKEN`` will be 535 mapped internally to "index.html". 536 537 .. http:get:: /kyc-spa/$ACCESS_TOKEN 538 539 A set of ``/kyc-spa/$ACCESS_TOKEN`` GET endpoints is created per account 540 hash that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint 541 will in principle redirect clients. The KYC SPA will use the 542 ``$ACCESS_TOKEN`` of its URL to initialize itself via the 543 ``/kyc-info/$ACCESS_TOKEN`` endpoint family. The KYC SPA may download 544 additional resources via ``/kyc-spa/$FILENAME``. The filenames must not 545 match base32-encoded 256-bit values. 546 547 .. http:get:: /kyc-info/$ACCESS_TOKEN 548 549 The ``/kyc-info/$ACCESS_TOKEN`` endpoints are created per client 550 account hash (but access controlled via a unique target token) 551 to return information about the state of the KYC or AML process 552 to the KYC SPA. The SPA uses this information to show the user an 553 appropriate dialog. The SPA should also long-poll this endpoint for changes 554 to the AML/KYC state. Note that this is a client-facing endpoint, so it will 555 only provide a restricted amount of information to the customer (as some 556 laws may forbid us to inform particular customers about their true status). 557 The endpoint will typically inform the SPA about possible choices to 558 proceed, such as directly uploading files, contacting AML staff, or 559 proceeding with a particular KYC process at an external provider (such as 560 Challenger). If the user chooses to initate a KYC process at an external 561 provider, the SPA must request the respective process to be set-up by the 562 exchange via the ``/kyc-start/`` endpoint. 563 564 **Request:** 565 566 *If-None-Match*: 567 The client MAY provide an ``If-None-Match`` header with an ETag. 568 569 :query timeout_ms=MILLISECONDS: 570 *Optional.* If specified, the exchange will wait up to MILLISECONDS for 571 a change to a more recent legitimization measure before returning a 304 572 Not Modified status. 573 574 **Response:** 575 576 *Etag*: Will be set to the serial ID of the measure. Used for long-polling (only for 200 OK responses). 577 578 :http:statuscode:`200 OK`: 579 The body is a `KycProcessClientInformation`. 580 :http:statuscode:`204 No Content`: 581 There are no open KYC requirements or possible voluntary checks 582 the client might perform. 583 :http:statuscode:`304 Not Modified`: 584 The KYC requirements did not change. 585 586 587 .. http:post:: /kyc-upload/$ID 588 589 The ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload 590 client-provided evidence. The ``$ID`` will be provided as part of the 591 ``/kyc-info`` body. This is for checks of type ``FORM``. In practice, 592 ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of the selected 593 measure (but this should be irrelevant for the client). 594 595 **Request:** 596 597 Basically oriented along the possible formats of a HTTP form being 598 POSTed. Details will depend on the form. The server will try to decode the 599 uploaded body from whatever format it is provided in. 600 601 **Response:** 602 603 :http:statuscode:`204 No Content`: 604 The information was successfully uploaded. The SPA should fetch 605 an updated ``/kyc-info/``. 606 :http:statuscode:`404 Not Found`: 607 The ``$ID`` is unknown to the exchange. 608 :http:statuscode:`409 Conflict`: 609 The upload conflicts with a previous upload. 610 :http:statuscode:`413 Request Entity Too Large`: 611 The body is too large. 612 613 .. http:post:: /kyc-start/$ID 614 615 The ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new external 616 KYC process. It will return the URL that the client must GET to begin the 617 KYC process. The SPA should probably open this URL in a new window or tab. 618 The ``$ID`` will be provided as part of the ``/kyc-info`` body. In 619 practice, ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of 620 the selected measure (but this should be irrelevant for the client). 621 622 **Request:** 623 624 Use empty JSON body for now. 625 626 **Response:** 627 628 :http:statuscode:`200 Ok`: 629 The KYC process was successfully initiated. The URL is in a 630 `KycProcessStartInformation` object. 631 632 :http:statuscode:`404 Not Found`: 633 The ``$ID`` is unknown to the exchange. 634 635 .. note:: 636 637 As this endpoint is involved in every KYC check at the beginning, this 638 is also the place where we could integrate the payment process for the KYC fee 639 in the future (since **vATTEST**). 640 641 642 .. http:get:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO 643 644 Upon completion of the process at the external KYC provider, the provider 645 must redirect the client (browser) to trigger a GET request to a new 646 ``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION`` endpoint. Once this endpoint is 647 triggered, the exchange will pass the received arguments to the respective 648 logic plugin. The logic plugin will then (asynchronously) update the KYC 649 status of the user. The logic plugin should redirect the user to the KYC 650 SPA. This endpoint deliberately does not use the ``$ACCESS_TOKEN`` as the 651 external KYC provider should not learn that token. 652 653 This endpoint is thus accessed from the user's browser at the *end* of a KYC 654 process, possibly providing the exchange with additional credentials to 655 obtain the results of the KYC process. Specifically, the URL arguments 656 should provide information to the exchange that allows it to verify that the 657 user has completed the KYC process. The details depend on the logic, which 658 is selected by the "$PROVIDER_SECTION". 659 660 While this is a GET (and thus safe, and idempotent), the operation may 661 actually trigger significant changes in the exchange's state. In 662 particular, it may update the KYC status of a particular payment target. 663 664 **Request:** 665 666 Details on the request depend on the specific KYC logic that was used. 667 668 If the KYC plugin logic is OAuth 2.0, the query parameters are: 669 670 :query code=CODE: 671 OAuth 2.0 code argument. 672 :query state=STATE: 673 OAuth 2.0 state argument with the H_PAYTO. 674 675 .. note:: 676 677 Depending on the OAuth variant used, additional 678 query parameters may need to be passed here. 679 680 **Response:** 681 682 Given that the response is returned to a user using a browser and **not** to 683 a Taler wallet, the response format is in human-readable HTML and not in 684 machine-readable JSON. 685 686 :http:statuscode:`302 Found`: 687 The KYC operation succeeded and the 688 payment target is now authorized to transact. 689 The browser is redirected to a human-readable 690 page configured by the exchange operator. 691 :http:statuscode:`401 Unauthorized`: 692 The provided authorization token is invalid. 693 :http:statuscode:`404 Not found`: 694 The payment target is unknown. 695 :http:statuscode:`502 Bad Gateway`: 696 The exchange received an invalid reply from the 697 legitimization service. 698 :http:statuscode:`504 Gateway Timeout`: 699 The exchange did not receive a reply from the legitimization 700 service within a reasonable time period. 701 702 703 .. http:get:: /kyc-webhook/$PROVIDER_SECTION/* 704 .. http:post:: /kyc-webhook/$PROVIDER_SECTION/* 705 .. http:get:: /kyc-webhook/$LOGIC/* 706 .. http:post:: /kyc-webhook/$LOGIC/* 707 708 Alternatively, the KYC confirmation may be triggered by a ``/kyc-webhook`` 709 request. As KYC **providers** do not necessarily support passing detailed 710 information in the URL arguments, the ``/kyc-webhook`` only needs to specify 711 either the ``PROVIDER_SECTION`` *or* the ``LOGIC`` (the name of the plugin 712 implementing the KYC API). The API-specific webhook logic must then figure 713 out what exactly the webhook is about on its own. The ``/kyc-webhook/`` 714 endpoint works for GET or POST, again as details depend on the KYC provider. 715 In contrast to ``kyc-proof``, the response does NOT go to the end-users' 716 browser and should thus only indicate success or failure. 717 718 **Request:** 719 720 Details on the request depend on the specific KYC logic that was used. 721 722 **Response:** 723 724 :http:statuscode:`204 No content`: 725 The operation succeeded. 726 :http:statuscode:`404 Not found`: 727 The specified logic is unknown. 728 729 730 .. http:post:: /kyc-wallet 731 732 The ``/kyc-wallet`` POST endpoint allows a wallet to notify an exchange if 733 it will cross a balance threshold. Here, the ``balance`` specified should be 734 the threshold (from the ``wallet_balance_limit_without_kyc`` array) that the 735 wallet would cross, and *not* the *exact* balance of the wallet. The exchange 736 will respond with a wire target UUID. The wallet can then use this UUID to 737 begin the KYC process at ``/kyc-check/``. The wallet must only proceed to 738 obtain funds exceeding the threshold after the KYC process has concluded. 739 While wallets could be "hacked" to bypass this measure (we cannot 740 cryptographically enforce this), such modifications are a terms of service 741 violation which may have legal consequences for the user. 742 743 Setup KYC identification for a wallet. Returns the KYC UUID. This endpoint 744 is used by compliant Taler wallets when they are about to hit the balance 745 threshold and thus need to have the customer provide their personal details 746 to the exchange. The wallet is identified by its long-lived reserve public 747 key (which is used for P2P payments, not for withdrawals). 748 749 **Request:** 750 751 The request body must be a `WalletKycRequest` object. 752 753 **Response:** 754 755 :http:statuscode:`204 No Content`: 756 KYC is disabled at this exchange, or the balance is below the 757 threshold that requires KYC, or this wallet already satisfied 758 the KYC check for the given balance. 759 :http:statuscode:`403 Forbidden`: 760 The provided signature is invalid. 761 This response comes with a standard `ErrorDetail` response. 762 :http:statuscode:`451 Unavailable for Legal Reasons`: 763 The wallet must undergo a KYC check. A KYC ID was created. 764 The response will be a `LegitimizationNeededResponse` object. 765 766 .. http:get:: /aml/$OFFICER_PUB/measures 767 768 To enable the AML staff SPA to give AML staff a choice of possible measures, a 769 new endpoint ``/aml/$OFFICER_PUB/measures`` is added that allows the AML SPA 770 to dynamically GET the list of available measures. It returns a list of known 771 KYC checks (by name) with their descriptions and a list of AML programs with 772 information about the required context. 773 774 **Request:** 775 776 *Taler-AML-Officer-Signature*: 777 The client must provide Base-32 encoded EdDSA signature with 778 ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that 779 this is merely a simple authentication mechanism, the details of the 780 request are not protected by the signature. 781 782 **Response:** 783 784 :http:statuscode:`200 Ok`: 785 Information about possible measures is returned in a 786 `AvailableMeasureSummary` object. 787 788 .. http:get:: /aml/$OFFICER_PUB/kyc-statistics/$NAME 789 790 Returns the number of KYC events matching the given event type ``$NAME`` in 791 the specified time range. Note that this query can be slow as the 792 statistics are computed on-demand. (This is OK as such requests should be 793 rare.) 794 795 **Request:** 796 797 *Taler-AML-Officer-Signature*: 798 The client must provide Base-32 encoded EdDSA signature with 799 ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that this 800 is merely a simple authentication mechanism, the details of the request are 801 not protected by the signature. 802 803 :query start_date=TIMESTAMP: 804 *Optional*. Specifies the date when to 805 start looking (inclusive). If not given, the start time of the 806 exchange operation is used. 807 :query end_date=TIMESTAMP: 808 *Optional*. Specifies the date when to 809 stop looking (exclusive). If not given, the current date is used. 810 811 **Response:** 812 813 :http:statuscode:`200 OK`: 814 The response will be an `EventCounter` message. 815 816 .. http:get:: /aml/$OFFICER_PUB/decisions 817 818 **Request:** 819 820 *Taler-AML-Officer-Signature*: 821 The client must provide Base-32 encoded EdDSA signature with 822 ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that 823 this is merely a simple authentication mechanism, the details of the 824 request are not protected by the signature. 825 826 :query limit: 827 *Optional*. takes value of the form ``N (-N)``, so that at 828 most ``N`` values strictly older (younger) than ``start`` are returned. 829 Defaults to ``-20`` to return the last 20 entries (before ``start``). 830 :query offset: 831 *Optional*. Row number threshold, see ``delta`` for its 832 interpretation. Defaults to ``INT64_MAX``, namely the biggest row id 833 possible in the database. 834 :query h_payto: 835 *Optional*. Account selector. All matching accounts are returned if this 836 filter is absent, otherwise only decisions for this account. 837 :query active: 838 *Optional*. If set to yes, only return active decisions, if no only 839 decisions that have been superseded. Do not give (or use "all") to 840 see all decisions regardless of activity status. 841 :query investigation: 842 *Optional*. If set to yes, only return accounts that are under 843 AML investigation, if no only accounts that are not under investigation. 844 Do not give (or use "all") to see all accounts regardless of 845 investigation status. 846 847 **Response:** 848 849 :http:statuscode:`200 OK`: 850 The response will be an `AmlDecisionsResponse` message. 851 :http:statuscode:`204 No content`: 852 There are no matching AML records. 853 :http:statuscode:`403 Forbidden`: 854 The signature is invalid. 855 :http:statuscode:`404 Not found`: 856 The designated AML account is not known. 857 :http:statuscode:`409 Conflict`: 858 The designated AML account is not enabled. 859 860 .. http:get:: /aml/$OFFICER_PUB/attributes/$H_PAYTO 861 862 Obtain attributes obtained as part of AML/KYC processes for a 863 given account. 864 865 **Request:** 866 867 *Taler-AML-Officer-Signature*: 868 The client must provide Base-32 encoded EdDSA signature with 869 ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that 870 this is merely a simple authentication mechanism, the details of the 871 request are not protected by the signature. 872 873 :query limit: 874 *Optional*. takes value of the form ``N (-N)``, so that at 875 most ``N`` values strictly older (younger) than ``start`` are returned. 876 Defaults to ``-20`` to return the last 20 entries (before ``start``). 877 :query offset: 878 *Optional*. Row number threshold, see ``delta`` for its 879 interpretation. Defaults to ``INT64_MAX``, namely the biggest row id 880 possible in the database. 881 882 **Response:** 883 884 :http:statuscode:`200 OK`: 885 The response will be an `KycAttributesResponse` message. 886 :http:statuscode:`204 No content`: 887 There are no matching KYC attributes. 888 :http:statuscode:`403 Forbidden`: 889 The signature is invalid. 890 :http:statuscode:`404 Not found`: 891 The designated AML account is not known. 892 :http:statuscode:`409 Conflict`: 893 The designated AML account is not enabled. 894 895 .. http:post:: /aml/$OFFICER_PUB/decision 896 897 Make an AML decision. Triggers the respective action and 898 records the justification. 899 900 **Request:** 901 902 The request body must be an `AmlDecisionRequest` message. 903 904 **Response:** 905 906 :http:statuscode:`204 No Content`: 907 The AML decision has been executed and recorded successfully. 908 :http:statuscode:`403 Forbidden`: 909 The signature is invalid. 910 :http:statuscode:`404 Not Found`: 911 The address the decision was made upon is unknown to the exchange or 912 the designated AML account is not known. 913 :http:statuscode:`409 Conflict`: 914 The designated AML account is not enabled or a more recent 915 decision was already submitted. 916 917 Modifications to existing endpoints 918 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 919 920 .. note:: 921 922 This subsection describes an earlier KYC API design. The core exchange API 923 and KYC manual supersede its endpoint names and processing model. 924 925 When withdrawing, the exchange checks if the KYC status is acceptable. If no 926 KYC was done and if either the amount withdrawn over a particular timeframe 927 exceeds the threshold or the reserve received a P2P transfer, then a 928 ``451 Unavailable for Legal Reasons`` is returned which redirects the consumer 929 to the new ``/kyc-check/`` handler. 930 931 When depositing, the exchange aggregator (!) checks the KYC status and if 932 negative, returns an additional information field via the 933 ``aggregation_transient`` table which is returned via GET ``/deposits/`` to the 934 merchant. 935 936 When merging into a reserve, the KYC status is checked and again the 937 merge fails with ``451 Unavailable for Legal Reasons`` to trigger the 938 KYC process. 939 940 To allow the wallet to do the KYC check if it is about to exceed a set balance 941 threshold, we modify the ``/keys`` response to add an optional array 942 ``wallet_balance_limit_without_kyc`` of threshold amounts is returned. 943 Whenever the wallet crosses one of these thresholds for the first time, it 944 should trigger the KYC process. If this field is absent, there is no limit. 945 If the field is provided, a correct wallet must create a long-term 946 account-reserve key pair. This should be the same key that is also used to 947 receive wallet-to-wallet payments. Then, *before* a wallet performs an 948 operation that would cause it to exceed the balance threshold in terms of 949 funds held from a particular exchange, it *should* first request the user to 950 complete the KYC process. For that, the wallet should POST to the new 951 ``/kyc-wallet`` endpoint, providing its long-term reserve-account public key 952 and a signature requesting permission to exceed the account limit. 953 954 955 Configuration of external KYC providers 956 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 957 958 For each KYC provider that could contribute to checks the configuration 959 specifies a ``$PROVIDER_SECTION`` for each authentication procedure. For each 960 (enabled) provider, the exchange has a logic plugin which (asynchronously) 961 determines the redirect URL for a given wire target. See below for a 962 description of the high-level process for different providers. 963 964 .. code-block:: ini 965 966 [kyc-provider-$PROVIDER_ID] 967 968 # Which plugin is responsible for this provider? 969 LOGIC = PLUGIN_NAME 970 971 # Plus additional logic-specific options, e.g.: 972 AUTHORIZATION_TOKEN = superdupersecret 973 974 # Other logic-specific internal options (example): 975 FORM_ID = business_legi_form 976 977 978 Configuration of possible KYC/AML checks 979 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 980 981 The configuration specifies a set of possible KYC checks offered by external 982 providers, one per configuration section: 983 984 .. code-block:: ini 985 986 [kyc-check-$CHECK_NAME] 987 988 # Which type of check is this? Also determines 989 # the SPA form to show to the user for this check. 990 # 991 # INFO: wait for staff or contact staff out-of band 992 # (only information shown, no SPA action) 993 # FORM: SPA should show an inline (HTML) form 994 # LINK: SPA may start external KYC process or upload 995 # 996 TYPE = INFO|LINK|FORM 997 998 # Provider id, present only if type is LINK. 999 # Refers to a ``kyc-provider-$PROVIDER_ID`` section. 1000 PROVIDER_ID = id 1001 1002 # Name of the SPA form, if type is FORM 1003 # "INFO" and "LINK" are reserved and must not be used. 1004 # The exchange server and the SPA must agree on a list 1005 # of supported forms and the resulting attributes. 1006 # 1007 # The SPA should include a JSON resource file 1008 # "forms.json" mapping form names to arrays of 1009 # attribute names each form provides. 1010 FORM_NAME = name 1011 1012 # Descriptions to use in the SPA to display the check. 1013 DESCRIPTION = "Upload your passport picture" 1014 DESCRIPTION_I18N = "{"en":"Upload scan of your passport"}" 1015 1016 # ';'-separated list of fields that the CONTEXT must 1017 # provide as inputs to this check. For example, 1018 # for a FORM of type CHOICE, this might state 1019 # ``choices: string[];``. The type after the ":" 1020 # is for now purely for documentation and is 1021 # not checked. However, it may be shown to AML staff 1022 # when they configure measures. 1023 REQUIRES = requirement; 1024 1025 # Description of the outputs provided by the check. 1026 # Basically, the check's output is expected to 1027 # provide the following fields as attribute inputs into 1028 # a subsequent AML program. 1029 # Only given for type FORM; INFO never has any outputs, 1030 # and for type LINK we can obtain the same information 1031 # from the CONVERTER via ``--list-outputs``. 1032 OUTPUTS = business_name street city country registration 1033 1034 # **original** measure to take if the check fails 1035 # (for any reason, e.g. provider or form fail to 1036 # satisfy constraints or provider signals user error) 1037 # Usually should point to a measure that requests 1038 # AML staff to investigate. The fallback measure 1039 # context always includes the reasons for the 1040 # failure. 1041 FALLBACK = MEASURE_NAME 1042 1043 The list of possible FORM names is fixed in the SPA 1044 for a particular exchange release. 1045 1046 The outcome of *any* check should always be uploaded encrypted into the 1047 ``kyc_attributes`` table. It MUST include an ``expiration_time``. 1048 1049 The "check_name" value "skip" is reserved and must not be defined. It can be 1050 used in measures where the AML program must be run immediately without any 1051 input. 1052 1053 1054 Configuration of legitimization requirement triggers 1055 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1056 1057 The configuration also specifies a set of legitimization rules including the 1058 condition and the measure the condition triggers, one condition per 1059 configuration section: 1060 1061 .. code-block:: ini 1062 1063 [kyc-rule-$RULE_NAME] 1064 1065 # Operation that triggers this rule. 1066 # Must be one of "WITHDRAW", "DEPOSIT", 1067 # (p2p) "MERGE", (wallet) "BALANCE", 1068 # (reserve) "CLOSE", "AGGREGATE", 1069 # "TRANSACTION" or "REFUND". 1070 OPERATION_TYPE = WITHDRAW 1071 1072 # Space-separated list of next measures to be performed. 1073 # The SPA should display *all* of these measures to the user. 1074 # (They have a choice of either which ones, or in 1075 # which order they are to be performed.) 1076 # A special measure name "verboten" is used if the 1077 # specified threshold may never be crossed 1078 # (under this set of rules). 1079 NEXT_MEASURES = SWISSNESS KYB 1080 1081 # "YES" if all NEXT_MEASURES will eventually need 1082 # to be satisfied, "NO" if the user has a choice between 1083 # them. Not actually enforced by the exchange, but 1084 # primarily used to inform the user whether this is 1085 # an "and" or "or". YES for "and". 1086 IS_AND_COMBINATOR = YES 1087 1088 # YES if the rule (specifically, operation type, 1089 # threshold, timeframe) and the general nature of 1090 # the next measure (verboten or approval required) 1091 # should be exposed to the client. 1092 # Defaults to NO if not set. 1093 EXPOSED = YES 1094 1095 # Threshold amount above which the rule is 1096 # triggered. The total must be exceeded in the given 1097 # timeframe. 1098 THRESHOLD = KUDOS:100 1099 1100 # Timeframe over which the amount to be compared to 1101 # the THRESHOLD is calculated. 1102 # Ignored for WALLET-BALANCE. Can be 'forever'. 1103 TIMEFRAME = 30 days 1104 1105 # Set to YES to enable the rule (default is NO) 1106 ENABLED = NO 1107 1108 1109 AML programs 1110 ^^^^^^^^^^^^ 1111 1112 AML programs are helper programs that can: 1113 1114 * Generate a list of *required* context field names 1115 for the helper (introspection!) using the "--required-context" 1116 command-line switch. The output should use the same 1117 syntax as the REQUIRES clause of ``[kyc-check-]`` 1118 configuration sections, except that new lines 1119 MUST be used to separate fields instead of ";". 1120 * Generate a list of *required* attribute names 1121 for the helper (introspection!) using the "--required-attributes" 1122 command-line switch. The output should use the same 1123 list of names as the ATTRIBUTES in the 1124 ``[kyc-provider-]`` configuration section 1125 (but may also include FORM field names). 1126 * Process an input JSON object of type 1127 `AmlProgramInput` into a JSON object of 1128 type `AmlOutcome`. 1129 This is the default behavior if no command-line switches 1130 are provided. 1131 1132 If the AML program fails (exits with a failure code or 1133 does not provide well-formed JSON output) the AML/KYC 1134 process continues with the FALLBACK measure. This should 1135 usually be one that asks AML staff to contact the 1136 systems administrator. 1137 1138 AML programs are listed in the configuration file, one program per section: 1139 1140 .. code-block:: ini 1141 1142 [aml-program-$PROG_NAME] 1143 1144 # Program to run. 1145 COMMAND = taler-helper-aml-pep 1146 1147 # Human-readable description of what this 1148 # AML helper program will do. Used to show 1149 # to the AML staff. 1150 DESCRIPTION = "check if the customer is a PEP" 1151 1152 # True if this AML program is enabled (and thus can be 1153 # used in measures and exposed to AML staff). 1154 # Optional, default is NO. 1155 ENABLED = YES 1156 1157 # **original** measure to take if COMMAND fails 1158 # Usually points to a measure that asks AML staff 1159 # to contact the systems administrator. The fallback measure 1160 # context always includes the reasons for the 1161 # failure. 1162 FALLBACK = MEASURE_NAME 1163 1164 1165 Configuration of measures 1166 ^^^^^^^^^^^^^^^^^^^^^^^^^ 1167 1168 Finally, the configuration specifies a set of 1169 **original** *measures* one per configuration section: 1170 1171 .. code-block:: ini 1172 1173 [kyc-measure-$MEASURE_NAME] 1174 1175 # Possible check for this measure. Optional. 1176 # If not given, PROGRAM should be run immediately 1177 # (on an empty set of attributes). 1178 CHECK_NAME = IB_FORM 1179 1180 # Context for the check. The context can be 1181 # just an empty JSON object if there is none. 1182 CONTEXT = {"choices":["individual","business"]} 1183 1184 # Program name to run on the context and check data to 1185 # determine the outcome and next measure. 1186 # Refers to a ``[aml-program-$PROG_NAME]`` section name. 1187 PROGRAM = taler-aml-program 1188 1189 # Optional. Set to YES to allow this measure to be 1190 # done voluntarily by a client. Used to offer the 1191 # KYC SPA to display measure even if they are 1192 # not required. Default is NO. 1193 VOLUNTARY = YES/NO 1194 1195 1196 If ``CHECK_NAME`` is set to "SKIP" (or is not provided at all), the AML 1197 ``PROGRAM`` is to be run immediately. This is useful if no client-interaction 1198 is required to arrive at a decision. 1199 1200 .. note:: 1201 1202 The list of *measures* is not complete: AML staff may freely define new 1203 measures dynamically, usually by selecting checks, an AML program, and 1204 providing context. 1205 1206 1207 Sanity checking 1208 ^^^^^^^^^^^^^^^ 1209 1210 On start-up, ``taler-exchange-httpd`` should sanity-check its 1211 configuration. Specifically, it should validate that for all AML programs the 1212 input requirements (attributes and context) are claimed to be satisfied by the 1213 respective checks that may trigger those programs, and similarly that for all 1214 checks the original measures satisfy the context requirements for their KYC 1215 checks. 1216 1217 As a result, any component (AML program, form or external check) is warranted 1218 to be always called with the declared required inputs. Furthermore, we can 1219 detect if a component fails to produce the required output and the 1220 configuration contains (presumably safe) FALLBACKs to address this case. The 1221 exchange *MUST* detect circular failures, like when a FALLBACK triggers a 1222 measure that itself immediately triggers again the same FALLBACK. 1223 1224 1225 Exchange database schema 1226 ^^^^^^^^^^^^^^^^^^^^^^^^ 1227 1228 We introduce a new ``wire_targets`` table into the exchange database. This 1229 table is referenced as the source or destination of payments (regular deposits 1230 and also P2P payments). A positive side-effect is that we reduce duplication 1231 in the ``reserves_in``, ``wire_out`` and ``deposits`` tables as they can 1232 reference this table. 1233 1234 We introduce a new ``legitimization_processes`` table that tracks the status 1235 of a legitimization process at a provider, including the configuration section 1236 name, the user/account name at the provider, and some legitimization 1237 identifier for the process at the provider. In this table, we additionally 1238 store information related to the KYC status of the underlying payto://-URI, in 1239 particular when the KYC expires (0 if it was never done). 1240 1241 Finally, we introduce a new ``legitimization_requirements`` table that 1242 contains a list of checks required for a particular wire target. When KYC is 1243 triggered (say when some endpoint returns an HTTP status code of 451) a 1244 new requirement is first put into the requirements table. Then, when the 1245 client identifies as business or individual the specific legitimization 1246 process is started. When the taler-exchange-aggregator triggers a KYC check 1247 the merchant can observe this when a 202 (Accepted) status code is returned 1248 on GET ``/deposits/`` with the respective legitimization requirement row. 1249 1250 1251 .. sourcecode:: sql 1252 1253 CREATE TABLE wire_targets 1254 (wire_target_serial_id BIGSERIAL UNIQUE 1255 ,wire_target_h_payto BYTEA PRIMARY KEY CHECK (LENGTH(wire_target_h_payto)=32), 1256 ,access_token BYTEA UNIQUE CHECK (LENGTH(access_token)=32) DEFAULT gen_random_bytes(32) 1257 ,target_pub BYTEA CHECK (LENGTH(target_pub)=32) DEFAULT NULL 1258 ,payto_uri STRING NOT NULL 1259 ) 1260 PARTITION BY HASH (wire_target_h_payto); 1261 1262 COMMENT ON TABLE wire_targets 1263 IS 'All recipients of money via the exchange'; 1264 COMMENT ON COLUMN wire_targets.h_payto 1265 IS 'Unsalted hash of payto_uri'; 1266 COMMENT ON COLUMN wire_targets.access_token 1267 IS 'high-entropy random value that is used as a token to authorize access to the KYC process (without requiring a signature by target_priv)'; 1268 COMMENT ON COLUMN wire_targets.target_pub 1269 IS 'Public key (reserve_pub or merchant_pub) associated with the account; NULL if KYC is not allowed for the account (if there was no incoming KYC wire transfer yet); updated, thus NOT available to the auditor'; 1270 COMMENT ON COLUMN wire_targets.payto_uri 1271 IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)'; 1272 1273 CREATE TABLE IF NOT EXISTS legitimization_measures 1274 (legitimization_measure_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY 1275 ,access_token BYTEA NOT NULL UNIQUE CHECK (LENGTH(access_token)=32) 1276 REFERENCES wire_targets (access_token) 1277 ,start_time INT8 NOT NULL 1278 ,jmeasures TEXT NOT NULL 1279 ,display_priority INT4 NOT NULL 1280 ,is_finished BOOL NOT NULL DEFAULT(FALSE) 1281 ) 1282 PARTITION BY HASH (access_token); 1283 1284 COMMENT ON TABLE legitimization_measures 1285 IS 'Rules that have been triggered for the account (FIXME: check this is consistent with usage)'; 1286 COMMENT ON COLUMN legitimization_measures.access_token 1287 IS 'Used to uniquely identify the account and as a symmetric access control mechanism for the SPA'; 1288 COMMENT ON COLUMN legitimization_measures.start_time 1289 IS 'Time when the measure was triggered (by decision or rule)'; 1290 COMMENT ON COLUMN legitimization_measures.jmeasures 1291 IS 'JSON object of type LegitimizationMeasures with KYC/AML measures for the account encoded'; 1292 COMMENT ON COLUMN legitimization_measures.display_priority 1293 IS 'Display priority of the rule that triggered this measure; if in the meantime another rule also triggers, the measure is only replaced if the new rule has a higher display priority'; 1294 COMMENT ON COLUMN legitimization_measures.is_finished 1295 IS 'Set to TRUE if this set of measures was processed; used to avoid indexing measures that are done'; 1296 1297 CREATE INDEX ON legitimization_measures (access_token) 1298 WHERE NOT is_finished; 1299 1300 CREATE TABLE legitimization_outcomes 1301 (outcome_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY 1302 ,h_payto BYTEA CHECK (LENGTH(h_payto)=32) 1303 REFERENCES wire_targets (wire_target_h_payto) 1304 ,decision_time INT8 NOT NULL DEFAULT(0) 1305 ,expiration_time INT8 NOT NULL DEFAULT(0) 1306 ,jproperties TEXT, 1307 ,new_measure_name TEXT, 1308 ,to_investigate BOOL NOT NULL 1309 ,is_active BOOL NOT NULL DEFAULT(TRUE) 1310 ,jnew_rules TEXT NOT NULL 1311 ) 1312 PARTITION BY HASH (h_payto); 1313 1314 COMMENT ON TABLE legitimization_outcomes 1315 IS 'Outcomes can come from AML programs'; 1316 COMMENT ON COLUMN legitimization_outcomes.h_payto 1317 IS 'hash of the payto://-URI this outcome is about'; 1318 COMMENT ON COLUMN legitimization_outcomes.decision_time 1319 IS 'when was this outcome decided'; 1320 COMMENT ON COLUMN legitimization_outcomes.expiration_time 1321 IS 'time when the decision expires and the expiration jnew_rules should be applied'; 1322 COMMENT ON COLUMN legitimization_outcomes.jproperties 1323 IS 'JSON object of type AccountProperties, such as PEP status, business domain, risk assessment, etc.'; 1324 COMMENT ON COLUMN legitimization_outcomes.to_investigate 1325 IS 'AML staff should investigate the activity of this account'; 1326 COMMENT ON COLUMN legitimization_outcomes.is_active 1327 IS 'TRUE if this is the current authoritative legitimization outcome'; 1328 COMMENT ON COLUMN legitimization_outcomes.new_measure_name 1329 IS 'space-separated list of measures to trigger immediately, NULL for none, prefixed with a "+" to indicate AND combination for the measures'; 1330 COMMENT ON COLUMN legitimization_outcomes.jnew_rules 1331 IS 'JSON object of type LegitimizationRuleSet with rules to apply to the various operation types for this account; all KYC checks should first check if active new rules for a given account exist in this table (and apply specified measures); if not, it should check the default rules to decide if a measure is required'; 1332 1333 CREATE INDEX legitimization_outcomes_active 1334 ON legitimization_outcomes(h_payto) 1335 WHERE is_active; 1336 1337 CREATE TABLE legitimization_processes 1338 (legitimization_process_serial_id BIGSERIAL UNIQUE 1339 ,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64) 1340 REFERENCES wire_targets (wire_target_h_payto) 1341 ,start_time INT8 NOT NULL 1342 ,expiration_time INT8 NOT NULL DEFAULT (0) 1343 ,legitimization_measure_serial_id INT8 1344 REFERENCES legitimization_measures (legitimization_measure_serial_id) 1345 ,measure_index INT4 1346 ,provider_section TEXT NOT NULL 1347 ,provider_user_id TEXT DEFAULT NULL 1348 ,provider_legitimization_id TEXT DEFAULT NULL 1349 ,redirect_url TEXT DEFAULT NULL 1350 ,finished BOOLEAN DEFAULT (FALSE) 1351 ) 1352 PARTITION BY HASH (h_payto); 1353 1354 COMMENT ON TABLE legitimization_processes 1355 IS 'here we track KYC processes we initiated with external providers; the main reason is so that we do not initiate a second process when an equivalent one is still active; note that h_payto, provider_section, jcontext must match and the process must not be finished or expired for an existing redirect_url to be re-used; given that clients may voluntarily initiate KYC processes, there may not always be a legitimization_measure that triggered the setup'; 1356 COMMENT ON COLUMN legitimization_processes.h_payto 1357 IS 'foreign key linking the entry to the wire_targets table, NOT a primary key (multiple KYC setups are possible per wire target)'; 1358 COMMENT ON COLUMN legitimization_processes.start_time 1359 IS 'when was the legitimization process initiated'; 1360 COMMENT ON COLUMN legitimization_processes.expiration_time 1361 IS 'when does the process expire (and needs to be manually set up again)'; 1362 COMMENT ON COLUMN legitimization_processes.measure_index 1363 IS 'index of the measure in legitimization_measures that was selected for this KYC setup; NULL if legitimization_measure_serial_id is NULL; enables determination of the context data provided to the external process'; 1364 COMMENT ON COLUMN legitimization_processes.provider_section 1365 IS 'Configuration file section with details about this provider'; 1366 COMMENT ON COLUMN legitimization_processes.provider_user_id 1367 IS 'Identifier for the user at the provider that was used for the legitimization. NULL if provider is unaware.'; 1368 COMMENT ON COLUMN legitimization_processes.provider_legitimization_id 1369 IS 'Identifier for the specific legitimization process at the provider. NULL if legitimization was not started.'; 1370 COMMENT ON COLUMN legitimization_processes.legitimization_measure_serial_id 1371 IS 'measure that enabled this setup, NULL if client voluntarily initiated the process'; 1372 COMMENT ON COLUMN legitimization_processes.redirect_url 1373 IS 'Where the user should be redirected for this external KYC process'; 1374 COMMENT ON COLUMN legitimization_processes.finished 1375 IS 'set to TRUE when the specific legitimization process is finished'; 1376 1377 CREATE TABLE kyc_attributes 1378 (kyc_attributes_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY 1379 ,h_payto BYTEA PRIMARY KEY CHECK (LENGTH(h_payto)=32) 1380 REFERENCES wire_targets (wire_target_h_payto) 1381 ,legitimization_process_serial_id INT8 1382 REFERENCES legitimization_processes (legitimization_process_serial_id) 1383 DEFAULT NULL 1384 ,collection_time INT8 NOT NULL 1385 ,expiration_time INT8 NOT NULL 1386 ,trigger_outcome_serial INT8 NOT NULL 1387 REFERENCES legitimization_outcomes(outcome_serial_id) 1388 ,encrypted_attributes BYTEA NOT NULL 1389 ) PARTITION BY HASH (h_payto); 1390 1391 COMMENT ON COLUMN kyc_attributes.h_payto 1392 IS 'identifies the account this is about'; 1393 COMMENT ON COLUMN kyc_attributes.legitimization_process_serial_id 1394 IS 'serial ID of the legitimization process that resulted in these attributes, NULL if the attributes are from a form directly supplied by the account owner via a form'; 1395 COMMENT ON COLUMN kyc_attributes.collection_time 1396 IS 'when were these attributes collected'; 1397 COMMENT ON COLUMN kyc_attributes.expiration_time 1398 IS 'when are these attributes expected to expire'; 1399 COMMENT ON COLUMN kyc_attributes.trigger_outcome_serial 1400 IS 'ID of the outcome that was returned by the AML program based on the KYC data collected'; 1401 COMMENT ON COLUMN kyc_attributes.encrypted_attributes 1402 IS 'encrypted JSON object with the attribute data the check provided'; 1403 1404 CREATE TABLE aml_history 1405 (aml_history_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY 1406 ,h_payto BYTEA CHECK (LENGTH(h_payto)=32) 1407 REFERENCES wire_targets (wire_target_h_payto) 1408 ,outcome_serial_id INT8 NOT NULL 1409 REFERENCES legitimization_outcomes (outcome_serial_id) 1410 ,justification TEXT NOT NULL 1411 ,decider_pub BYTEA CHECK (LENGTH(decider_pub)=32) 1412 ,decider_sig BYTEA CHECK (LENGTH(decider_sig)=64); 1413 1414 COMMENT ON TABLE aml_history 1415 IS 'Records decisions by AML staff with the respective signature and free-form justification.'; 1416 COMMENT ON COLUMN aml_history.outcome_serial_id 1417 IS 'Actual outcome for the account (included in what decider_sig signs over)'; 1418 COMMENT ON COLUMN aml_history.decider_sig 1419 IS 'Signature key of the staff member affirming the AML decision; of type AML_DECISION'; 1420 1421 CREATE TABLE kyc_events 1422 (kyc_event_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY 1423 ,event_timestamp INT8 NOT NULL 1424 ,event_type TEXT NOT NULL); 1425 1426 COMMENT ON TABLE kyc_events 1427 IS 'Records of key events for statistics. Populated via triggers.'; 1428 COMMENT ON COLUMN kyc_events.event_type 1429 IS 'Name of the event, such as account-open or sar-filed'; 1430 1431 CREATE INDEX kyc_event_index 1432 ON kyc_events(event_type,event_timestamp); 1433 1434 1435 The ``jmeasures`` JSON in the ``legitimization_measures`` 1436 table is of type `LegitimizationMeasures`. 1437 1438 The ``jnew_rules`` JSON in the ``legitimization_outcomes`` 1439 table is of type `LegitimizationRuleSet`. 1440 1441 The ``jproperties`` JSON in the ``legitimization_outcomes`` table is of 1442 type `AccountProperties`. 1443 1444 1445 KYC forms 1446 ^^^^^^^^^ 1447 1448 The KYC SPA run by clients needs to support three TYPEs of checks. INFO is 1449 only about displaying the provided information, LINK is about setting up an 1450 exteral KYC check and redirecting there. FORM is about displaying a particular 1451 (HTML) form to the user and POSTing the entered information directly with the 1452 exchange. Here we describe the forms that must be supported: 1453 1454 * **CHOICE**: Asks the client a multiple-choice question. The context must 1455 include "choices: string[]" with a list of choices to show. Used, for 1456 example, to ask a client if they are an individual or a business. The 1457 resulting HTML FORM field name must be "choice" and it must be mapped to 1458 strings from the choices list. 1459 1460 * **UPLOAD**: Asks the client to upload a single file. 1461 The context must include a ``validity_duration`` which 1462 will be converted to the ``expiration_time`` for 1463 the uploaded data. The context may furthermore include 1464 ``extensions?: string[]`` with a list of allowed file extensions the client's 1465 file must end with (e.g. "png", "pdf", "gif"). In the absence of this 1466 context, any file may be uploaded. The context may also include a 1467 ``size_limit?: Integer`` with the maximum file size in bytes that can be 1468 uploaded. The resulting HTTP POST should provide at least two fields, "filename" and 1469 "filedata". "filename" must be set to the basename of the original file (to 1470 the extend that it is available), and "filedata" to the base64-encoding of 1471 the uploaded data. 1472 1473 As with other SPA checks, the KYC form should also show 1474 the description of the check. 1475 1476 1477 Merchant modifications 1478 ^^^^^^^^^^^^^^^^^^^^^^ 1479 1480 A new setting is required where the merchant backend can be configured for a 1481 business (default) or individual. 1482 1483 We introduce new ``kyc_ok``, ``aml_decision``, ``kyc_timestamp`` and 1484 ``exchange_kyc_serial`` fields into a new table ``merchant_kyc`` with primary 1485 keys ``exchange_url`` and ``account_serial``. This status is updated whenever 1486 a deposit is created or tracked, or whenever the mechant backend receives a 1487 ``/kyc-check/`` response from the exchange. Initially, 1488 ``exchange_kyc_serial`` is zero, indicating that the merchant has not yet made 1489 any deposits and thus does not have an account at the exchange. 1490 1491 A new private endpoint ``/kyc`` is introduced which allows frontends to 1492 request the ``/kyc`` status of any configured account (including with long 1493 polling). If the KYC status is negative or the ``kyc_timestamp`` not recent 1494 (say older than one month), the merchant backend will re-check the KYC status 1495 at the exchange (and update its cached status). The endpoint then returns 1496 either that the KYC is OK, or information (same as from the exchange endpoint) 1497 to begin the KYC process. 1498 1499 The merchant backend uses the new field to remember that a KYC is pending 1500 (after detection in ``taler-merchant-depositcheck``) and the SPA then shows a 1501 notification whenever the staff is logged in to the system. The notification 1502 can be hidden for the current day (remembered in local storage). 1503 1504 The notification links to a (new) KYC status page. When opened, the KYC SPA 1505 first re-checks the KYC status with the exchange. If the KYC is still 1506 unfinished, that SPA will show forms, links or contact information to begin 1507 the KYC process (for example, redirecting to the OAuth 2.0 login page of the 1508 legitimization resource server), otherwise it shows that the KYC process is 1509 done. If the KYC is unfinished, the merchant SPA should use long-polling on 1510 the KYC status on this page to ensure it is always up-to-date, and change to 1511 ``KYC satisfied`` should the long-poller return with positive news. 1512 1513 .. note:: 1514 1515 Semi-related: The TMH_setup_wire_account() is changed to use 1516 128-bit salt values (to keep ``deposits`` table small) and checks for salt 1517 to be well-formed should be added "everywhere". 1518 1519 1520 1521 Bank requirements 1522 ^^^^^^^^^^^^^^^^^ 1523 1524 The exchange primarily requires a KYC provider to be operated by the 1525 bank that offers an endpoint for with an API implemented by one of 1526 the logic plugins (and the respective legitimization configuration). 1527 1528 1529 Logic plugins 1530 ^^^^^^^^^^^^^ 1531 1532 The ``$PROVIDER_SECTION`` is based on the name of the configuration section, 1533 not on the name of the logic plugin (that we call ``$LOGIC``). Using the 1534 configuration section, the exchange then determines the logic plugin to use. 1535 1536 This section describes the general API for all of the supported KYC providers, 1537 as well as some details of how this general API could be implemented by the 1538 logic for different APIs. 1539 1540 1541 General KYC Logic Plugin API 1542 ---------------------------- 1543 1544 This section provides a sketch of the proposed API for the KYC logic plugins. 1545 1546 * initiation of KYC check (``kyc-check``): 1547 1548 - inputs: 1549 + provider_section (for additional configuration) 1550 + h_payto 1551 - outputs: 1552 + success/provider-failure 1553 + redirect URL (or NULL) 1554 + provider_user_id (or NULL) 1555 + provider_legitimization_id (or NULL) 1556 1557 * KYC status check (``kyc-proof``): 1558 1559 - inputs: 1560 + provider_section (for additional configuration) 1561 + h_payto 1562 + provider_user_id (or NULL) 1563 + provider_legitimization_id (or NULL) 1564 - outputs: 1565 + success/pending/user-aborted/user-failure/provider-failure status code 1566 + HTML response for end-user 1567 1568 * Webhook notification handler (``kyc-webhook``): 1569 1570 - inputs: 1571 + HTTP method (GET/POST) 1572 + rest of URL (after provider_section) 1573 + HTTP body (if applicable!) 1574 - outputs: 1575 + success/pending/user-aborted/user-failure/provider-failure status code 1576 + h_payto (for DB status update) 1577 + HTTP response to be returned to KYC provider 1578 1579 The plugins do not directly interact with the database, the caller sets the 1580 expiration on ``success`` and also updates ``provider_user_id`` and 1581 ``provider_legitimization_id`` in the tables as required. 1582 1583 1584 For the webhook, we need a way to lookup ``h_payto`` by other data, so the 1585 KYC logic plugin API should be provided a method lookup with: 1586 1587 - inputs: 1588 + ``provider_section`` 1589 + ``provider_legitimization_id`` 1590 - outputs: 1591 + ``h_payto`` 1592 + ``legitimization_process_row`` 1593 1594 1595 OAuth 2.0 specifics 1596 ------------------- 1597 1598 In terms of configuration, the OAuth 2.0 logic requires the respective client 1599 credentials to be configured apriori to enable access to the legitimization 1600 service. 1601 1602 For the ``/kyc-check/`` endpoint, the OAuth 2.0 logic may need to create and 1603 store a nonce to be used during ``/kyc-proof/``, depending on the OAuth 1604 variant used. This may require another exchange table. The OAuth 2.0 process 1605 must then be set up to end at the new ``/kyc-proof/$PROVIDER_ID/`` endpoint. 1606 1607 This ``/kyc-proof/oauth2/`` endpoint must query the OAuth 2.0 server using the 1608 ``code`` argument provided as a query parameter. Based on the result, it then 1609 updates the KYC table of the exchange with the legitimization status and 1610 returns a human-readable KYC status page. 1611 1612 The ``/kyc-webhook/`` is not applicable. 1613 1614 1615 Persona specifics 1616 ----------------- 1617 1618 We would use the hosted flow. Endpoints return a ``request-id``, which we should 1619 log for diagnosis. 1620 1621 For ``/kyc-check/``: 1622 1623 * Post to ``/api/v1/accounts`` using ``reference-id`` set to our ``h_payto``. 1624 Returns ``id`` (account_id). 1625 1626 * Create ``/verify`` endpoint using ``template-id`` (from configuration), 1627 and ``account_id`` (from previous step) and a ``reference-id`` (use 1628 the ``legitimization_serial_id`` for the new process). Set 1629 ``redirect-uri`` to ``/kyc-proof/$PROVIDER_ID/``. However, we cannot 1630 rely on the user clicking this, so we must also configure a webhook. 1631 The request returns a '``verification-id``. That we store under 1632 the ``provider_legitimization_id`` in the database. 1633 1634 For ``/kyc-proof/``: 1635 1636 * Use the ``/api/v1/verifications`` endpoint to get the verification 1637 status. Requires the ``verification-id`` from the previous step. 1638 Results include: created/pending/completed/expired (aborted)/failed. 1639 1640 For ``/kyc-webhook/``: 1641 1642 * The webhook is authenticated using a shared secret, which should 1643 be in the configuration. So all we should have to do is parse 1644 the POSTed body to find the status and the ``verification-id`` to 1645 lookup ``h_payto`` and return the result. 1646 1647 1648 KYC AID specifics 1649 ----------------- 1650 1651 For ``/kyc-check/``: 1652 1653 * Post to ``/applicants`` with a type (person or company) to 1654 obtain ``applicant_id``. Store that under ``provider_user_id``. 1655 ISSUE: *we* need to get the company_name, business_activity_id 1656 and registration_country before this somehow! 1657 1658 * start with create form URL ``/forms/$FORM_ID/urls`` 1659 providing our ``h_payto`` as the ``external_applicant_id``, 1660 using the ``applicant_id`` from above, 1661 and the ``/kyc-proof/$PROVIDER_ID`` for the ``redirect_url``. 1662 1663 * redirect customer to the ``form_url``, 1664 store the ``verification_id`` under ``provider_legitimization_id`` 1665 in the database. 1666 1667 For ``/kyc-proof/``: 1668 1669 * Not needed, just return an error. 1670 1671 For ``/kyc-webhook/``: 1672 1673 * For security, we should probably simply trigger the GET on 1674 ``/verifications/{verification_id}`` to not trust an unsigned POST 1675 to tell us anything for sure. The result is then returned. 1676 1677 1678 Types of KYC events 1679 ^^^^^^^^^^^^^^^^^^^ 1680 1681 The ``/aml/$OFFICER_PUB/kyc-statistics`` endpoint exposes statistics for 1682 various KYC event types. 1683 1684 We will initially support the use of the following types of KYC events in the 1685 SPA (and have a dialog to show the total number of any of these for any 1686 specified time range): 1687 1688 * account-open 1689 * account-closed 1690 * voluntary-sar 1691 * mandatory-sar 1692 * pep-started 1693 * pep-ended 1694 * risky-started 1695 * risky-ended 1696 * account-frozen 1697 * account-unfrozen 1698 1699 Based on these, the SPA should also be albe to show active 1700 statistics (for any given timestamp) on the total number of: 1701 1702 * open accounts 1703 * frozen accounts 1704 * high-risk accounts 1705 * PEPs served 1706 1707 .. note:: 1708 1709 This can be done by simply running the queries with 1710 a start time of zero and subtracting. 1711 1712 1713 Alternatives 1714 ============ 1715 1716 We could also store the access token (returned by OAuth 2.0), but that seems 1717 slightly more dangerous and given the close business relationship is 1718 unnecessary. Furthermore, not all APIs offer this. 1719 1720 We could extend the KYC logic API to return key attributes about the user 1721 (such as legal name, phone number, address, etc.) which we could then sign and 1722 return to the user. This would be useful in P2P payments to identify the 1723 origin of an invoice. However, we might want to be careful to not disclose 1724 the key attributes via the API by accident. This could likely be done by 1725 limiting access to the respective endpoint to messages with a signature by the 1726 reserve private key (which is the only case where we care to certify things 1727 anyway). 1728 1729 1730 Drawbacks 1731 ========= 1732 1733 1734 Discussion / Q&A 1735 ================ 1736 1737 (This should be filled in with results from discussions on mailing lists / personal communication.) 1738 1739 1740 If you have questions, remarks or suggestions regarding this information, 1741 please feel free to leave a comment at our 1742 `TALER Integration Community Hub <https://ich.taler.net/t/gnu-taler-exchange-documentation/78>`_.