summaryrefslogtreecommitdiff
path: root/doc/sphinx
diff options
context:
space:
mode:
Diffstat (limited to 'doc/sphinx')
-rw-r--r--doc/sphinx/anastasis_reducer_recovery.drawio2
-rw-r--r--doc/sphinx/anastasis_reducer_recovery.pngbin52610 -> 51922 bytes
-rw-r--r--doc/sphinx/anastasis_reducer_recovery.svg3
-rw-r--r--doc/sphinx/conf.py6
-rw-r--r--doc/sphinx/configuration.rst3
-rw-r--r--doc/sphinx/core/api-common.rst15
-rw-r--r--doc/sphinx/cryptography.rst19
-rw-r--r--doc/sphinx/frags/configuration-format.rst7
-rw-r--r--doc/sphinx/frags/legal.rst183
-rw-r--r--doc/sphinx/manpages/anastasis-dbconfig.1.rst61
-rw-r--r--doc/sphinx/manpages/anastasis-dbinit.1.rst67
-rw-r--r--doc/sphinx/manpages/anastasis-gtk.1.rst15
-rw-r--r--doc/sphinx/manpages/anastasis-reducer.1.rst14
-rw-r--r--doc/sphinx/manpages/anastasis.conf.5.rst6
-rw-r--r--doc/sphinx/reducer.rst135
-rw-r--r--doc/sphinx/rest.rst257
16 files changed, 679 insertions, 114 deletions
diff --git a/doc/sphinx/anastasis_reducer_recovery.drawio b/doc/sphinx/anastasis_reducer_recovery.drawio
index ac1f617..4ffb43e 100644
--- a/doc/sphinx/anastasis_reducer_recovery.drawio
+++ b/doc/sphinx/anastasis_reducer_recovery.drawio
@@ -1 +1 @@
-<mxfile host="app.diagrams.net" modified="2021-07-14T14:05:15.539Z" agent="5.0 (X11)" etag="RgsPOgrkZ7CacnVb-4wQ" version="14.8.6" type="device"><diagram id="PpkpfZO7TL7CUlFfbbxv" name="Seite-1">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</diagram></mxfile> \ No newline at end of file
+<mxfile host="app.diagrams.net" modified="2022-01-20T15:16:50.498Z" agent="5.0 (X11)" etag="8Qk7PxXMA2Ox2VhIvYvE" version="16.4.3" type="device"><diagram id="PpkpfZO7TL7CUlFfbbxv" name="Seite-1">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</diagram></mxfile> \ No newline at end of file
diff --git a/doc/sphinx/anastasis_reducer_recovery.png b/doc/sphinx/anastasis_reducer_recovery.png
index b08c763..2c0c33c 100644
--- a/doc/sphinx/anastasis_reducer_recovery.png
+++ b/doc/sphinx/anastasis_reducer_recovery.png
Binary files differ
diff --git a/doc/sphinx/anastasis_reducer_recovery.svg b/doc/sphinx/anastasis_reducer_recovery.svg
index 7020498..fc20336 100644
--- a/doc/sphinx/anastasis_reducer_recovery.svg
+++ b/doc/sphinx/anastasis_reducer_recovery.svg
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!-- Do not edit this file with editors other than diagrams.net -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1076px" height="398px" viewBox="-0.5 -0.5 1076 398" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2021-07-14T14:05:44.086Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;heBcjQYLP05MJ4nWahSj&quot; version=&quot;14.8.6&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;PpkpfZO7TL7CUlFfbbxv&quot; name=&quot;Seite-1&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="231" y="42" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 70px; margin-left: 232px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">CONTINENT_SELECTING</div></div></div></foreignObject><text x="307" y="73" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">CONTINENT_SELECTING</text></switch></g><rect x="535" y="42" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 70px; margin-left: 536px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">COUNTRY_SELECTING</div></div></div></foreignObject><text x="611" y="73" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">COUNTRY_SELECTING</text></switch></g><rect x="839" y="42" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 70px; margin-left: 840px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">USER_ATTRIBUTES<br />_COLLECTING</div></div></div></foreignObject><text x="915" y="73" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">USER_ATTRIBUTES...</text></switch></g><rect x="307" y="231" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 259px; margin-left: 308px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">CHALLENGE_SELECTING</div></div></div></foreignObject><text x="383" y="262" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">CHALLENGE_SELECTING</text></switch></g><path d="M 709 212.75 L 890.78 253" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 895.91 254.13 L 888.32 256.04 L 890.78 253 L 889.83 249.2 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="557" y="185" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 213px; margin-left: 558px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">CHALLENGE_PAYING</div></div></div></foreignObject><text x="633" y="216" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">CHALLENGE_PAYING</text></switch></g><path d="M 383 55.88 L 528.63 55.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 533.88 55.88 L 526.88 59.38 L 528.63 55.88 L 526.88 52.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 56px; margin-left: 459px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">select_continent</div></div></div></foreignObject><text x="459" y="59" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">select_continent</text></switch></g><path d="M 535 83.63 L 389.37 83.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 384.12 83.63 L 391.12 80.13 L 389.37 83.63 L 391.12 87.13 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 83px; margin-left: 460px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">unselect_continent</div></div></div></foreignObject><text x="460" y="86" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">unselect_continent</text></switch></g><path d="M 687 55.88 L 832.63 55.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 837.88 55.88 L 830.88 59.38 L 832.63 55.88 L 830.88 52.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 56px; margin-left: 762px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">select_country</div></div></div></foreignObject><text x="762" y="59" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">select_country</text></switch></g><path d="M 839 83.63 L 693.37 83.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 688.12 83.63 L 695.12 80.13 L 693.37 83.63 L 695.12 87.13 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 85px; margin-left: 763px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">back</div></div></div></foreignObject><text x="763" y="88" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 991 55.88 L 1067 56 L 1067 151 L 27 151 L 27 245 L 40.63 245" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 45.88 245 L 38.88 248.5 L 40.63 245 L 38.88 241.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 150px; margin-left: 602px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">enter_user_attributes</div></div></div></foreignObject><text x="602" y="154" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">enter_user_attributes</text></switch></g><path d="M 47 273 L 7 273 L 7 127 L 144 126.75 L 1037 127 L 1037 84 L 997.37 83.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 992.12 83.63 L 999.15 80.19 L 997.37 83.68 L 999.09 87.19 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 126px; margin-left: 629px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">back</div></div></div></foreignObject><text x="629" y="130" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 459 244.88 L 550.95 214.73" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 555.94 213.1 L 550.38 218.6 L 550.95 214.73 L 548.2 211.95 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 229px; margin-left: 508px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">select_challenge</div></div></div></foreignObject><text x="508" y="232" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">select_challenge</text></switch></g><path d="M 573 42 Q 611 -19 645.63 36.59" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 648.41 41.05 L 641.74 36.96 L 645.63 36.59 L 647.68 33.26 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 6px; margin-left: 613px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">select_continent</div></div></div></foreignObject><text x="613" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">select_continent</text></switch></g><path d="M 671 301 L 671 246.87" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 671 241.62 L 674.5 248.62 L 671 246.87 L 667.5 248.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="557" y="301" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 329px; margin-left: 558px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">CHALLENGE_SOLVING</div></div></div></foreignObject><text x="633" y="332" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">CHALLENGE_SOLVING</text></switch></g><path d="M 595 241 L 595 294.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 595 299.88 L 591.5 292.88 L 595 294.63 L 598.5 292.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 268px; margin-left: 597px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">pay</div></div></div></foreignObject><text x="597" y="272" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">pay</text></switch></g><path d="M 459 272.63 L 551.15 312.35" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 555.97 314.43 L 548.16 314.88 L 551.15 312.35 L 550.93 308.45 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 286px; margin-left: 539px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">select_challenge</div></div></div></foreignObject><text x="539" y="289" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">select_challenge</text></switch></g><path d="M 557 328.75 L 464.85 289.02" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.03 286.94 L 467.84 286.5 L 464.85 289.02 L 465.07 292.93 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 306px; margin-left: 510px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">back</div></div></div></foreignObject><text x="510" y="309" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 557 198.88 L 465.05 229.02" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.06 230.65 L 465.62 225.15 L 465.05 229.02 L 467.8 231.8 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 211px; margin-left: 504px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">back</div></div></div></foreignObject><text x="504" y="214" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><rect x="897" y="240.5" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 268px; margin-left: 898px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">RECOVERY_FINISHED</div></div></div></foreignObject><text x="973" y="272" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">RECOVERY_FINISHED</text></switch></g><path d="M 709 328.75 L 890.94 270.2" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 895.94 268.59 L 890.34 274.07 L 890.94 270.2 L 888.2 267.41 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 308px; margin-left: 791px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">solve_challenge</div></div></div></foreignObject><text x="791" y="312" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">solve_challenge</text></switch></g><path d="M 633 356.5 Q 633.03 391 508.03 391 Q 383.03 391 383 292.87" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 383 287.62 L 386.5 294.62 L 383 292.87 L 379.5 294.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 391px; margin-left: 488px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">solve_challenge</div></div></div></foreignObject><text x="488" y="395" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">solve_challenge</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 271px; margin-left: 677px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">solve_challenge</div></div></div></foreignObject><text x="677" y="275" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">solve_challenge</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 226px; margin-left: 797px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">pay</div></div></div></foreignObject><text x="797" y="230" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">pay</text></switch></g><rect x="47" y="231" width="152" height="55.5" rx="8.32" ry="8.32" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 259px; margin-left: 48px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">SECRET_SELECTING</div></div></div></foreignObject><text x="123" y="262" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">SECRET_SELECTING</text></switch></g><path d="M 307 270 L 205.37 270.3" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 200.12 270.31 L 207.11 266.79 L 205.37 270.3 L 207.13 273.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 271px; margin-left: 258px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">back</div></div></div></foreignObject><text x="258" y="275" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 199 251 L 300.63 251" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 305.88 251 L 298.88 254.5 L 300.63 251 L 298.88 247.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 251px; margin-left: 257px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">next</div></div></div></foreignObject><text x="257" y="255" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">next</text></switch></g><path d="M 85 231 Q 123 171 157.59 225.62" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 160.4 230.06 L 153.7 226.01 L 157.59 225.62 L 159.61 222.27 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 196px; margin-left: 125px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">change_version</div></div></div></foreignObject><text x="125" y="200" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">change_version</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1076px" height="398px" viewBox="-0.5 -0.5 1076 398" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-20T15:17:03.380Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;ddZmVuAcNf0rTgyu1oTB&quot; version=&quot;16.4.3&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;PpkpfZO7TL7CUlFfbbxv&quot; name=&quot;Seite-1&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="231" y="41" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 69px; margin-left: 232px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">CONTINENT_SELECTING</div></div></div></foreignObject><text x="307" y="72" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">CONTINENT_SELECTING</text></switch></g><rect x="535" y="41" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 69px; margin-left: 536px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">COUNTRY_SELECTING</div></div></div></foreignObject><text x="611" y="72" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">COUNTRY_SELECTING</text></switch></g><rect x="839" y="41" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 69px; margin-left: 840px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">USER_ATTRIBUTES<br />_COLLECTING</div></div></div></foreignObject><text x="915" y="72" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">USER_ATTRIBUTES...</text></switch></g><rect x="307" y="230" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 258px; margin-left: 308px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">CHALLENGE_SELECTING</div></div></div></foreignObject><text x="383" y="261" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">CHALLENGE_SELECTING</text></switch></g><path d="M 709 211.75 L 890.78 252" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 895.91 253.13 L 888.32 255.04 L 890.78 252 L 889.83 248.2 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><rect x="557" y="184" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 212px; margin-left: 558px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">CHALLENGE_PAYING</div></div></div></foreignObject><text x="633" y="215" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">CHALLENGE_PAYING</text></switch></g><path d="M 383 54.88 L 528.63 54.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 533.88 54.88 L 526.88 58.38 L 528.63 54.88 L 526.88 51.38 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 55px; margin-left: 460px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">select_continent</div></div></div></foreignObject><text x="460" y="59" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">select_continent</text></switch></g><path d="M 535 82.63 L 389.37 82.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 384.12 82.63 L 391.12 79.13 L 389.37 82.63 L 391.12 86.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 82px; margin-left: 460px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">unselect_continent</div></div></div></foreignObject><text x="460" y="85" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">unselect_continent</text></switch></g><path d="M 687 54.88 L 832.63 54.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 837.88 54.88 L 830.88 58.38 L 832.63 54.88 L 830.88 51.38 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 55px; margin-left: 763px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">select_country</div></div></div></foreignObject><text x="763" y="59" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">select_country</text></switch></g><path d="M 839 82.63 L 693.37 82.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 688.12 82.63 L 695.12 79.13 L 693.37 82.63 L 695.12 86.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 84px; margin-left: 763px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">back</div></div></div></foreignObject><text x="763" y="87" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 991 54.88 L 1067 55 L 1067 150 L 27 150 L 27 244 L 40.63 244" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 45.88 244 L 38.88 247.5 L 40.63 244 L 38.88 240.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 150px; margin-left: 603px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">enter_user_attributes</div></div></div></foreignObject><text x="603" y="153" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">enter_user_attributes</text></switch></g><path d="M 47 272 L 7 272 L 7 126 L 144 125.75 L 1037 126 L 1037 83 L 997.37 82.68" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 992.12 82.63 L 999.15 79.19 L 997.37 82.68 L 999.09 86.19 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 126px; margin-left: 629px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">back</div></div></div></foreignObject><text x="629" y="129" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 459 243.88 L 550.95 213.73" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 555.94 212.1 L 550.38 217.6 L 550.95 213.73 L 548.2 210.95 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 228px; margin-left: 509px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">select_challenge</div></div></div></foreignObject><text x="509" y="231" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">select_challenge</text></switch></g><path d="M 573 41 Q 611 -20 645.63 35.59" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 648.41 40.05 L 641.74 35.96 L 645.63 35.59 L 647.68 32.26 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 6px; margin-left: 613px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">select_continent</div></div></div></foreignObject><text x="613" y="9" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">select_continent</text></switch></g><path d="M 671 300 L 671 245.87" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 671 240.62 L 674.5 247.62 L 671 245.87 L 667.5 247.62 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><rect x="557" y="300" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 328px; margin-left: 558px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">CHALLENGE_SOLVING</div></div></div></foreignObject><text x="633" y="331" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">CHALLENGE_SOLVING</text></switch></g><path d="M 595 240 L 595 293.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 595 298.88 L 591.5 291.88 L 595 293.63 L 598.5 291.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 268px; margin-left: 598px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">pay</div></div></div></foreignObject><text x="598" y="271" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">pay</text></switch></g><path d="M 459 271.63 L 551.15 311.35" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 555.97 313.43 L 548.16 313.88 L 551.15 311.35 L 550.93 307.45 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 285px; margin-left: 539px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">select_challenge</div></div></div></foreignObject><text x="539" y="289" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">select_challenge</text></switch></g><path d="M 557 327.75 L 464.85 288.02" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.03 285.94 L 467.84 285.5 L 464.85 288.02 L 465.07 291.93 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 305px; margin-left: 511px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">back</div></div></div></foreignObject><text x="511" y="308" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 557 197.88 L 465.05 228.02" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.06 229.65 L 465.62 224.15 L 465.05 228.02 L 467.8 230.8 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 210px; margin-left: 504px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">back</div></div></div></foreignObject><text x="504" y="213" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><rect x="897" y="239.5" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 267px; margin-left: 898px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">RECOVERY_FINISHED</div></div></div></foreignObject><text x="973" y="271" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">RECOVERY_FINISHED</text></switch></g><path d="M 709 327.75 L 890.94 269.2" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 895.94 267.59 L 890.34 273.07 L 890.94 269.2 L 888.2 266.41 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 307px; margin-left: 791px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">solve_challenge</div></div></div></foreignObject><text x="791" y="311" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">solve_challenge</text></switch></g><path d="M 633 355.5 Q 633 390 508 390 Q 383 390 383 291.87" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 383 286.62 L 386.5 293.62 L 383 291.87 L 379.5 293.62 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 391px; margin-left: 488px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">solve_challenge</div></div></div></foreignObject><text x="488" y="394" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">solve_challenge</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 271px; margin-left: 677px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">solve_challenge</div></div></div></foreignObject><text x="677" y="274" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">solve_challenge</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 226px; margin-left: 798px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">pay</div></div></div></foreignObject><text x="798" y="229" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">pay</text></switch></g><rect x="47" y="230" width="152" height="55.5" rx="8.32" ry="8.32" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 150px; height: 1px; padding-top: 258px; margin-left: 48px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">SECRET_SELECTING</div></div></div></foreignObject><text x="123" y="261" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">SECRET_SELECTING</text></switch></g><path d="M 307 269 L 205.37 269.3" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 200.12 269.31 L 207.11 265.79 L 205.37 269.3 L 207.13 272.79 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 271px; margin-left: 258px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">back</div></div></div></foreignObject><text x="258" y="274" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">back</text></switch></g><path d="M 199 250 L 300.63 250" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 305.88 250 L 298.88 253.5 L 300.63 250 L 298.88 246.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 250px; margin-left: 257px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">next</div></div></div></foreignObject><text x="257" y="254" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">next</text></switch></g><path d="M 85 230 Q 123 170 157.59 224.62" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 160.4 229.06 L 153.7 225.01 L 157.59 224.62 L 159.61 221.27 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 195px; margin-left: 125px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"><div>add_provider</div></div></div></div></foreignObject><text x="125" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">add_provider</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file
diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py
index 932692b..40928c6 100644
--- a/doc/sphinx/conf.py
+++ b/doc/sphinx/conf.py
@@ -260,6 +260,12 @@ man_pages = [
("manpages/anastasis-httpd.1", "anastasis-httpd",
"anastasis HTTP backend", "Anastasis SARL",
1),
+ ("manpages/anastasis-dbconfig.1", "anastasis-dbconfig",
+ "configure Anastasis database", "Anastasis SARL",
+ 1),
+ ("manpages/anastasis-dbinit.1", "anastasis-dbinit",
+ "initialize Anastasis database", "Anastasis SARL",
+ 1),
("manpages/anastasis.conf.5", "anastasis.conf",
"anastasis configuration file", "Anastasis SARL",
5),
diff --git a/doc/sphinx/configuration.rst b/doc/sphinx/configuration.rst
index a2c9ad0..b99c712 100644
--- a/doc/sphinx/configuration.rst
+++ b/doc/sphinx/configuration.rst
@@ -28,3 +28,6 @@ configuration format.
.. include:: frags/configuration-format.rst
.. include:: frags/using-anastasis-config.rst
+
+.. include:: frags/legal.rst
+
diff --git a/doc/sphinx/core/api-common.rst b/doc/sphinx/core/api-common.rst
index a7d77ba..423de2d 100644
--- a/doc/sphinx/core/api-common.rst
+++ b/doc/sphinx/core/api-common.rst
@@ -297,6 +297,14 @@ denoting microseconds since the UNIX Epoch. ``UINT64_MAX`` represents "never".
struct GNUNET_TIME_AbsoluteNBO {
uint64_t abs_value_us__; // in network byte order
};
+ struct GNUNET_TIME_Timestamp {
+ // must be round value (multiple of seconds)
+ struct GNUNET_TIME_Absolute abs_time;
+ };
+ struct GNUNET_TIME_TimestampNBO {
+ // must be round value (multiple of seconds)
+ struct GNUNET_TIME_AbsoluteNBO abs_time;
+ };
Cryptographic primitives
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -318,6 +326,13 @@ uses 512-bit hash codes (64 bytes).
uint8_t ecdh_pub[32];
};
+.. _ANASTASIS_TruthKeyP:
+.. sourcecode:: c
+
+ struct ANASTASIS_TruthKeyP {
+ struct GNUNET_HashCode key;
+ };
+
.. sourcecode:: c
struct UUID {
diff --git a/doc/sphinx/cryptography.rst b/doc/sphinx/cryptography.rst
index 6c25fc0..a38f6e7 100644
--- a/doc/sphinx/cryptography.rst
+++ b/doc/sphinx/cryptography.rst
@@ -233,7 +233,9 @@ Signatures
----------
The EdDSA keys are used to sign the data sent from the client to the
-server. Everything the client sends to server is signed. The following
+server. This signature ensures that an adversary that observes the upload is not
+able to upload a new version of the policy without knowing the user's identity attributes.
+The signature is made over a hash of the request body. The following
algorithm is equivalent for **Anastasis-Policy-Signature**.
.. code-block:: none
@@ -248,21 +250,6 @@ algorithm is equivalent for **Anastasis-Policy-Signature**.
**ver_res**: A boolean value. True: Signature verification passed, False: Signature verification failed.
-When requesting policy downloads, the client must also provide a signature:
-
-.. code-block:: none
-
- (anastasis-account-signature) := eddsa_sign(version, eddsa_priv)
- ver_res := eddsa_verifiy(version, anastasis-account-signature, eddsa_pub)
-
-**anastasis-account-signature**: Signature over the SHA-512 hash of the body using the purpose code ``TALER_SIGNATURE_ANASTASIS_POLICY_DOWNLOAD`` (1401) (see GNUnet EdDSA signature API for the use of purpose).
-
-**version**: The version requested as a 64-bit integer, 2^64-1 for the "latest version".
-
-**ver_res**: A boolean value. True: Signature verification passed, False: Signature verification failed.
-
-
-
Availability Considerations
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/doc/sphinx/frags/configuration-format.rst b/doc/sphinx/frags/configuration-format.rst
index f9b28e1..6f7ad86 100644
--- a/doc/sphinx/frags/configuration-format.rst
+++ b/doc/sphinx/frags/configuration-format.rst
@@ -59,11 +59,6 @@ Note that, in this stage of development, the file
component. For example, both an exchange and a bank can read values from
it.
-The repository ``git://taler.net/deployment`` contains examples of
+The repository ``git://git.taler.net/deployment`` contains examples of
configuration file used in our demos. See under ``deployment/config``.
- **Note**
-
- Expectably, some components will not work just by using default
- values, as their work is often interdependent. For example, a
- merchant needs to know an exchange URL, or a database name.
diff --git a/doc/sphinx/frags/legal.rst b/doc/sphinx/frags/legal.rst
new file mode 100644
index 0000000..dcecfaa
--- /dev/null
+++ b/doc/sphinx/frags/legal.rst
@@ -0,0 +1,183 @@
+..
+ This file is part of GNU TALER.
+
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Christian Grothoff
+
+
+The service has well-known API endpoints to return its legal conditions to the
+user in various languages and various formats. This section describes how to
+setup and configure the legal conditions.
+
+
+Terms of Service
+----------------
+
+The service has an endpoint "/terms" to return the terms of service (in legal
+language) of the service operator. Client software show these terms of
+service to the user when the user is first interacting with the service.
+Terms of service are optional for experimental deployments, if none are
+configured, the service will return a simple statement saying that there are
+no terms of service available.
+
+To configure the terms of service response, there are two options
+in the configuration file for the service:
+
+- ``TERMS_ETAG``: The current "Etag" to return for the terms of service.
+ This value must be changed whenever the terms of service are
+ updated. A common value to use would be a version number.
+ Note that if you change the ``TERMS_ETAG``, you MUST also provide
+ the respective files in ``TERMS_DIR`` (see below).
+- ``TERMS_DIR``: The directory that contains the terms of service.
+ The files in the directory must be readable to the service
+ process.
+
+
+Privacy Policy
+--------------
+
+The service has an endpoint "/pp" to return the terms privacy policy (in legal
+language) of the service operator. Clients should show the privacy policy to
+the user when the user explicitly asks for it, but it should not be shown by
+default. Privacy policies are optional for experimental deployments, if none
+are configured, the service will return a simple statement saying that there
+is no privacy policy available.
+
+To configure the privacy policy response, there are two options
+in the configuration file for the service:
+
+- ``PRIVACY_ETAG``: The current "Etag" to return for the privacy policy.
+ This value must be changed whenever the privacy policy is
+ updated. A common value to use would be a version number.
+ Note that if you change the ``PRIVACY_ETAG``, you MUST also provide
+ the respective files in ``PRIVACY_DIR`` (see below).
+- ``PRIVACY_DIR``: The directory that contains the privacy policy.
+ The files in the directory must be readable to the service
+ process.
+
+
+Legal policies directory layout
+-------------------------------
+
+The ``TERMS_DIR`` and ``PRIVACY_DIR`` directory structures must follow a
+particular layout. You may use the same directory for both the terms of
+service and the privacy policy, as long as you use different ETAGs. Inside of
+the directory, there should be sub-directories using two-letter language codes
+like "en", "de", or "jp". Each of these directories would then hold
+translations of the current terms of service into the respective language.
+Empty directories are permitted in case translations are not available.
+
+Then, inside each language directory, files with the name of the value set as
+the ``TERMS_ETAG`` or ``PRIVACY_ETAG`` must be provided. The extension of each
+of the files should be typical for the respective mime type. The set of
+supported mime types is currently hard-coded in the service, and includes
+".epub", ".html", ".md", ".pdf" and ".txt" files. If other files are present,
+the service may show a warning on startup.
+
+Example
+^^^^^^^
+
+A sample file structure for a ``TERMS_ETAG`` of "tos-v0" would be:
+
+- TERMS_DIR/en/tos-v0.txt
+- TERMS_DIR/en/tos-v0.html
+- TERMS_DIR/en/tos-v0.pdf
+- TERMS_DIR/en/tos-v0.epub
+- TERMS_DIR/en/tos-v0.md
+- TERMS_DIR/de/tos-v0.txt
+- TERMS_DIR/de/tos-v0.html
+- TERMS_DIR/de/tos-v0.pdf
+- TERMS_DIR/de/tos-v0.epub
+- TERMS_DIR/de/tos-v0.md
+
+If the user requests an HTML format with language preferences "fr" followed by
+"en", the service would return ``TERMS_DIR/en/tos-v0.html`` lacking a version in
+French.
+
+
+Generating the Legal Terms
+--------------------------
+
+The ``taler-terms-generator`` script can be used to generate directories with
+terms of service and privacy policies in multiple languages and all required
+data formats from a single source file in ``.rst`` format and GNU gettext
+translations in ``.po`` format.
+
+To use the tool, you need to first write your legal conditions in English in
+reStructuredText (rst). You should find a templates in
+``$PREFIX/share/terms/*.rst`` where ``$PREFIX`` is the location where you
+installed the service to. Whenever you make substantive changes to the legal
+terms, you must use a fresh filename and change the respective ``ETAG``. The
+resulting file must be called ``$ETAG.rst`` and the first line of the file should be the title of the document.
+
+Once you have written the ``$ETAG.rst`` file in English, you can
+generate the first set of outputs:
+
+.. code-block:: shell-session
+
+ $ taler-terms-generator -i $ETAG
+
+Afterwards, you should find the terms in various formats for all configured
+languages (initially only English) in ``$PREFIX/share/terms/``. The generator
+has a few options which are documented in its man page.
+
+
+Adding translations
+-------------------
+
+Translations must be available in subdirectories
+``locale/$LANGUAGE/LC_MESSAGES/$ETAG.po``.
+To start translating, you first need to add a new
+language:
+
+.. code-block:: shell-session
+
+ $ taler-terms-generator -i $ETAG -l $LANGUAGE
+
+Here, ``$LANGUAGE`` should be a two-letter language
+code like ``de`` or ``fr``. The command will generate
+a file ``locale/$LANGUAGE/LC_MESSAGES/$ETAG.po``
+which contains each English sentence or paragraph
+in the original document and an initially empty
+translation. Translators should update the ``.po``
+file. Afterwards, simply re-run
+
+.. code-block:: shell-session
+
+ $ taler-terms-generator -i $ETAG
+
+to make the current translation(s) available to the
+service.
+
+.. note::
+
+ You must restart the service whenever adding or updating legal documents or their translations.
+
+
+Updating legal documents
+------------------------
+
+When making minor changes without legal implications, edit the ``.rst`` file,
+then re-run the step to add a new language for each existing translation to
+produce an updated ``.po`` file. Translate the sentences that have changed and
+finally run the generator (without ``-l``) on the ETAG (``-i $ETAG``) to
+create the final files.
+
+When making major changes with legal implications, you should first rename (or
+copy) the existing ``.rst`` file and the associated translation files to a new
+unique name. Afterwards, make the major changes, update the ``.po`` files,
+complete the translations and re-create the final files. Finally, do not
+forget to update the ``ETAG`` configuration option to the new name and to
+restart the service.
diff --git a/doc/sphinx/manpages/anastasis-dbconfig.1.rst b/doc/sphinx/manpages/anastasis-dbconfig.1.rst
new file mode 100644
index 0000000..3483c36
--- /dev/null
+++ b/doc/sphinx/manpages/anastasis-dbconfig.1.rst
@@ -0,0 +1,61 @@
+anastasis-dbconfig(1)
+#####################
+
+.. only:: html
+
+ Name
+ ====
+
+ **anastasis-dbconfig** - configure Anastasis database
+
+
+Synopsis
+========
+
+**anastasis-dbconfig**
+[**-c** *FILENAME* *]
+[**-h**]
+[**-n** *NAME* *]
+[**-r**]
+[**-s**]
+[**-u** *USER* *]
+
+Description
+===========
+
+**anastasis-dbconfig** is a simple shell script that configures
+a Postgresql database for use by the GNU Anastasis servers.
+
+Its options are as follows:
+
+**-c** *FILENAME*
+ Write the database configuration to FILENAME. The tool
+ will append the required ``CONFIG`` option for the
+ Postgresql access to the respective file.
+
+**-h**
+ Print short help on options.
+
+**-n** *DBNAME*
+ Use DBNAME for the name of the created database.
+
+**-r**
+ Reset any existing database. Looses all existing data. DANGEROUS.
+
+**-s**
+ Skip database initialization. Useful if you want to run
+ ``anastasis-dbinit`` manually.
+
+**-u** *USER*
+ Specifies the (main) Anastasis user that will access the database.
+
+See Also
+========
+
+anastassis-dbinit(1), anastasis.conf(5).
+
+Bugs
+====
+
+Report bugs by using https://bugs.taler.net or by sending electronic
+mail to <taler@gnu.org>.
diff --git a/doc/sphinx/manpages/anastasis-dbinit.1.rst b/doc/sphinx/manpages/anastasis-dbinit.1.rst
new file mode 100644
index 0000000..1b0d5fa
--- /dev/null
+++ b/doc/sphinx/manpages/anastasis-dbinit.1.rst
@@ -0,0 +1,67 @@
+anastasis-dbinit(1)
+###################
+
+.. only:: html
+
+ Name
+ ====
+
+ **anastasis-dbinit** - initialize Anastasis database
+
+
+Synopsis
+========
+
+**anastasis-dbinit**
+[**-c** *FILENAME* | **--config=**\ ‌\ *FILENAME*]
+[**-g** | **--gc**]
+[**-h** | **--help**]
+[**-L** *LOGLEVEL* | **--loglevel=**\ ‌\ *LOGLEVEL*]
+[**-l** *FILENAME* | **--logfile=**\ ‌\ *FILENAME*]
+[**-r** | **--reset**]
+[**-v** | **--version**]
+
+Description
+===========
+
+**anastasis-dbinit** is a command-line tool to initialize the GNU
+Anastasis database. It creates the necessary tables and indices for
+an Anastasis server to operate.
+
+Its options are as follows:
+
+**-c** *FILENAME* \| **--config=**\ ‌\ *FILENAME*
+ Use the configuration and other resources for the exchange to operate
+ from *FILENAME*.
+
+**-g** \| **--gc**
+ Garbage collect database. Deletes all unnecessary data in the
+ database.
+
+**-h** \| **--help**
+ Print short help on options.
+
+**-L** *LOGLEVEL* \| **--loglevel=**\ ‌\ *LOGLEVEL*
+ Specifies the log level to use. Accepted values are: ``DEBUG``, ``INFO``,
+ ``WARNING``, ``ERROR``.
+
+**-l** *FILENAME* \| **--logfile=**\ ‌\ *FILENAME*
+ Send logging output to *FILENAME*.
+
+**-r** \| **--reset**
+ Drop tables. Dangerous, will delete all existing data in the database
+ before creating the tables.
+
+**-v** \| **–version**
+ Print version information.
+
+See Also
+========
+
+anastasis-httpd(1), anastasis.conf(5).
+
+Bugs
+====
+
+Report bugs by using https://bugs.taler.net or by sending electronic
+mail to <taler@gnu.org>.
diff --git a/doc/sphinx/manpages/anastasis-gtk.1.rst b/doc/sphinx/manpages/anastasis-gtk.1.rst
index 697f820..b039fbf 100644
--- a/doc/sphinx/manpages/anastasis-gtk.1.rst
+++ b/doc/sphinx/manpages/anastasis-gtk.1.rst
@@ -12,6 +12,7 @@ Synopsis
========
**anastasis-gtk**
+[**-A**_*ID*_|_**--application=**\ \ *ID*]
[**-c** *FILENAME* | **––config=**\ ‌\ *FILENAME*]
[**-h** | **––help**]
[**-L** *LOGLEVEL* | **––loglevel=**\ ‌\ *LOGLEVEL*]
@@ -26,6 +27,20 @@ Description
key recover and backup operations.
+**-A** *ID* \| **--application=**\ \ *ID*
+ Set the application ID to *ID*. Default is ``anastasis-gtk``. Used
+ to store different types of secrets from different applications
+ while using the same user attributes. Basically the application ID
+ is included in the user attributes. Not changable by the GUI as
+ only advanced users should even known about this. Applications that
+ tightly integrate Anastasis should set the application ID to their
+ respective unique name, for example the GNU Taler wallet may use
+ ``gnu-taler-wallet`` for the application ID. If anastasis-gtk is
+ to be used to recover such a secret, the respective application ID
+ must be provided on the command-line. Users that only use
+ anastasis-gtk to backup and restore secrets should not set the
+ application ID, as forgetting the ID makes the secrets irrecoverable.
+
**-c** *FILENAME* \| **––config=**\ ‌\ *FILENAME*
Use the configuration from *FILENAME*.
diff --git a/doc/sphinx/manpages/anastasis-reducer.1.rst b/doc/sphinx/manpages/anastasis-reducer.1.rst
index 859dc49..9884b18 100644
--- a/doc/sphinx/manpages/anastasis-reducer.1.rst
+++ b/doc/sphinx/manpages/anastasis-reducer.1.rst
@@ -12,6 +12,7 @@ Synopsis
========
**anastasis-reducer**
+[**-A**_*ID*_|_**--application=**\ \ *ID*]
[**-a**_*JSON*_|_**--arguments=\ \ *JSON*]
[**-b**_|_**--backup]
[**-c** *FILENAME* | **––config=**\ ‌\ *FILENAME*]
@@ -37,6 +38,19 @@ chapter.
**-a** *JSON* \| **––arguments=**\ \ *JSON*
Provide JSON inputs for the given command.
+**-A** *ID* \| **--application=**\ \ *ID*
+ Set the application ID to *ID*. Default is empty, which means the application-id must be explicitly provided
+ as part of the JSON inputs or it will be omitted.
+ The **-A** option overrides any application ID that
+ may be given in the **-a** arguments. Application IDs
+ are used to store different types of secrets from different applications
+ while using the same user attributes. Basically the application ID
+ is included in the user attributes. Applications that
+ tightly integrate Anastasis should set the application ID to their
+ respective unique name, for example the GNU Taler wallet may use
+ ``gnu-taler-wallet`` for the application ID.
+ Forgetting the application ID makes the secrets irrecoverable.
+
**-b** \| **--backup**
Begin fresh reducer operation for a back up operation.
diff --git a/doc/sphinx/manpages/anastasis.conf.5.rst b/doc/sphinx/manpages/anastasis.conf.5.rst
index 000d8f0..a32cd4c 100644
--- a/doc/sphinx/manpages/anastasis.conf.5.rst
+++ b/doc/sphinx/manpages/anastasis.conf.5.rst
@@ -125,20 +125,20 @@ SMS Authorization options
^^^^^^^^^^^^^^^^^^^^^^^^^
COMMAND
- Helper command to run to send SMS.
+ Helper command to run to send SMS. The command will be given the phone number as its first argument. The message to be transmitted will be passed via STDIN.
Email Authorization options
^^^^^^^^^^^^^^^^^^^^^^^^^^^
COMMAND
- Helper command to run to send E-mail.
+ Helper command to run to send E-mail. The command will be given the e-mail address as its first argument. The message to be transmitted will be passed via STDIN.
Post Authorization options
^^^^^^^^^^^^^^^^^^^^^^^^^^
COMMAND
- Helper command to run to send physical mail.
+ Helper command to run to send physical mail. The command will be given the mailing address address as its first argument in JSON object fields 'full_name', 'street', 'city', 'postcode' and 'country'. The message to be transmitted will be passed via STDIN.
IBAN Authorization options
diff --git a/doc/sphinx/reducer.rst b/doc/sphinx/reducer.rst
index 9728d7d..50fec42 100644
--- a/doc/sphinx/reducer.rst
+++ b/doc/sphinx/reducer.rst
@@ -1,6 +1,6 @@
..
This file is part of Anastasis
- Copyright (C) 2019-2021 Anastasis SARL
+ Copyright (C) 2019-2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -499,7 +499,8 @@ If contacting the provider failed, the information returned is:
**add_provider**:
-This operation can be performed in state ``USER_ATTRIBUTES_COLLECTING``. It
+This operation can be performed in state ``USER_ATTRIBUTES_COLLECTING``.
+It
adds one or more Anastasis providers to the list of providers the reducer
should henceforth consider. Note that removing providers is not possible at
this time.
@@ -513,8 +514,8 @@ use. For example:
{
"http://localhost:8088/" : { "disabled" : false },
- "http://localhost:8089/" : { "disabled" : false }
- "http://localhost:8090/" : { "disabled" : true },
+ "http://localhost:8089/" : { "disabled" : false },
+ "http://localhost:8090/" : { "disabled" : true }
}
Note that existing providers will remain in the state they were in. The following is an
@@ -1055,7 +1056,7 @@ The reducer will simply transition to the ``SECRET_EDITING`` state:
{
"backup_state": "SECRET_EDITING",
- "upload_fees" : [ "KUDOS:42" ],
+ "upload_fees" : [ { "fee": "KUDOS:42" } ],
"expiration" : { "t_ms" : 1245362362 }
}
@@ -1101,7 +1102,7 @@ be updated.
"mime" : "text/plain"
},
"expiration" : { "t_ms" : 1245362362 },
- "upload_fees" : [ "KUDOS:42" ]
+ "upload_fees" : [ { "fee": "KUDOS:42" } ]
}
@@ -1293,11 +1294,73 @@ the backup process. Example arguments would thus be:
}
}
-However, in contrast to the backup process, the reducer will attempt to
-retrieve the latest recovery document from all known providers for the
-selected currency given the above inputs. If a recovery document was found
-by any provider, the reducer will attempt to load it and transition to
-a state where the user can choose which challenges to satisfy:
+Afterwards, the reducer transitions into the ``SECRET_SELECTING`` state:
+
+.. code-block:: json
+
+ {
+ "recovery_state": "SECRET_SELECTING",
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "social_security_number": "123456789",
+ "birthdate": "2000-01-01",
+ "birthplace": "Earth"
+ }
+ }
+
+Typically, the special policy discovery process (outside of the state
+machine) is expected to be run in this state. The discovery process
+will use the state (and in particular the identity attributes and the
+list of active providers) to discover a set of possible recovery
+documents with their respective provider URLs, policy version and
+identity attribute mask. An identity attribute mask is a bitmask that
+describes which of the optional attributes from the identity
+attributes should be omitted to recover this backup. Once the user
+has selected a backup providing this triplet, it is possible to
+proceed using ``next``.
+
+Especially if the discovered policies are inadequate, it is again
+possible to add providers using ``add_provider``.
+
+
+**add_provider**:
+
+This operation can be performed in state ``SECRET_SELECTING``. It
+adds one additional Anastasis provider to the list of providers that
+the discovery process should henceforth consider. Note that removing
+providers is not possible at this time.
+
+Here, the client must provide an object with the base URL of the
+providers to add, for example:
+
+.. code-block:: json
+
+ {
+ "provider_url" : "http://localhost:8088/"
+ }
+
+
+**select_version**:
+
+Using the ``select_version`` transition in the ``SECRET_SELECTING`` state,
+it is possible to trigger the download and decryption of a recovery
+policy document. Here, the arguments specify which provider, version
+and mask should be used to download the document:
+
+.. code-block:: json
+
+ {
+ "providers" : [ {
+ "url": "https://localhost:8088/",
+ "version": 0
+ } ],
+ "attribute_mask": 0
+ }
+
+The reducer will attempt to retrieve the specified recovery document
+from that provider. If a recovery document was found, the reducer
+will attempt to load it and transition to a state where the user can
+choose which challenges to satisfy:
.. code-block:: json
@@ -1307,13 +1370,13 @@ a state where the user can choose which challenges to satisfy:
"challenges": [
{
"uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G",
- "cost": "TESTKUDOS:0",
+ "uuid-display": "MW2R3RC",
"type": "question",
"instructions": "q1"
},
{
"uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
- "cost": "TESTKUDOS:0",
+ "uuid-display": "TXYKGE",
"type": "email",
"instructions": "e-mail address m?il@f*.bar"
},
@@ -1347,12 +1410,11 @@ obtained and its ``version`` are also provided. Each challenge comes with
four mandatory fields:
- **uuid**: A unique identifier of the challenge; this is what the
- UUIDs in the policies array refer to, but also this UUID may be
- included in messages sent to the user. They allow the user to
+ UUIDs in the policies array refer to.
+ - **uuid-display**: Shortened idenfier which is included in messages
+ send to the user. Allows the user to
distinguish different PIN/TANs should say the same phone number be
used for SMS-authentication with different providers.
- - **cost**: This is the amount the Anastasis provider will charge
- to allow the user to pass the challenge.
- **type**: This is the type of the challenge, as a string.
- **instructions**: Contains additional important hints for the user
to allow the user to satisfy the challenge. It typically includes
@@ -1383,26 +1445,33 @@ However, in general it should be sufficient to display the slightly
more generic Taler error code that is returned with the new state.
-**change_version:**
+**sync_providers**
+
+The downloaded policy may include secrets from providers for which
+we do not (yet) have the cost structure or even the salt. So here
+an application can use the ``sync_providers`` request to download
+``/config`` from providers that are in the challenge list but not
+yet known with their salt and other attributes in the provider list.
-Even if a recovery document was found, it is possible that the user
-intended to recover a different version, or recover a backup where
-the recovery document is stored at a different provider. Thus, the
-reducer allows the user to explicitly switch to a different provider
-or recovery document version using the ``change_version`` transition,
-which takes a provider URL and policy version as arguments:
+The transition fails if all providers relevant for the selected
+policy are already downloaded. Applications may either internally
+check the state for this, or call ``sync_providers`` until it fails
+with this error:
.. code-block:: json
- {
- "provider_url": "https://localhost:8080/",
- "version": 2
- }
+ {
+ "detail": "already in sync",
+ "code": 8400,
+ "hint": "The given action is invalid for the current state of the reducer."
+ }
-Note that using a version of 0 implies fetching "the latest version". The
-resulting states are the same as those of the ``enter_user_attributes``
-transition, except that the recovery document version is not necessarily the
-latest available version at the provider.
+As providers may fail to respond, this action may need to be called
+repeatedly. The action will block until progress is made on any provider.
+As some providers may never respond, the application should disable
+challenge buttons for challenges where providers are down. However,
+users should be able to solve challenges where the provider is up while
+the reducer is polling for ``/config`` in the background.
**select_challenge:**
@@ -1691,7 +1760,7 @@ that applications must all handle. States other than ``solved`` are:
**poll:**
-With a ``poll`` transition, the application indicates that it wants to wait longer for one or more of the challenges that are in state ``authentication-timeout`` to possibly complete. While technically optional, the ``timeout`` argument should really be provided to enable long-polling, for example:
+With a ``poll`` transition, the application indicates that it wants to wait longer for one or more of the challenges that are awaiting some external authentication (state ``external-instructions``) or experienced some kind of timeout (state ``authentication-timeout``) to possibly complete. While technically optional, the ``timeout`` argument should really be provided to enable long-polling, for example:
.. code-block:: json
diff --git a/doc/sphinx/rest.rst b/doc/sphinx/rest.rst
index 03ee138..d1fb790 100644
--- a/doc/sphinx/rest.rst
+++ b/doc/sphinx/rest.rst
@@ -1,6 +1,6 @@
..
This file is part of Anastasis
- Copyright (C) 2019-2021 Anastasis SARL
+ Copyright (C) 2019-2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -35,6 +35,7 @@ Receiving Configuration
.. http:get:: /config
Obtain the configuration details of the escrow provider.
+ This specification corresponds to ``current`` protocol being version **0**.
**Response:**
@@ -54,8 +55,12 @@ Receiving Configuration
// The format is "current:revision:age".
version: string;
- // Currency in which this provider processes payments.
- currency: string;
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+
+ // Name of the business operating the service (for display to the user).
+ business_name?: string;
// Supported authorization methods.
methods: AuthorizationMethodConfig[];
@@ -80,7 +85,7 @@ Receiving Configuration
// **provider salt** is then used in various operations to ensure
// cryptographic operations differ by provider. A provider must
// never change its salt value.
- server_salt: string;
+ provider_salt: string;
}
@@ -141,6 +146,46 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
.. _`RFC 4122`: https://tools.ietf.org/html/rfc4122
+.. http:get:: /policy/$ACCOUNT_PUB/meta[?max_version=$NUMBER]
+
+ Get meta data about a customer's encrypted recovery documents.
+ If ``max_version`` is specified, only return results up to the
+ given version number. The response may not contain meta data
+ for all versions if there are way too many. In this case,
+ ``max_version`` must be used to incrementally fetch more versions.
+
+ **Response**:
+
+ :http:statuscode:`200 OK`:
+ The escrow provider responds with a RecoveryMetaSummary_ object.
+ :http:statuscode:`400 Bad request`:
+ The ``$ACCOUNT_PUB`` is not an EdDSA public key.
+ :http:statuscode:`402 Payment Required`:
+ The account's balance is too low for the specified operation.
+ See the Taler payment protocol specification for how to pay.
+ :http:statuscode:`404 Not found`:
+ The requested resource was not found.
+
+ **Details:**
+
+ .. _RecoveryMetaSummary:
+ .. ts:def:: RecoveryMetaSummary
+
+ interface RecoveryMetaSummary {
+ // Version numbers as a string (!) are used as keys.
+ "$VERSION": MetaData;
+ }
+
+ interface MetaData {
+ // The meta value can be NULL if the document
+ // exists but no meta data was provided.
+ meta: string;
+
+ // Server-time indicative of when the recovery
+ // document was uploaded.
+ upload_time: Timestamp;
+ }
+
.. http:get:: /policy/$ACCOUNT_PUB[?version=$NUMBER]
Get the customer's encrypted recovery document. If ``version``
@@ -173,8 +218,6 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
:http:statuscode:`402 Payment Required`:
The account's balance is too low for the specified operation.
See the Taler payment protocol specification for how to pay.
- :http:statuscode:`403 Forbidden`:
- The required account signature was invalid.
:http:statuscode:`404 Not found`:
The requested resource was not found.
@@ -187,7 +230,6 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
*If-None-Match*: If this is not the very first request of the client, this contains the Etag-value which the client has received before from the server.
The client SHOULD send this header with every request (except for the first request) to avoid unnecessary downloads.
- *Anastasis-Account-Signature*: The client must provide Base-32 encoded EdDSA signature over hash of body with ``$ACCOUNT_PRIV``, affirming desire to download the requested encrypted recovery document. The purpose used MUST be ``TALER_SIGNATURE_ANASTASIS_POLICY_DOWNLOAD`` (1401).
.. http:post:: /policy/$ACCOUNT_PUB
@@ -217,7 +259,7 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
:query timeout_ms=NUMBER: *Optional.* If specified, the Anastasis server will
wait up to ``timeout_ms`` milliseconds for completion of the payment before
sending the HTTP response. A client must never rely on this behavior, as the
- backend may return a response immediately.
+ backend may return a response immediately. If a ``timeout_ms`` is not given, the Anastasis server may apply a default timeout (usually 30s) when talking to the merchant backend.
*If-None-Match*: This header MUST be present and set to the SHA512 hash (Etag) of the body by the client.
The client SHOULD also set the ``Expect: 100-Continue`` header and wait for ``100 continue``
@@ -226,9 +268,11 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
The server MUST refuse the upload with a ``304`` status code if the Etag matches
the latest version already known to the server.
+ *Anastasis-Policy-Meta-Data*: Encrypted meta data to be stored by the server and returned with the respective endpoint to provide an overview of the available policies. Encrypted using a random nonce and a key derived from the user ID using the salt "rmd". The plaintext metadata must consist of the policy hash (for deduplication) and the (human readable) secret name.
+
*Anastasis-Policy-Signature*: The client must provide Base-32 encoded EdDSA signature over hash of body with ``$ACCOUNT_PRIV``, affirming desire to upload an encrypted recovery document.
- *Payment-Identifier*: Base-32 encoded 32-byte payment identifier that was included in a previous payment (see ``402`` status code). Used to allow the server to check that the client paid for the upload (to protect the server against DoS attacks) and that the client knows a real secret of financial value (as the **kdf_id** might be known to an attacker). If this header is missing in the client's request (or the associated payment has exceeded the upload limit), the server must return a ``402`` response. When making payments, the server must include a fresh, randomly-generated payment-identifier in the payment request.
+ *Payment-Identifier*: Base-32 encoded 32-byte payment identifier that was included in a previous payment (see ``402`` status code). Used to allow the server to check that the client paid for the upload (to protect the server against DoS attacks) and that the client knows a real secret of financial value (as the **kdf_id** might be known to an attacker). If this header is missing in the client's request (or the associated payment has exceeded the upload limit), the server must return a ``402`` response. When making payments, the server must include a fresh, randomly-generated payment-identifier in the payment request. If a payment identifier is given, the Anastasis backend may block for the payment to be confirmed by Taler as specified by the ``timeout_ms`` argument.
**Response**:
@@ -309,7 +353,7 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
truth_key: [32]; //bytearray
// Salt used to hash the security answer if appliccable.
- truth_salt: [32]; //bytearray
+ question_salt: [32]; //bytearray
// Salt from the provider to derive the user ID
// at this provider.
@@ -332,7 +376,7 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
interface DecryptionPolicy {
// Salt included to encrypt master key share when
// using this decryption policy.
- salt: [32]; //bytearray
+ master_salt: [32]; //bytearray
// Master key, AES-encrypted with key derived from
// salt and keyshares revealed by the following list of
@@ -370,10 +414,11 @@ charge per truth operation using GNU Taler.
.. http:post:: /truth/$UUID
+ **Request:**
+
Upload a `TruthUploadRequest`_-Object according to the policy the client created before (see `RecoveryDocument`_).
If request has been seen before, the server should do nothing, and otherwise store the new object.
- **Request:**
:query timeout_ms=NUMBER: *Optional.* If specified, the Anastasis server will
wait up to ``timeout_ms`` milliseconds for completion of the payment before
@@ -426,23 +471,22 @@ charge per truth operation using GNU Taler.
// For how many years from now would the client like us to
// store the truth?
- storage_duration_years: Integer;
+ storage_duration_years: number;
}
-.. http:get:: /truth/$UUID
- Get the stored encrypted key share.
- Also, the user has to provide the correct *truth_encryption_key* with every get request (see below).
+ .. http:post:: /truth/$UUID/solve
+
+ Solve the challenge and get the stored encrypted key share.
+ Also, the user has to provide the correct *truth_encryption_key* with the request (see below).
The encrypted key share is returned simply as a byte array and not in JSON format.
- :query response=H_RESPONSE: *Optional.* If ``$H_RESPONSE`` is specified by the client,
- the server checks if ``$H_RESPONSE`` matches the expected response. This can be the
- hash of the security question (as specified before by the client
- within the `TruthUploadRequest`_ (see ``encrypted_truth``)), or the hash of the
- PIN code sent via SMS, E-mail or postal communication channels.
- When ``$H_RESPONSE`` is correct, the server responds with the encrypted key share.
- :query timeout_ms=NUMBER: *Optional.* If specified, the Anastasis server will
+ **Request**:
+
+ Upload a `TruthSolutionRequest`_-Object.
+
+ :query timeout_ms=NUMBER: *Optional.* If specified, the Anastasis server will
wait up to ``timeout_ms`` milliseconds for completion of the payment or the
challenge before sending the HTTP response. A client must never rely on this
behavior, as the backend may return a response immediately.
@@ -451,46 +495,50 @@ charge per truth operation using GNU Taler.
:http:statuscode:`200 OK`:
`EncryptedKeyShare`_ is returned in body (in binary).
- :http:statuscode:`202 Accepted`:
- The escrow provider will respond out-of-band (i.e. SMS).
- The body may contain human- or machine-readable instructions on next steps.
- In case the response is in JSON, the format is given
- by `ChallengeInstructionMessage`_.
- :http:statuscode:`208 Already Reported`:
- An authentication challenge was recently send, client should
- simply respond to the pending challenge.
- :http:statuscode:`303 See other`:
- The provider redirects for authentication (i.e. video identification/WebRTC).
- If the client is not a browser, it should launch a browser at the URL
- given in the ``Location`` header and allow the user to re-try the operation
- after successful authorization.
:http:statuscode:`402 Payment required`:
The service requires payment for access to truth.
See the Taler payment protocol specification for how to pay.
The response body MAY provide alternative means for payment.
:http:statuscode:`403 Forbidden`:
- The server requires a valid "response" to the challenge associated with the UUID.
+ The `h_response` provided is not a good response to the challenge associated
+ with the UUID, or at least the answer is not valid yet. A generic
+ response is provided with an error code.
:http:statuscode:`404 Not found`:
The server does not know any truth under the given UUID.
- :http:statuscode:`408 Request Timeout`:
- Accessing this truth requires satisfying an external authentication challenge
- (and not merely passing a response in the request) and this has not happened
- before the timeout was reached.
- :http:statuscode:`410 Gone`:
- The server has not (recently) issued a challenge under the given UUID,
- but a reply was provided. (This does not apply for secure question.)
- :http:statuscode:`417 Expectation Failed`:
- The decrypted ``truth`` does not match the expectations of the authentication
- backend, i.e. a phone number for sending an SMS is not a number, or
- an e-mail address for sending an E-mail is not a valid e-mail address.
+ :http:statuscode:`429 Too Many Requests`:
+ The client exceeded the number of allowed attempts at providing
+ a valid response for the given time interval.
+ The response format is given by `RateLimitedMessage`_.
:http:statuscode:`503 Service Unavailable`:
Server is out of Service.
- *Truth-Decryption-Key*: Key used to encrypt the **truth** (see encrypted_truth within `TruthUploadRequest`_) and which has to provided by the user. The key is stored with
- the according `EscrowMethod`_. The server needs this key to get the info out of `TruthUploadRequest`_ needed to verify the ``$RESPONSE``.
-
**Details:**
+ .. _TruthSolutionRequest:
+ .. ts:def:: TruthSolutionRequest
+
+ interface TruthSolutionRequest {
+
+ // Hash over the response that solves the challenge
+ // issued for this truth. This can be the
+ // hash of the security question (as specified before by the client
+ // within the `TruthUploadRequest`_ (see ``encrypted_truth``)), or the hash of the
+ // PIN code sent via SMS, E-mail or postal communication channels.
+ // Only when ``$H_RESPONSE`` is correct, the server responds with the encrypted key share.
+ h_response: HashCode;
+
+ // Key that was used to encrypt the **truth** (see encrypted_truth within `TruthUploadRequest`_)
+ // and which has to provided by the user. The key is stored with
+ // the according `EscrowMethod`_. The server needs this key to get the
+ // info out of `TruthUploadRequest`_ to verify the ``h_response``.
+ truth_decryption_key: ANASTASIS_TruthKeyP;
+
+ // Reference to a payment made by the client to
+ // pay for this request. Optional.
+ payment_secret?: ANASTASIS_PaymentSecretP;
+ }
+
+
.. _EncryptedKeyShare:
.. ts:def:: EncryptedKeyShare
@@ -523,16 +571,90 @@ charge per truth operation using GNU Taler.
}
+ .. _RateLimitedMessage:
+ .. ts:def:: RateLimitedMessage
+
+ interface RateLimitedMessage {
+
+ // Taler error code, TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED.
+ code: number;
+
+ // How many attempts are allowed per challenge?
+ request_limit: number;
+
+ // At what frequency are new challenges issued?
+ request_frequency: RelativeTime;
+
+ // The error message.
+ hint: string;
+
+ }
+
+
+ .. http:post:: /truth/$UUID/challenge
+
+ NEW API (#7064):
+
+ Initiate process to solve challenge associated with the given truth object.
+
+ **Request**:
+
+ Upload a `TruthChallengeRequest`_-Object.
+
+ **Response**:
+
+ :http:statuscode:`200 Ok`:
+ The escrow provider will respond out-of-band (i.e. SMS).
+ The body may contain human- or machine-readable instructions on next steps.
+ In case the response is in JSON, the format is given
+ by `ChallengeInstructionMessage`_.
+ :http:statuscode:`402 Payment required`:
+ The service requires payment to issue a challenge.
+ See the Taler payment protocol specification for how to pay.
+ The response body MAY provide alternative means for payment.
+ :http:statuscode:`403 Forbidden`:
+ This type of truth does not permit requests to trigger a challenge.
+ This is the case for security questions and TOTP methods.
+ :http:statuscode:`404 Not found`:
+ The server does not know any truth under the given UUID.
+ :http:statuscode:`424 Failed Dependency`:
+ The decrypted ``truth`` does not match the expectations of the authentication
+ backend, i.e. a phone number for sending an SMS is not a number, or
+ an e-mail address for sending an E-mail is not a valid e-mail address.
+ :http:statuscode:`503 Service Unavailable`:
+ Server is out of Service.
+
+ **Details:**
+
+ .. _TruthChallengeRequest:
+ .. ts:def:: TruthChallengeRequest
+
+ interface TruthChallengeRequest {
+
+ // Key that was used to encrypt the **truth** (see encrypted_truth within `TruthUploadRequest`_)
+ // and which has to provided by the user. The key is stored with
+ // the according `EscrowMethod`_. The server needs this key to get the
+ // info out of `TruthUploadRequest`_ to verify the ``h_response``.
+ truth_decryption_key: ANASTASIS_TruthKeyP;
+
+ // Reference to a payment made by the client to
+ // pay for this request. Optional.
+ payment_secret?: ANASTASIS_PaymentSecretP;
+ }
+
+
.. _ChallengeInstructionMessage:
.. ts:def:: ChallengeInstructionMessage
type ChallengeInstructionMessage =
- | IbanChallengeInstructionMessage;
+ | FileChallengeInstructionMessage
+ | IbanChallengeInstructionMessage
+ | PinChallengeInstructionMessage;
interface IbanChallengeInstructionMessage {
// What kind of challenge is this?
- method: "iban";
+ method: "IBAN_WIRE";
// How much should be wired?
amount: Amount;
@@ -544,9 +666,36 @@ charge per truth operation using GNU Taler.
business_name: string;
// What is the expected wire transfer subject?
- wire_transfer_subject: Integer;
+ wire_transfer_subject: string;
+
+ // What is the numeric code (also part of the
+ // wire transfer subject) to be hashed when
+ // solving the challenge?
+ answer_code: number;
// Hint about the origin account that must be used.
debit_account_hint: string;
}
+
+ interface PinChallengeInstructionMessage {
+
+ // What kind of challenge is this?
+ method: "TAN_SENT";
+
+ // Where was the PIN code sent? Note that this
+ // address will most likely have been obscured
+ // to improve privacy.
+ tan_address_hint: string;
+
+ }
+
+ interface FileChallengeInstructionMessage {
+
+ // What kind of challenge is this?
+ method: "FILE_WRITTEN";
+
+ // Name of the file where the PIN code was written.
+ filename: string;
+
+ }