Skip to content

suraj95/VaultPGP

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

No releases published

Packages

 
 
 

Contributors