mdenc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +141 -0
- package/dist/chunk-FFRUPAVV.js +669 -0
- package/dist/chunk-FFRUPAVV.js.map +1 -0
- package/dist/cli.js +548 -0
- package/dist/cli.js.map +1 -0
- package/dist/hooks-ZO2DIE5U.js +16 -0
- package/dist/hooks-ZO2DIE5U.js.map +1 -0
- package/dist/index.cjs +455 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# mdenc
|
|
2
|
+
|
|
3
|
+
**Encrypt your Markdown. Keep your diffs.**
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## What it looks like
|
|
8
|
+
|
|
9
|
+
Say you have some private notes:
|
|
10
|
+
|
|
11
|
+
```markdown
|
|
12
|
+
# Meeting Notes
|
|
13
|
+
|
|
14
|
+
Discussed the Q3 roadmap. We agreed to prioritize
|
|
15
|
+
the mobile app rewrite.
|
|
16
|
+
|
|
17
|
+
## Action Items
|
|
18
|
+
|
|
19
|
+
Alice will draft the technical spec by Friday.
|
|
20
|
+
Bob is handling the database migration plan.
|
|
21
|
+
|
|
22
|
+
## Budget
|
|
23
|
+
|
|
24
|
+
Total allocated: $450k for the quarter.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`mdenc encrypt notes.md -o notes.mdenc` turns it into:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
mdenc:v1 salt_b64=tWpYJ1TX... file_id_b64=QtPSfysG... argon2=m=65536,t=3,p=1
|
|
31
|
+
hdrauth_b64=2Iox+FH0IuoSHittEzcxSI8Ew7VUJNIBAP+3RKs3TRg=
|
|
32
|
+
u+C4c6fq3ShPpe0nmAdVdB3gt+Jr45rPChOYd8W3W827Hw6ye1tO7eBh... <- # Meeting Notes
|
|
33
|
+
iFeHLHNGgHT3cBM20/BlPfaDjeY+WL3rZh1unY951Ha/wGHI5D8yYmMi... <- Discussed the Q3...
|
|
34
|
+
dm54GXdXI+MpbgeCpbUQj9x5HYOvJ/wIIymaQxcwgraQO2lwCYUqfUka... <- ## Action Items
|
|
35
|
+
2W4gqkAK/b/UD9euXLVE4I27+LnxFHdPr7lQajtI5HxC7eED4YUYtoaG... <- Alice will draft...
|
|
36
|
+
JQgoywFO02b4OdkZEKhk5ZjpXyLzJCuIFAU6mi73ZazKhy+qw1Drz6k8... <- ## Budget
|
|
37
|
+
NNdpCjf++ncLe9yrRbotyPUWuib8Oe68xjkaTnEJVNO7snSFS0Z6cGwY... <- Total allocated...
|
|
38
|
+
seal_b64=K7mQ2xR9f4pVnBt5z8wJH3kY6LdWqA0oNc1iEgMvT+s=
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Each paragraph becomes one line of base64. A seal HMAC at the end protects the file's integrity. The file is plain UTF-8 text that git tracks normally.
|
|
42
|
+
|
|
43
|
+
Now you edit the "Action Items" paragraph and re-encrypt. Here's what `git diff` shows:
|
|
44
|
+
|
|
45
|
+
```diff
|
|
46
|
+
mdenc:v1 salt_b64=tWpYJ1TX... file_id_b64=QtPSfysG... argon2=m=65536,t=3,p=1
|
|
47
|
+
hdrauth_b64=2Iox+FH0IuoSHittEzcxSI8Ew7VUJNIBAP+3RKs3TRg=
|
|
48
|
+
u+C4c6fq3ShPpe0nmAdVdB3gt+Jr45rPChOYd8W3W827Hw6ye1tO7eBh...
|
|
49
|
+
iFeHLHNGgHT3cBM20/BlPfaDjeY+WL3rZh1unY951Ha/wGHI5D8yYmMi...
|
|
50
|
+
dm54GXdXI+MpbgeCpbUQj9x5HYOvJ/wIIymaQxcwgraQO2lwCYUqfUka...
|
|
51
|
+
-2W4gqkAK/b/UD9euXLVE4I27+LnxFHdPr7lQajtI5HxC7eED4YUYtoaG...
|
|
52
|
+
+29eDDzd58m8BtTV3PA3zyetTyuhL3Qqimlz7APvXDZsGL/rtZtld9R0u...
|
|
53
|
+
JQgoywFO02b4OdkZEKhk5ZjpXyLzJCuIFAU6mi73ZazKhy+qw1Drz6k8...
|
|
54
|
+
NNdpCjf++ncLe9yrRbotyPUWuib8Oe68xjkaTnEJVNO7snSFS0Z6cGwY...
|
|
55
|
+
-seal_b64=K7mQ2xR9f4pVnBt5z8wJH3kY6LdWqA0oNc1iEgMvT+s=
|
|
56
|
+
+seal_b64=Px8nVdR2aLw0tYj3Km5qFh9sBcW7e6Uo4gZi1DxAf+E=
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
One paragraph changed, one line in the diff (plus the seal updates). Even inserting a new paragraph between existing ones only adds one line -- surrounding chunks stay unchanged. Compare that to GPG, where the entire file would show as changed.
|
|
60
|
+
|
|
61
|
+
## Why
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
mdenc encrypts at paragraph granularity. Unchanged paragraphs produce identical ciphertext, so git only tracks the paragraphs you actually touched.
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install mdenc
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## CLI
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Encrypt
|
|
77
|
+
mdenc encrypt notes.md -o notes.mdenc
|
|
78
|
+
|
|
79
|
+
# Decrypt
|
|
80
|
+
mdenc decrypt notes.mdenc -o notes.md
|
|
81
|
+
|
|
82
|
+
# Re-encrypt after editing (unchanged paragraphs keep same ciphertext)
|
|
83
|
+
mdenc decrypt notes.mdenc -o notes.md
|
|
84
|
+
# ... edit notes.md ...
|
|
85
|
+
mdenc encrypt notes.md -o notes.mdenc
|
|
86
|
+
|
|
87
|
+
# Verify file integrity
|
|
88
|
+
mdenc verify notes.mdenc
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Password is read from `MDENC_PASSWORD` env var or prompted interactively (no echo).
|
|
92
|
+
|
|
93
|
+
## Library
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { encrypt, decrypt, verifySeal } from 'mdenc';
|
|
97
|
+
|
|
98
|
+
// Encrypt (always includes integrity seal)
|
|
99
|
+
const encrypted = await encrypt(markdown, password);
|
|
100
|
+
|
|
101
|
+
// Decrypt (verifies seal automatically)
|
|
102
|
+
const plaintext = await decrypt(encrypted, password);
|
|
103
|
+
|
|
104
|
+
// Re-encrypt with diff optimization
|
|
105
|
+
const updated = await encrypt(editedMarkdown, password, {
|
|
106
|
+
previousFile: encrypted,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Verify integrity without decrypting
|
|
110
|
+
const ok = await verifySeal(encrypted, password);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## How it works
|
|
114
|
+
|
|
115
|
+
1. Your Markdown is split into chunks at paragraph boundaries (runs of 2+ newlines)
|
|
116
|
+
2. Each chunk is encrypted with XChaCha20-Poly1305 using a deterministic nonce derived from the content
|
|
117
|
+
3. The output is plain UTF-8 text -- one base64 line per chunk, plus a seal HMAC
|
|
118
|
+
4. Same content + same keys = same ciphertext, so unchanged chunks produce identical output and minimal diffs
|
|
119
|
+
5. The seal HMAC covers all lines, detecting reordering, truncation, and rollback on decrypt
|
|
120
|
+
|
|
121
|
+
The password is stretched with Argon2id (64 MiB, 3 iterations). Keys are derived via HKDF-SHA256 with separate keys for encryption, header authentication, and nonce derivation.
|
|
122
|
+
|
|
123
|
+
## What leaks
|
|
124
|
+
|
|
125
|
+
mdenc is designed for diff-friendliness, not metadata hiding. An observer can see:
|
|
126
|
+
|
|
127
|
+
- How many paragraphs your document has
|
|
128
|
+
- Approximate size of each paragraph
|
|
129
|
+
- Which paragraphs changed between commits
|
|
130
|
+
- Identical paragraphs within a file (they produce identical ciphertext)
|
|
131
|
+
|
|
132
|
+
The *content* of your paragraphs stays confidential.
|
|
133
|
+
|
|
134
|
+
## Docs
|
|
135
|
+
|
|
136
|
+
- [SECURITY.md](SECURITY.md) -- threat model, crypto details, accepted tradeoffs
|
|
137
|
+
- [SPECIFICATION.md](SPECIFICATION.md) -- wire format for implementers
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
ISC
|