.. This file is part of PEPSI. Copyright (C) 2026 Pepsi contributors 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-keys ========== *Manage the end-to-end key store.* Role ==== ``pepsi-keys`` is the operator's tool for Pepsi's end-to-end cryptography key store: ``pepsi.crypto_identity`` (the key pairs of the addresses we serve, private half included and wrapped at rest), ``pepsi.peer_key`` (remote correspondents' cached public keys and certificates) and ``pepsi.ca_trust`` (the CA certificates inbound S/MIME chains are validated against). It connects through the shared ``[pepsi-postgres]`` section and reads the ``[pepsi]`` ``CRYPTO_*`` options and the ``[pepsi-crypto]`` section. It is **not** a stage. Reference: :manpage:`pepsi-keys(1)`; the design is explained in :doc:`../key-management`. It is one of the standalone binaries, outside the unified ``pepsi`` executable, for two reasons: it is the only program that generates key material (folding it in would link key generation and certificate issuance into every stage worker), and a site may install it setuid so users can manage their own identities — a bit a shared multi-call binary cannot carry. Features ======== * **identity list / show** — what material exists for an address: protocol, purpose, status, whether it is the primary for its capability, whether it is published, whether the private half is present, purged or was never held. * **identity generate** — create key material and store it. OpenPGP produces one transferable key (Ed25519 by default, RSA optional); S/MIME produces a *signing* and an *encryption* certificate, each self-signed for immediate use, or one certificate with both key usages under ``CRYPTO_SMIME_SHARED_KEY``. ``--csr`` additionally emits a PKCS#10 request over each key, so a real CA can issue a replacement without re-keying. * **identity import / export / csr** — store material generated elsewhere, write material back out in its own binary form (``--pem`` for X.509), or build a certificate request over an identity's *existing* key. * **identity revoke / retire / forget-private** — the lifecycle. Revocation and the periodic ``retire`` sweep destroy private **signing** material and retain decryption material; ``forget-private`` is the explicit, confirmed way to destroy a decryption key. * **identity set-primary / publish / delete** — choose the material used for new messages, opt an address in or out of publication, or remove a row outright. * **peer list / show / import / pin / unpin / refresh / remove** — the cached peer keys. ``show`` applies the matching rule (an exact address always beats a hand-entered ``*@domain`` fallback); ``import`` stores a key with source ``manual``, the only source permitted to name a whole domain. * **ca list / add / enable / disable / remove** — the S/MIME trust anchors. A certificate without ``basicConstraints CA:TRUE`` is refused, since no chain could ever end at it. * **wrap rotate** — re-wrap every stored private key under a new key-encryption key, incrementally and with ``--dry-run`` to look first. * **dns** — print the ``OPENPGPKEY`` (RFC 7929) and ``SMIMEA`` (RFC 8162) records that publish an address, under the hashed owner names those standards define. Only worth publishing in a DNSSEC-signed zone, which the output says. Privilege model =============== Reaching a private key takes two independently guarded things: the ``pepsi-crypto`` database role — the only one granted the ``crypto_identity.private_wrapped`` column, every other service role holding a column-level grant that omits it — and the key-encryption key, which lives only in ``secrets.d/pepsi-crypto.secret``. A stolen database yields ciphertext; the secret alone yields nothing. ``pepsi-keys`` assumes that role only for the duration of each database call: started as ``root`` it *becomes* the account (``root`` has no database role of its own) and returns afterwards. PostgreSQL peer authentication keys off the effective **uid**, not the gid, so a setgid bit would grant the group that guards the secret fragment but not the database identity. The binary ships **without** a setuid bit, unlike :doc:`pepsi-whitelist`: one program able to open every private key in a deployment is a much larger prize than a single whitelist table. A site that wants users to manage their own identities installs it setuid ``pepsi-crypto``; identities are owned by the passwd login their address resolves to, and an unprivileged caller may see and change only their own — enforced either way, so making it setuid is a site decision rather than a code change. Peer keys and trust anchors are deployment-wide and operator-only, listing included. See also ======== :doc:`../key-management`, :doc:`pepsi-setup`, :doc:`pepsi-whitelist`, :doc:`pepsi-settings`, :doc:`../architecture`, :manpage:`pepsi-keys(1)`, :manpage:`pepsi.conf(5)`.