PGP Key Servers PGP Key Servers Admin path: Encryption > PGP Key Servers ( view_pgp_key_servers.cfm, inc/publish_pgp_keyring.cfm). This page maintains the HKP keyserver publish list — the set of public OpenPGP keyservers Hermes will push ( gpg --send-keys) recipient public keys to when an admin clicks Publish on a keyring row in Encryption > External Recipients. Each row is a hostname only (no scheme, no port, no path) stored in the pgp_keyservers table. Important: publish, not lookup. Despite the page name, the keyserver list is currently outbound-only. Hermes does NOT auto-query these servers to fetch a recipient's PGP key at send time — recipient keys must be imported manually (paste-in or file upload) on Encryption > External Recipients > PGP Keyrings. The keyservers configured here are used solely by the Publish action in inc/publish_pgp_keyring.cfm, which pushes a key the operator already holds (typically the local CipherMail server's public key or a recipient's key that was imported and now needs broader distribution). What the page does The page is a thin CRUD over a 3-column table: pgp_keyservers column Purpose id PK keyserver Hostname only, e.g. keys.openpgp.org note Free-text label, e.g. "Primary keyserver" Three actions: Action Form value Effect Add action=add Validates hostname via IsValid("email", "bob@" & ks) (rejects URLs and host:port), checks for duplicate keyserver, INSERTs the row Single delete action=delete with delete_id DELETE one row by id Bulk delete action=bulk_delete with selected_ids (CSV) DELETE every selected id in a loop The existing-servers card is a DataTable with select-all + per-row checkboxes + a Delete Selected button. There is no per-row enable flag, no protocol/port column, no priority ordering — every row in the table is offered as a publish target in the modal on the keyring page, indexed by id. What "publish" actually runs When the operator clicks Publish on a keyring row at External Recipients > PGP Keyrings, the publish_pgp_keyring.cfm include does the following for each selected keyserver: /usr/bin/gpg --homedir /opt/hermes/.gnupg/ \ --keyserver \ --send-keys The temp script is written to /opt/hermes/tmp/_publish_pgp_key.sh, chmod'd, executed, and deleted. The standard Hermes temp-script pattern. The keyserver hostname is substituted via REReplace of the THE-KEY-SERVER placeholder in /opt/hermes/scripts/publish_pgp_key.sh. GPG itself picks the protocol — gpg defaults to hkps:// (HKP over TLS on tcp/443) for a bare hostname when the local dirmngr is configured for it; otherwise it falls back to hkp:// (tcp/11371). Hermes does not pass an explicit scheme. Failure modes the include recognizes (sets session.m and redirects): GPG stderr fragment Meaning session.m Server indicated a failure Keyserver rejected the upload (rate limit, policy, malformed key) 22 No name Local GPG keyring has no user-id matching the requested key id 23 Not found Local GPG keyring does not hold the requested key id 24 Not a key ID The key id parameter was malformed 25 A successful publish returns no recognized fragment and falls through to the success branch. Recommended seed list The default install seeds one row: Hostname Note keyserver.ubuntu.com Ubuntu SKS OpenPGP Public Key Server Practical 2026 replacements / additions the operator should consider: Hostname Network Caveats keys.openpgp.org Identity-verified standalone (Hagrid) Strips third-party signatures (no web-of-trust); requires email verification before a key becomes searchable by email address; does not distribute revocation certificates the way SKS did keyserver.ubuntu.com SKS-style federated Was the last reliable SKS-network bridge; survives but is no longer broadly federated pgp.mit.edu Legacy SKS Largely defunct in 2026 — uploads may not propagate; leave off unless legacy compatibility is required Internal HKP daemon (e.g. Hagrid) Useful if the operator runs an authoritative keyserver for their own domain — same publish path The page does NOT validate keyserver reachability at add time; an unreachable host simply produces a publish failure when the operator clicks Publish later. What is NOT on this page Several things an operator might reasonably expect from a "PGP Key Servers" page that are intentionally elsewhere or absent: Expectation Where it actually lives Per-server enable/disable toggle Not implemented — every row is a publish target Search-order priority Not applicable — publish iterates the explicit selection from the modal, not the full list Inbound recipient-key auto-lookup at send time ( gpg --search-keys / recv-keys) Not implemented anywhere in Hermes; recipient keys must be imported manually on External Recipients > PGP Keyrings Automatic refresh of imported keys (re-fetch + merge updates) Not implemented; operators must re-import a key if a recipient rotates DANE OPENPGPKEY DNS lookup Not currently surfaced in the Hermes admin or CipherMail engine config WKD (Web Key Directory) discovery at https:///.well-known/openpgpkey/... Not currently surfaced in the Hermes admin or CipherMail engine config HKP port override Not on this page; GPG picks the port Encryption policy decisions ("fail closed vs send plaintext if no key") Encryption Settings, not here The page is deliberately scoped to one job: a list of HKP endpoints the publish flow can push to. When the operator should populate this list Two practical scenarios: The organization wants its own gateway PGP key to be publicly discoverable. Add the operator's preferred public keyserver(s), then publish the local CipherMail key from External Recipients > PGP Keyrings. External counterparties running gpg --recv-keys against the same keyserver can then pull it for encrypting mail back to Hermes-served users. A specific recipient has asked for their key (which the operator already holds locally) to be pushed somewhere centralized. Less common — usually recipients self-publish — but the workflow supports it. If the deployment never publishes keys outward (typical Community deployments that use S/MIME exclusively, or PGP deployments that exchange keys out-of-band via attachment), this page can remain empty with no functional impact. Container and database touch-points Component Location Role Page config/hermes/var/www/html/admin/2/view_pgp_key_servers.cfm ( hermes_commandbox) CRUD UI Publish include config/hermes/var/www/html/admin/2/inc/publish_pgp_keyring.cfm ( hermes_commandbox) Builds + runs the temp gpg --send-keys script Template script /opt/hermes/scripts/publish_pgp_key.sh Single line: /usr/bin/gpg --homedir /opt/hermes/.gnupg/ --keyserver THE-KEY-SERVER --send-keys THE_KEY_ID 2>&1 GPG home /opt/hermes/.gnupg/ (bind-mounted into hermes_commandbox) Local GPG keyring holding the keys eligible for publish Storage pgp_keyservers in hermes DB ( hermes_db_server) The list itself Engine hermes_ciphermail (separate from publish — handles actual signing/encryption at send time) NOT touched by this page; this page only manages the GPG outbound-publish list The publish flow runs gpg on hermes_commandbox (which has the /opt/hermes/.gnupg/ keyring bind-mounted) — not inside hermes_ciphermail. CipherMail keeps its own per-recipient PGP store in the djigzo DB for actual encryption/decryption operations. Related External Recipients — per-counterparty key store; the Publish action that consumes this list lives on the keyring sub-page there Encryption Settings — outbound encryption policy that decides whether absence of a recipient PGP key blocks the message or falls through to plaintext Internal CA — sibling page for the S/MIME side of recipient key issuance and trust Advanced Settings (sidebar link to /ciphermail/) — CipherMail's own admin UI for the deep PGP keyring operations the Hermes admin does not surface