summaryrefslogtreecommitdiff
path: root/design-documents
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2024-04-14 20:47:53 +0200
committerChristian Grothoff <christian@grothoff.org>2024-04-14 20:47:53 +0200
commit2fd678a6bf5841e6461d2819c0bac4d46d8a856a (patch)
tree92d2920018b3fba02e239e93a57311835842d936 /design-documents
parent6f44c7b029b5c59ac7602bde05fb49ad1599cb3e (diff)
downloaddocs-2fd678a6bf5841e6461d2819c0bac4d46d8a856a.tar.gz
docs-2fd678a6bf5841e6461d2819c0bac4d46d8a856a.tar.bz2
docs-2fd678a6bf5841e6461d2819c0bac4d46d8a856a.zip
kyc spec work
Diffstat (limited to 'design-documents')
-rw-r--r--design-documents/023-taler-kyc.rst694
1 files changed, 559 insertions, 135 deletions
diff --git a/design-documents/023-taler-kyc.rst b/design-documents/023-taler-kyc.rst
index 85478d8e..1eb0669e 100644
--- a/design-documents/023-taler-kyc.rst
+++ b/design-documents/023-taler-kyc.rst
@@ -84,9 +84,14 @@ There are also various *outcomes*:
* held, AML staff reviewing evidence for plausibilization (new measure)
* automatically frozen until certain day (due to sanctions)
* institutionally frozen until certain day (due to order by state authority)
+* operation is categorically not allowed (at least above certain limits)
-The outcome of a *check* can trigger further *measures* (including
-expiration of the outcome state).
+Outcomes may also be (partially) public, that is exposed to the client. For
+example, we may want to tell a wallet that it has hit a hard withdraw limit,
+but might succeed at withdrawing a smaller amount.
+
+The outcome of a *check* can set new rules or trigger another *measure* (the
+latter is conditional on reaching the expiration time of the outcome).
As a result, we largely end up in a large state machine where the AML staff has
serious flexibiltiy while the user needs guidance as to the possible next moves
@@ -219,11 +224,11 @@ Terminology
* **Measure**: Describes the possible outgoing edges from one state in the state machine (including how to show the current state). Each edge is given some *context* and a *check* to be performed as well as a *program* to decide the *outcome* and the next *measure*.
-* **Outcome**: Describes the account state that an account ends up in due to the result of a *check*. Outcomes can be that an account is frozen (no transactions possible until freeze expires), held (no transactions possible until another *measure* has been taken), or operating normally.
+* **Outcome**: Describes the account state that an account ends up in due to the result of a *check*. Outcomes can be that an account is frozen (no transactions possible until freeze expires), held (no transactions possible until another *measure* has been taken), or operating normally. Outcomes also include a new set of *legitimization rules* to apply and an expiration time at which point a new *measure* will be automatically taken. Finally, parts of the outcome may be explained to the client (for example, to allow a wallet to stay below hard withdraw thresholds).
* **Provider**: A provider performs a specific set of *checks* at a certain *cost*. Interaction with a provider is performed by provider-specific *logic*.
-* **Program**: An AML helper *program* is given *context* about the current state of an account and the data from a *check* to compute the *outcome*. For example, a *program* may look at the "PEP" field of a KYC check and decide if the outcome is to put the account into ``normal`` or ``held-for-manual-review`` state. A *program* operating on an AML form filed by AML staff will likely be trivial and directly apply the explicit decision taken by the staff member.
+* **Program**: An AML helper *program* is given *context* about the current state of an account and the attribute data from a *check* to compute the *outcome*. For example, a *program* may look at the "PEP" field of a KYC check and decide if the outcome is to put the account into ``normal`` or ``held-for-manual-review`` state.
* **Type of operation**: The operation type determines which Taler-specific operation has triggered the KYC requirement. We support four types of operation: withdraw (by customer), deposit (by merchant), P2P receive (by wallet) and (high) wallet balance.
@@ -231,7 +236,7 @@ Terminology
New Endpoints
^^^^^^^^^^^^^
-The new ``/kyc-check/`` endpoint is based on the legitimization requirements
+The ``/kyc-check/`` endpoint is based on the legitimization requirements
serial number and receives the business vs. individual status from the client.
Access is ``authenticated`` by also passing the hash of the payto://-URI.
(Weak authentication is acceptable, as the KYC status or the ability to
@@ -243,73 +248,332 @@ GET. It must always be the same endpoint for the same client, as the
wallet/merchant backend are not required to check for changes to this
endpoint.
-A new set of ``/kyc-spa/$HASH`` GET endpoints is created per client ``$HASH`` that
-serves the KYC SPA. This is where the ``/kyc-check/`` endpoint will redirect
-clients unless all KYC/AML requirements are satisfied. The KYC SPA will
-use the ``$HASH`` of its URL to initialize itself via the ``/kyc-info/$HASH``
-endpoint family.
-
-A new set of ``/kyc-info/$HASH`` GET endpoints is created per client ``$HASH``
-to return information about the state of the KYC or AML process to the client.
-The SPA uses this information to show the user an appropriate dialog. The SPA
-should also long-poll this endpoint for changes to the AML/KYC state. Note
-that this is a client-facing endpoint, so it will only provide a restricted
-amount of information to the customer (as some laws may forbid us to inform
-particular customers about their true status). The endpoint will typically
-inform the SPA about possible choices to proceed, such as directly uploading
-files, contacting AML staff, or proceeding with a particular KYC process at
-an external provider (such as Challenger). If the user chooses to initate a
-KYC process at an external provider, the SPA must request the respective
-process to be set-up by the exchange via the ``/kyc-start/`` endpoint.
-
-The new ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload
-client-provided evidence. The ``$ID`` will be provided as part of the
-``/kyc-info`` body. This is for checks of type ``FORM``.
-
-The new ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new
-external KYC process. It will return the (GET) URL that the client must open
-to begin the KYC process. The SPA should probably open this URL in a new
-window or tab. The ``$ID`` will be provided as part of the ``/kyc-info``
-body. As this endpoint is involved in every KYC check at the beginning, this
-is also the place where we could integrate the payment process for the KYC fee
-in the future.
-
-Upon completion of the process at the external KYC provider, the provider must
-trigger a GET request to a new ``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION``
-endpoint. This may be done either by redirecting the browser of the user to
-that endpoint. Once this endpoint is triggered, the exchange will pass the
-received arguments to the respective logic plugin. The logic plugin will then
-(asynchronously) update the KYC status of the user. The logic plugin should
-return a human-readable HTML page with the KYC result to the user.
-
-Alternatively, the KYC confirmation may be triggered by a ``/kyc-webhook``
-request. As KYC providers do not necessarily support passing detailed
-information in the URL arguments, the ``/kyc-webhook`` only needs to specify
-either the ``PROVIDER_SECTION`` *or* the ``LOGIC`` (the name of the plugin
-implementing the KYC API). The API-specific webhook logic must then figure
-out what exactly the webhook is about on its own. The ``/kyc-webhook/``
-endpoint works for GET or POST, again as details depend on the KYC provider.
-In contrast to ``kyc-proof``, the response does NOT go to the end-users'
-browser and should thus only indicate success or failure.
-
-The new ``/wallet-kyc`` POST endpoint allows a wallet to notify an exchange
-if it will cross a balance threshold. Here, the ``balance`` specified should
-be the threshold (from the ``wallet_balance_limit_without_kyc`` array) that
-the wallet would cross, and *not* the *exact* balance of the wallet. The
-exchange will respond with a wire target UUID. The wallet can then use this
-UUID to being the KYC process at ``/kyc-check/``. The wallet must only proceed
-to obtain funds exceeding the threshold after the KYC process has
-concluded. While wallets could be "hacked" to bypass this measure (we cannot
-cryptographically enforce this), such modifications are a terms of service
-violation which may have legal consequences for the user.
-
-
-To enable the AML staff SPA to give AML staff a choice of possible measures, a
-new endpoint ``/aml/$OFFICER_PUB/measures`` is added that allows the AML SPA
-to dynamically GET the list of available measures.
-It returns a list of known KYC checks (by name)
-with their descriptions and a list of AML programs
-with information about the required context.
+FIXME: update /kyc-check/ endpoint to expose *public* outcomes of previous
+checks (such as hard withdrawal limits)!
+
+.. http:get:: /kyc-spa/{$HASH,$FILENAME}
+
+ A set of ``/kyc-spa/$HASH`` GET endpoints is created per client ``$HASH``
+ that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint will
+ redirect clients unless all KYC/AML requirements are satisfied. The KYC SPA
+ will use the ``$HASH`` of its URL to initialize itself via the
+ ``/kyc-info/$HASH`` endpoint family. The KYC SPA may download additional
+ resources via ``/kyc-spa/$FILENAME``. The filenames must not match
+ base32-encoded SHA-512 hashes.
+
+.. http:get:: /kyc-info/$HASH
+
+ A new set of ``/kyc-info/$HASH`` GET endpoints is created per client
+ ``$HASH`` to return information about the state of the KYC or AML process to
+ the KYC SPA. The SPA uses this information to show the user an appropriate
+ dialog. The SPA should also long-poll this endpoint for changes to the AML/KYC
+ state. Note that this is a client-facing endpoint, so it will only provide a
+ restricted amount of information to the customer (as some laws may forbid us
+ to inform particular customers about their true status). The endpoint will
+ typically inform the SPA about possible choices to proceed, such as directly
+ uploading files, contacting AML staff, or proceeding with a particular KYC
+ process at an external provider (such as Challenger). If the user chooses to
+ initate a KYC process at an external provider, the SPA must request the
+ respective process to be set-up by the exchange via the ``/kyc-start/``
+ endpoint.
+
+ **Request**:
+
+ *If-None-Match*: The client MAY provide an ``If-None-Match`` header with
+ an ETag.
+
+ :query timeout_ms=MILLISECONDS: *Optional.* If specified, the exchange will wait up to MILLISECONDS for a change to a more recent legitimization measure before returning a 304 Not Modified.
+
+ **Response**:
+
+ :http:statuscode:`200 OK`:
+ The body is a `KycProcessClientInformation`.
+
+ *Etag*: Will be set to the serial ID of the measure. Used for long-polling.
+
+ .. ts:def:: KycProcessClientInformation
+
+ interface KycProcessClientInformation {
+
+ // List of requirements.
+ requirements?: { name : KycRequirementInformation};
+
+ // True if the client is expected to eventually satisfy all requirements.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean
+
+ // List of available voluntary checks the client could pay for.
+ // Since **vATTEST**.
+ voluntary_checks?: { name : KycCheckInformation};
+ }
+
+ .. ts:def:: KycRequirementInformation
+
+ interface KycRequirementInformation {
+
+ // Which form should be used? Common values include "INFO"
+ // (to just show the descriptions but allow no action),
+ // "LINK" (to enable the user to obtain a link via
+ // ``/kyc-start/``) or any build-in form name supported
+ // by the SPA.
+ form: string;
+
+ // English description of the requirement.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n ?: { [lang_tag: string]: string };
+
+ // ID of the requirement, useful to construct the
+ // ``/kyc-upload/$ID`` or ``/kyc-start/$ID`` endpoint URLs.
+ // Present if and only if "form" is not "INFO".
+ id?: string;
+
+ }
+
+ .. ts:def:: KycCheckInformation
+
+ // Since **vATTEST**.
+ interface KycCheckInformation {
+
+ // How much would this check cost the client?
+ cost: Amount;
+
+ // English description of the check.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n ?: { [lang_tag: string]: string };
+
+ }
+
+ :http:statuscode:`204 No Content`:
+ There are no open KYC requirements or possible voluntary checks
+ the client might perform.
+
+ :http:statuscode:`304 Not Modified`:
+ The KYC requirements did not change.
+
+.. http:post:: /kyc-upload/$ID
+
+ The ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload
+ client-provided evidence. The ``$ID`` will be provided as part of the
+ ``/kyc-info`` body. This is for checks of type ``FORM``.
+
+ **Request**:
+
+ Basically oriented along the possible formats of a HTTP form being
+ POSTed. Details will depend on the form. The server will try to
+ decode the uploaded body from whatever format it is provided in.
+
+ **Response**:
+
+ :http:statuscode:`204 No Content`:
+ The information was successfully uploaded. The SPA should fetch
+ an updated ``/kyc-info/``.
+ :http:statuscode:`404 Not Found`:
+ The ``$ID`` is unknown to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The upload conflicts with a previous upload.
+ :http:statuscode:`413 Content Too Large`:
+ The body is too large.
+
+.. http:post:: /kyc-start/$ID
+
+ The ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new
+ external KYC process. It will return the URL that the client must GET
+ to begin the KYC process. The SPA should probably open this URL in a new
+ window or tab. The ``$ID`` will be provided as part of the ``/kyc-info``
+ body.
+
+ **Request**:
+
+ Use empty JSON body for now.
+
+ **Response**:
+
+ :http:statuscode:`200 Ok`:
+ The KYC process was successfully initiated. The URL is in a
+ `KycProcessStartInformation` object.
+
+ .. ts:def:: KycProcessStartInformation
+
+ interface KycProcessStartInformation {
+
+ // URL to open.
+ redirect_url: string;
+ }
+
+ :http:statuscode:`404 Not Found`:
+ The ``$ID`` is unknown to the exchange.
+
+ .. note::
+
+ As this endpoint is involved in every KYC check at the beginning, this
+ is also the place where we could integrate the payment process for the KYC fee
+ in the future (since **vATTEST**).
+
+
+.. http:get:: /kyc-proof/$H_PAYTO/$PROVIDER_SECTION
+
+ Upon completion of the process at the external KYC provider, the provider
+ must trigger a GET request to a new ``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION``
+ endpoint. This may be done either by redirecting the browser of the user to
+ that endpoint. Once this endpoint is triggered, the exchange will pass the
+ received arguments to the respective logic plugin. The logic plugin will then
+ (asynchronously) update the KYC status of the user. The logic plugin should
+ return a human-readable HTML page with the KYC result to the user.
+
+.. http:get:: /kyc-webhook/*``
+.. http:post:: /kyc-webhook/*``
+
+ Alternatively, the KYC confirmation may be triggered by a ``/kyc-webhook``
+ request. As KYC providers do not necessarily support passing detailed
+ information in the URL arguments, the ``/kyc-webhook`` only needs to specify
+ either the ``PROVIDER_SECTION`` *or* the ``LOGIC`` (the name of the plugin
+ implementing the KYC API). The API-specific webhook logic must then figure
+ out what exactly the webhook is about on its own. The ``/kyc-webhook/``
+ endpoint works for GET or POST, again as details depend on the KYC provider.
+ In contrast to ``kyc-proof``, the response does NOT go to the end-users'
+ browser and should thus only indicate success or failure.
+
+.. http:post:: /wallet-kyc``
+
+ The ``/wallet-kyc`` POST endpoint allows a wallet to notify an exchange if
+ it will cross a balance threshold. Here, the ``balance`` specified should be
+ the threshold (from the ``wallet_balance_limit_without_kyc`` array) that the
+ wallet would cross, and *not* the *exact* balance of the wallet. The exchange
+ will respond with a wire target UUID. The wallet can then use this UUID to
+ being the KYC process at ``/kyc-check/``. The wallet must only proceed to
+ obtain funds exceeding the threshold after the KYC process has concluded.
+ While wallets could be "hacked" to bypass this measure (we cannot
+ cryptographically enforce this), such modifications are a terms of service
+ violation which may have legal consequences for the user.
+
+.. http:get:: /aml/$OFFICER_PUB/measures
+
+ To enable the AML staff SPA to give AML staff a choice of possible measures, a
+ new endpoint ``/aml/$OFFICER_PUB/measures`` is added that allows the AML SPA
+ to dynamically GET the list of available measures. It returns a list of known
+ KYC checks (by name) with their descriptions and a list of AML programs with
+ information about the required context.
+
+ **Request**:
+
+ FIXME: see other AML GET requests?
+
+ **Response**:
+
+ :http:statuscode:`200 Ok`:
+ Information about possible measures is returned in a
+ `AvailableMeasureSummary` object.
+
+ .. ts:def:: AvailableMeasureSummary
+
+ interface AvailableMeasureSummary {
+
+ // Available original measures that can be
+ // triggered directly by default rules.
+ roots: { "$measure_name" : MeasureInformation };
+
+ // Available AML programs.
+ programs: { "$prog_name" : AmlProgramRequirement };
+
+ // Available KYC checks.
+ checks: { "$check_name" : KycCheckInformation };
+
+ }
+
+ .. ts:def:: MeasureInformation
+
+ interface MeasureInformation {
+
+ // Name of a KYC check.
+ check_name: string;
+
+ // Name of an AML program.
+ prog_name: string;
+
+ // Context for the check. Could be empty.
+ context: Object;
+
+ }
+
+ .. ts:def:: AmlProgramRequirement
+
+ interface AmlProgramRequirement {
+
+ // Description of what the AML program does.
+ description: string;
+
+ // List of required field names in the context
+ // to run this AML program.
+ // SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using
+ // this AML program.
+ context: string[];
+
+ // List of required attribute names in the
+ // input of this AML program. These attributes
+ // are the minimum that the check must produce
+ // (it may produce more).
+ inputs: string[];
+
+ }
+
+ .. ts:def:: KycCheckInformation
+
+ interface KycCheckInformation {
+
+ // Description of the KYC check. Should be shown
+ // to the AML staff but will also be shown to the
+ // client when they initiate the check in the KYC SPA.
+ description: string;
+ description_i18n: {};
+
+ // Names of the fields that the CONTEXT must provide
+ // as inputs to this check.
+ // SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using
+ // this check.
+ requires: string[];
+
+ // Names of the attributes the check will output.
+ // SPA must check that the outputs match the
+ // required inputs when combining a KYC check
+ // with an AML program into a measure.
+ outputs: string[];
+
+ // Name of a root measure taken when this check fails.
+ fallback: string;
+ }
+
+.. http:get:: /aml/kyc-statistics/$NAME
+
+ Returns the number of KYC events matching the given
+ event type ``$NAME`` in the specified time range.
+ Note that this query can be slow as the statistics
+ are computed on-demand. (This is OK as such requests
+ should be rare.)
+
+ **Request**:
+
+ FIXME: add authorization checks, as with other /aml/ requests.
+
+ :query start_date=TIMESTAMP: *Optional*. Specifies the date when to start looking (inclusive). If not given, the start time of the exchange operation is used.
+ :query end_date=TIMESTAMP: *Optional*. Specifies the date when to stop looking (exclusive). If not given, the current date is used.
+
+ **Response**:
+
+ .. ts:def:: EventCounter
+
+ interface EventCounter {
+ // Number of events of the specified type in
+ // the given range.
+ cnt: Integer;
+ }
Modifications to existing endpoints
@@ -365,6 +629,7 @@ description of the high-level process for different providers.
# Optional cost, useful if clients want to voluntarily
# trigger authentication procedures for attestation.
+ # Since **vATTEST**.
COST = EUR:5
# Plus additional logic-specific options, e.g.:
@@ -373,12 +638,17 @@ description of the high-level process for different providers.
# Other logic-specific internal options (example):
FORM_ID = business_legi_form
- # Description of the outputs provided by the check.
- # Basically, the check's output is expected to
- # provide the following fields as inputs into
- # a subsequent AML program.
- ATTRIBUTES = business_name street city country registration
-
+ # Name of a program to run on the output of the plugin
+ # to convert the result into the desired set of attributes.
+ # The converter must create a log for the system administrator
+ # if the provided inputs do not match expectations.
+ # Note that the converter will be expected to output the
+ # set of attributes listed under the respective ``[kyc-check-*]``
+ # sections. Calling the converter with ``--list-outputs``
+ # should generate a (newline-separated) list of attributes
+ # the converter promises to generate in its JSON output
+ # (when run regularly).
+ CONVERTER = taler-exchange-helper-$NAME
Configuration of possible KYC/AML checks
@@ -406,12 +676,20 @@ providers, one per configuration section:
# still have to pay for it). Used to offer the
# SPA to display checks even if they are
# not required. Default is NO.
+ # Since **vATTEST**.
VOLUNTARY = YES/NO
- # Provider name, if type is LINK
- PROVIDER_NAME = name
+ # Provider id, present only if type is LINK.
+ PROVIDER_ID = id
- # Provider name, if type is FORM
+ # Name of the SPA form, if type is FORM
+ # "INFO" and "LINK" are reserved and must not be used.
+ # The exchange server and the SPA must agree on a list
+ # of supported forms and the resulting attributes.
+ #
+ # The SPA should include a JSON resource file
+ # "forms.json" mapping form names to arrays of
+ # attribute names each form provides.
FORM_NAME = name
# Descriptions to use in the SPA to display the check.
@@ -427,6 +705,12 @@ providers, one per configuration section:
# when they configure measures.
REQUIRES = requirement;
+ # Description of the outputs provided by the check.
+ # Basically, the check's output is expected to
+ # provide the following fields as inputs into
+ # a subsequent AML program.
+ OUTPUTS = business_name street city country registration
+
# **original** measure to take if the check fails
# (for any reason, e.g. provider or form fail to
# satisfy constraints or provider signals user error)
@@ -463,17 +747,26 @@ configuration section:
# display *all* of these measures to the user.
# (they have a choice of either which ones, or in
# which order they are to be performed).
+ # A special measure "verboten" is used if the
+ # threshold may never be crossed.
NEXT_MEASURES = SWISSNESS KYB
# Context for each of the above measures, optional.
MEASURE_CONTEXT_$NAME = CONTEXT
- # AND if all REQUIRED_MEASURES will eventually need
- # to be satisfied, OR if the user has a choice between
+ # "yes" if all REQUIRED_MEASURES will eventually need
+ # to be satisfied, "no" if the user has a choice between
# them. Not actually enforced by the exchange, but
# primarily used to inform the user whether this is
- # an "and" or "or".
- COMBINATOR = AND|OR
+ # an "and" or "or". YES for "and".
+ AND_COMBINATOR = YES
+
+ # YES if the rule (specifically, operation type,
+ # threshold, timeframe) and the general nature of
+ # the next measure (verboten or approval required)
+ # should be exposed to the client.
+ # Defaults to NO if not set.
+ EXPOSED = YES
# Threshold amount above which the legitimization is
# triggered. The total must be exceeded in the given
@@ -506,13 +799,136 @@ AML programs are helper programs that can:
list of names as the ATTRIBUTES in the
``[kyc-provider-]`` configuration section
(but may also include FORM field names).
-* Process an input JSON object with context and
- attributes into an *outcome*. This is the
- default behavior if no command-line switches
+* Process an input JSON object of type
+ `AmlProgramInput` into a JSON object of
+ type `AmlProgramOutcome`.
+ This is the default behavior if no command-line switches
are provided.
-AML programs are listed in the configuration file,
-one program per section:
+.. ts:def:: AmlProgramInput
+
+ interface AmlProgramInput {
+
+ // JSON object that was provided as
+ // part of the *measure*. This JSON object is
+ // provided under "context" in the main JSON object
+ // input to the AML program. This "context" should
+ // satify both the REQUIRES clause of the respective
+ // check and the output of "--requires" from the
+ // AML program's command-line option.
+ context?: Object;
+
+ // JSON object that captures the
+ // output of a ``[kyc-provider-]`` or (HTML) FORM.
+ // The keys in the JSON object will be the attribute
+ // names and the values must be strings representing
+ // the data. In the case of file uploads, the data
+ // MUST be base64-encoded.
+ attributes: Object;
+
+ // JSON array with the results of historic
+ // AML desisions about the account.
+ // FIXME: review AmlDecisionDetail spec!
+ // (need to enable new outcomes!)
+ aml_history: AmlDecisionDetail[];
+
+ // JSON array with the results of historic
+ // KYC data about the account.
+ // FIXME: review KycDetail spec!
+ // (need to include AmlProgramOutcome!)
+ kyc_history: KycDetail[];
+
+ }
+
+.. ts:def:: AmlProgramOutcome
+
+ interface AmlProgramOutcome {
+
+ // Should the client's account be investigated
+ // by AML staff?
+ // Defaults to false.
+ to_investigate?: boolean;
+
+ // Should the client's account be frozen?
+ // Defaults to false.
+ is_frozen?: boolean;
+
+ // Was the client's account reported to the authorities?
+ // Defaults to false.
+ is_reported?: boolean;
+
+ // Free-form properties about the account.
+ // Can be used to store properties such as PEP,
+ // risk category, type of business, hits on
+ // sanctions lists, etc.
+ properties?: Object;
+
+ // Types of events to add to the KYC events table.
+ // (for statistics).
+ events?: string[];
+
+ // When does the outcome expire?
+ expiration: Timestamp;
+
+ // Name of the measure to apply the outcome expires.
+ // If not set, we revert to the default set
+ // of rules (and the default account state).
+ successor_measure: string;
+
+ // Array of KYC rules to apply. Note that this
+ // array overrides *all* of the default rules.
+ // Thus, if the array does not have an entry for
+ // a particular operation, there would be
+ // no thresholds for that operation!
+ rules: KycRule[];
+
+ // Custom measures that KYC rules may refer to.
+ custom_measures: { "name" : MeasureInformation };
+
+ }
+
+.. ts:def:: KycRule
+
+ interface KycRule {
+
+ // Type of operation to which the rule applies.
+ operation_type: String;
+
+ // Measure to be taken if the given
+ // threshold is crossed over the given timeframe.
+ threshold: Amount;
+
+ // Over which duration should the threshold be
+ // computed.
+ timeframe: RelativeTime;
+
+ // Array of names of measures to apply.
+ // Names listed can be original measures or
+ // custom measures from the `AmlProgramOutcome`.
+ // A special measure "verboten" is used if the
+ // threshold may never be crossed.
+ measures: String[];
+
+ // True if the rule (specifically, operation_type,
+ // threshold, timeframe) and the general nature of
+ // the measures (verboten or approval required)
+ // should be exposed to the client.
+ // Defaults to "false" if not set.
+ exposed?: boolean;
+
+ // True if all the measures will eventually need to
+ // be satisfied, false if any of the measures should
+ // do.
+ and_combinator: boolean;
+ }
+
+If the AML program fails (exits with a failure code or
+does not provide well-formed JSON output) the AML/KYC
+process continues with the FALLBACK measure. This should
+usually be one that asks AML staff to contact the
+systems administrator.
+
+AML programs are listed in the configuration file, one program per section:
.. code-block:: ini
@@ -521,8 +937,15 @@ one program per section:
# Program to run.
COMMAND = taler-helper-aml-pep
- # Enabled (default is NO)
- ENABLED = NO
+ # Human-readable description of what this
+ # AML helper program will do. Used to show
+ # to the AML staff.
+ DESCRIPTION = "check if the customer is a PEP"
+
+ # True if this AML program is enabled (and thus can be
+ # used in measures and exposed to AML staff).
+ # Optional, default is NO.
+ ENABLED = YES
# **original** measure to take if COMMAND fails
# Usually points to a measure that asks AML staff
@@ -531,37 +954,6 @@ one program per section:
# failure.
FALLBACK = MEASURE_NAME
-The JSON input of an AML program consists of three parts:
-
-* "context": JSON object that was provided as
- part of the *measure*. This JSON object is
- provided under "context" in the main JSON object
- input to the AML program. This "context" should
- satify both the REQUIRES clause of the respective
- check and the output of "--requires" from the
- AML program's command-line option.
-* "attributes": JSON object that captures the
- output of a ``[kyc-provider-]`` or (HTML) FORM.
- The keys in the JSON object will be the attribute
- names and the values must be strings representing
- the data. In the case of file uploads, the data
- MUST be base64-encoded.
-* "history": JSON array with the results of historic
- data collected about the user.
-
-The output of an AML programs must be JSON objects which must state:
-
-* outcome: what to do with the client's account
-* expiration_date: when does the decision expire (zero to take the next measure immediately)
-* combinator: "AND" if all of the 'next_measures' will eventually need to be satisfied, "OR" if those are choices and the user only has to satisfy one of them.
-* next_measures: measures to trigger upon expiration of the current outcome; array entries must be either a string with the name of an **original** context-free measure, or JSON objects with the same information that is usually in ``[kyc-measures-]`` configuration sections (with a check name, context, and AML program name).
-
-If the AML program fails (exits with a failure code or
-does not provide well-formed JSON output) the AML/KYC
-process continues with the FALLBACK measure. This should
-usually be one that asks AML staff to contact the
-systems administrator.
-
Configuration of measures
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -578,8 +970,7 @@ Finally, the configuration specifies a set of
# (on an empty set of attributes).
CHECK_NAME = IB_FORM
- # Context for the check.
- # The context can be
+ # Context for the check. The context can be
# just an empty JSON object if there is none.
CONTEXT = {"choices":["individual","business"]}
@@ -587,16 +978,15 @@ Finally, the configuration specifies a set of
# determine the outcome and next measure.
PROGRAM = taler-aml-program
-If no ``CHECK_NAME`` is provided at all, the
-AML ``PROGRAM`` is to be run immediately. This is
-useful if no client-interaction is required to
-arrive at a decision.
+If no ``CHECK_NAME`` is provided at all, the AML ``PROGRAM`` is to be run
+immediately. This is useful if no client-interaction is required to arrive at
+a decision.
.. note::
- The list of *measures* is not complete: AML staff may freely define new
-measures dynamically, usually by selecting checks, an AML program, and
-providing context.
+ The list of *measures* is not complete: AML staff may freely define new
+ measures dynamically, usually by selecting checks, an AML program, and
+ providing context.
Sanity checking
@@ -717,7 +1107,7 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
COMMENT ON COLUMN legitimization_outcomes.is_active
IS 'TRUE if this is the current authoritative legitimization outcome';
COMMENT ON COLUMN legitimization_outcomes.new_rules
- IS 'JSON-encoding of KYC-rules to apply to the various operation types for this account; KYC check should first check if active new rules for a given account exist (and apply specified measures); if not, it should check the default rules to decide if a measure is required';
+ IS 'JSON array of KycRule objects 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';
CREATE INDEX legitimization_outcomes_active
ON legitimization_outcomes(h_payto)
@@ -858,8 +1248,8 @@ the description of the check.
Merchant modifications
^^^^^^^^^^^^^^^^^^^^^^
-A new setting is required where the merchant backend
-can be configured for a business (default) or individual.
+A new setting is required where the merchant backend can be configured for a
+business (default) or individual.
We introduce new ``kyc_ok``, ``aml_decision``, ``kyc_timestamp`` and
``exchange_kyc_serial`` fields into a new table ``merchant_kyc`` with primary
@@ -1056,6 +1446,40 @@ For ``/kyc-webhook/``:
to tell us anything for sure. The result is then returned.
+Types of KYC events
+^^^^^^^^^^^^^^^^^^^
+
+The ``/aml/kyc-statistics`` endpoint exposes statistics
+for various KYC event types.
+
+We will initially support the use of the following types
+of KYC events in the SPA (and have a dialog to show the
+total number of any of these for any specified time range):
+
+* account-open
+* account-closed
+* voluntary-sar
+* mandatory-sar
+* pep-started
+* pep-ended
+* risky-started
+* risky-ended
+* account-frozen
+* account-unfrozen
+
+Based on these, the SPA should also be albe to show active
+statistics (for any given timestamp) on the total number of:
+
+* open accounts
+* frozen accounts
+* high-risk accounts
+* PEPs served
+
+.. note::
+
+ This can be done by simply running the queries with
+ a start time of zero and subtracting.
+
Alternatives
============