MNPPI/wkd-proxy-worker
Cloudflare Worker that proxies OpenPGP Web Key Directory (WKD) requests to Proton Mail for custom domains
WKD Proxy Worker
A Cloudflare Worker that proxies OpenPGP Web Key Directory (WKD) requests to Proton Mail's API for your custom domains.
If you use Proton Mail with custom domains, this Worker enables WKD key discovery so that email clients can automatically find your OpenPGP public keys via the standard WKD protocol.
How It Works
When an email client looks up an OpenPGP key for user@yourdomain.com, it queries either:
https://openpgpkey.yourdomain.com/hu/<hash>?l=user(direct method)https://yourdomain.com/.well-known/openpgpkey/hu/<hash>?l=user(advanced method)
This Worker intercepts those requests via Cloudflare route patterns and proxies them to Proton Mail's WKD endpoint, which serves the actual key data.
Features
- Supports unlimited custom domains via a single environment variable
- Handles both WKD direct (subdomain) and advanced (
.well-knownpath) methods - Automated DNS record creation for
openpgpkey.*subdomains - Domain names masked in CI/CD logs for privacy
- 100% test coverage with Cloudflare Workers vitest integration
- Full observability: structured logging, traces, and logpush
Quick Start
1. Fork This Repository
Fork this repo and clone your fork locally.
2. Install Dependencies
pnpm install3. Configure Your Domains
Set the DOMAINS GitHub secret with a comma-separated list of your custom domains:
gh secret set DOMAINS --body "yourdomain.com,anotherdomain.org"4. Configure Cloudflare API Token
Create a Cloudflare API token with these permissions:
| Permission Scope | Permission | Access |
|---|---|---|
| Account | Workers Scripts | Edit |
| Zone | Workers Routes | Edit |
| Zone | DNS | Edit |
| Zone | Zone | Read |
The token must cover all zones (domains) you plan to use. Set it as a GitHub secret:
gh secret set CLOUDFLARE_API_TOKEN --body "your-token-here"5. Push to Deploy
Push to main and the GitHub Actions workflow will:
- Run typecheck, lint, and tests with 100% coverage
- Create
openpgpkey.*DNS CNAME records (idempotent - skips existing) - Deploy the Worker with routes for all configured domains
6. Verify
curl https://openpgpkey.yourdomain.com/policyA 200 response confirms the Worker is serving WKD requests for that domain.
Local Development
pnpm run devThis starts a local dev server. The DOMAINS variable defaults to example domains from wrangler.jsonc for local development.
Testing
pnpm run test # Run tests
pnpm run coverage # Run with 100% coverage enforcement
pnpm run typecheck # TypeScript strict mode
pnpm run lint # ESLint strict type-checkedHow Deployment Works
The deploy workflow generates three route patterns per domain:
| Pattern | Purpose |
|---|---|
openpgpkey.{domain}/* |
WKD direct method (subdomain) |
{domain}/.well-known/openpgpkey/* |
WKD advanced method (path) |
*.{domain}/.well-known/openpgpkey/* |
WKD advanced on any subdomain |
Before deploying, it ensures the required DNS records exist for each domain:
openpgpkey.{domain}- A proxied CNAME pointing to the root domain, for the WKD direct method- Root domain A/AAAA records - If the root domain has no existing A, AAAA, or CNAME records (common for email-only domains), proxied placeholder records are created (
192.0.2.1/100::) so Cloudflare can intercept.well-knownrequests
All DNS creation is idempotent - existing records are never modified or overwritten.
If your domain already hosts a website: The deploy workflow detects existing root domain DNS records and leaves them untouched. Your web hosting is not affected. The Worker only intercepts requests matching the WKD route patterns above - all other traffic passes through normally.
The DOMAINS value is injected as a Worker environment variable at deploy time via --var, so domains never appear in source code.
Adding or Removing Domains
- Update the
DOMAINSGitHub secret - Push any commit to
main(or trigger the workflow manually) - New DNS records are created automatically; removed domains simply stop receiving routes
For removed domains, you may want to manually delete the orphaned openpgpkey.* DNS records in the Cloudflare dashboard.
Prerequisites
- Custom domains added to Cloudflare (DNS managed by Cloudflare)
- Proton Mail account with those custom domains configured
- OpenPGP keys published in Proton Mail for the email addresses you want discoverable
License
This project is licensed under the GNU Affero General Public License v3.0.