taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

commit 678c99c753a2a93ab3bcfc4920cd70edff6281c0
parent a6cd7011bf079f59231aa8a6950db70f6338d1a3
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 25 May 2026 17:07:47 +0200

add paivana manual

Diffstat:
Mconf.py | 1+
Mflake.nix | 1+
Mtaler-paivana-manual.rst | 554++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 523 insertions(+), 33 deletions(-)

diff --git a/conf.py b/conf.py @@ -61,6 +61,7 @@ extensions = [ "httpdomain.httpdomain", "myst_parser", "sphinx_multitoc_numbering", + "sphinx_design", ] imgmath_image_format = 'svg' diff --git a/flake.nix b/flake.nix @@ -33,6 +33,7 @@ python-pkgs.sphinx-book-theme python-pkgs.myst-parser python-pkgs.sphinxcontrib-httpdomain + python-pkgs.sphinx-design ])) ]; buildInputs = [ diff --git a/taler-paivana-manual.rst b/taler-paivana-manual.rst @@ -79,75 +79,563 @@ Paivana service, you must have the base URL, username and password of the selected merchant backend at hand. +.. _Paivana-httpd: + Paivana-httpd ============= This chapter documents the installation and operation of the Paivana -reverse proxy. - +reverse proxy ``paivana-httpd``. The reverse proxy sits between the +public Internet and an upstream Web service, intercepting requests +that have not yet been paid for and presenting the client with a +GNU Taler paywall. Once a payment has been confirmed by the +configured GNU Taler merchant backend, ``paivana-httpd`` forwards +subsequent requests of that client to the upstream service. + +The full list of command-line options is documented in +:manpage:`paivana-httpd(1)`; the configuration file is +documented in :manpage:`paivana.conf(5)`. + + +Architecture overview +--------------------- + +``paivana-httpd`` does not implement any payment logic of its own. +Instead, every Paivana deployment combines three components: + +1. **The upstream web service.** This is the existing HTTP service + whose content should be sold (a static website, a cgit service, + a REST API, …). It does not need to be modified to + work with Paivana. +2. **A GNU Taler merchant backend** (``taler-merchant-httpd``). The + merchant backend manages templates, creates orders, talks to one + or more Taler exchanges, and ultimately reports back whether a + given order has been paid. See the + :ref:`Taler Merchant Backend Operator Manual + <taler-merchant-backend-operator-manual>` for full details. +3. **``paivana-httpd`` itself.** This is the reverse proxy that + gates the upstream service. It reads a single + :ref:`paivana.conf <Paivana-Configuration>` configuration file + that points at both the merchant backend and the upstream + service. + +Typically a TLS-terminating reverse proxy (Nginx or Apache) is +deployed in front of ``paivana-httpd`` to handle HTTPS and to route +multiple virtual hosts; see :ref:`Paivana-ReverseProxy` below. + +In normal operation the request flow is: + +:: + + client ──▶ Nginx/Apache (TLS) ──▶ paivana-httpd ──▶ upstream + │ + ▼ + taler-merchant-httpd + │ + ▼ + Taler exchange + + +Installation +------------ Installing from source ----------------------- - -The following instructions will show how to install libgnunetutil and -the core GNU Taler libraries from source. +^^^^^^^^^^^^^^^^^^^^^^ -The package sources can be find in our +The package sources can be found in our `download directory <http://ftpmirror.gnu.org/taler/>`__. -GNU Taler components version numbers follow the ``MAJOR.MINOR.MICRO`` format. -The general rule for compatibility is that ``MAJOR`` and ``MINOR`` must match. -Exceptions to this general rule are documented in the release notes. For -example, paivana-httpd 1.6.0 should be compatible with Taler exchange 1.6.x as -the MAJOR version matches. A MAJOR version of 0 indicates experimental -development, and you are expected to always run all of the *latest* releases -together (no compatibility guarantees). +GNU Taler components follow the ``MAJOR.MINOR.MICRO`` version +scheme. The general rule for compatibility is that ``MAJOR`` and +``MINOR`` must match across components; exceptions are noted in the +release notes. For example, ``paivana-httpd`` 1.6.x is expected to +work with ``taler-merchant-httpd`` 1.6.x. A ``MAJOR`` version of 0 +indicates experimental development; in that case you should always +run the *latest* releases of every component together. -First, the following packages need to be installed before we can compile the -backend: +The following packages must be installed before compiling +``paivana-httpd``: -- Golang >= 1.19 +- GNUnet (``libgnunetutil``) matching the Taler release +- GNU Taler exchange libraries (``libtalerexchange``, + ``libtalerutil``) +- GNU Taler merchant client library (``libtalermerchant``) +- GNU Taler HTTP daemon helpers (``libtalermhd``, + ``libtalertemplating``) +- libmicrohttpd, libcurl, libjansson, libgcrypt, zlib -- taler-merchant >= 1.6 +Build and install with: +.. code-block:: shell-session + + $ ./bootstrap + $ ./configure --prefix=$PREFIX + $ make + $ sudo make install -Installing the directory binary packages on Debian --------------------------------------------------- +Installing the binary packages on Debian +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. include:: frags/installing-debian.rst -To install paivana-httpd can now simply run: +To install ``paivana-httpd`` you can now simply run: .. code-block:: shell-session # apt install paivana-httpd -Note that the package does not perform any configuration work except for -setting up the various users and the systemd service scripts. You still must -configure the HTTP request routing. +The package does not perform any deployment-specific configuration +work; it only sets up the ``paivana-httpd`` system user, the systemd +service and socket units, and installs example configuration +snippets for Nginx and Apache under ``/etc/nginx/sites-available/`` +and ``/etc/apache2/sites-available/``. You still must configure the +HTTP request routing and the Paivana templates as described below. -Installing the GNU Taler binary packages on Ubuntu --------------------------------------------------- +Installing the binary packages on Ubuntu +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. include:: frags/installing-ubuntu.rst -To install the Taler exchange, you can now simply run: +To install ``paivana-httpd``, run: .. code-block:: shell-session # apt install paivana-httpd -Note that the package does not perform any configuration work except for -setting up the various users and the systemd service scripts. You still must -configure the HTTP request routing. +As on Debian, the package does not perform any deployment-specific +configuration work. -Configuration -------------- +.. _Paivana-Configuration: -FIXME! +Configuring paivana-httpd +------------------------- + +The main configuration file is ``/etc/paivana/paivana.conf``. Its +syntax follows the standard GNUnet configuration file format and is +documented in full in :manpage:`paivana.conf(5)`. Default values +shipped with the package live under +``/usr/share/paivana/config.d/``; values in ``paivana.conf`` +override those defaults. + +All Paivana-specific keys live in the ``[paivana]`` section. At a +minimum, the file must specify three things: + +- where ``paivana-httpd`` should listen for incoming requests + (``SERVE``, ``UNIXPATH`` / ``PORT``); +- where it should forward paid requests to + (``DESTINATION_BASE_URL``); +- how it should reach the merchant backend + (``MERCHANT_BACKEND_URL`` and ``MERCHANT_ACCESS_TOKEN``). + +A typical configuration that listens on a UNIX domain socket +managed by systemd and forwards to a local upstream server looks +like this: + +.. code-block:: ini + + [paivana] + # Listen on the socket provided by paivana-httpd.socket. + SERVE = unix + UNIXPATH = /run/paivana/httpd/paivana-http.sock + UNIXPATH_MODE = 660 + + # Public base URL of this paywall as seen by clients. + # Used when the Host/X-Forwarded-Host headers are unavailable. + BASE_URL = https://paywall.example.com/ + + # Upstream service that gets proxied after payment. + DESTINATION_BASE_URL = http://127.0.0.1:8080/ + + # Merchant backend used to create and verify orders. + MERCHANT_BACKEND_URL = http://localhost:9966/ + MERCHANT_ACCESS_TOKEN = secret-token:CHANGE-ME + + # Stable secret used to MAC the access cookie. + # If unset, a random value is generated at every startup, + # invalidating all previously issued cookies. + SECRET = please-change-this-to-a-long-random-value + + # Resources that should never trigger the paywall, e.g. + # logos, stylesheets or favicons. + WHITELIST = ^/(favicon\.ico|assets/.*|robots\.txt)$ + +The exhaustive list of supported keys (``SERVE``, ``PORT``, +``BIND_TO``, ``UNIXPATH``, ``UNIXPATH_MODE``, ``BASE_URL``, +``DESTINATION_BASE_URL``, ``MERCHANT_BACKEND_URL``, +``MERCHANT_BACKEND_UNIX_PATH``, ``MERCHANT_ACCESS_TOKEN``, +``SECRET``, ``WHITELIST``) is documented in +:manpage:`paivana.conf(5)`. + +If you reach the merchant backend over a UNIX domain socket on the +same host (recommended for a single-machine deployment), replace +the ``MERCHANT_BACKEND_URL`` block with: + +.. code-block:: ini + + MERCHANT_BACKEND_URL = http://localhost/ + MERCHANT_BACKEND_UNIX_PATH = /run/taler-merchant/merchant.sock + +.. note:: + + ``MERCHANT_ACCESS_TOKEN`` and ``SECRET`` are sensitive values. + Make sure ``paivana.conf`` is only readable by the + ``paivana-httpd`` user. The Debian package installs the file + accordingly. + +When ``paivana-httpd`` runs behind a trusted reverse proxy +(Nginx/Apache), pass ``-f`` / ``--respect-forwarded-headers`` in the +systemd unit's ``ExecStart=`` so the real client address is taken +from ``X-Forwarded-For``. See :manpage:`paivana-httpd(1)` for the +remaining command-line flags (in particular ``-g`` to require only +a single payment per site and ``-n`` to disable the paywall for +debugging). + + +Starting and stopping the service +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Debian/Ubuntu package ships a socket-activated systemd unit. +After editing ``/etc/paivana/paivana.conf`` enable and start it: + +.. code-block:: shell-session + + # systemctl enable --now paivana-httpd.socket + # systemctl status paivana-httpd + +The socket listens on ``/run/paivana/httpd/paivana-http.sock`` with +group ``www-data``, which lets a co-located Nginx or Apache talk to +the daemon without granting it broader filesystem access. Logs are +sent to the journal: + +.. code-block:: shell-session + + # journalctl -u paivana-httpd -f + + +.. _Paivana-Templates: + +Configuring Paivana templates +----------------------------- + +``paivana-httpd`` does not store any per-site pricing or URL-matching rules +itself. Instead, all rules are expressed as :ref:`merchant templates +<template>` of type ``paivana`` in the merchant backend. When +``paivana-httpd`` starts up it asks the merchant backend for every template +configured for the instance identified by ``MERCHANT_BACKEND_URL`` and uses +the ``website_regex`` field of each template to decide which template (and +therefore which payment options) applies to an incoming request URL. + +The corresponding REST API is documented in detail in the +:ref:`Merchant Backend HTTP API <merchant-api>`; see in particular +the +`POST /private/templates +<https://docs.taler.net/core/api-merchant.html#post--private-templates>`__ +endpoint and the +:ts:type:`TemplateContractPaivana` definition. + +Prerequisites +^^^^^^^^^^^^^ + +Before creating a template you need: + +- a running ``taler-merchant-httpd`` (see the + :ref:`Launching-the-backend` section of the merchant manual); +- a merchant :ref:`instance <Instance-setup>` with at least one + configured :ref:`bank account <instance-bank-account>`; +- the access token of that instance (used as + ``MERCHANT_ACCESS_TOKEN`` in ``paivana.conf``). + +In the examples below we assume the merchant backend is reachable +at ``http://localhost:9966/``, the default instance is ``default``, +its access token is ``secret-token:sandbox`` and the currency is +``KUDOS``. Adjust the URLs, tokens and amounts to match your +deployment. The +`src/backend/test.sh +<https://git.taler.net/paivana.git/tree/src/backend/test.sh>`__ +script that ships with Paivana sets up exactly this minimal +configuration and is a good starting point for experimentation. + +Creating a single global template +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The simplest Paivana setup uses one template that matches every +URL on the site and charges a fixed price. This is the +configuration created by ``src/backend/test.sh``: + +.. code-block:: bash + + $ curl -X POST http://localhost:9966/private/templates \ + -H 'Authorization: Bearer secret-token:sandbox' \ + -H 'Content-Type: application/json' \ + -d '{ + "template_id": "paivana", + "template_description": "A Paivana template", + "template_contract": { + "template_type": "paivana", + "summary": "Access to example.com", + "website_regex": ".*", + "choices": [ { "amount": "KUDOS:1" } ] + } + }' + +The ``template_type`` must be ``"paivana"``: this allows +``paivana-httpd`` to pick the template up at startup and +also enables some required logic in the merchant backend. The +``website_regex`` is a POSIX extended regular expression that is +matched against the request URL; ``.*`` covers everything. Each +entry in ``choices`` describes one way the client may pay and is an +:ts:type:`OrderChoice` object (so the paywall can also support +the use of subscription tokens, discount coupons, etc.). + +A successful create returns HTTP ``204 No Content``. After +creating the template, (re)start ``paivana-httpd`` so that it +re-reads the template list: + +.. code-block:: shell-session + + # systemctl restart paivana-httpd + +Multiple templates with URL-specific pricing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When a single site contains content with different prices, define one template +per price bucket and use ``website_regex`` to scope each template to the +matching URLs. When several templates match the same URL ``paivana-httpd`` +picks the first one if finds that matches. Be careful: if multiple templates +match a URL, the result is non-deterministic! + +For example, a news site might charge 2 KUDOS for premium articles +and 50 cents (``KUDOS:0.5``) for standard articles: + +.. code-block:: bash + + $ curl -X POST http://localhost:9966/private/templates \ + -H 'Authorization: Bearer secret-token:sandbox' \ + -H 'Content-Type: application/json' \ + -d '{ + "template_id": "premium", + "template_description": "Premium long-form articles", + "template_contract": { + "template_type": "paivana", + "summary": "Premium article on example.com", + "website_regex": "^/premium/.*", + "choices": [ { "amount": "KUDOS:2" } ] + } + }' + + $ curl -X POST http://localhost:9966/private/templates \ + -H 'Authorization: Bearer secret-token:sandbox' \ + -H 'Content-Type: application/json' \ + -d '{ + "template_id": "default", + "template_description": "Standard articles", + "template_contract": { + "template_type": "paivana", + "summary": "Standard article on example.com", + "website_regex": "^/standard/.*", + "choices": [ { "amount": "KUDOS:0.5" } ] + } + }' + +Offering multiple payment options +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``choices`` array lets a single template offer several mutually exclusive +ways to pay. A common pattern is to accept either a cash payment or to sell a +subscription; the wallet shows both options and the customer picks one. The +third option, where the customer already has a subscription, will be used +automatically by the wallet for subscribers and the customer will not even +have to click to bypass the paywall as a subscriber. See the merchant manual +for the details of :ref:`OrderChoice <template-choice>` objects. + +.. code-block:: bash + + $ curl -X POST http://localhost:9966/private/templates \ + -H 'Authorization: Bearer secret-token:sandbox' \ + -H 'Content-Type: application/json' \ + -d '{ + "template_id": "article", + "template_description": "Single article, paid or via subscription", + "template_contract": { + "template_type": "paivana", + "summary": "Article on example.com", + "website_regex": ".*", + "choices": [ + { "amount": "KUDOS:1", + "description": "Pay per article" }, + { "amount": "KUDOS:100", + "description": "Buy subscription", + "outputs": [ { "token": "monthly-subscription" } ] }, + { "amount": "KUDOS:0", + "description": "Use my subscription", + "inputs": [ { "token": "monthly-subscription" } ], + "outputs": [ { "token": "monthly-subscription" } ] } + ] + } + }' + +Managing templates +^^^^^^^^^^^^^^^^^^ + +Templates can be listed, updated and deleted through the merchant +backend's REST API or through the merchant backend SPA at +``$MERCHANT_BACKEND_URL/``. See the merchant manual section on +:ref:`templates <template>` for details, and the API reference for +the relevant endpoints: + +- `GET /private/templates + <https://docs.taler.net/core/api-merchant.html#get--private-templates>`__ — + list all templates of the instance; +- `PATCH /private/templates/$TEMPLATE_ID + <https://docs.taler.net/core/api-merchant.html#patch--private-templates-$TEMPLATE_ID>`__ — + update a template; +- `DELETE /private/templates/$TEMPLATE_ID + <https://docs.taler.net/core/api-merchant.html#delete--private-templates-$TEMPLATE_ID>`__ — + remove a template. + +After any change, restart ``paivana-httpd`` so the new template +list takes effect. + + +.. _Paivana-ReverseProxy: + +Reverse proxy configuration +--------------------------- + +``paivana-httpd`` itself speaks plain HTTP on a UNIX socket (or a +local TCP port). In production it is often run behind an Internet-facing +reverse proxy that terminates TLS and forwards requests to the +Paivana socket. This section gives minimal working examples for +both Nginx and Apache. The same approach is used for the merchant +backend; see the merchant manual's +:ref:`reverse-proxy-configuration` section for additional +discussion. + +The examples assume the public domain is ``example.com``, +that ``paivana-httpd`` is socket-activated by the shipped +``paivana-httpd.socket`` unit (so its listening socket lives at +``/run/paivana/httpd/paivana-http.sock``) and that TLS termination +happens at the reverse proxy. + +.. tab-set:: + + .. tab-item:: Nginx + + Place the snippet below in + ``/etc/nginx/sites-available/example.com`` (the + Debian package installs a starter template under + ``/etc/nginx/sites-available/paivana``), then enable it via + ``ln -s ../sites-available/example.com + /etc/nginx/sites-enabled/`` and reload Nginx + (``systemctl reload nginx``). + + .. code-block:: nginx + + server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name example.com; + + ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; + + location / { + proxy_pass http://unix:/run/paivana/httpd/paivana-http.sock; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Proto https; + } + } + + server { + listen 80; + listen [::]:80; + server_name example.com; + return 301 https://$host$request_uri; + } + + Make sure ``paivana-httpd`` is started with + ``--respect-forwarded-headers`` (see + :manpage:`paivana-httpd(1)`) so the ``X-Forwarded-For`` + header set above is honoured. + + .. tab-item:: Apache + + Enable the required modules once: + + .. code-block:: shell-session + + # a2enmod proxy proxy_http headers ssl + # systemctl reload apache2 + + Then drop the following into + ``/etc/apache2/sites-available/example.com.conf`` + (the Debian package installs a starter template at + ``/etc/apache2/sites-available/paivana.conf``), enable it + with ``a2ensite example.com`` and reload Apache. + + .. code-block:: apacheconf + + <VirtualHost *:80> + ServerName example.com + Redirect permanent / https://example.com/ + </VirtualHost> + + <VirtualHost *:443> + ServerName example.com + + SSLEngine on + SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem + + <Location "/"> + ProxyPass "unix:/run/paivana/httpd/paivana-http.sock|http://example.com/" + ProxyPassReverse "unix:/run/paivana/httpd/paivana-http.sock|http://example.com/" + RequestHeader set X-Forwarded-Proto "https" + RequestHeader set X-Forwarded-Host "example.com" + </Location> + </VirtualHost> + + As with Nginx, run ``paivana-httpd`` with + ``--respect-forwarded-headers`` so that the client IP is + taken from ``X-Forwarded-For``. + +If you operate both Paivana and the merchant backend on the same +host, you typically expose them under two different hostnames (e.g. +``example.com`` and ``backend.example.com``); the merchant +backend must *never* be proxied through ``paivana-httpd``, only +the upstream content service should be. + + +Verifying the setup +------------------- + +After completing the steps above, a quick smoke test is to request +a paywalled URL with ``curl``: + +.. code-block:: shell-session + + $ curl -i https://example.com/some-article + +An unpaid request should return ``HTTP/1.1 402 Payment Required`` together +with a Taler-formatted paywall body containing the ``taler://pay/...`` URI of +the freshly created order. Paying that order with any GNU Taler wallet (see +the `Wallet documentation <https://docs.taler.net/wallet/>`__) and +re-requesting the URL from the same client should then yield the upstream +content unchanged. If the page is run in a browser, the client-side +JavaScript should automatically trigger the required reload of the page after +the wallet made the payment. + +For interactive debugging, ``paivana-httpd -n`` disables the +paywall and turns the daemon into a transparent reverse proxy; +this is useful to confirm that the network plumbing to the +upstream service works before involving the merchant backend. +See :manpage:`paivana-httpd(1)` for the other runtime flags. Drupal integration