Skip to content

Releases: queelius/pagevault

v0.4.1 — Critical bug fixes for v0.4.0

25 Apr 07:06

Choose a tag to compare

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

25 Apr 01:09

Choose a tag to compare

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

24 Apr 07:42

Choose a tag to compare

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, making lock(lock(html)) == lock(html) idempotent. To compose encryption layers, explicitly wrap with mark_elements first.

  • PostMessage origin hardening: Site renderer now verifies e.source === iframe.contentWindow before processing pagevault-fetch / pagevault-nav messages. Under file:// 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={}) and rewrap_keys(new_users={}) now raise PagevaultError with a clear message instead of crashing deep in _wrap_cek_for_users with AttributeError.

  • info command perf: Chunk-counting was O(N²) via while soup.find(...) loop. Now O(N) with a single find_all regex scan. On a 500 MB encrypted file (500 chunks), info is 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.
  • .gitignore now excludes .playwright-mcp/ debug logs and root-level screenshots.

Upgrade

pip install --upgrade pagevault

v0.3.1 — Hardened Script Activation

10 Apr 01:42

Choose a tag to compare

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 breaking document.addEventListener. Now serialized via a page-level promise chain (__pvActivationChain).

  • window.onload no longer fires repeatedly. Previously invoked unconditionally after every decryption, causing the host page's handler to run N+1 times. Now snapshots window.onload before 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, replaceChild would throw and escape the activation function, leaving shims installed forever. Now wrapped in try/catch with guaranteed finalize() cleanup, plus parentNode guard before replaceChild.

Important Fixes

  • Inline type="module" scripts preserved correctly. Previously stripped type=module, converting modules to classic scripts (breaking import/export). Modern browsers handle inline modules fine.

  • Two-event split: pagevault:decrypted fires immediately after content injection; pagevault:activated fires 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/writeln suppression 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 pagevault

v0.3.0 — SPA Rendering & Script Activation

15 Mar 04:45

Choose a tag to compare

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 destruction
  • window.onload support — 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
  • srcset attribute parsing — responsive image descriptors parsed individually
  • CSS @import rewriting — 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--pad null bytes now trimmed using meta.size
  • CLI: plaintext password warningconfig set password warns about plaintext storage
  • CLI: boolean echoconfig set pad true echoes lowercase true not Python True

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 pagevault

pagevault v0.2.0: CLI Unification & Full Rename

05 Feb 08:54

Choose a tag to compare

🎉 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: lockhtmlpagevault
  • ✅ 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.html

New 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

  1. Review documentation at https://queelius.github.io/pagevault/
  2. Update projects using lockhtml
  3. Feedback and contributions welcome!

Changelog: f8f27f5...92290b4