.. This file is part of PEPSI. Copyright (C) 2026 GNUnet e.V. PEPSI 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 3, or (at your option) any later version. =============== pepsi-telemetry =============== *The central, anonymous, opt-in feature-telemetry collector.* Role ==== ``pepsi-telemetry`` collects anonymous usage telemetry from Pepsi installations that opt in. It is a small HTTP server, deliberately separate from ``pepsi-httpd`` and shipped in its **own Debian package**, intended to run on one host — by default ``telemetry.pepsi.taler.net`` — behind an existing nginx or Apache front server. Run it with ``pepsi-telemetry serve``. A reporting Pepsi node is identified only by a random 256-bit ``system_id`` (no personally identifying information). The collector connects to the shared ``pepsi`` PostgreSQL schema as the ``pepsi-telemetry`` role and stores everything in the ``pepsi.telemetry`` table, counting usage per reported Pepsi version. Features ======== * **Two open submission endpoints.** ``POST /telemetry/features`` records which features a deployment has enabled (a snapshot: listed features become enabled, omitted ones disabled, with usage history retained), and ``POST /telemetry/usage`` accumulates per-feature usage counts (deltas, summed server-side). Both are unauthenticated — the only identity is the anonymous ``system_id`` — and bounded by ``MAX_BODY`` plus the front server's rate limits. * **Per-version counting.** The ``version`` reported in each submission is part of the table key, so usage events are attributed to the Pepsi release that produced them and an upgrade accrues separate rows. * **Public aggregate report.** ``GET /telemetry/report`` returns per-feature rollups (distinct enabled deployments and total uses) with a per-version breakdown, as JSON. It **never** exposes an individual ``system_id``, so it is safe to serve openly. * **No TLS of its own.** It serves plain HTTP on a UNIX socket (``/run/pepsi-collector/pepsi-telemetry.sock``, socket-activated via ``SERVE = systemd`` as shipped, or bound by the daemon itself with ``SERVE = unix``) for a reverse proxy, or plaintext TCP for local testing. TLS is terminated by the fronting nginx/Apache. Feature-stability table ======================= The companion script ``contrib/update-feature-stability.sh`` fetches ``GET /telemetry/report`` and regenerates :doc:`../feature-stability`, assigning each feature a tier from **both** how widely it is deployed and how heavily it is exercised (a feature must clear both thresholds for a tier): * **stable** — deployments ≥ 100 and total uses ≥ 100000 * **used** — deployments ≥ 10 and total uses ≥ 1000 * **experimental** — otherwise The thresholds, source URL and output path are overridable via environment variables (see the script header). Configuration ============= ``[pepsi-telemetry]``: ``SERVE`` (``unix``/``tcp``/``systemd``) with the matching transport options (``UNIXPATH``/``UNIXPATH_MODE``/``UNIXPATH_GROUP``, or ``BIND_TO``/``PORT``), ``MAX_BODY`` (default 65536), ``MAX_CONNECTIONS`` (default 128) and ``DB_POOL_SIZE`` (default 2). The database connection comes from the shared ``[pepsi-postgres]`` section. See :doc:`../configuration` and :manpage:`pepsi-telemetry(1)`. Deployment ========== The Debian ``pepsi-telemetry`` package ships a systemd service and socket unit (serving on ``/run/pepsi-collector/pepsi-telemetry.sock``), its configuration file ``/etc/pepsi-telemetry/pepsi-telemetry.conf``, and reverse-proxy site files for both nginx and Apache, installed under their canonical site names: .. code-block:: text /etc/nginx/sites-available/telemetry.pepsi.taler.net /etc/apache2/sites-available/telemetry.pepsi.taler.net.conf They arrive complete and disabled, already pointing at the socket the shipped units bind, so enabling one is only a symlink — there is no proxy path to fill in and so none to get wrong: .. code-block:: console # nginx $ ln -s ../sites-available/telemetry.pepsi.taler.net /etc/nginx/sites-enabled/ $ systemctl reload nginx # Apache (a2ensite makes the same symlink) $ a2enmod proxy proxy_http ssl ratelimit headers $ a2ensite telemetry.pepsi.taler.net $ systemctl reload apache2 Then provision a certificate for the host, and install the schema from the collector's own file, which needs nothing but its ``[pepsi-postgres]`` section:: $ pepsi-setup -c /etc/pepsi-telemetry/pepsi-telemetry.conf schema Package upgrades upgrade the schema the same way, and the collector refuses to start against a schema from another release until they have (see :ref:`upgrading`). Both site files are ``conffiles``, so local edits — a different ``server_name``, say, for a private collector — survive package upgrades. Change the socket path and it becomes one statement in four places: the ``.socket`` unit's ``ListenStream=``, the service's ``RuntimeDirectory=``, and the two site files (plus ``UNIXPATH`` in the collector's own configuration, if you switch it to ``SERVE = unix``). ``make check-telemetry-socket``, which ``make check`` runs, compares them and fails the build if any two disagree — worth having, because a mismatch is invisible in testing and reaches production as nothing but a 502 from the front server. The collector's runtime directory belongs to it alone, and that is the point. It is not ``/run/pepsi`` (the mail pipeline's, shared by ``pepsi-ingress``, ``pepsi-httpd`` and the setup-apply doorbell), and it is not ``/run/pepsi-telemetry`` (the telemetry *client's*, whose socket ``/run/pepsi-telemetry/socket`` is owned by ``pepsi``). systemd gives a runtime directory to the declaring unit's own ``User=`` — chowning it *and its contents* on every start — and, without ``RuntimeDirectoryPreserve=``, removes it when that unit stops. So a directory shared between services running as different accounts ends up owned by whichever started last, and one service stopping deletes the sockets the others still have bound. Distinct file names in a shared directory are not protection. Were the collector's socket in the client's directory, a host running both would work until the client next started; the collector's socket would then be chowned to ``pepsi:pepsi``, lose the ``www-data`` group the front server connects through, and every request would become a 502 — with the collector ``active``, healthy and silent, having never seen a connection. Running the collector on a host of its own is still the intended deployment, but it is not what keeps the two apart. See also ======== :doc:`pepsi-telemetry-client` (the per-host client that submits to this collector), :doc:`pepsi-httpd`, :doc:`pepsi-setup`, :doc:`../feature-stability`, :manpage:`pepsi.conf(5)`.