paivana

HTTP paywall reverse proxy
Log | Files | Refs | Submodules | README | LICENSE

commit 73b2170520dfbd70416755ad0d1171bb435b80fd
parent 342bdebc4947df5a2c39a7e961362dcd1f8a2afc
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 31 May 2026 12:38:04 +0200

write README

Diffstat:
MREADME | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 188 insertions(+), 0 deletions(-)

diff --git a/README b/README @@ -0,0 +1,188 @@ +Paivana +======= + +Paivana is an HTTP reverse proxy that gates access to a target website +behind a GNU Taler payment. Unpaid visitors receive a paywall page +where they can pay using a GNU Taler wallet; once payment is confirmed +the request is forwarded transparently to the configured upstream +server. + +The sole binary is `paivana-httpd`. + + +How it works +------------ + +0. `paivana-httpd` learns prices from the Paivana templates configured + in the taler-merchant-backend. Paivana templates include a regular + expression which determines the set of pages the template applies to. +1. An HTTP client accesses a page at `paivana-httpd`. +2. If the paywall is enabled for the respective URL and no valid access + cookie is present, `paivana-httpd` renders a static paywall HTML page + (customizable Mustache template) referencing the payment template. + The page includes a "Paivana" HTTP header to also facilitate agentic + payments. +3. The browser computes a unique payment identifier and + renders a dynamic payment request (taler:// QR code + or link) and long-polls the taler-merchant-backend awaiting + completion of the payment. +4. The user instructs their Taler wallet to complete the payment. +5. The browser notices that the payment is complete and calls back to + `POST /.well-known/paivana` (provided by `paivana-httpd`) + with a reference to the unique payment identifier. +6. `paivana-httpd` verifies the payment with the merchant, sets + an HMAC access cookie, and redirects the browser to the original URL. +7. Requests with a valid cookie are forwarded to the upstream server + via libcurl and the response is streamed back. + +The cookie is an HMAC over `(expiration time, website, client address)` keyed by a +`paivana_secret` derived from the configured `SECRET`. If `SECRET` is absent, +a random nonce is used and cookies do not survive a restart of `paivana-httpd` + + +Dependencies +------------ + +- GNUnet (libgnunetutil, libgnunetcurl) +- libmicrohttpd +- libcurl >= 7.34.0 +- libjansson +- libgcrypt >= 1.6.1 +- zlib +- GNU Taler: libtalerutil, libtalermerchant, libtalerexchange, + libtalermhd, libtalertemplating + + +Build +----- + +The project uses Meson but supports a GNU build process. + + ./bootstrap + ./configure --prefix=$TARGET + make + sudo make install + + +Configuration +------------- + +Paivana reads an INI-style `.conf` file. The only section used is +`[paivana]`. A minimal working configuration: + + [paivana] + DESTINATION_BASE_URL = https://example.com/ + MERCHANT_BACKEND_URL = https://backend.demo.taler.net/instances/sandbox/ + MERCHANT_ACCESS_TOKEN = secret-token:sandbox + SERVE = tcp + PORT = 9967 + +### Required keys + + Key Description + ---------------------- ----------------------------------------------------- + DESTINATION_BASE_URL Upstream server to proxy to once payment is confirmed. + MERCHANT_BACKEND_URL Base URL of the Taler merchant backend. + MERCHANT_ACCESS_TOKEN Bearer token for all calls to the merchant backend. + +### Optional keys + + Key Description + -------- --------------------------------------------------------------- + BASE_URL Public base URL of Paivana. Derived from request headers + (X-Forwarded-Host / Host / X-Forwarded-Port) if absent. + SECRET Stable secret for cookie MAC and Paivana ID derivation. + A random nonce is generated on every startup if absent. + SERVE `tcp` (default) or `unix` (Unix-domain socket) or `systemd` + (systemd socket activation). + PORT TCP port, used when SERVE = tcp. + BIND_TO IP address to bind to; dual-stack wildcard if absent. + UNIXPATH UNIX path do bind to, used when SERVE = unix. + + +Running +------- + +$ paivana-httpd -c /etc/paivana/paivana.conf + +Pass `-n` / `--no-payment` to bypass the paywall entirely (pure reverse +proxy, useful for testing upstream plumbing). + +The daemon does not serve requests until it has fetched paywall templates +from the merchant backend. If template loading fails, startup is aborted. + + +Deployment behind a reverse proxy +---------------------------------- + +The recommended production setup runs Paivana over a Unix socket and +places nginx or Apache in front for TLS termination. + +nginx (`/etc/nginx/sites-available/paivana`): + + server { + listen 443 ssl; + server_name example.com; + + location / { + proxy_pass http://unix:/run/paivana/httpd/paivana-http.sock; + proxy_set_header Host $host; + } + } + +Apache (requires mod_proxy and mod_proxy_http): + + <Location "/"> + ProxyPass "unix:/var/lib/paivana/httpd/paivana.sock|http://example.com/" + </Location> + +Set `BASE_URL` in the configuration file to the public HTTPS URL so +that redirects and cookie domains are correct. + + +Source layout +------------- + + src/backend/ Main binary and all subsystems + paivana-httpd.c Entry point, scheduler, global state, shutdown + paivana-httpd_reverse.c Request-proxying state machine (core) + paivana-httpd_pay.c POST /.well-known/paivana handler + paivana-httpd_cookie.c HMAC access-cookie logic + paivana-httpd_templates.c Paywall template loading and rendering + paivana-httpd_helper.c Client IP / base URL helpers + paivana-httpd_daemon.c MHD daemon startup + paivana_pd.c GNUnet project-data descriptor + src/include/platform.h GNUnet-style platform header (include first) + src/tests/ Automated reverse-proxy tests + contrib/paywall.en.must Default Mustache paywall template + doc/prebuilt/ Git submodule: taler-docs (man pages) + + +Architecture notes +------------------ + +Single-threaded event loop: GNUnet scheduler drives both inbound HTTP +(libmicrohttpd) and outbound requests (libgnunetcurl / libcurl multi). +Running multiple `paivana-httpd` processes on the same port is +supported as the main way to scale-up the system. + +Requests and responses are currently not streamed in the reverse +proxy; as a result, uploads and downloads are currently hard-capped by +the implementation at 40 MiB. Lower limits for the upload may be +configured. + +The MHD daemon is not started until paywall templates have been fetched +from the merchant backend asynchronously. + + +License +------- + +GNU Affero General Public License version 3 or later. +See COPYING for the full text. + + +Bug reports +----------- + +Please report bugs at https://bugs.taler.net/.