A simple OpenPGP public key server that validates email address ownership of uploaded keys.
Go to file
Jakub Filo 7adfc6aa1d
All checks were successful
Build & publish images / build-and-push-image (push) Successful in 24s
Add docker-compose
2023-06-19 07:41:58 +00:00
.gitea/workflows Add Docker builds 2023-06-19 06:50:05 +00:00
config Ignore config/development.js 2017-08-24 12:48:06 +08:00
locales Add views for key search result, verify success and removal success pages. 2019-06-17 16:15:07 +02:00
src Update dependencies 2021-05-08 18:07:52 +02:00
test Update dependencies 2021-05-08 18:07:52 +02:00
.dockerignore Add Docker builds 2023-06-19 06:50:05 +00:00
.eslintrc.json Update dependencies 2019-08-09 14:45:50 +02:00
.gitignore Add package-lock.json. Set dependency versions fixed. Set min node version to 10. 2019-03-02 12:12:25 +01:00
.travis.yml Update travis deployment 2019-03-04 13:12:46 +01:00
Changelog.md Release v3.0 2019-03-04 18:28:56 +01:00
docker-compose.yml Add docker-compose 2023-06-19 07:41:58 +00:00
Dockerfile Add Docker builds 2023-06-19 06:50:05 +00:00
env.sample Add docker-compose 2023-06-19 07:41:58 +00:00
index.js Update dependencies 2019-05-27 14:31:22 +02:00
LICENSE Initial commit 2016-05-25 08:57:32 +02:00
mongo-init.js Add docker-compose 2023-06-19 07:41:58 +00:00
package-lock.json Update dependencies 2021-05-08 18:07:52 +02:00
package.json Update dependencies 2021-05-08 18:07:52 +02:00
README.md Improve documentation with more details to get started 2021-07-06 14:23:17 +02:00

Mailvelope Keyserver Build Status

A simple OpenPGP public key server that validates email address ownership of uploaded keys.

Why not use Web of Trust?

There are already OpenPGP key servers like the SKS keyserver that employ the Web of Trust to provide a way to authenticate a user's PGP keys. The problem with these servers are discussed here.


The web of trust raises some valid privacy concerns. Not only is a user's social network made public, common SKS servers are also not compliant with the EU Data Protection Directive due to lack of key deletion. This key server addresses these issues by not employing the web of trust and by allowing key removal.


The main issue with the Web of Trust though is that it does not scale in terms of usability. The goal of this key server is to enable a better user experience for OpenPGP user agents by providing a more reliable source of public keys. Similar to messengers like Signal, users verify their email address by clicking on a link of a PGP encrypted message. This prevents user A from uploading a public key for user B. With this property in place, automatic key lookup is more reliable than with standard SKS servers.

This requires more trust to be placed in the service provider that hosts a key server, but we believe that this trade-off is necessary to improve the user experience for average users. Tech-savvy users or users with a threat model that requires stronger security may still choose to verify PGP key fingerprints just as before.

Standardization and (De)centralization

The idea is that an identity provider such as an email provider can host their own key directory under a common openpgpkeys subdomain. An OpenPGP supporting user agent should attempt to lookup keys under the user's domain e.g. https://openpgpkeys.example.com for user@example.com first. User agents can host their own fallback key server as well, in case a mail provider does not provide its own key directory.


Try out the server here: https://keys.mailvelope.com


The key server provides a modern RESTful API, but is also backwards compatible to the OpenPGP HTTP Keyserver Protocol (HKP). The following properties are enforced by the key server to enable reliable automatic key look in user agents:

  • Only public keys with at least one verified email address are served
  • There can be only one public key per verified email address at a given time
  • A key ID specified in a query must be at least 16 hex characters (64-bit long key ID)
  • Key ID collisions are checked upon key upload to prevent collision attacks


The HKP APIs are not documented here. Please refer to the HKP specification to learn more. The server generally implements the full specification, but has some constraints to improve the security for automatic key lookup:

Accepted search parameters

  • Email addresses
  • V4 Fingerprints
  • Key IDs with 16 digits (64-bit long key ID)

Accepted op parameters

  • get
  • index
  • vindex

Accepted options parameters

  • mr


Lookup a key

By key ID

GET /api/v1/key?keyId=b8e4105cc9dedc77

By fingerprint

GET /api/v1/key?fingerprint=e3317db04d3958fd5f662c37b8e4105cc9dedc77

By email address

GET /api/v1/key?email=user@example.com

Payload (JSON):

  "keyId": "b8e4105cc9dedc77",
  "fingerprint": "e3317db04d3958fd5f662c37b8e4105cc9dedc77",
  "userIds": [
      "name": "Jon Smith",
      "email": "jon@smith.com",
      "verified": "true"
      "name": "Jon Smith",
      "email": "jon@organization.com",
      "verified": "false"
  "created": "Sat Oct 17 2015 12:17:03 GMT+0200 (CEST)",
  "algorithm": "rsa_encrypt_sign",
  "keySize": "4096",
  "publicKeyArmored": "-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----"
  • keyId: The 16 char key id in hex
  • fingerprint: The 40 char key fingerprint in hex
  • userIds.name: The user ID's name
  • userIds.email: The user ID's email address
  • userIds.verified: If the user ID's email address has been verified
  • created: The key creation time as a JavaScript Date
  • algorithm: The primary key alogrithm
  • keySize: The key length in bits
  • publicKeyArmored: The ascii armored public key block

Upload new key

POST /api/v1/key

Payload (JSON):

  "publicKeyArmored": "-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-----"
  • publicKeyArmored: The ascii armored public PGP key to be uploaded

E.g. to upload a key from shell:

curl https://keys.mailvelope.com/api/v1/key --data "{\"publicKeyArmored\":\"$( \
  gpg --armor --export-options export-minimal --export $GPGKEYID | sed ':a;N;$!ba;s/\n/\\n/g' \
GET /api/v1/key?op=verify&keyId=b8e4105cc9dedc77&nonce=6a314915c09368224b11df0feedbc53c

Request key removal

DELETE /api/v1/key?keyId=b8e4105cc9dedc77 OR ?email=user@example.com
GET /api/v1/key?op=verifyRemove&keyId=b8e4105cc9dedc77&nonce=6a314915c09368224b11df0feedbc53c

Language & DB

The server is written is in JavaScript ES7 and runs on Node.js v8+.

It uses MongoDB v3.2+ as its database.

Getting started


Node.js (Mac OS)

This is how to install node on Mac OS using homebrew. For other operating systems, please refer to the Node.js download page.

brew update
brew install node

MongoDB (Mac OS)

This is the installation guide to get a local development installation on Mac OS using homebrew. For other operating systems, please refer to the MongoDB Getting Started Guide.

brew update
brew install mongodb
mongod --config /usr/local/etc/mongod.conf

Now the mongo daemon should be running in the background. To have mongo start automatically as a background service on startup you can also do:

brew services start mongodb

Now you can use the mongo CLI client to create a new test database. The username and password used here match the ones in the config/development.js file. Be sure to change them for production use:

use keyserver-test
db.createUser({ user:"keyserver-user", pwd:"trfepCpjhVrqgpXFWsEF", roles:[{ role:"readWrite", db:"keyserver-test" }] })


npm install


Configuration settings may be provided as environment variables. The files in the config directory read the environment variables and define configuration values for settings with no corresponding environment variable. Warning: Default settings are only provided for a small minority of settings in these files (as most of them are very individual like host/user/password)!

If settings are configured in multiple places, the priority ranking is as follows (individually for each setting):

  1. Environment variable
  2. config/production.js or config/development.js (depending on NODE_ENV)
  3. config/default.js


If you don't use environment variables to configure settings, create config/development.js and use config/default.js as a template. Creating development.js instead of just editing config/default.js is recommended to prevent accidental commits of locally used settings.


For production use, settings configuration with environment variables is recommended as NODE_ENV=production is REQUIRED to be set as environment variable to instruct node.js to adapt e.g. logging to production use.

Other settings you may also configure within config/production.js and use config/default.js as a template; but ensure then the environment variable NODE_ENV=production or production.js will not be read!


Available settings with its environment-variable-names, possible/example values and meaning (if not self-explainable). Defaults bold:

  • NODE_ENV=development|production (no default + needs to be set as environment variable!)
  • LOG_LEVEL=silly|error|warn|info|debug
  • PORT=8888 (application server port)
  • HTTPS_UPGRADE=true (upgrade HTTP requests to HTTPS and use HSTS)
  • HTTPS_KEY_PIN=base64_encoded_sha256 (optional, see HPKP)
  • HTTPS_KEY_PIN_BACKUP=base64_encoded_sha256 (optional, see HPKP)
  • MONGO_USER=db_user
  • MONGO_PASS=db_password
  • SMTP_PORT=465
  • SMTP_TLS=true
  • SMTP_PGP=true (encrypt verification message with public key (allows to verify presence + usability of private key at owner of the mail-address))
  • SMTP_USER=smtp_user
  • SMTP_PASS=smtp_pass
  • SENDER_NAME="OpenPGP Key Server"
  • SENDER_EMAIL=noreply@example.com
  • PUBLIC_KEY_PURGE_TIME=30 (number of days after which uploaded keys are deleted if they have not been verified)

Notes on SMTP

The key server uses nodemailer to send out emails upon public key upload to verify email address ownership. To test this feature locally, configure SMTP_USER and SMTP_PASS settings to your Gmail test account. Make sure that SMTP_USER and SENDER_EMAIL match. Otherwise the Gmail SMTP server will block any emails you try to send. Also, make sure to enable Allow less secure apps in the Gmail security settings. You can read more on this in the Nodemailer documentation.

For production you should use a service like Amazon SES, Mailgun or Sendgrid. Nodemailer supports all of these out of the box.

Run tests

npm test

Start local server

npm start


AGPL v3.0

See the LICENSE file for details


Among others, this project relies on the following open source libraries: