Releases: queelius/pagevault
v0.4.1 — Critical bug fixes for v0.4.0
Patch release fixing two critical bugs found in v0.4.0 post-release code review.
Fixes
🔴 `lock_html` no longer drops runtime injection
When re-running `pagevault lock` on a file with existing `data-pv-v4` elements but a missing runtime, the runtime was being injected into the parsed DOM but then thrown away because the function returned the original input string. Re-locking could never repair a file that had lost its runtime — output was byte-identical to input.
Fix: `_inject_runtime` now returns whether it actually injected; `lock_html` returns the serialized soup if any mutation happened.
Regression test added.
🔴 `pagevault inspect` viewer list never shown
The viewer-detection regex in `inspect` matched v0.3.x dispatch shape (`'mime/type': __pv_viewer`), but v0.4.0 uses `window.__pv_viewers['mime/type'] = viewer`. The "Viewers:" line silently never printed.
Fix: regex updated to match v4 shape.
🟡 Format detection no longer misfires on stray script ids
`inspect` and `_find_first_envelope` previously detected wrap format by searching for `<script id="pv-meta">` anywhere in the document. A region-encrypted page containing plaintext that referenced `pv-meta` (e.g. example documentation) would be misclassified as wrap.
Fix: detect format via `` marker on the element instead.
Other improvements
10 small simplifications committed since v0.4.0 (pre-release): `_html_escape` replaced with stdlib `html.escape`, `encrypt_mode` deprecated param removed, lock-helper deduplication, json import hoisted, `_wrap_iife` extracted to share IIFE assembly between `build_region_js`/`build_wrap_js`, etc. Net -19 lines.
Upgrade
```bash
pip install --upgrade pagevault
```
v0.4.0 — Architecture Overhaul
What's New
Major architectural overhaul addressing the v0.3.1 design review.
⚠️ Breaking Changes
- v0.3.x encrypted files cannot be decrypted by v0.4.0. Users must re-lock their files. No backwards compatibility.
- `pagevault info` and `pagevault check` removed — replaced by `pagevault inspect` (with optional `--check`).
- `pagevault serve` removed — replaced by `pagevault dev serve`.
- Removed Python API aliases: `encrypt_html`, `decrypt_html`, `wrap_elements_for_encryption`, `wrap_body_for_encryption`. Use `lock_html`, `unlock_html`, `mark_elements`, `mark_body`.
Highlights
Runtime extraction
All browser-side JavaScript now lives in real .js files under `src/pagevault/runtime/`, loaded via `importlib.resources`. No more JS-inside-Python-f-strings — major readability win.
- `runtime/core/` — escape, crypto, chunks, storage, progress, activation
- `runtime/region/handler.js` — region encryption handler
- `runtime/wrap/` — file/site renderers + nav injector + ZIP shim
- Viewer JS in `src/pagevault/js/viewers/` (one .js + .css per viewer plugin)
Unified v4 envelope format
Replaced v2 (attribute-embedded) and v3 (chunked) with a single v4 chunked format used everywhere:
- Region encryption stores meta + chunks as `<script>` children inside `` (no more giant `data-encrypted=` attribute)
- File and site wrapping use the same envelope
- One encrypt path (`encrypt_v4`), one decrypt path (`decrypt_v4`)
- See `docs/v4-envelope.md` for spec
CLI split
`cli.py` (2155 lines) split into `cli/init.py` (49 lines) + `cli/shared.py` + `cli/commands/*.py` (one file per command). Easier to navigate, easier to test.
Renderer fixes
- #2 listener leak: `window.addEventListener('message', ...)` in site renderer now attached at module scope; no duplicate listeners on repeated renders.
- #5 overbroad regex: removed the quoted-file-path rewrite pass that could corrupt JS string literals. Use `fetch()` (handled by the fetch shim) for dynamic resource references.
Node-based JS unit tests
New `tests/js/` directory runs the browser runtime's crypto primitives under Node 20+ using built-in `node:test` + `node:vm`. No npm dependencies. Run: `node --test "tests/js/**/*.mjs"`.
Codebase shrinkage
- `wrap.py`: 1321 → 573 lines (-56%)
- `cli.py`: 2155 lines → `cli/init.py` 49 lines + 8 command modules
- Net deletion of ~1000 lines of inline JS-in-Python-strings
Tests
- 708 Python tests pass
- 12 Playwright browser tests pass
- 5 Node JS unit tests pass
Upgrade
```bash
pip install --upgrade pagevault
```
If you have v0.3.x encrypted files, you'll need to re-lock them with v0.4.0.
v0.3.2 — Closure Property Fix + Security Hardening
What's New
Bug fixes and hardening from a comprehensive code review. No API changes.
Fixes
-
Closure property:
lock_html()on already-encrypted content now preserves ciphertext instead of silently destroying it. Previously, re-locking encrypted the (already-cleared) inner content as empty string and overwrote the original payload. Now: already-encrypted elements are skipped, makinglock(lock(html)) == lock(html)idempotent. To compose encryption layers, explicitly wrap withmark_elementsfirst. -
PostMessage origin hardening: Site renderer now verifies
e.source === iframe.contentWindowbefore processingpagevault-fetch/pagevault-navmessages. Underfile://this was moot (opaque origins), but under HTTP-served sites any other same-origin frame could forge messages. Defense in depth. -
Empty users validation:
encrypt(users={})andrewrap_keys(new_users={})now raisePagevaultErrorwith a clear message instead of crashing deep in_wrap_cek_for_userswithAttributeError. -
infocommand perf: Chunk-counting was O(N²) viawhile soup.find(...)loop. Now O(N) with a singlefind_allregex scan. On a 500 MB encrypted file (500 chunks),infois now fast.
Testing
- Hypothesis property-based tests for the closure property. 6 properties × many generated inputs verify: roundtrip preservation, no plaintext leakage, idempotence, mark+lock+unlock roundtrip. Addresses the architecture review's "Risk #1: closure property is load-bearing and under-specified."
Code Quality
- Previous commit: simplification pass (-130 lines, 20 improvements) — removed dead backward-compat aliases, consolidated duplicate patterns, extracted helpers.
.gitignorenow excludes.playwright-mcp/debug logs and root-level screenshots.
Upgrade
pip install --upgrade pagevaultv0.3.1 — Hardened Script Activation
What's New
Fixes three critical bugs in the script activation routine introduced in v0.3.0, plus code quality improvements.
Critical Fixes
-
Concurrent decryption no longer corrupts global shims. Multiple
<pagevault>elements with stored credentials would race — each handler saved the previous handler's shim as the "original" and restored it at cleanup, permanently breakingdocument.addEventListener. Now serialized via a page-level promise chain (__pvActivationChain). -
window.onloadno longer fires repeatedly. Previously invoked unconditionally after every decryption, causing the host page's handler to run N+1 times. Now snapshotswindow.onloadbefore activation and only fires if a decrypted script replaced it. -
Script errors no longer leak shims permanently. If a script removed an ancestor of a later script,
replaceChildwould throw and escape the activation function, leaving shims installed forever. Now wrapped in try/catch with guaranteedfinalize()cleanup, plusparentNodeguard beforereplaceChild.
Important Fixes
-
Inline
type="module"scripts preserved correctly. Previously strippedtype=module, converting modules to classic scripts (breakingimport/export). Modern browsers handle inline modules fine. -
Two-event split:
pagevault:decryptedfires immediately after content injection;pagevault:activatedfires after all scripts have run. Listeners that just need "the content is here" use the first; integrations waiting for scripts use the second. -
Permissive JS MIME-type detection — handles
application/x-javascript,text/ecmascript, etc. -
document.write/writelnsuppression now warns instead of silently dropping calls.
Code Quality
-
Extracted
_ACTIVATION_JS— 113 lines of JS moved from f-string (with{{/}}brace doubling) to a plain string constant. The JS is now directly readable. -
12 new Playwright browser tests that load locked HTML in real Chromium and verify actual runtime behavior. Covers every critical bug scenario.
Upgrade
pip install --upgrade pagevaultv0.3.0 — SPA Rendering & Script Activation
What's New
Script Activation After Decryption
Region-encrypted HTML pages with JavaScript now fully work after decryption. The runtime re-executes <script> tags, shims lifecycle events, and preserves execution order.
- DOMContentLoaded & window.load shims — callbacks registered by decrypted scripts fire immediately
- Sequential script execution — external scripts wait to load before the next script runs
- Module script support — inline
<script type="module">converted for execution - JSON script skipping —
<script type="application/json">data blocks are left alone document.write()protection — neutralized during activation to prevent page destructionwindow.onloadsupport — property assignments are detected and invoked
Site Renderer Improvements
Encrypted sites (--site mode) now handle more complex static sites and SPAs:
fetch()interception — relative URL fetch calls are intercepted via postMessage and served from the in-memory zip- Absolute path resolution — root-relative paths (
/images/logo.png) now resolve correctly srcsetattribute parsing — responsive image descriptors parsed individually- CSS
@importrewriting — bare string syntax (@import 'file.css') now rewritten - Form submission interception —
<form action="page.html">navigates within the encrypted site target="_blank"handling — external links no longer intercepted
Bug Fixes
- v3 padding strip —
--padnull bytes now trimmed usingmeta.size - CLI: plaintext password warning —
config set passwordwarns about plaintext storage - CLI: boolean echo —
config set pad trueechoes lowercasetruenot PythonTrue
Code Quality
- Removed ~270 lines of dead v2 renderer code
- 711 tests, 84% coverage
- 31 new script activation tests covering all edge cases
Upgrade
pip install --upgrade pagevaultpagevault v0.2.0: CLI Unification & Full Rename
🎉 Major Release: Package Rename + CLI Unification
pagevault v0.2.0 marks a major milestone: complete rename from lockhtml to pagevault and unification of the CLI interface.
✨ What's New
Package Rename
- ✅ Full rename:
lockhtml→pagevault - ✅ Element tag:
<lockhtml-encrypt>→<pagevault> - ✅ Config:
.lockhtml.yaml→.pagevault.yaml - ✅ All imports, references, and documentation updated
Unified Lock Command
The separate pagevault wrap file and pagevault wrap site commands are now integrated into pagevault lock with smart file type detection:
# HTML files: encrypt marked regions
pagevault lock page.html
# Non-HTML files: wrap entire file
pagevault lock document.pdf # → document.html
# Directories as sites
pagevault lock mysite/ --site # → mysite.html
pagevault lock mysite/ --site -o out.htmlNew flags:
--site- Bundle directory as encrypted site-o/--output- Output file for non-HTML or site mode--entry- Entry point for --site mode (default: index.html)
Comprehensive Documentation
- 📖 New mkdocs site with 7 guides (~50KB)
- Getting Started with Hugo integration examples
- Complete CLI Reference
- Configuration guide with multi-user setup
- Real-world use cases (Hugo blogs, knowledge bases, client work)
- Security documentation with cryptography details
- 30+ FAQ items
- 📄 Enhanced README focused on Hugo blog use case
- 🚀 Auto-deploying to GitHub Pages via GitHub Actions
Testing
- ✅ 30 new tests for unified lock command
- ✅ 357 total tests passing
- ✅ 83% CLI module coverage
- ✅ Updated integration tests for new routing
🔄 Breaking Changes
| Old | New |
|---|---|
pagevault wrap file <file> |
pagevault lock <file> |
pagevault wrap site <dir> |
pagevault lock <dir> --site |
<lockhtml-encrypt> |
<pagevault> |
.lockhtml.yaml |
.pagevault.yaml |
📚 Migration Guide
Upgrading from lockhtml to pagevault:
# 1. Install pagevault
pip install pagevault
# 2. Rename config file
mv .lockhtml.yaml .pagevault.yaml
# 3. Update HTML
# Change: <lockhtml-encrypt>content</lockhtml-encrypt>
# To: <pagevault>content</pagevault>
# 4. Use new CLI
# Old: pagevault wrap file document.pdf
# New: pagevault lock document.pdf
# Old: pagevault wrap site mysite/
# New: pagevault lock mysite/ --site🔐 Security
- AES-256-GCM encryption (unchanged)
- PBKDF2-SHA256 with 310,000 iterations (unchanged)
- All processing client-side (unchanged)
- WebCrypto compatible (unchanged)
📊 Release Stats
- Files changed: 27
- Insertions: 8,156
- Deletions: 442
- Tests: 357 passing (30 new)
- Documentation: 7 new guides + improved README
- GitHub Pages: Auto-deploying via Actions
🙏 Credits
- CLI unification design and implementation
- Comprehensive documentation with real-world examples
- Full test coverage for routing logic
- GitHub Pages setup for automatic doc deployment
🚀 Next Steps
- Review documentation at https://queelius.github.io/pagevault/
- Update projects using lockhtml
- Feedback and contributions welcome!
Changelog: f8f27f5...92290b4