mdenc

Encrypt your Markdown. Keep your diffs.

mdenc lets you store encrypted Markdown in git without losing the ability to see what changed. Edit one paragraph, and only that paragraph changes in the encrypted output. Your git log stays useful. Your pull request reviews stay sane.

What it looks like

Say you have some private notes:

# Meeting Notes

Discussed the Q3 roadmap. We agreed to prioritize
the mobile app rewrite.

## Action Items

Alice will draft the technical spec by Friday.
Bob is handling the database migration plan.

## Budget

Total allocated: $450k for the quarter.

mdenc encrypt notes.md -o notes.mdenc turns it into:

mdenc:v1 salt_b64=1z94qTI8... file_id_b64=eLP+c5cP... scrypt=N=16384,r=8,p=1
hdrauth_b64=griicznFYhBTVCeq1lpvB+J73wsJJbheGghxNOIJlu0=
Qnp4sPf/aN1z/VSkZ8yjGWwk0ZLqwpFAJB...    ← # Meeting Notes
nD1KIHOMX5VhlSU4USUxWHTrl2Qi6cev/b6...    ← Discussed the Q3...
Hes/oW+FeONHytgUa7c9ZzdF4d/w7Ei0tnG...    ← ## Action Items
yT7vkHbaXHR390bWz1d/qcK6yVeF3p5/quv...    ← Alice will draft...
dkM7awElU/pfUYs1goxQFlgcyUq8FNHcnZr...    ← ## Budget
ZBRV9kdXm7gNiF4BvI9eklrtTjhkI9tLHu0...    ← Total allocated...
seal_b64=29ylXnDTWQ09nzZjvoYtYUpfyr4X4NLONxpT/HOC9TU=

Each paragraph becomes one line of base64. A seal HMAC at the end protects the file's integrity.

Now edit one paragraph and re-encrypt. Here's the git diff — one paragraph changed, one line in the diff:

 mdenc:v1 salt_b64=1z94qTI8... file_id_b64=eLP+c5cP...
 hdrauth_b64=griicznFYhBTVCeq1lpvB+J73wsJJbheGghxNOIJlu0=
 Qnp4sPf/aN1z/VSkZ8yjGWwk0ZLqwpFAJB...
 nD1KIHOMX5VhlSU4USUxWHTrl2Qi6cev/b6...
 Hes/oW+FeONHytgUa7c9ZzdF4d/w7Ei0tnG...
-yT7vkHbaXHR390bWz1d/qcK6yVeF3p5/quv...
+1RgyC3rXcjykvoL0GgsQsHBmxy5axdD/tqM...
 dkM7awElU/pfUYs1goxQFlgcyUq8FNHcnZr...
 ZBRV9kdXm7gNiF4BvI9eklrtTjhkI9tLHu0...
-seal_b64=29ylXnDTWQ09nzZjvoYtYUpfyr4X...
+seal_b64=iNhYjNp69tyv4tkzgDJK5Fh2h1WL...

Compare that to GPG, where the entire file would show as changed.

Why

You want to keep private notes, journals, or sensitive docs in a git repo. GPG-encrypting the whole file works, but every tiny edit produces a completely different blob. The entire file shows as changed in every commit.

mdenc encrypts at paragraph granularity. Unchanged paragraphs produce identical ciphertext, so git only tracks the paragraphs you actually touched.

Install

npm install -g mdenc

CLI

# Encrypt
mdenc encrypt notes.md -o notes.mdenc

# Decrypt
mdenc decrypt notes.mdenc -o notes.md

# Re-encrypt (unchanged paragraphs keep same ciphertext)
mdenc encrypt notes.md -o notes.mdenc

# Verify file integrity
mdenc verify notes.mdenc

Password is read from MDENC_PASSWORD env var or prompted interactively.

Git integration

# Set up smudge/clean filter
mdenc init

# Generate a random password
mdenc genpass

# Mark a directory for encryption
mdenc mark docs/private

# Check encryption status
mdenc status

After mdenc init and mdenc mark, the workflow is automatic: edit .md files normally, and git's clean filter encrypts them on staging. The smudge filter decrypts on checkout.

Library

import { encrypt, decrypt, verifySeal } from 'mdenc';

// Encrypt
const encrypted = await encrypt(markdown, password);

// Decrypt (verifies seal automatically)
const plaintext = await decrypt(encrypted, password);

// Re-encrypt with diff optimization
const updated = await encrypt(editedMarkdown, password, {
  previousFile: encrypted,
});

// Verify integrity without decrypting
const ok = await verifySeal(encrypted, password);

How it works

  1. Markdown is split into chunks at paragraph boundaries (2+ newlines)
  2. Each chunk is encrypted with XChaCha20-Poly1305 using a deterministic nonce derived from the content
  3. The output is plain UTF-8 text — one base64 line per chunk, plus a seal HMAC
  4. Same content + same keys = same ciphertext, so unchanged chunks produce identical output
  5. The seal HMAC covers all lines, detecting reordering, truncation, and rollback

The password is stretched with scrypt (N=16384, r=8, p=1). Keys are derived via HKDF-SHA256 with separate subkeys for encryption, header authentication, and nonce derivation.

What leaks

mdenc is designed for diff-friendliness, not metadata hiding. An observer can see:

The content of your paragraphs stays confidential.

License

ISC