summaryrefslogtreecommitdiff
path: root/docker/demo
diff options
context:
space:
mode:
authorMS <ms@taler.net>2022-11-08 15:27:38 +0100
committerMS <ms@taler.net>2022-11-08 15:27:38 +0100
commit6db359f4cf48899be194d9771176293f2d27e919 (patch)
tree9aa507f110fd9ddff83fc92e4f0ed55ec6933a7f /docker/demo
parentee304e9dab845ec004b7d18d45283443ddc642cc (diff)
downloaddeployment-6db359f4cf48899be194d9771176293f2d27e919.tar.gz
deployment-6db359f4cf48899be194d9771176293f2d27e919.tar.bz2
deployment-6db359f4cf48899be194d9771176293f2d27e919.zip
rename docker compose directory
Diffstat (limited to 'docker/demo')
-rw-r--r--docker/demo/README199
-rwxr-xr-xdocker/demo/backup.sh31
-rw-r--r--docker/demo/config/deployment.conf34
-rw-r--r--docker/demo/docker-compose.yml64
-rw-r--r--docker/demo/images/base/Dockerfile65
-rw-r--r--docker/demo/images/exchange/Dockerfile6
-rw-r--r--docker/demo/images/exchange/startup.sh96
-rw-r--r--docker/demo/images/exchange/taler.conf103
-rw-r--r--docker/demo/images/libeufin/Dockerfile7
-rw-r--r--docker/demo/images/libeufin/create_bank_accounts.sh43
-rw-r--r--docker/demo/images/libeufin/nginx.conf14
-rw-r--r--docker/demo/images/libeufin/startup.sh174
-rw-r--r--docker/demo/images/merchant/Dockerfile7
-rw-r--r--docker/demo/images/merchant/create_instances.sh18
-rw-r--r--docker/demo/images/merchant/startup.sh141
-rw-r--r--docker/demo/images/merchant/taler.conf28
-rw-r--r--docker/demo/images/postgres/Dockerfile9
-rw-r--r--docker/demo/images/postgres/init.sh15
-rwxr-xr-xdocker/demo/import-backup.sh29
-rwxr-xr-xdocker/demo/test-docker-gv.sh13
-rwxr-xr-xdocker/demo/test-docker-mvp.sh9
21 files changed, 1105 insertions, 0 deletions
diff --git a/docker/demo/README b/docker/demo/README
new file mode 100644
index 0000000..fd8932d
--- /dev/null
+++ b/docker/demo/README
@@ -0,0 +1,199 @@
+Description
+===========
+
+This setup orchestrates the following containers:
+
+1. Banking (libEufin)
+2. Shop(s)
+3. Payment service provider (Taler exchange and helpers)
+4. Database
+
+Note: one stratagem was however needed to make it work.
+The merchant container needs to redirect requests to
+"localhost:$EXCHANGE_PORT_AT_HOST" to the Docker network,
+in order to actually reach the exchange. That fixes the
+"/pay" handling, since wallets suggest the exchange base URL
+but have a different network view, because they run outside
+of Docker.
+
+How to compile
+==============
+
+Base image
+----------
+
+This image contains a minimal Debian distribution
+with ALL the Taler software and its dependencies.
+
+Navigate to the "images/base" folder, and run the
+following command:
+
+ $ docker build --no-cache -t taler_local/taler_base .
+
+Composed containers
+-------------------
+
+From this folder, run:
+
+ $ docker-compose build
+
+How to run
+==========
+
+Configuration
+-------------
+
+Export the env variable TALER_DEPLOYMENT_CONFIG to an
+absolute path of a configuration file. See config/deployment.conf
+for an example.
+
+Volumes
+-------
+
+Data is kept into Docker volumes. From this directory,
+run the following command to export database, key material, and logs:
+
+ $ ./backup.sh
+
+The following command imports the TAR backup from
+the previous step into the Docker volumes. From this directory:
+
+ $ ./import-backup.sh
+
+If data needs to be removed, from this folder:
+ $ docker-compose down -v
+
+Run
+---
+
+From this folder:
+ $ docker-compose up --remove-orphans --abort-on-container-exit
+
+Logs
+----
+
+Newest rotated logs can be seen by:
+
+ $ docker run -v hybrid_talerlogs:/logs -it taler_local/taler_base /bin/bash
+
+The started container should now have all the logs under /logs.
+
+How to test on localhost
+========================
+
+From this folder:
+
+ $ ./test-docker-mvp.sh
+
+The above test registers a new bank account to libEufin,
+withdraw coins and spend them directly at the merchant backend.
+
+How to deploy to online sites
+=============================
+
+Assuming that TLS is already configured, the following
+Nginx configuration example deploys this sandbox under
+"example.com":
+
+ server {
+ server_name exchange.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ root /dev/null;
+
+ location / {
+ proxy_pass http://localhost:5555/;
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ }
+ }
+
+ server {
+ server_name backend.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_set_header X-Forwarded-Host "backend.example.com";
+ proxy_set_header X-Forwarded-Proto "https";
+ proxy_set_header X-Forwarded-Prefix "/";
+ proxy_pass http://localhost:5556/;
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ }
+ }
+
+ server {
+ server_name webui-bank.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location = / {
+ # Serves the SPA
+ index index.html;
+ proxy_pass http://localhost:15002/;
+ }
+ }
+
+ server {
+ server_name bank.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_set_header X-Forwarded-Host "bank.example.com";
+ proxy_set_header X-Forwarded-Proto "https";
+ proxy_set_header X-Forwarded-Prefix /;
+ proxy_pass http://localhost:15000/;
+ }
+ }
+
+ server {
+ server_name blog.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_set_header X-Forwarded-Host "blog.example.com";
+ proxy_set_header X-Forwarded-Proto "https";
+ proxy_set_header X-Forwarded-Prefix /;
+ proxy_pass http://localhost:5559/;
+ }
+ }
+
+ server {
+ server_name donations.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_set_header X-Forwarded-Host "donations.example.com";
+ proxy_set_header X-Forwarded-Proto "https";
+ proxy_set_header X-Forwarded-Prefix /;
+ proxy_pass http://localhost:5560/;
+ }
+ }
+
+ server {
+ server_name survey.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_set_header X-Forwarded-Host "survey.example.com";
+ proxy_set_header X-Forwarded-Proto "https";
+ proxy_set_header X-Forwarded-Prefix /;
+ proxy_pass http://localhost:5561/;
+ }
+ }
+
+ # Landing page that explains the demo.
+ server {
+ server_name intro.example.com;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_pass http://localhost:5562/;
+ }
+ }
diff --git a/docker/demo/backup.sh b/docker/demo/backup.sh
new file mode 100755
index 0000000..18b8152
--- /dev/null
+++ b/docker/demo/backup.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+set -eu
+
+if ! which docker > /dev/null; then
+ echo docker not found.
+ exit 1
+fi
+
+if ! docker images | grep debian | grep stable > /dev/null; then
+ echo debian:stable not found. Please extract backup with custom image.
+ exit 2
+fi
+
+BACKUP_FILE="/tmp/$(date +%Y-%m-%d)-taler-backup.tar"
+
+if test -a $BACKUP_FILE; then
+ echo "Backup file $BACKUP_FILE exists already, please move it and run the script again."
+ exit 3
+fi
+
+# 'chown' should still help rootful runs to
+# have the TAR owned by the user invoking the command.
+docker run \
+ -v /tmp:/tmp \
+ -v hybrid_talerdata:/taler-data \
+ -v hybrid_talerlogs:/taler-logs \
+ -it debian:stable \
+ /bin/bash -c "tar --no-same-owner --no-same-permissions -c -f ${BACKUP_FILE} /taler-data /taler-logs && chown $(id --user):$(id --group) ${BACKUP_FILE}" > /dev/null
+
+echo Backup at: ${BACKUP_FILE}
diff --git a/docker/demo/config/deployment.conf b/docker/demo/config/deployment.conf
new file mode 100644
index 0000000..78d7240
--- /dev/null
+++ b/docker/demo/config/deployment.conf
@@ -0,0 +1,34 @@
+[taler-deployment]
+currency = EUR
+merchant-apikey = secret
+merchant-url = http://localhost:5556/
+exchange-nexus-username = exchange-at-nexus
+exchange-nexus-password = secret-at-nexus
+
+# Frontends URLs
+landing-url = http://localhost:5562/
+blog-url = http://localhost:5559/
+donations-url = http://localhost:5560/
+survey-url = http://localhost:5561/
+bank-url = http://localhost:15002/
+
+# Bank accounts
+exchange-sandbox-username = exchange-at-sandbox
+exchange-sandbox-password = secret-at-sandbox
+blog-sandbox-username = blog-at-sandbox
+blog-sandbox-password = secret-at-sandbox
+gnunet-sandbox-username = gnunet-at-sandbox
+gnunet-sandbox-password = secret-at-sandbox
+taler-sandbox-username = taler-at-sandbox
+taler-sandbox-password = secret-at-sandbox
+tor-sandbox-username = tor-at-sandbox
+tor-sandbox-password = secret-at-sandbox
+survey-sandbox-username = survey-at-sandbox
+survey-sandbox-password = secret-at-sandbox
+# default merchant instance
+default-sandbox-username = default-at-sandbox
+default-sandbox-password = secret-at-sandbox
+
+db-password = db-secret
+# exchange URL, as seen outside of the container
+default-exchange = http://localhost:5555/
diff --git a/docker/demo/docker-compose.yml b/docker/demo/docker-compose.yml
new file mode 100644
index 0000000..5e763a2
--- /dev/null
+++ b/docker/demo/docker-compose.yml
@@ -0,0 +1,64 @@
+version: '3' # it's a constant
+
+volumes:
+ talerdata:
+ talerlogs:
+
+services:
+ talerdb:
+ build: ./images/postgres
+ ports:
+ - 8888:5432
+ volumes:
+ - talerlogs:/logs
+ - talerdata:/var/lib/postgresql/data/
+ - ${TALER_DEPLOYMENT_CONFIG:?Please export TALER_DEPLOYMENT_CONFIG}:/config/deployment.conf
+ environment:
+ # root is the only role existing in the DBMS. That
+ # matches the role used by other containers when
+ # they connect to the database.
+ POSTGRES_USER: root
+ # this changes to the password used by other
+ # containers to connect here. This definition
+ # only makes the init logic happy.
+ POSTGRES_PASSWORD: nonce
+ POSTGRES_HOST_AUTH_METHOD: scram-sha-256
+ # the final "/postgresql" is used to create
+ # a "postgresql" subfolder in the data volume.
+ PGDATA: /var/lib/postgresql/data/postgresql
+
+ exchange:
+ build: ./images/exchange
+ depends_on:
+ - talerdb
+ ports:
+ - 5555:80
+ volumes:
+ - talerlogs:/logs
+ - talerdata:/data
+ - ${TALER_DEPLOYMENT_CONFIG:?Please export TALER_DEPLOYMENT_CONFIG}:/config/deployment.conf
+
+ merchant:
+ build: ./images/merchant
+ depends_on:
+ - talerdb
+ ports:
+ - 5556:80 # backend
+ - 5559:8080 # blog
+ - 5560:8081 # donations
+ - 5561:8082 # survey
+ - 5562:8083 # landing
+ volumes:
+ - talerlogs:/logs
+ - ${TALER_DEPLOYMENT_CONFIG:?Please export TALER_DEPLOYMENT_CONFIG}:/config/deployment.conf
+
+ bank:
+ build: ./images/libeufin
+ ports:
+ - 15000:15000 # Sandbox
+ - 15001:15001 # Nexus
+ - 15002:80 # Nginx serving the SPA
+ volumes:
+ - talerlogs:/logs
+ - talerdata:/data
+ - ${TALER_DEPLOYMENT_CONFIG:?Please export TALER_DEPLOYMENT_CONFIG}:/config/deployment.conf
diff --git a/docker/demo/images/base/Dockerfile b/docker/demo/images/base/Dockerfile
new file mode 100644
index 0000000..3d12cf9
--- /dev/null
+++ b/docker/demo/images/base/Dockerfile
@@ -0,0 +1,65 @@
+FROM debian:stable
+RUN apt-get update
+
+RUN apt-get install -y autoconf autopoint libtool texinfo \
+ libgcrypt-dev libidn11-dev zlib1g-dev libunistring-dev \
+ libjansson-dev python3-pip git recutils libsqlite3-dev \
+ libpq-dev postgresql libcurl4-openssl-dev libsodium-dev git \
+ libqrencode-dev zip jq npm openjdk-17-jre nginx procps \
+ curl python3-jinja2 wget curl python3-sphinx socat apache2-utils \
+ python3-sphinx-rtd-theme
+
+RUN pip3 install requests click poetry uwsgi
+
+RUN git clone git://git.gnunet.org/libmicrohttpd
+RUN git clone git://git.gnunet.org/gnunet
+RUN git clone git://git.taler.net/exchange /exchange
+RUN git clone git://git.taler.net/merchant /merchant
+RUN git clone git://git.taler.net/libeufin /libeufin
+RUN git clone git://git.taler.net/taler-merchant-demos /taler-merchant-demos
+RUN git clone git://git.taler.net/wallet-core /wallet-core
+
+WORKDIR /libmicrohttpd
+RUN ./bootstrap
+RUN ./configure --disable-doc
+RUN make install
+
+WORKDIR /gnunet
+RUN ./bootstrap
+RUN ./configure --enable-logging=verbose --disable-documentation
+RUN make install
+
+WORKDIR /exchange
+RUN ./bootstrap
+RUN ./configure CFLAGS="-ggdb -O0" --enable-logging=verbose --disable-doc
+RUN make install
+
+WORKDIR /merchant
+RUN ./bootstrap
+RUN ./configure CFLAGS="-ggdb -O0" \
+ --enable-logging=verbose \
+ --disable-doc
+RUN make install
+
+WORKDIR /libeufin
+RUN ./bootstrap
+RUN ./configure
+RUN make install
+
+WORKDIR /taler-merchant-demos
+RUN ./bootstrap
+RUN ./configure
+RUN make install
+
+
+# From: https://github.com/nodesource/distributions/blob/master/README.md#debinstall
+RUN curl -fsSL https://deb.nodesource.com/setup_19.x | bash - && \
+apt-get install -y nodejs
+RUN npm install -g pnpm
+WORKDIR /wallet-core
+RUN ./bootstrap
+WORKDIR ./packages/demobank-ui
+RUN ./configure
+RUN make install
+
+WORKDIR /
diff --git a/docker/demo/images/exchange/Dockerfile b/docker/demo/images/exchange/Dockerfile
new file mode 100644
index 0000000..4f744a5
--- /dev/null
+++ b/docker/demo/images/exchange/Dockerfile
@@ -0,0 +1,6 @@
+FROM taler_local/taler_base
+
+COPY taler.conf /config/taler.conf
+COPY startup.sh /
+RUN chmod +x /startup.sh
+ENTRYPOINT /startup.sh
diff --git a/docker/demo/images/exchange/startup.sh b/docker/demo/images/exchange/startup.sh
new file mode 100644
index 0000000..48382fc
--- /dev/null
+++ b/docker/demo/images/exchange/startup.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+set -eu
+set -x
+export LD_LIBRARY_PATH=/usr/local/lib
+
+# Values from config file mounted at run time:
+CURRENCY=`taler-config -c /config/deployment.conf -s taler-deployment -o currency`
+EXCHANGE_URL=`taler-config -c /config/deployment.conf -s taler-deployment -o default-exchange`
+
+EXCHANGE_NEXUS_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o exchange-nexus-username`
+EXCHANGE_NEXUS_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o exchange-nexus-password`
+EXCHANGE_IBAN=DE159593
+TALER_FACADE_NAME=taler-facade
+DB_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o db-password`
+sed -i "s;__EXCHANGE_URL__;${EXCHANGE_URL};" /config/taler.conf
+sed -i "s;__DB_PASSWORD__;${DB_PASSWORD};" /config/taler.conf
+sed -i "s/__CURRENCY__/${CURRENCY}/" /config/taler.conf
+sed -i "s/__EXCHANGE_NEXUS_USERNAME__/${EXCHANGE_NEXUS_USERNAME}/" /config/taler.conf
+sed -i "s/__EXCHANGE_NEXUS_PASSWORD__/${EXCHANGE_NEXUS_PASSWORD}/" /config/taler.conf
+sed -i "s/__EXCHANGE_IBAN__/${EXCHANGE_IBAN}/" /config/taler.conf
+sed -i "s/__TALER_FACADE_NAME__/${TALER_FACADE_NAME}/" /config/taler.conf
+sed -i "s;__NEXUS_URL__;http://bank:15001;" /config/taler.conf
+
+while ! pg_isready -h talerdb -d taler; do
+ echo DB not ready yet.
+ sleep 2
+done
+echo Now DB is ready.
+
+echo -n "Init database... "
+taler-exchange-dbinit -L DEBUG -c /config/taler.conf
+echo DONE
+
+echo -n "Starting EDDSA helper..."
+taler-exchange-secmod-eddsa -L DEBUG \
+ -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-secmod-eddsa-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Starting RSA helper..."
+taler-exchange-secmod-rsa -L DEBUG \
+ -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-secmod-rsa-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Starting CS helper..."
+taler-exchange-secmod-cs -L DEBUG \
+ -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-secmod-cs-%Y-%m-%d 86400 &
+echo DONE
+EXCHANGE_MASTER_PUB=$(taler-exchange-offline -c /config/taler.conf setup)
+
+sed -i "s/__EXCHANGE_MASTER_PUB__/$EXCHANGE_MASTER_PUB/" /config/taler.conf
+echo -n "Launching exchange HTTPD..."
+taler-exchange-httpd -L DEBUG -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-httpd-%Y-%m-%d 86400 &
+for n in `seq 1 50`
+ do
+ echo "."
+ sleep 0.3
+ OK=1
+ wget http://exchange/ -o /dev/null -O /dev/null >/dev/null && break
+ OK=0
+ done
+ if [ 1 != $OK ]
+ then
+ echo "ERROR: failed to launch Exchange"
+ exit 1
+ fi
+echo DONE
+# echo -n "Launching sync..."
+# sync-httpd -L DEBUG -c /config/taler.conf 2>&1 | \
+# rotatelogs -e /logs/sync-httpd-%Y-%m-%d 86400 &
+# echo DONE
+echo -n "Launching wirewatch..."
+taler-exchange-wirewatch -L DEBUG -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-wirewatch-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Launching transfer service..."
+taler-exchange-transfer -L DEBUG -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-transfer-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Launching aggregator service..."
+taler-exchange-aggregator -L DEBUG -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-exchange-aggregator-%Y-%m-%d 86400 &
+echo DONE
+echo
+echo -n "Setup keys and fees with taler-exchange-offline..."
+taler-exchange-offline -L DEBUG -c /config/taler.conf \
+ download sign \
+ enable-account "payto://iban/SANDBOXX/${EXCHANGE_IBAN}?receiver-name=Exchange+Company" \
+ wire-fee now iban ${CURRENCY}:0.01 ${CURRENCY}:0.01 \
+ global-fee now ${CURRENCY}:0 ${CURRENCY}:0 ${CURRENCY}:0 1h 1year 5 \
+ upload 2>&1
+echo DONE
+
+wait
diff --git a/docker/demo/images/exchange/taler.conf b/docker/demo/images/exchange/taler.conf
new file mode 100644
index 0000000..fec00c3
--- /dev/null
+++ b/docker/demo/images/exchange/taler.conf
@@ -0,0 +1,103 @@
+[taler]
+currency = __CURRENCY__
+currency_round_unit = __CURRENCY__:0.01
+
+[paths]
+taler_data_home = /data/exchange
+
+[taler-exchange-secmod-eddsa]
+unixpath = /eddsa.http
+
+[taler-exchange-secmod-rsa]
+sm_priv_key = /data/taler-exchange-secmod-rsa/secmod-private-key
+unixpath = /sockets/exchange-secmod-rsa.sock
+
+[taler-exchange-secmod-cs]
+sm_priv_key = /data/taler-exchange-secmod-cs/secmod-private-key
+unixpath = /sockets/exchange-secmod-cs.sock
+
+[exchange-accountcredentials-1]
+username = __EXCHANGE_NEXUS_USERNAME__
+wire_gateway_auth_method = basic
+wire_gateway_url = __NEXUS_URL__/facades/__TALER_FACADE_NAME__/taler-wire-gateway/
+password = __EXCHANGE_NEXUS_PASSWORD__
+
+[exchange-account-1]
+enable_credit = yes
+enable_debit = yes
+payto_uri = payto://iban/SANDBOXX/__EXCHANGE_IBAN__?receiver-name=Name+unknown
+
+[exchange]
+master_public_key = __EXCHANGE_MASTER_PUB__
+privacy_etag = 0
+privacy_dir = /usr/local/share/taler/exchange/pp
+terms_etag = tos
+terms_dir = /usr/local/share/taler/exchange/tos
+base_url = __EXCHANGE_URL__
+unixpath = /sockets/exchange.sock
+serve = tcp
+port = 80
+# serve = unix
+
+[exchangedb-postgres]
+config = postgres://root:__DB_PASSWORD__@talerdb/taler
+
+[coin___CURRENCY___10]
+rsa_keysize = 2048
+fee_deposit = __CURRENCY__:0.01
+fee_refund = __CURRENCY__:0.01
+fee_refresh = __CURRENCY__:0.01
+fee_withdraw = __CURRENCY__:0.01
+duration_legal = 10 years
+duration_spend = 5 years
+duration_withdraw = 3 years
+value = __CURRENCY__:10
+cipher = RSA
+
+[coin___CURRENCY___5]
+rsa_keysize = 2048
+fee_deposit = __CURRENCY__:0.01
+fee_refund = __CURRENCY__:0.01
+fee_refresh = __CURRENCY__:0.01
+fee_withdraw = __CURRENCY__:0.01
+duration_legal = 10 years
+duration_spend = 5 years
+duration_withdraw = 3 years
+value = __CURRENCY__:5
+cipher = RSA
+
+[coin___CURRENCY___2]
+rsa_keysize = 2048
+fee_deposit = __CURRENCY__:0.01
+fee_refund = __CURRENCY__:0.01
+fee_refresh = __CURRENCY__:0.01
+fee_withdraw = __CURRENCY__:0.01
+duration_legal = 10 years
+duration_spend = 5 years
+duration_withdraw = 3 years
+value = __CURRENCY__:2
+cipher = RSA
+
+[coin___CURRENCY___1]
+rsa_keysize = 2048
+fee_deposit = __CURRENCY__:0.01
+fee_refund = __CURRENCY__:0.01
+fee_refresh = __CURRENCY__:0.01
+fee_withdraw = __CURRENCY__:0.01
+duration_legal = 10 years
+duration_spend = 5 years
+duration_withdraw = 3 years
+value = __CURRENCY__:1
+cipher = RSA
+
+[coin___CURRENCY___ct_10]
+rsa_keysize = 2048
+fee_deposit = __CURRENCY__:0.01
+fee_refund = __CURRENCY__:0.01
+fee_refresh = __CURRENCY__:0.01
+fee_withdraw = __CURRENCY__:0.01
+duration_legal = 10 years
+duration_spend = 5 years
+duration_withdraw = 3 years
+value = __CURRENCY__:0.10
+cipher = RSA
diff --git a/docker/demo/images/libeufin/Dockerfile b/docker/demo/images/libeufin/Dockerfile
new file mode 100644
index 0000000..e55c80d
--- /dev/null
+++ b/docker/demo/images/libeufin/Dockerfile
@@ -0,0 +1,7 @@
+FROM taler_local/taler_base
+
+COPY startup.sh /
+COPY create_bank_accounts.sh /
+RUN chmod +x /startup.sh
+COPY nginx.conf /
+ENTRYPOINT /startup.sh
diff --git a/docker/demo/images/libeufin/create_bank_accounts.sh b/docker/demo/images/libeufin/create_bank_accounts.sh
new file mode 100644
index 0000000..449f46e
--- /dev/null
+++ b/docker/demo/images/libeufin/create_bank_accounts.sh
@@ -0,0 +1,43 @@
+BLOG_IBAN=DE940993
+GNUNET_IBAN=DE463312
+DEFAULT_IBAN=DE474361
+TOR_IBAN=DE358263
+TALER_IBAN=DE102893
+SURVEY_IBAN=DE731371
+
+EXCHANGE_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o exchange-sandbox-username`
+EXCHANGE_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o exchange-sandbox-password`
+BLOG_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o blog-sandbox-username`
+BLOG_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o blog-sandbox-password`
+GNUNET_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o gnunet-sandbox-username`
+GNUNET_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o gnunet-sandbox-password`
+DEFAULT_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o default-sandbox-username`
+DEFAULT_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o default-sandbox-password`
+TOR_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o tor-sandbox-username`
+TOR_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o tor-sandbox-password`
+TALER_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o taler-sandbox-username`
+TALER_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o taler-sandbox-password`
+SURVEY_SANDBOX_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o survey-sandbox-username`
+SURVEY_SANDBOX_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o survey-sandbox-password`
+
+echo -n "create default merchant instance bank account..."
+register_sandbox_account $DEFAULT_SANDBOX_USERNAME $DEFAULT_SANDBOX_PASSWORD $DEFAULT_IBAN "default merchant instance"
+echo DONE
+echo -n "create exchange bank account..."
+register_sandbox_account $EXCHANGE_SANDBOX_USERNAME $EXCHANGE_SANDBOX_PASSWORD $EXCHANGE_IBAN "exchange company"
+echo DONE
+echo -n "create Blog bank account..."
+register_sandbox_account $BLOG_SANDBOX_USERNAME $BLOG_SANDBOX_PASSWORD $BLOG_IBAN BlogCompany
+echo DONE
+echo -n "create GNUnet bank account..."
+register_sandbox_account $GNUNET_SANDBOX_USERNAME $GNUNET_SANDBOX_PASSWORD $GNUNET_IBAN GNUnet
+echo DONE
+echo -n "create Taler bank account..."
+register_sandbox_account $TALER_SANDBOX_USERNAME $TALER_SANDBOX_PASSWORD $TALER_IBAN Taler
+echo DONE
+echo -n "create Tor bank account..."
+register_sandbox_account $TOR_SANDBOX_USERNAME $TOR_SANDBOX_PASSWORD $TOR_IBAN Tor
+echo DONE
+echo -n "create survey bank account..."
+register_sandbox_account $SURVEY_SANDBOX_USERNAME $SURVEY_SANDBOX_PASSWORD $SURVEY_IBAN Survey
+echo DONE
diff --git a/docker/demo/images/libeufin/nginx.conf b/docker/demo/images/libeufin/nginx.conf
new file mode 100644
index 0000000..d5436f5
--- /dev/null
+++ b/docker/demo/images/libeufin/nginx.conf
@@ -0,0 +1,14 @@
+error_log /dev/stdout;
+daemon off;
+events {}
+http {
+ access_log /dev/stdout;
+ server {
+ include /etc/nginx/mime.types;
+ listen 80;
+ listen [::]:80;
+ location / {
+ root /usr/local/share/taler/demobank-ui;
+ }
+ }
+}
diff --git a/docker/demo/images/libeufin/startup.sh b/docker/demo/images/libeufin/startup.sh
new file mode 100644
index 0000000..777ddcc
--- /dev/null
+++ b/docker/demo/images/libeufin/startup.sh
@@ -0,0 +1,174 @@
+#!/bin/bash
+
+set -eu
+
+MAYBE_VOLUME_MOUNTPOINT="/data/libeufin"
+export LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:sqlite:${MAYBE_VOLUME_MOUNTPOINT}/sandbox.sqlite3"
+export LIBEUFIN_NEXUS_DB_CONNECTION="jdbc:sqlite:${MAYBE_VOLUME_MOUNTPOINT}/nexus.sqlite3"
+# This file indicates that data preparation ran already
+# once. It helps against some non idempotent commands.
+INIT_MARKER=${MAYBE_VOLUME_MOUNTPOINT}/init-done
+export LD_LIBRARY_PATH=/usr/local/lib # helps taler-config
+CURRENCY=`taler-config -c /config/deployment.conf -s taler-deployment -o currency`
+SANDBOX_PORT=15000
+NEXUS_PORT=15001
+SANDBOX_BASE_URL="http://localhost:${SANDBOX_PORT}"
+EXCHANGE_URL=`taler-config -c /config/deployment.conf -s taler-deployment -o default-exchange`
+CAPTCHA_URL=`taler-config -c /config/deployment.conf -s taler-deployment -o bank-url`
+# As wanted by the Libeufin CLI:
+export LIBEUFIN_SANDBOX_URL="${SANDBOX_BASE_URL}/demobanks/default"
+export LIBEUFIN_NEXUS_URL="http://localhost:${NEXUS_PORT}"
+
+# invoke: username password iban name
+register_sandbox_account() {
+ export LIBEUFIN_SANDBOX_USERNAME=$1
+ export LIBEUFIN_SANDBOX_PASSWORD=$2
+ # A unavailable username upon registration should
+ # fail, hence non idempotence is acceptable here.
+ test -a $INIT_MARKER || libeufin-cli sandbox demobank register --name "$4" --iban $3
+ unset LIBEUFIN_SANDBOX_USERNAME
+ unset LIBEUFIN_SANDBOX_PASSWORD
+}
+
+# takes port and service name
+is_serving() {
+ echo Is $1 serving?
+ for n in `seq 1 80`
+ do
+ echo "."
+ sleep 0.1
+ OK=1
+ wget $1 -o /dev/null -O /dev/null >/dev/null && break
+ OK=0
+ done
+ if [ 1 != $OK ]
+ then
+ echo "$2 unreachable."
+ exit 1
+ fi
+ echo $2 reachable.
+}
+EXCHANGE_IBAN=DE159593
+
+mkdir -p ${MAYBE_VOLUME_MOUNTPOINT}
+export LIBEUFIN_SANDBOX_ADMIN_PASSWORD=secret
+echo -n "Creating ${CURRENCY} default demobank (CAPTCHA URL: ${CAPTCHA_URL})..."
+test -a $INIT_MARKER || libeufin-sandbox config \
+ --currency ${CURRENCY} \
+ --with-signup-bonus \
+ --captcha-url ${CAPTCHA_URL} \
+ default
+echo DONE
+echo -n "Specify default exchange..."
+test -a $INIT_MARKER || libeufin-sandbox default-exchange \
+ ${EXCHANGE_URL} \
+ "payto://iban/SANDBOXX/${EXCHANGE_IBAN}?receiver-name=Exchange+Company"
+echo DONE
+
+# Provide navigation bar links.
+export TALER_ENV_URL_MERCHANT_BLOG=`taler-config -c /config/deployment.conf -s taler-deployment -o blog-url`
+export TALER_ENV_URL_MERCHANT_DONATIONS=`taler-config -c /config/deployment.conf -s taler-deployment -o donations-url`
+export TALER_ENV_URL_MERCHANT_SURVEY=`taler-config -c /config/deployment.conf -s taler-deployment -o survey-url`
+export TALER_ENV_URL_INTRO=`taler-config -c /config/deployment.conf -s taler-deployment -o landing-url`
+export TALER_ENV_URL_BANK=`taler-config -c /config/deployment.conf -s taler-deployment -o bank-url`
+
+echo -n "Launching Sandbox..."
+libeufin-sandbox serve --no-localhost-only --port $SANDBOX_PORT 2>&1 | \
+ rotatelogs -e /logs/libeufin-sandbox-serve-%Y-%m-%d 86400 &
+echo DONE
+is_serving "${LIBEUFIN_SANDBOX_URL}/integration-api/config" Sandbox
+
+source create_bank_accounts.sh
+
+echo -n "Create exchange EBICS subscriber at Sandbox.."
+export LIBEUFIN_SANDBOX_USERNAME=admin
+export LIBEUFIN_SANDBOX_PASSWORD=secret
+echo -n "Create EBICS host at Sandbox.."
+test -a $INIT_MARKER || libeufin-cli sandbox --sandbox-url $SANDBOX_BASE_URL ebicshost create --host-id talerebics
+echo DONE
+echo -n "Create exchange's EBICS subscriber at Sandbox.."
+test -a $INIT_MARKER || libeufin-cli sandbox \
+ demobank new-ebicssubscriber --host-id talerebics \
+ --user-id exchangeebics --partner-id talerpartner \
+ --bank-account $EXCHANGE_SANDBOX_USERNAME
+echo DONE
+## NEXUS SETUP
+EXCHANGE_NEXUS_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o exchange-nexus-username`
+EXCHANGE_NEXUS_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o exchange-nexus-password`
+
+echo -n "Creating Nexus superuser..."
+# Idempotent in the sense that if the user is found,
+# they'll get the password changed.
+libeufin-nexus superuser $EXCHANGE_NEXUS_USERNAME \
+ --password $EXCHANGE_NEXUS_PASSWORD
+echo DONE
+echo -n "Launching Nexus..."
+libeufin-nexus serve --no-localhost-only --port $NEXUS_PORT 2>&1 | \
+ rotatelogs -e /logs/libeufin-nexus-serve-%Y-%m-%d 86400 &
+echo DONE
+is_serving $LIBEUFIN_NEXUS_URL Nexus
+
+export LIBEUFIN_NEXUS_USERNAME=$EXCHANGE_NEXUS_USERNAME
+export LIBEUFIN_NEXUS_PASSWORD=$EXCHANGE_NEXUS_PASSWORD
+
+echo -n Creating a EBICS connection at Nexus..
+# Not idempotent: the implementation does check if
+# a connection with the requested name exists, and
+# returns "409 Conflict". FIXME
+test -a $INIT_MARKER || libeufin-cli connections new-ebics-connection \
+ --ebics-url "${SANDBOX_BASE_URL}/ebicsweb" \
+ --host-id talerebics \
+ --partner-id talerpartner \
+ --ebics-user-id exchangeebics \
+ talerconn
+echo DONE
+echo -n Setup EBICS keying..
+# idempotent (noop if 'talerconn' is found)
+libeufin-cli connections connect talerconn > /dev/null
+echo DONE
+echo -n Download bank account name from Sandbox..
+# idempotent (only stores new bank account names)
+libeufin-cli connections download-bank-accounts talerconn
+echo DONE
+echo -n Importing bank account info into Nexus..
+# idempotent
+NEXUS_IMPORTED_BANKACCOUNT=nexus-bankaccount
+libeufin-cli connections import-bank-account \
+ --offered-account-id $EXCHANGE_SANDBOX_USERNAME \
+ --nexus-bank-account-id $NEXUS_IMPORTED_BANKACCOUNT \
+ talerconn
+echo DONE
+echo -n Setup payments submission task..
+# Tries every second.
+# Not idempotent, FIXME
+test -a $INIT_MARKER || libeufin-cli accounts task-schedule \
+ --task-type submit \
+ --task-name exchange-payments \
+ --task-cronspec "* * *" \
+ $NEXUS_IMPORTED_BANKACCOUNT
+echo DONE
+# Tries every second. Ask C52
+echo -n Setup history fetch task..
+# Not idempotent, FIXME
+test -a $INIT_MARKER || libeufin-cli accounts task-schedule \
+ --task-type fetch \
+ --task-name exchange-history \
+ --task-cronspec "* * *" \
+ --task-param-level report \
+ --task-param-range-type latest \
+ $NEXUS_IMPORTED_BANKACCOUNT
+echo DONE
+echo -n Create the Taler facade at Nexus..
+# Not idempotent, in the sense that a duplicate
+# facade will be created. FIXME
+FACADE_NAME=taler-facade
+test -a $INIT_MARKER || libeufin-cli facades \
+ new-taler-wire-gateway-facade \
+ --currency ${CURRENCY} --facade-name $FACADE_NAME \
+ talerconn $NEXUS_IMPORTED_BANKACCOUNT
+echo DONE
+# starting the SPA
+nginx -c /nginx.conf 2>&1 | rotatelogs -e /logs/bank-ui-%Y-%m-%d 86400 &
+touch $INIT_MARKER
+
+wait
diff --git a/docker/demo/images/merchant/Dockerfile b/docker/demo/images/merchant/Dockerfile
new file mode 100644
index 0000000..4121177
--- /dev/null
+++ b/docker/demo/images/merchant/Dockerfile
@@ -0,0 +1,7 @@
+FROM taler_local/taler_base
+
+COPY taler.conf /config/taler.conf
+COPY startup.sh /
+COPY create_instances.sh /
+RUN chmod +x /startup.sh
+ENTRYPOINT /startup.sh
diff --git a/docker/demo/images/merchant/create_instances.sh b/docker/demo/images/merchant/create_instances.sh
new file mode 100644
index 0000000..2098663
--- /dev/null
+++ b/docker/demo/images/merchant/create_instances.sh
@@ -0,0 +1,18 @@
+echo -n "Create default instance..."
+curl -s -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/'$DEFAULT_IBAN'?receiver-name=Merchant43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"'${CURRENCY}':1", "default_max_deposit_fee":"'${CURRENCY}':1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 1},"default_pay_delay":{"d_us": 3600000000}}' http://merchant/management/instances
+echo DONE
+echo -n "Create blog instance..."
+curl -s -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/'$BLOG_IBAN'?receiver-name=BlogCompany"],"id":"blog","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"'${CURRENCY}':1", "default_max_deposit_fee":"'${CURRENCY}':1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 1000000},"default_pay_delay":{"d_us": 3600000000}}' http://merchant/management/instances
+echo DONE
+echo -n "Create GNUnet instance..."
+curl -s -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/'$GNUNET_IBAN'?receiver-name=GNUnet"],"id":"GNUnet","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"'${CURRENCY}':1", "default_max_deposit_fee":"'${CURRENCY}':1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 1000000},"default_pay_delay":{"d_us": 3600000000}}' http://merchant/management/instances
+echo DONE
+echo -n "Create Taler instance..."
+curl -s -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/'$TALER_IBAN'?receiver-name=GNUnet"],"id":"Taler","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"'${CURRENCY}':1", "default_max_deposit_fee":"'${CURRENCY}':1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 1000000},"default_pay_delay":{"d_us": 3600000000}}' http://merchant/management/instances
+echo DONE
+echo -n "Create Tor instance..."
+curl -s -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/'$TOR_IBAN'?receiver-name=GNUnet"],"id":"Tor","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"'${CURRENCY}':1", "default_max_deposit_fee":"'${CURRENCY}':1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 1000000},"default_pay_delay":{"d_us": 3600000000}}' http://merchant/management/instances
+echo DONE
+echo -n "Create survey instance..."
+curl -s -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/'$SURVEY_IBAN'?receiver-name=GNUnet"],"id":"survey","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"'${CURRENCY}':1", "default_max_deposit_fee":"'${CURRENCY}':1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 1000000},"default_pay_delay":{"d_us": 3600000000}}' http://merchant/management/instances
+echo DONE
diff --git a/docker/demo/images/merchant/startup.sh b/docker/demo/images/merchant/startup.sh
new file mode 100644
index 0000000..7b55a94
--- /dev/null
+++ b/docker/demo/images/merchant/startup.sh
@@ -0,0 +1,141 @@
+#!/bin/bash
+
+set -eu
+export LD_LIBRARY_PATH=/usr/local/lib
+
+# Values from config file mounted at run time:
+CURRENCY=`taler-config -c /config/deployment.conf -s taler-deployment -o currency`
+BACKEND_APIKEY=`taler-config -c /config/deployment.conf -s taler-deployment -o merchant-apikey`
+BACKEND_URL=`taler-config -c /config/deployment.conf -s taler-deployment -o merchant-url`
+EXCHANGE_URL=`taler-config -c /config/deployment.conf -s taler-deployment -o default-exchange`
+DB_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o db-password`
+
+BLOG_IBAN=DE940993
+GNUNET_IBAN=DE463312
+DEFAULT_IBAN=DE474361
+TOR_IBAN=DE358263
+TALER_IBAN=DE102893
+SURVEY_IBAN=DE731371
+
+while ! pg_isready -h talerdb -d taler; do
+ echo DB not ready yet.
+ sleep 2
+done
+echo Now DB is ready.
+
+# FIXME: wallets external to the containers put localhost'ed
+# exchanges along a /pay request. That breaks here, since the
+# exchange listens from another container. The following
+# command routes every request to 5555 (port on the host
+# system that points to a contained exchange AND where the
+# merchant tries to /deposit), to the container where the exchange listens.
+socat TCP-LISTEN:5555,fork,reuseaddr TCP:exchange:80 &
+
+# FIXME: browsers can only get redirected to merchant backends
+# as they appear outside of the container (port 5556). OTOH,
+# merchant frontends can only talk to backends as they appear
+# _inside_ the container (port 80). Config, ultimately, must
+# specify backends as they appear outside, otherwise frontends
+# would redirect browsers with in-container addresses, that
+# would make the backend not reached. The following redirection
+# allows to bridge the external merchant port to the internal,
+# to make frontends reach the backend.
+socat TCP-LISTEN:5556,fork,reuseaddr TCP:localhost:80 &
+
+# $2 might have Authorization header.
+is_serving () {
+set +u # tolerate missing $2
+echo Checking $1
+for n in `seq 1 50`
+ do
+ echo "."
+ sleep 0.5
+ OK=1
+ # auth case.
+ if test -n "$2"; then
+ wget --header "$2" $1 -o /dev/null -O /dev/null >/dev/null && break
+ else
+ wget $1 -o /dev/null -O /dev/null >/dev/null && break
+ fi
+ OK=0
+ done
+ if [ 1 != $OK ]
+ then
+ echo "ERROR: $1 unreachable."
+ exit 1
+ fi
+ set -e
+}
+
+is_serving ${EXCHANGE_URL}
+
+EXCHANGE_MASTER_PUB=$(curl -s ${EXCHANGE_URL}keys | jq -r .master_public_key)
+echo Found Exchange Pub: $EXCHANGE_MASTER_PUB
+sed -i "s;__EXCHANGE_URL__;${EXCHANGE_URL};" /config/taler.conf
+sed -i "s/__EXCHANGE_PUB__/${EXCHANGE_MASTER_PUB}/" /config/taler.conf
+sed -i "s/__CURRENCY__/${CURRENCY}/" /config/taler.conf
+sed -i "s/__BACKEND_APIKEY__/${BACKEND_APIKEY}/" /config/taler.conf
+sed -i "s;__BACKEND_URL__;${BACKEND_URL};" /config/taler.conf
+sed -i "s;__DB_PASSWORD__;${DB_PASSWORD};" /config/taler.conf
+
+echo "Init database... "
+taler-merchant-dbinit -L DEBUG -c /config/taler.conf
+echo DONE
+echo -n "Launch merchant backend..."
+taler-merchant-httpd -c /config/taler.conf 2>&1 | \
+ rotatelogs -e /logs/taler-merchant-httpd-%Y-%m-%d 86400 &
+echo DONE
+sleep 1
+
+source /create_instances.sh
+
+export TALER_ENV_URL_MERCHANT_BLOG=`taler-config -c /config/deployment.conf -s taler-deployment -o blog-url`
+export TALER_ENV_URL_MERCHANT_DONATIONS=`taler-config -c /config/deployment.conf -s taler-deployment -o donations-url`
+export TALER_ENV_URL_MERCHANT_SURVEY=`taler-config -c /config/deployment.conf -s taler-deployment -o survey-url`
+export TALER_ENV_URL_INTRO=`taler-config -c /config/deployment.conf -s taler-deployment -o landing-url`
+export TALER_ENV_URL_BANK=`taler-config -c /config/deployment.conf -s taler-deployment -o bank-url`
+
+echo -n "Launch blog..."
+${HOME}/.local/bin/taler-merchant-demos -c /config/taler.conf --http-port 8080 blog 2>&1 | rotatelogs -e /logs/blog-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Launch donations..."
+${HOME}/.local/bin/taler-merchant-demos -c /config/taler.conf --http-port 8081 donations 2>&1 | rotatelogs -e /logs/donations-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Launch Survey..."
+${HOME}/.local/bin/taler-merchant-demos -c /config/taler.conf --http-port 8082 survey 2>&1 | rotatelogs -e /logs/survey-%Y-%m-%d 86400 &
+echo DONE
+echo -n "Launch Landing..."
+${HOME}/.local/bin/taler-merchant-demos -c /config/taler.conf --http-port 8083 landing 2>&1 | rotatelogs -e /logs/landing-%Y-%m-%d 86400 &
+echo DONE
+
+echo -n Creating a reserve for tips...
+
+PAYTO_RESERVE=$(
+ taler-merchant-setup-reserve \
+ --amount ${CURRENCY}:20 \
+ --exchange-url ${EXCHANGE_URL} \
+ --merchant-url http://localhost/instances/survey/ \
+ --apikey "Bearer {BACKEND_APIKEY}" \
+ --wire-method iban
+)
+
+SANDBOX_URL="http://bank:15000/demobanks/default"
+is_serving "${SANDBOX_URL}/integration-api/config"
+SURVEY_USERNAME=`taler-config -c /config/deployment.conf -s taler-deployment -o survey-sandbox-username`
+SURVEY_PASSWORD=`taler-config -c /config/deployment.conf -s taler-deployment -o survey-sandbox-password`
+# Check/wait that the Survey site got its bank account.
+curl "${SANDBOX_URL}/access-api/public-accounts"
+is_serving "${SANDBOX_URL}/access-api/accounts/${SURVEY_USERNAME}" \
+ "Authorization: Basic $(echo -n $SURVEY_USERNAME:$SURVEY_PASSWORD | base64)"
+export LIBEUFIN_SANDBOX_USERNAME=${SURVEY_USERNAME}
+export LIBEUFIN_SANDBOX_PASSWORD=${SURVEY_PASSWORD}
+libeufin-cli sandbox \
+ --sandbox-url ${SANDBOX_URL} \
+ demobank new-transaction --bank-account ${LIBEUFIN_SANDBOX_USERNAME} \
+ --payto-with-subject ${PAYTO_RESERVE} --amount 20
+unset LIBEUFIN_SANDBOX_USERNAME
+unset LIBEUFIN_SANDBOX_PASSWORD
+
+echo DONE
+
+wait
diff --git a/docker/demo/images/merchant/taler.conf b/docker/demo/images/merchant/taler.conf
new file mode 100644
index 0000000..a85510e
--- /dev/null
+++ b/docker/demo/images/merchant/taler.conf
@@ -0,0 +1,28 @@
+[taler]
+currency = __CURRENCY__
+
+[paths]
+taler_data_home = /data
+
+[merchant-exchange-__CURRENCY__]
+currency = __CURRENCY__
+exchange_base_url = __EXCHANGE_URL__
+master_key = __EXCHANGE_PUB__
+
+[merchantdb-postgres]
+config = postgres://root:__DB_PASSWORD__@talerdb/taler
+
+[merchant]
+default_max_deposit_fee = __CURRENCY__:0.05
+default_max_wire_fee = __CURRENCY__:0.01
+wire_transfer_delay = 0 s
+port = 80
+serve = tcp
+
+[merchant-account-merchant]
+wire_file_mode = 770
+wire_response = ${TALER_DATA_HOME}/merchant/wire/merchant.json
+
+[frontends]
+backend = __BACKEND_URL__
+backend_apikey = __BACKEND_APIKEY__
diff --git a/docker/demo/images/postgres/Dockerfile b/docker/demo/images/postgres/Dockerfile
new file mode 100644
index 0000000..d0fde23
--- /dev/null
+++ b/docker/demo/images/postgres/Dockerfile
@@ -0,0 +1,9 @@
+FROM docker.io/postgres
+
+# Default "${PGDATA}/log" directory was problematic
+# when mounted in a volume. Prefer arbitrary "/logs".
+RUN mkdir /logs
+RUN chown postgres:postgres /logs
+
+COPY init.sh /docker-entrypoint-initdb.d/init.sh
+RUN chmod +x /docker-entrypoint-initdb.d/init.sh
diff --git a/docker/demo/images/postgres/init.sh b/docker/demo/images/postgres/init.sh
new file mode 100644
index 0000000..cc28f0d
--- /dev/null
+++ b/docker/demo/images/postgres/init.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -eu
+
+# FIXME: use taler-config.
+CUSTOM_PASSWORD=$(grep ^db-password < /config/deployment.conf | awk -F= '{print $2}' | tr -d "[:space:]")
+if test -z "${CUSTOM_PASSWORD}"; then
+ echo ERROR: database password empty.
+fi
+echo "ALTER ROLE root WITH PASSWORD '"${CUSTOM_PASSWORD}"';" | psql -U root
+createdb -U root -O root taler
+echo "ALTER SYSTEM SET logging_collector TO 'true';" | psql -U root
+echo "ALTER SYSTEM SET log_directory TO '/logs';" | psql -U root
+echo "ALTER SYSTEM SET log_filename TO 'postgres-%Y-%m-%d';" | psql -U root
+pg_ctl restart
diff --git a/docker/demo/import-backup.sh b/docker/demo/import-backup.sh
new file mode 100755
index 0000000..4fdb9c4
--- /dev/null
+++ b/docker/demo/import-backup.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -e
+
+if ! which docker > /dev/null; then
+ echo docker not found.
+ exit 1
+fi
+
+if ! docker images | grep debian | grep stable > /dev/null; then
+ echo debian:stable not found. Please extract backup with custom image.
+ exit 2
+fi
+
+if test -z $1; then
+ echo "Please, give the backup (TAR) file\'s path as the one argument."
+ exit 1
+fi
+
+if ! test -a $1; then
+ echo File $1 not found.
+ exit 1
+fi
+
+docker run \
+ -v $1:/tmp/backup.tar \
+ -v hybrid_talerdata:/taler-data \
+ -v hybrid_talerlogs:/taler-logs \
+ -it debian:stable /bin/bash -c "tar -x -f /tmp/backup.tar"
diff --git a/docker/demo/test-docker-gv.sh b/docker/demo/test-docker-gv.sh
new file mode 100755
index 0000000..e932eb1
--- /dev/null
+++ b/docker/demo/test-docker-gv.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+CURRENCY=KUDOS
+HOST="demo.taler.net"
+# HOST="int.taler.net"
+
+taler-wallet-cli --no-throttle api --expect-success 'runIntegrationTest' \
+ '{"amountToSpend":"'$CURRENCY':1",
+ "amountToWithdraw":"'$CURRENCY':3",
+ "bankBaseUrl":"https://bank.'$HOST'/demobanks/default/access-api/",
+ "exchangeBaseUrl":"https://exchange.'$HOST'/",
+ "merchantBaseUrl":"https://backend.'$HOST'/"
+ }'
diff --git a/docker/demo/test-docker-mvp.sh b/docker/demo/test-docker-mvp.sh
new file mode 100755
index 0000000..f8b9763
--- /dev/null
+++ b/docker/demo/test-docker-mvp.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+taler-wallet-cli --no-throttle api --expect-success 'runIntegrationTest' \
+ '{"amountToSpend":"EUR:10",
+ "amountToWithdraw":"EUR:30",
+ "bankBaseUrl":"http://localhost:15000/demobanks/default/access-api/",
+ "exchangeBaseUrl":"http://localhost:5555/",
+ "merchantBaseUrl":"http://localhost:5556/"
+ }'