.. 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-stage-arc =============== *ARC verification, signing and sealing — the entry stage on the inbound path.* Role ==== ``pepsi-stage-arc`` implements ARC (RFC 8617). It verifies any existing inbound ARC chain and, when an ``ARC_DOMAIN`` and its keys are configured, seals the message as our ADMD so the boundary authentication verdict survives Pepsi's forwarding hop. Because it applies only to mail we **receive and forward**, it must never touch our own submissions (``state.local_origin``) — those are signed as the author domain by :doc:`pepsi-stage-dkim-sign` instead, and the stage **skips** any such message. On a host that both receives and submits, place it on the **inbound branch only**, after the ``state.local_origin`` split (ahead of SRS); on a receive-only host it is the ``[stage-init]`` stage. Reference: :manpage:`pepsi-stage-arc(1)`. Features ======== * **ARC verification** of any inbound chain; the verdict is recorded under ``state.auth.arc``. * **ARC sealing:** prepends a fresh ARC set — ``ARC-Authentication-Results`` (AAR), ``ARC-Message-Signature`` (AMS) and ``ARC-Seal`` (AS) — as the ``ARC_DOMAIN`` identity, signed with the **single** ``ARC_ALGORITHM`` (``rsa`` or ``ed25519``; ARC permits one signature per hop). * **AAR from the boundary verdict:** the AAR reuses the ``Authentication-Results`` fragment ingress stored under ``state.origin.ar_results``, so SPF/DKIM/DMARC are verified exactly once, at ingress. Only the inbound ARC chain is verified here, to compute the chain-validation (``cv=``) value and the AAR's ``arc=`` line. * **Prepend-only:** the seal is added at the top, so existing lower signatures and the ingress ``Received:`` header are undisturbed; only the ``headers`` column is rewritten, never the body. * **Fail-open:** if the origin context, ``[pepsi]`` config or keys are missing, or sealing errors, the message is advanced **unsealed** with a warning (the verdict is still recorded when computable). Configuration ============= ``[stage-]``: ``PROGRAM = pepsi-stage-arc``, ``NEXT_STAGE`` *(required — the stage always hands the message on, so a missing one fails every message)*, the DNS settings ``DNS_SERVERS`` (empty = system resolver) / ``DNS_TIMEOUT``, and the shared signature parameters ``HEADER_CANONICALIZATION`` / ``BODY_CANONICALIZATION`` (``relaxed`` by default, or ``simple``), ``SIGNED_HEADERS`` (which must include ``From``) and ``SIGNATURE_EXPIRATION_DAYS`` (no ``x=`` by default) applied to the AMS and the ARC-Seal. The signing identity, keys and algorithm come from the shared ``[pepsi]`` section (``ARC_DOMAIN``, ``ARC_ALGORITHM``, ``KEY_DIR``, ``DKIM_SELECTOR``). See :manpage:`pepsi-stage-arc(1)`. State ===== * **Inputs:** ``state.local_origin`` — when true the message is advanced untouched (no ARC on mail we originate); otherwise ``state.origin`` — the ``authserv_id`` (required to reproduce the AAR identity; without it the stage advances unsealed), ``ar_results`` (reused in the AAR), and ``remote_ip`` / ``helo`` for the ARC verification context. * **Outputs:** overwrites ``state.auth.arc`` with the re-evaluated chain verdict and records the chain's sealer domains under ``state.auth.arc_sealers`` (the rest of ``state``, including the other ``state.auth`` siblings and ``state.dsn``, is preserved). Note ``arc_sealers`` says only who *claimed* to seal, so any reader must first require ``state.auth.arc == "pass"``. See also ======== :doc:`pepsi-ingress`, :doc:`pepsi-stage-dkim-sign`, :doc:`../features`, :manpage:`pepsi-stage-arc(1)`.