summaryrefslogtreecommitdiff
path: root/design-documents
diff options
context:
space:
mode:
Diffstat (limited to 'design-documents')
-rw-r--r--design-documents/000-template.rst25
-rw-r--r--design-documents/001-new-browser-integration.rst214
-rw-r--r--design-documents/002-wallet-exchange-management.rst365
-rw-r--r--design-documents/003-tos-rendering.rst105
-rw-r--r--design-documents/004-wallet-withdrawal-flow.rst144
-rw-r--r--design-documents/005-wallet-backup-sync.rst331
-rw-r--r--design-documents/006-anastasis-ux.rst180
-rw-r--r--design-documents/index.rst18
8 files changed, 1382 insertions, 0 deletions
diff --git a/design-documents/000-template.rst b/design-documents/000-template.rst
new file mode 100644
index 00000000..f620248d
--- /dev/null
+++ b/design-documents/000-template.rst
@@ -0,0 +1,25 @@
+Template
+########
+
+Summary
+=======
+
+Motivation
+==========
+
+Requirements
+============
+
+Proposed Solution
+=================
+
+Alternatives
+============
+
+Drawbacks
+=========
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/001-new-browser-integration.rst b/design-documents/001-new-browser-integration.rst
new file mode 100644
index 00000000..8dbe2a43
--- /dev/null
+++ b/design-documents/001-new-browser-integration.rst
@@ -0,0 +1,214 @@
+Design Doc 001: New Browser Integration
+#######################################
+
+.. warning::
+
+ We have decided not to follow through with the proposed solution in this
+ design doc. We care a lot about a nice upgrade path for when better
+ browser integration becomes available. Encouraging the ``#taler://`` fragment
+ based integration might lead merchant frontends to **only** support this type
+ of integration.
+
+ Instead, the following path will be taken:
+
+ 1. CSS-based presence detection will be removed from the wallet,
+ as there is no satisfactory upgrade path to better mechanisms
+ 2. Manual triggering will be implemented as described in this design doc.
+ 3. The ``webRequest`` permission that allows ``"Taler: "`` header based
+ browser integration will become opt-in.
+ 4. The interactive API will be put on hold. Instead, SPAs should
+ ask the user to open the wallet popup (and/or render a QR code for mobile wallets).
+ 5. To enable easier integration for merchants, the reference merchant backend
+ might include a page to trigger payments, which displays the QR code
+ correctly, does long-polling via JS and serves the ``"Taler: "`` header.
+ 6. The presence detection ``taler://`` URI described in this document
+ will **not** be supported, as allowing presence detection might
+ encourage merchants to treat mobile / detached wallets as 2nd class
+ citizens.
+
+Summary
+=======
+
+A new and improved mechanism for the integration of GNU Taler wallets with web
+browsers is proposed. The mechanism is meant for browsers that support the
+WebExtension API, but do not have native support for GNU Taler.
+
+The new approach allows the wallet extension to be installed without
+excessive, "scary" permissions, while being simpler and still flexible.
+
+
+Motivation
+==========
+
+The current browser integration of the GNU Taler wallet relies heavily being
+able to hook into various browser mechanisms via the following mechanisms:
+
+* A blocking ``webRequest`` handler that is run for every request the browser
+ makes, and looks at the status code and the presence of a "``Taler:``" HTTP header.
+* A content script that's injected on every (!) page, which injects CSS (for
+ wallet presence detection) and JavaScript listeners into the page. The
+ injection is opt-in via a "data-taler" tag on the root html element.
+
+This has multiple problems:
+
+* It requires excessive permissions on **all** Websites. This is scary for us (in case we mess up)
+ and for users. It also slows down the publication of the extension on extension stores.
+* We have not measured the performance implications, but our JavaScript code is executed for every
+ single request the browser is making.
+* The CSS-based wallet detection integration is not very flexible. Only being able
+ to show/hide some element when the wallet is detected / not detected might not be
+ the optimal thing to do when we now have mobile wallets.
+
+
+Requirements
+============
+
+* The new browser integration should require as few permissions as possible.
+ In particular, the wallet may not require "broad host" permissions.
+* Fingerprinting via this API should be minimized.
+* It must be possible for Websites to interact with the wallet without using JavaScript.
+* Single Page Apps (using JavaScript) should be able to interact the wallet without
+ requiring a browser navigation.
+
+
+Proposed Solution
+=================
+
+We first have to accept the fundamental limitation that a WebExtension is not
+able to read a page's HTTP request headers without intrusive permissions.
+Instead, we need to rely on the content and/or URL of the fallback page that is
+being rendered by the merchant backend.
+
+To be compatible with mobile wallets, merchants and banks **must** always render a fallback
+page that includes the same ``taler://`` URI.
+
+Manual Triggering
+-----------------
+
+Using the only the ``activeTab`` permission, we can access a page's content
+*while and only while* the user is opening the popup (or a page action).
+The extension should look at the DOM and search for ``taler://`` links.
+If such a link as been found, the popup should display an appropriate
+dialog to the user (e.g. "Pay with GNU Taler on the current page.").
+
+Using manual triggering is not the best user experience, but works on every Website
+that displays a ``taler://`` link.
+
+.. note::
+
+ Using additional permissions, we could also offer:
+
+ * A context ("right click") menu for ``taler://pay`` links
+ * A declarative pageAction, i.e. an additional clickable icon that shows up
+ on the right side of the address bar. Clicking it would lead to directly
+ processing the ``taler://`` link.
+
+ It's not clear if this improves the user experience though.
+
+
+Fragment-based Triggering
+-------------------------
+
+This mechanism improves the user experience, but requires extra support from merchants
+and broader permissions, namely the ``tabs`` permission. This permission
+is shown as "can read your history", which sounds relatively intrusive.
+We might decide to make this mechanism opt-in.
+
+The extension uses the ``tabs`` permission to listen to changes to the
+URL displayed in the currently active tab. It then parses the fragment,
+which can contain a ``taler://`` URI, such as:
+
+.. code:: none
+
+ https://shop.taler.net/checkout#taler://pay/backend.shop.taler.net/-/-/2020.099-03C5F644XCNMR
+
+The fragment is processed the same way a "Taler: " header is processed.
+For examle, a ``taler://pay/...`` fragment navigates to an in-wallet page
+and shows a payment request to the user.
+
+
+Fragment-based detection
+------------------------
+
+To support fragment-based detection of the wallet, a special
+``taler://check-presence/${redir}`` URL can be used to cause a navigation to
+``${redir}`` if the wallet is installed. The redirect URL can be absolute or
+relative to the current page and can contain a fragment.
+
+For example:
+
+.. code:: none
+
+ https://shop.taler.net/checkout#taler://check-presence/taler-installed
+
+ -> (when wallet installed)
+
+ https://shop.taler.net/taler-installed
+
+
+To preserve correct browser history navigation, the wallet does not initiate the redirect if
+the tab's URL changes from ``${redir}`` back to the page with the ``check-presence`` fragment.
+
+
+Asynchronous API
+----------------
+
+The fragment-based triggering does not work well on single-page apps: It
+interferes with the SPA's routing, as it requires a change to the navigation
+location's fragment.
+
+The only way to communicate with a WebExtension is by knowing its extension ID.
+However, we want to allow users to build their own version of the WebExtension,
+and extensions are assigned different IDs in different browsers. We thus need
+a mechanism to obtain the wallet extension ID in order to asynchronously communicate
+with it.
+
+To allow the Website to obtain this extension ID, we can extend the redirection URL
+of the ``taler://check-presence`` fragment to allow a placeholder for the extension ID.
+
+.. code:: none
+
+ https://shop.taler.net/checkout#taler://check-presence/#taler-installed-${extid}
+
+ -> (when wallet installed)
+
+ https://shop.taler.net/checkout#taler-installed-12345ASDFG
+
+.. warning::
+
+ This allows fingerprinting, and thus should be an opt-in feature.
+ The wallet could also ask the user every time to allow a page to obtain the
+
+.. note::
+
+ To avoid navigating away from an SPA to find out the extension ID, the SPA
+ can open a new tab/window and communicate the updated extension ID back to
+ original SPA page.
+
+Once the Website has obtained the extension ID, it can use the ``runtime.connect()`` function
+to establish a communication channel to the extension.
+
+
+Alternatives
+============
+
+* Manual copy&paste of ``taler://`` URIs :-)
+* Integration of GNU Taler into all major browsers :-)
+* Convincing Google and/or Mozilla to provide better support
+ for reacting to a limited subset of request headers in
+ a declarative way
+* Convince Google and/or Mozilla to implement a general mechanism
+ where extensions can offer a "service" that websites can then
+ connect to without knowing some particular extension ID.
+* Convince Google and/or Mozilla to add better support for
+ registering URI schemes from a WebExtension, so that
+ we can register a handler for ``taler://``. For a better user experience,
+ there should also be some way to check whether some particular URI scheme
+ has a handler.
+
+Drawbacks
+=========
+
+* Firefox currently does not support messages from a website to an extension, and currently
+ cannot support the asynchronous wallet API.
+ There is a bug open for this issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1319168
diff --git a/design-documents/002-wallet-exchange-management.rst b/design-documents/002-wallet-exchange-management.rst
new file mode 100644
index 00000000..b3deee06
--- /dev/null
+++ b/design-documents/002-wallet-exchange-management.rst
@@ -0,0 +1,365 @@
+Design Doc 002: Wallet Exchange Management
+##########################################
+
+.. note::
+
+ This design document is currently a draft, it
+ does not reflect any implementation decisions yet.
+
+Summary
+=======
+
+This document presents the requirements and proposed interface for an API that
+wallet-core exposes (to clients such as the CLI, WebExtension, Android Wallet)
+to manage exchanges known to and used by the wallet.
+
+
+Motivation
+==========
+
+There currently is no documented API for this functionality. The API that the
+WebExtension API uses doesn't support all required functionality and exposes
+the internal DB storage format.
+
+
+Background and Requirements
+===========================
+
+The wallet maintains a list of known exchanges. For each exchange in this
+list, the wallet regularly makes network queries to fetch updated information
+about the exchange's cryptographic key material and fee structure.
+
+Additionally, the wallet maintains a list of *trusted auditors*. Auditors
+certify that they audit a (sub)set of denominations offered by the exchange.
+
+When an exchange is marked as *directly trusted*, the wallet can use it
+for withdrawals independent of how the exchange is audited. Otherwise,
+a withdrawal can only proceed if an adequate set of denominations is
+audited by a trusted auditor.
+
+An exchange might only be known the wallet temporarily. For example,
+the wallet UI may allow the user to review the fee structure of an
+exchange before the wallet is permanently added to the wallet.
+Once a an exchange is either (a) marked as trusted or (b) used for a
+withdrawal operation, it is marked as permanent.
+
+Exchanges that are not permanent will be automatically be removed
+("garbage-collected") by the wallet after some time.
+
+Exchanges also expose their terms of service (ToS) document.
+Before withdrawing, the wallet must ensure that the user
+has reviewed and accepted the current version of this ToS document.
+
+Exchange Management During Withdrawal
+-------------------------------------
+
+The functions to list / view exchanges can either be used in the context of
+some exchange management activity or in the context of a withdrawal. In the
+context of a withdrawal, additional filtering must be applied, as not every
+exchange is compatible with every withdrawal process. Additionally, the list
+of exchanges might contain additional details pertaining to this particular
+withdrawal process.
+
+An exchange is considered *compatible* if it accepts wire transfers with a wire
+method that matches the one of the withdrawal *and* the current exchange
+protocol version of the exchange is compatible with the exchange protocol
+version of the wallet.
+
+During the withdrawal process, the bank can also suggest an exchange. Unless
+the exchange is already known to the wallet, this exchange will be added
+non-permanently to the wallet. The bank-suggested will only be selected by
+default if no other trusted exchange compatible with the withdrawal process is
+known to the wallet.
+
+Otherwise, the exchange selected by default will be the exchange that has most
+recently been used for a withdrawal and is compatible with the current withdrawal.
+
+
+Open Questions
+--------------
+
+If the user reviews a **new** exchange during withdrawal
+but then does not decide to use it, will this exchange be permanent?
+
+Pro:
+
+* Staying permanently in the list might help when comparing multiple exchanges
+
+Con:
+
+* It clutters the list of exchanges, especially as we're not planning
+ to have a mechanism to remove exchanges.
+
+=> Maybe non-permanent exchanges can be "sticky" to some particular
+withdrawal session?
+
+
+Proposed Solution
+=================
+
+We will add the following functions (invoked over IPC with wallet-core).
+
+queryExchangeInfo
+-----------------
+
+This function will query information about an exchange based on the base URL
+of the exchange. If the exchange is not known yet to the wallet, it will be
+added non-permanently.
+
+Request:
+
+.. code:: ts
+
+ interface QueryExchangeInfoRequest {
+ // If given, return error description if the exchange is
+ // not compatible with this withdrawal operation.
+ talerWithdrawUri?: string;
+
+ // Exchange base URL to use for the query.
+ exchangeBaseUrl: string;
+
+ // If true, the query already returns a result even if
+ // /wire and denomination signatures weren't processed yet
+ partial: boolean;
+ }
+
+Response:
+
+.. code:: ts
+
+ interface QueryExchangeInfoResponse {
+ exchangeBaseUrl: string;
+
+ // Master public key
+ exchangePub: string;
+
+ trustedDirectly: boolean;
+
+ // The "reasonable-ness" of the exchange's fees.
+ feeStructureSummary: FeeStructureSummary | undefined;
+
+ // Detailled info for each individual denomination
+ denominations: ExchangeDenomination[];
+
+ // Currency of the exchange.
+ currency: string;
+
+ // Last observed protocol version range of the exchange
+ protocolVersionRange: string;
+
+ // Is this exchange either trusted directly or in use?
+ permanent: boolean;
+
+ // Only present if the last exchange information update
+ // failed. Same error as the corresponding pending operation.
+ lastError?: OperationError;
+
+ wireInfo: ExchangeWireInfo;
+
+ // Auditing state for each auditor.
+ auditingState: ExchangeAuditingState[];
+
+ // Do we trust an auditor that sufficiently audits
+ // this exchange's denominations?
+ trustedViaAuditor: boolean;
+
+ currentTosVersion: string;
+ acceptedTosVersion: string;
+
+ // When (if so) was this exchange last used for withdrawal?
+ lastUsedForWithdrawal: Timestamp | undefined;
+
+ withdrawalRelatedInfo?: {
+ // Can the user accept the withdrawal directly?
+ // This field is redundant and derivable from other fields.
+ acceptable: boolean;
+
+ recommendedByBank: boolean;
+
+ // Is this exchange the default exchange for this withdrawal?
+ isDefault: boolean;
+
+ withdrawalWithdrawnAmount: Amount;
+ withdrawalCreditAmount: Amount;
+ withdrawalFeeAmount: Amount;
+ withdrawalOverheadAmount: Amount;
+ };
+ }
+
+ export interface ExchangeWireInfo {
+ feesForType: { [wireMethod: string]: WireFee[] };
+ accounts: { paytoUri: string }[];
+ }
+
+ interface ExchangeAuditingState {
+ auditorName: string;
+ auditorBaseUrl: string;
+ auditorPub: string;
+
+ // Is the auditor already trusted by the wallet?
+ trustedByWallet: boolean;
+
+ // Does the auditor audit some reasonable set of
+ // denominations of the exchange?
+ // If this is false, at least some warning should be shown.
+ auditedDenominationsReasonable: boolean;
+ }
+
+
+ interface FeeStructureSummary {
+ // Does the fee structure fulfill our basic reasonableness
+ // requirements?
+ reasonable: boolean;
+
+ // Lower range of amounts that this exchange can
+ // deal with efficiently.
+ smallAmount: Amount;
+
+ // Upper range of amounts that this exchange can deal
+ // with efficiently.
+ bigAmount: Amount;
+
+ // Rest to be specified later
+ // [ ... ]
+ }
+
+
+getExchangeTos
+--------------
+
+Request:
+
+.. code:: ts
+
+ interface GetExchangeTosRequest {
+ exchangeBaseUrl: string;
+ }
+
+
+Response:
+
+.. code:: ts
+
+ interface GetTosResponse {
+ // Version of the exchange ToS (corresponds to tos ETag)
+ version: string;
+
+ // Text of the exchange ToS, with (optional) markdown markup.
+ tosMarkdownText: string;
+ }
+
+listExchanges
+-------------
+
+List exchanges known to the wallet. Either lists all exchanges, or exchanges
+related to a withdrawal process.
+
+Request:
+
+.. code:: ts
+
+ interface ExchangeListRequest {
+ // If given, only return exchanges that
+ // match the currency of this withdrawal
+ // process.
+ talerWithdrawUri?: string;
+ }
+
+Response:
+
+.. code:: ts
+
+ interface ExchangeListRespose {
+ // Only returned in the context of withdrawals.
+ // The base URL of the exchange that should
+ // be considered the default for the withdrawal.
+ withdrawalDefaultExchangeBaseUrl?: string;
+
+ exchanges: {
+ exchangeBaseUrl: string;
+
+ // Incompatible exchanges are also returned,
+ // as otherwise users might wonder why their expected
+ // exchange is not there.
+ compatibility: "compatible" |
+ "incompatible-version" | "incompatible-wire";
+
+ // Currency of the exchange.
+ currency: string;
+
+ // Does the wallet directly trust this exchange?
+ trustedDirectly: boolean;
+
+ // Is this exchange either trusted directly or in use?
+ permanent: boolean;
+
+ // This information is only returned if it's
+ // already available to us, as the list query
+ // must be fast!
+ trustedViaAuditor: boolean | undefined;
+
+ // The "reasonable-ness" of the exchange's fees.
+ // Only provided if available (if we've already queried
+ // and checked this exchange before).
+ feeStructureSummary: FeeStructureSummary | undefined;
+
+ // Did the user accept the current version of the exchange's ToS?
+ currentTosAccepted: boolean;
+
+ // When (if so) was this exchange last used for withdrawal?
+ lastUsedForWithdrawal: Timestamp | undefined;
+
+ withdrawalRelatedInfo?: {
+ // Can the user accept the withdrawal directly?
+ // This field is redundant and derivable from other fields.
+ acceptable: boolean;
+
+ recommendedByBank: boolean;
+
+ // Is this exchange the default exchange for this withdrawal?
+ isDefault: boolean;
+
+ withdrawalWithdrawnAmount: Amount;
+ withdrawalCreditAmount: Amount;
+ withdrawalFeeAmount: Amount;
+ withdrawalOverheadAmount: Amount;
+ };
+ }[];
+ }
+
+
+setExchangeTrust
+----------------
+
+Request:
+
+.. code:: ts
+
+ interface SetExchangeTrustRequest {
+ exchangeBaseUrl: string;
+
+ trusted: boolean;
+ }
+
+The response is an empty object or an error response.
+
+setExchangeTosAccepted
+----------------------
+
+Request:
+
+.. code:: ts
+
+ interface SetExchangeTosAccepted {
+ exchangeBaseUrl: string;
+ }
+
+The response is an empty object or an error response.
+
+
+Alternatives
+============
+
+* The UI could directly access the wallet's DB for more flexible access to the
+ required data. But this would make the UI less robust against changes in wallet-core.
+
diff --git a/design-documents/003-tos-rendering.rst b/design-documents/003-tos-rendering.rst
new file mode 100644
index 00000000..52d293c3
--- /dev/null
+++ b/design-documents/003-tos-rendering.rst
@@ -0,0 +1,105 @@
+Design Doc 003: ToS rendering
+#############################
+
+Summary
+=======
+
+This document describes how terms of service (ToS) as well as other "legal
+agreement documents" are served, represented and rendered.
+
+Motivation
+==========
+
+Different exchanges and backup/sync providers each have their custom legal
+agreement documents. As we don't know all providers and they are not centrally
+registered anywhere, these documents can't be hardcoded into wallet
+applications. Instead, these service providers expose endpoints that allow
+downloading the latest version of these legal agreement documents.
+
+These documents must be rendered on a variety of platforms in a user-friendly
+way.
+
+Proposed Solution
+=================
+
+The service providers can output legal agreements in various formats,
+determined via the ``"Accept: "`` request header. The format provider **must**
+support the ``text/plain`` mime type. The format provider **must** support
+the ``text/markdown`` mime type. Except for styling and navigation, the
+content of each format of the same legal agreement document **should** be the
+same.
+
+Legal documents with mime type ``text/markdown`` **should** confirm to the
+`commonmark specification <https://commonmark.org/>`__.
+
+When wallets render ``text/markdown`` legal documents, they **must** disable
+embedded HTML rendering. Wallets **may** style the markdown rendering to improve
+usability. For example, they can make sections collabsible or add a nagivation side-bar
+on bigger screens.
+
+It is recommended that the ``text/markdown`` document is used as the "master
+document" for generating the corresponding legal agreement document in other
+formats. However, service providers can also provide custom versions with more
+appropriate styling, like a logo in the header of a printable PDF document.
+
+Markdown Conventions
+--------------------
+
+The ``text/markdown`` document **should** follow
+the `commonmark spec <https://spec.commonmark.org/0.28/>`__.
+Main headlines (level 1) and their following content (until the next main headline)
+will be shown as expandable sections in wallets.
+
+The document **must** begin with a main headline:
+
+.. code-block::
+
+ # First Headline
+
+or
+
+.. code-block::
+
+ First Headline
+ ==============
+
+Alternatives
+============
+
+We considered and rejected the following alternatives:
+
+* Use only plain text. This is not user-friendly, as inline formatting (bold,
+ italic), styled section headers, paragraphs wrapped to the screen size,
+ formatted lists and tables are not supported.
+
+* Use HTML. This has a variety of issues:
+
+ * Service providers might provide HTML that does not render nicely on the
+ device that our wallet application is running on.
+ * Rendering HTML inside the application poses security risks.
+
+* Use a strict subset of HTML. This would mean we would have to define some
+ standardized subset that all wallet implementations support, which is too
+ much work. Existing HTML renderers (such as Android's ``Html.fromHTML``)
+ support undocumented subsets that lack features we want, such as ordered
+ lists. Defining our own HTML subset would also make authoring harder, as it
+ forces authors of legal agreement documents to author in our HTML subset, as
+ conversion tools from other format will not generate output in our HTML
+ subset.
+
+* Use reStructuredText (directly or via Sphinx). This at first looks like an
+ obvious choice for a master format, since Taler is already using reStructuredText
+ for all its documentation. But it doesn't work out well, since the only maintained
+ implementation of a parser/renderer is written in Python. Even with the Python implementation
+ (docutils / Sphinx), we can't convert ``.rst`` to Markdown nicely.
+
+Drawbacks
+=========
+
+* Markdown parsing / rendering libraries can be relatively large.
+
+Discussion / Q&A
+================
+
+* Should the legal agreement endpoints have some mechanism to determine what
+ content types they support?
diff --git a/design-documents/004-wallet-withdrawal-flow.rst b/design-documents/004-wallet-withdrawal-flow.rst
new file mode 100644
index 00000000..f6382446
--- /dev/null
+++ b/design-documents/004-wallet-withdrawal-flow.rst
@@ -0,0 +1,144 @@
+Design Doc 004: Wallet Withdrawal Flow
+######################################
+
+Summary
+=======
+
+This document describes the recommended way of implementing the user experience
+of withdrawing digital cash in GNU Taler wallets.
+
+Motivation
+==========
+
+When digital cash is withdrawn, it is tied to and in custody of an exchange.
+There can be many exchanges offered by different entities,
+each having their custom legal agreement documents and fee structures.
+The user is free to choose an exchange.
+Therefore, the process of withdrawing needs to account for this choice.
+
+Proposed Solution
+=================
+
+There are three screens involved in the process:
+
+1. **Select exchange**:
+ Here the user can pick an exchange from a list of known exchanges
+ or add a new one for immediate use.
+ For details see :doc:`002-wallet-exchange-management`.
+2. **Display an exchange's Terms of Service**:
+ Shows the terms and gives an option to accept them.
+ For details see :doc:`003-tos-rendering`.
+3. **Withdrawal details and confirmation**:
+ This should show the amount to be withdrawn along with its currency,
+ the currently selected exchange and the fee charged by it for the withdrawal.
+
+The user flow between these screens is described in the following graph:
+
+.. graphviz::
+
+ digraph G {
+ rankdir=LR;
+ nodesep=0.5;
+ default_exchange [
+ label = "Has default\nexchange?";
+ shape = diamond;
+ ];
+ tos_changed [
+ label = "ToS\nchanged?";
+ shape = diamond;
+ ];
+ tos_accepted [
+ label = "ToS\naccepted?";
+ shape = diamond;
+ ];
+ accept_tos [
+ label = "Accept\nToS?";
+ shape = diamond;
+ ];
+ withdrawal_action [
+ label = "Withdrawal\nAction";
+ shape = diamond;
+ ];
+ select_exchange [
+ label = "Select\nexchange";
+ shape = rect;
+ ];
+ tos [
+ label = "ToS";
+ shape = rect;
+ ];
+ withdraw [
+ label = "Confirm\nwithdrawal";
+ shape = rect;
+ ];
+ transactions [
+ label = "List of\nTransactions";
+ shape = oval;
+ ];
+
+ default_exchange -> tos_changed [label="Yes"];
+ default_exchange -> select_exchange [label="No"];
+ tos_changed -> tos [label="Yes"];
+ tos_changed -> withdraw [label="No"];
+ select_exchange -> tos_accepted;
+ tos_accepted -> tos_changed [label="Yes"];
+ tos_accepted -> tos [label="No"];
+ tos -> accept_tos;
+ accept_tos -> withdraw [label="Yes"];
+ accept_tos -> select_exchange [label="No"];
+ withdraw -> withdrawal_action;
+ withdrawal_action -> transactions [label="Confirm"];
+ withdrawal_action -> select_exchange [label="Change Exchange"];
+
+ { rank=same; tos_accepted; tos_changed; }
+ { rank=same; select_exchange; tos; }
+ { rank=same; withdrawal_action; withdraw; }
+ }
+
+This enables the user to change the current exchange at any time in the process.
+It ensures that the latest version of the exchange's terms of service have been accepted by the user
+before allowing them to confirm the withdrawal.
+
+Some special regional or test currencies might have only a single known exchange.
+For those, the wallet should not offer the option to change an exchange.
+
+After confirming the withdrawal,
+the user is brought to the list of transactions of the current currency.
+It will include a pending `TransactionWithdrawal` which might require additional user confirmation
+such as a two-factor-authentication step with the bank.
+
+1. The bank transfer happens immediately
+2. A second factor is required which is "detached",
+ i.e. you have to press "confirm" on a physical Taler ATM,
+ or a Taler cashier has to do a final "confirm" on their device.
+3. The bank provides a ``bankConfirmationUrl`` that the user needs to visit.
+
+If the withdrawal proceeds very quickly,
+the `TransactionWithdrawal` might not show as pending
+and its effective amount is added to the displayed balance right away.
+
+Alternatives
+============
+
+We considered and rejected the following alternatives:
+
+* Do not allow more than one exchange to make Taler simpler to use and understand:
+ Taler wants to allow custom exchanges for custom currencies
+ and foster competition between exchanges for the same currency
+ to provide the best possible service to users at the lowest fee.
+* Do not require acceptance to terms of service:
+ Having these terms and prompting the user to accept them
+ is a legal and business requirement in many jurisdictions,
+ so Taler needs to support them.
+ However, Taler encourages exchanges to keep their terms as short and simple as possible.
+
+Discussion / Q&A
+================
+
+* Should wallets pre-set a default exchange for the most common currencies,
+ so that users will not be burdened to understand exchanges and their fee structures
+ when making their first withdrawal?
+ This could increase user retention, but discourage
+* What should happen when an exchange changes its terms of service
+ and the user wants to use the funds stored there,
+ but does not initiate a new withdrawal with that exchange?
diff --git a/design-documents/005-wallet-backup-sync.rst b/design-documents/005-wallet-backup-sync.rst
new file mode 100644
index 00000000..26dbbf17
--- /dev/null
+++ b/design-documents/005-wallet-backup-sync.rst
@@ -0,0 +1,331 @@
+Design Doc 005: Wallet Backup and Sync
+######################################
+
+.. warning::
+
+ This is an unfinished draft.
+
+Summary
+=======
+
+This document discusses considerations for backup and synchronization of wallets.
+
+
+Requirements
+============
+
+* Backup and sync must not require any synchronous communication between the
+ wallets
+* Wallets operating (payments/withdrawals/...) for longer periods of time without
+ synchronizing should be handled well
+* Conflicts should be resolved automatically in pretty much all cases
+* One wallet can be enrolled with multiple sync servers, and a wallet can
+ join
+* Other wallets connected to the sync server are trusted.
+
+Proposed Solution
+=================
+
+The blob stored on the backup/sync server is a compressed and encrypted JSON file.
+
+The various entity types managed by the wallet are modeled LWW-Sets (Last Write
+Wins Set CRDT). Timestamps for inserts/deletes are are Lamport timestamps. Concurrent, conflicting insert/delete
+operations are resolved in favor of "delete".
+
+The managed entities are:
+
+* set of exchanges with the data from /keys, /wire
+* set of directly trusted exchange public keys
+* set of trusted auditors for currencies
+* set of reserves together with reserve history
+* set of accepted bank withdrawal operations
+* set of coins together with coin history and blinding secret (both for normal withdrawal and refresh)
+ and coin source info (refresh operation, tip, reserve)
+* set of purchases (contract terms, applied refunds, ...)
+* assignment of coins to their "primary wallet"
+
+(Some of these might be further split up to allow more efficient updates.)
+
+Entities that are **not** synchronized are:
+
+* purchases before the corresponding order has been claimed
+* withdrawal operations before they have been accepted by the user
+
+Entities that **could** be synchronized (to be decided):
+
+* private keys of other sync accounts
+* coin planchets
+* tips before the corresponding coins have been withdrawn
+* refresh sessions (not only the "meta data" about the operation,
+ but everything)
+
+
+Garbage collection
+------------------
+
+There are two types of garbage collection involved:
+
+1. CRDT tombstones / other administrative data in the sync blob. These can be deleted
+ after we're sure all wallets enrolled in the sync server have a Lamport timestamp
+ larger than the timestamp of the tombstone. Wallets include their own Lamport timestamp
+ in the sync blob:
+
+ .. code:: javascript
+
+ {
+ clocks: {
+ my_desktop_wallet: 5,
+ my_phone_wallet: 3
+ },
+ ...
+ }
+
+ All tombstones / overwritten set elements with a timestamp smaller than the
+ smallest clock value can be deleted.
+
+2. Normal wallet GC. The deletion operations resulting from the wallet garbage
+ collection (i.g. deleting legally expired denomination keys, coins, exchange
+ signing keys, ...) are propagated to the respective CRDT set in the sync
+ blob.
+
+
+Ghost Entities
+--------------
+
+Sometimes a wallet can learn about an operation that happened in another synced
+wallet **before** a sync over the sync server happens. An example of this is a
+deposit operation. When two synced wallets spend the same coin on something,
+one of them will receive an error from the exchange that proves the coin has
+been spent on something else. The wallet will add a "ghost entry" for such an
+event, in order to be able to show a consistent history (i.e. all numbers
+adding up) to the user.
+
+When the two wallets sync later, the ghost entry is replaced by the actual
+purchase entity from the wallet that initiated the spending.
+
+Ghost entities are not added to the sync state.
+
+
+Multiple sync servers
+---------------------
+
+When a wallet is connected to multiple sync servers, it automatically
+propagates changes it received from one sync server to the others. Local
+changes made by the wallet are propagated to all sync servers. The goal of
+this is to make the state of the sync servers converge.
+
+The different sync servers one wallet is enrolled with do not necessarily
+have the same set of other wallet enrolled. Each sync server has a separate Lamport clock
+and contains a separate CRDT.
+
+Backup user flow
+================
+
+.. graphviz::
+
+ digraph G {
+ nodesep=0.5;
+ withdrawal [
+ label = "First\nWithdrawal";
+ shape = oval;
+ ];
+ has_backup [
+ label = "Has backup\nconfigured?";
+ shape = diamond;
+ ];
+ app_settings [
+ label = "App\nSettings";
+ shape = rect;
+ ];
+ backup_onboarding [
+ label = "Backup\nOnboarding";
+ shape = rect;
+ ];
+ backup_settings [
+ label = "Backup Settings\n\n* time of last backup\n* current service";
+ shape = rect;
+ ];
+ choose_backup_service [
+ label = "Choose\nBackup Service";
+ shape = rect;
+ ];
+ tos_accepted [
+ label = "Current ToS\naccepted?";
+ shape = diamond;
+ ];
+ tos [
+ label = "ToS";
+ shape = rect;
+ ];
+ payment_required [
+ label = "Payment\nrequired?";
+ shape = diamond;
+ ];
+ payment_confirmation [
+ label = "Payment\nConfirmation";
+ shape = rect;
+ ];
+ backup_secret [
+ label = "Backup Secret\n\nStore or write down!";
+ shape = rect;
+ ];
+
+ withdrawal -> has_backup;
+ has_backup -> backup_onboarding [label="No"];
+ backup_onboarding -> backup_settings;
+ app_settings -> backup_settings;
+ backup_settings -> backup_settings [label="Disable Backup"];
+ backup_settings:w -> backup_settings:w [label="Sync now"];
+ backup_settings -> choose_backup_service;
+ choose_backup_service -> tos_accepted [label="Select Service"];
+ tos_accepted -> tos [label="No"];
+ tos_accepted -> payment_required [label="Yes"];
+ choose_backup_service:w -> choose_backup_service [label="Remove current service"];
+ choose_backup_service:n -> choose_backup_service:n [headlabel="Add new service", labeldistance=3.5];
+ tos -> payment_required [label="Accept"];
+ payment_required -> payment_confirmation [label="Yes"];
+ payment_confirmation -> backup_secret [label="Paid"];
+ backup_secret -> backup_settings [dir=both, label="Stored"];
+ payment_required:s -> backup_secret:w [label="No"];
+
+ { rank=same; has_backup; backup_onboarding; }
+ { rank=same; withdrawal; app_settings; }
+ { rank=same; tos_accepted; tos; }
+ { rank=same; payment_required; payment_confirmation; }
+ }
+
+Backup Settings Screen
+----------------------
+
+* **Backup my wallet** [on/off]
+* **Backup services**
+ No service active
+ (shows time of last backup per service)
+* **Show backup secret**
+ You need this secret to restore from backup
+* option to sync/backup now (hidden in action bar overflow menu)
+
+Choose Backup Service Screen
+----------------------------
+This screen can be reached by pressing the **Backup services** setting
+in the Backup Settings Screen.
+It lists the currently active service and other services that can be used.
+The user has the option to add new services to the list.
+
+A backup service has
+
+* a name
+* a base URL
+* a fee per year in an explicit currency
+
+Clicking an active service shows the above service information as well as:
+
+* the service secret that is required to restore from backup
+* last payment date and next scheduled payment date
+* option to deactivate the backup service
+
+Clicking an inactive service allows the user to use the backup service
+(after accepting ToS and making the payment).
+
+Terms of Service Screen
+-----------------------
+This screen always appears when a backup provider is selected
+and the user did not yet accept the current version of its terms of service.
+
+It shows the terms of service text and an accept checkbox,
+as well as the usual back button.
+
+Payment Confirmation Screen
+---------------------------
+This is the same screen that the user sees when doing other purchases.
+The only difference is that after successful payment,
+the user will be shown the service secret instead of the transaction list.
+
+Backup Secret Screen
+--------------------
+After setting up a backup service,
+the user needs to securely store the secret needed to restore from backup.
+The secret will be shown as a Taler URI in plain text.
+This has the form: ``taler://sync/$SYNC-DOMAIN/$SYNC-PATH#$PRIVATE-KEY``
+Additionally, the URI will be encoded as a QRcode.
+Depending on the platform, there should be an option to print or export (PDF) the secret.
+
+Backup Onboarding
+-----------------
+If no backup service was selected when the user makes the first withdrawal,
+an onboarding screen will be shown that takes the user to the backup configuration screen.
+
+ Don't loose your money, use a backup service!
+
+ Your wallet comes with a list of backup services
+ that can store an encrypted copy of your wallet.
+ Use one to keep your money safe!
+
+ [Set backup up now]
+
+References
+==========
+
+* Shapiro, M., Preguiça, N., Baquero, C., & Zawirski, M. (2011). A
+ comprehensive study of convergent and commutative replicated data types. [`PDF <https://hal.inria.fr/inria-00555588/document>`__]
+
+Discussion / Q&A
+================
+
+* Why is backup/sync not split into two services / use-cases?
+
+ * For privacy reasons, we can't use some interactive sync service. Thus we
+ use the backup blob as a CRDT that also synchronization for us.
+
+* Do we need to handle backup/sync state becoming very large e.g. by many transactions
+ and embedded product images potentially exceeding service quota?
+
+* Do we synchronize the list of other backup enrollments? How
+ do we handle distributing the different private keys for them?
+
+ * If we automatically sync the sync enrollments and the old sync account
+ is compromised, the new sync account would automatically be compromised as well!
+
+ * If every wallet had its own sync key pair, we could select which existing wallets
+ to roll over as well.
+
+* How do we handle a synced wallet that becomes malicious deleting all coins or purchased products?
+
+ * This needs to balance the genuine need to permanently delete data.
+
+ * Should the sync server allow to fetch previous versions of the sync blob?
+ (If not, how to provide backup functionality?)
+
+ * Should the individual wallets keep tombstones (i.e. entities just marked as deleted)
+ around for some time, or should they delete and "sanitize" (delete data not needed for the CRDT)
+ tombstones as soon as possible?
+
+* How do we make it easy to remove compromised devices from the sync group
+ and prevent them from getting access to future funds and transactions?
+
+ * We need to remove all sync connections on all connected devices
+ and then individually (and manually) add all devices to the new backup account.
+
+ * If we encrypted the key with each wallet's private sync key,
+ we could choose which wallets we want to migrate to the new sync account.
+
+ * Can we then roll-over wallets to the new account automatically
+ or does it have to be manually on each device to prevent an attacker to roll us over?
+
+* How are wallets identified for backup/sync?
+
+ * UUID / EdDSA pub and nick name? When nickname clashes,
+ some number is added based on lexical sort of the random id ("phone#1", "phone#2").
+
+* How do we explain users that it can take days for wallet state to synchronize to all devices?
+
+* How are people supposed to securely store their backup account key(s)?
+
+ * There can be an option to print/export the QR code
+ * They can manually write down the taler:// Uri containing the key.
+ * Maybe encode the key in a different format such as
+ `BIP39 <https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki>`__?
+
+* Do we have a passphrase for our backup account key(s)?
+
+ * ???
diff --git a/design-documents/006-anastasis-ux.rst b/design-documents/006-anastasis-ux.rst
new file mode 100644
index 00000000..9921f994
--- /dev/null
+++ b/design-documents/006-anastasis-ux.rst
@@ -0,0 +1,180 @@
+Design Doc 006: Anastasis User Experience
+#########################################
+
+Summary
+=======
+
+This document describes the recommended way of implementing the user experience
+of setting up and making use of :doc:`../anastasis` account recovery.
+
+Motivation
+==========
+
+Wallet state consisting of digital cash, transaction history etc. should not be lost.
+Taler provides a backup mechanism to prevent that.
+As an additional protection measure Anastasis can be used to provide access to the backup,
+even if all devices and offline secrets have been lost.
+
+Access to the backup key is shared with escrow providers that can be chosen by the user.
+
+Setup Steps
+===========
+
+.. graphviz::
+
+ digraph G {
+ rankdir=LR;
+ nodesep=0.5;
+ settings [
+ label = "Backup\nSettings";
+ shape = oval;
+ ];
+ backup_is_setup [
+ label = "Backup\nsetup?";
+ shape = diamond;
+ ];
+ provide_id [
+ label = "Provide\nIdentification";
+ shape = rectangle;
+ ];
+ select_auth [
+ label = "Select\nAuthentication Methods\n\nProvide\nAuthentication Data";
+ shape = rectangle;
+ ];
+ select_providers [
+ label = "Select\nService Providers";
+ shape = rectangle;
+ ];
+ threshold [
+ label = "Define\nRecovery Threshold";
+ shape = rectangle;
+ ];
+ pay [
+ label = "Payment";
+ shape = oval;
+ ];
+ settings -> backup_is_setup;
+ backup_is_setup -> provide_id [label="Yes: Setup Recovery"];
+ backup_is_setup -> settings [label="No"];
+ provide_id -> select_auth;
+ select_auth -> select_providers;
+ select_providers -> threshold;
+ threshold -> pay;
+ }
+
+Entry point: Settings
+---------------------
+
+The app settings should have a section for Anastasis
+using a different more universally understood name
+like Wallet Recovery.
+
+The section should have an option to setup Anastasis initially.
+This option should be disabled as long as no backup has been set up.
+The section could maybe be integrated into the backup settings.
+
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/menu.png
+ :width: 800
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/settings.png
+ :width: 800
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/backupsettings.png
+ :width: 800
+
+Providing Identification
+------------------------
+
+Instead of a forgettable freely chosen user name,
+Anastasis collects various static information from the user
+to generate a unique user identifier from that.
+Examples for such identifier would be a concatenation
+of the full name of the user and their social security or passport number(s).
+
+The information that can reasonably used here various from cultural context and jurisdiction.
+Therefore, one idea is to start by asking for continent
+and then the country of primary legal residence,
+and then continue from there with country-specific attributes
+(and also offer a stateless person option).
+
+Special care should be taken to avoid that information can later be provided ambiguously
+thus changing the user identifier and not being able to restore the user's data.
+This can be typographic issues like someone providing "Seestr."
+and later "Seestrasse" or "Seestraße" or "seestrasse".
+But it can also be simple typos that we can only prevent in some instances
+like when checking checksums in passport numbers.
+
+The user should be made aware that this data will not leave the app
+and that it is only used to compute a unique identifier that can not be forgotten.
+
+If possible, we should guide the user in the country selection
+by accessing permission-less information such as the currently set language/locale
+and the country of the SIM card.
+But nothing invasive like the actual GPS location.
+
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/userid.png
+ :width: 800
+
+Select Authentication Methods
+-----------------------------
+
+After creating a unique identifier,
+the user can chose one or more :ref:`anastasis-auth-methods`
+supported by Anastasis.
+
+Ideally when selecting a method,
+the user is already asked to provide the information
+required for the recovery with that method.
+For example, a photo of themselves, their phone number or mailing address.
+
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/truth.png
+ :width: 800
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/addtruth.png
+ :width: 800
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/addtruthmail.png
+ :width: 800
+
+
+Confirm/Change Service Providers
+--------------------------------
+
+Taler should propose a mapping of authentication methods to providers
+by minimizing cost (tricky: sign-up vs. recovery costs)
+and distributing the selected authentication methods across as many providers as possible.
+
+The user should be able to change the proposed default selection
+and add more than one provider to each chosen method.
+
+It should also be possible to add providers
+that are not included in the default list provided by the wallet.
+
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/policy.png
+ :width: 800
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/addpolicy.png
+ :width: 800
+.. image:: https://git.taler.net/anastasis.git/plain/doc/wireframe/png-export/addpolicymethod.png
+ :width: 800
+
+Defining Recovery Threshold
+---------------------------
+
+After mapping authentication methods to providers,
+the user needs select which combinations are sufficient to recover the secret.
+The default could be ``n-1`` out of ``n``.
+
+Maybe the `Dark Crystal UI Recommendations <https://dark-crystal-javascript.gitlab.io/ui-recommendations/>`__
+can be an inspiration here.
+
+Pay for Setup
+-------------
+
+As the last step when all information has been properly provided,
+the user is asked to pay for the service with the regular wallet payment confirmation screen.
+
+Show Service Status After Setup
+===============================
+
+TODO
+
+Recovery Steps
+==============
+
+TODO
diff --git a/design-documents/index.rst b/design-documents/index.rst
new file mode 100644
index 00000000..37f81bd4
--- /dev/null
+++ b/design-documents/index.rst
@@ -0,0 +1,18 @@
+Design Documents
+################
+
+This is a collection of design documents related to GNU Taler.
+The goal of these documents is to discuss facilitate discussion around
+new features while keeping track of the evolution of the whole system
+and protocol.
+
+.. toctree::
+ :glob:
+
+ 000-template
+ 001-new-browser-integration
+ 002-wallet-exchange-management
+ 003-tos-rendering
+ 004-wallet-withdrawal-flow
+ 005-wallet-backup-sync
+ 006-anastasis-ux