Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VaultPGP

FastAPI app for OpenPGP encrypt, decrypt, sign, and verify, with an integrated web UI (tabbed workspace) and a JSON API (/docs).

VaultPGP web UI — Encrypt, Decrypt, Sign, and Verify


Configure keys in .env:

Variable Purpose
OPENPGP_PRIVATE_KEY Armored secret key — decrypt, sign
OPENPGP_PUBLIC_KEY Armored public key (same keypair) — encrypt to yourself when recipient_mode is self
OPENPGP_KEY_PASSPHRASE Optional, if the secret key is passphrase-protected

Never commit .env. Copy .env.example and fill in real values. Export your public key once, e.g. gpg --armor --export YOUR_KEY_ID, and paste it into OPENPGP_PUBLIC_KEY.

Requirements

  • Python 3.11+
  • GnuPG (gpg) on your PATH

Virtual environment

Create the environment once from the project root:

cd /path/to/VaultPGP
python3 -m venv .venv

Activate it every time you open a new terminal before running pip or uvicorn:

Shell / OS Command
macOS / Linux (bash, zsh) source .venv/bin/activate
fish source .venv/bin/activate.fish
Windows (cmd) .venv\Scripts\activate.bat
Windows (PowerShell) .venv\Scripts\Activate.ps1

To leave the environment: deactivate

Install and run

With the venv activated:

pip install -e ".[dev]"
cp .env.example .env

Edit .env (use double quotes around multiline key blocks). Start the server:

uvicorn app.main:app --reload --host 127.0.0.1 --port 8000

Open http://127.0.0.1:8000.

Install without dev dependencies: pip install -e .

Web UI

The home page is a single workspace with:

  • Tabs — Encrypt, Decrypt, Sign, Verify (one action visible at a time).
  • Status chips — whether GnuPG is available and whether secret/public keys were loaded from .env.
  • After each form submit — the tab that handled the request stays selected so you see results immediately.
  • URL hash — switching tabs updates the address (e.g. #verify). You can bookmark or share http://127.0.0.1:8000#decrypt.
  • Copy — buttons on outputs use the clipboard when the browser allows it.
  • Verify — large field for a full clearsigned block; optional signer's public key when the signature is from someone else (otherwise GnuPG returns “no public key”). Detached verify is under an expandable section.
  • Sign — default output is signed + encrypted to OPENPGP_PUBLIC_KEY (armored BEGIN PGP MESSAGE) so you can paste it into Decrypt. Clearsigned and detached-only modes are available as radio options.

A short footer links to OpenAPI at /docs.

API

Four POST endpoints. Use JSON (Content-Type: application/json) with the same field names as the forms, or call from scripts.

curl, Accept, and armored output

  • If you POST JSON and do not send Accept: application/json (typical curl default is Accept: */*), encrypt, decrypt, and sign return raw armored text (text/plain) with real line breaks — safe to redirect to a file or pipe to gpg.
  • To get a JSON object, send Accept: application/json. That response includes human-readable fields plus ciphertext_b64, plaintext_b64, signature_b64 (UTF-8 armored content, base64-encoded) for one-line copy; decode with base64 -d.
  • ?format=plain or Accept: text/plain also forces a plain armored body.

Python requests: requests.post(..., json=...) often omits Accept: application/json, so you receive plain armored text, not JSON. Use headers={"Accept": "application/json"} if you call .json() on the response.

Endpoint JSON body
/encrypt plaintext, recipient_mode ("self" | "other"), and recipient_public_key when recipient_mode is "other"
/decrypt ciphertext (pasted ciphertext is normalized: literal \n, %0A, etc.)
/sign message, output: encrypted_signed (default, signed+encrypted armored PGP MESSAGE for OPENPGP_PUBLIC_KEY — paste into Decrypt), clearsigned, or detached. Legacy: detached boolean still maps to output when output is omitted.
/verify Clearsigned: clearsigned or signed_message — full block through END PGP SIGNATURE. Detached: message + signature. signer_public_key (optional armored public key) — use when GnuPG reports NO_PUBKEY / “Can't check signature: No public key” (the app’s keyring only has your .env keys unless you supply the signer’s key).

Interactive docs: http://127.0.0.1:8000/docs

Examples

Encrypt to yourself (plain armored response with default curl):

curl -s -H "Content-Type: application/json" \
  -d '{"plaintext":"note to self","recipient_mode":"self"}' \
  http://127.0.0.1:8000/encrypt > message.asc

Encrypt to someone else:

curl -s -H "Content-Type: application/json" \
  -d '{"plaintext":"hello","recipient_mode":"other","recipient_public_key":"'"$(cat their-pubkey.asc)"'"}' \
  http://127.0.0.1:8000/encrypt

JSON envelope (for jq or apps):

curl -s -H "Content-Type: application/json" -H "Accept: application/json" \
  -d '{"plaintext":"secret","recipient_mode":"self"}' \
  http://127.0.0.1:8000/encrypt | jq -r .ciphertext

Armored text and external tools

JSON string values escape newlines as \n. Do not paste raw JSON into GnuPG — you may see “illegal base64 data at input byte 0” or similar.

  1. Prefer plain curl output (no Accept: application/json) and save to .asc, or use jq -r .ciphertext, or ciphertext_b64 + base64 -d.
  2. Decrypting inside VaultPGP normalizes common paste issues before calling GnuPG.

Health check

GET /health — reports whether gpg is available.

Security notes

  • .env holds key material in plaintext on disk; restrict permissions and keep it out of version control.
  • Bind to 127.0.0.1 for local use only. Do not expose the app on a network without authentication and TLS.

About

Encrypt, decrypt, sign & verify — OpenPGP in the browser

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages