Client-side AES-256 protection toolkit for Hugo sites. Encrypt sensitive Markdown/HTML fragments offline, commit only ciphertext, and prompt visitors for a password at runtime.
- AES-256-GCM encryption with PBKDF2 key derivation (default 310k iterations).
- Node.js CLI that outputs ready-to-use Hugo shortcodes or front-matter snippets.
- Hugo shortcode for protecting inline/section content.
- Full-page lock overlay that requires a password before rendering any content.
- Front-end runtime (
static/hugo-protector/protector.js) that decrypts inside the browser via Web Crypto.
-
Install dependencies (if you plan to publish the package, otherwise just run via
npx):npm install
-
Make sure the front-end script is loaded. In your Hugo layout (e.g.,
layouts/_default/baseof.html), include:<script defer src="{{ "hugo-protector/protector.js" | relURL }}"></script>
-
(Optional) Add the full-page partial right after your
<main>element to render the password overlay when needed:{{ partial "protector/full_page.html" . }}
Use the CLI to encrypt raw HTML (typically the rendered Markdown fragment). Provide plaintext via file, --text, or stdin. Supply the password via --password-file, --password, or the HUGO_PROTECTOR_PASSWORD environment variable.
# Encrypt a snippet for shortcode usage
hugo-protector encrypt \
--input snippets/financials.html \
--password-file .password \
--mode shortcode
# Encrypt full-page content and copy the front-matter snippet
hugo-protector encrypt \
--input content/secret-page.rendered.html \
--password-file .password \
--mode pageFlags of interest:
| Flag | Description |
|---|---|
-i, --input <file> |
Read plaintext from file (defaults to stdin). |
-t, --text <string> |
Provide plaintext inline. |
-p, --password <value> |
Pass password directly (use only for testing). |
--password-file <file> |
Read password from file (recommended). |
--iterations <num> |
Override PBKDF2 iterations (default 310000). |
| `-m, --mode <shortcode | page>` |
| `--format <raw | helper>` |
| `--shortcode-format <html | markdown>` |
--prompt <text> |
Adds a prompt attribute to the shortcode helper output. |
--button <text> |
Adds a button attribute to the shortcode helper output. |
--hint <text> |
Adds a hint attribute to the shortcode helper output. |
Paste the helper output directly in your Markdown:
{{< protector payload="BASE64_PAYLOAD" prompt="Team password" hint="Contact ops" >}}
Parameters:
payload(required): base64 payload generated by the CLI.prompt(optional): label displayed above the password input.button(optional): custom unlock button text.hint(optional): small helper text rendered under the form. --format(optional):html(deprecated) ormarkdown(default). The runtime renders Markdown when this attribute is present and set tomarkdown. CLI helper output now defaults tomarkdown, so no extra flag is needed unless you explicitly wanthtmlbehavior.
When using the CLI helper output, pass --shortcode-format html to force plain HTML output, or use --prompt, --button, and --hint to localize the unlock form directly from the CLI.
When the page loads, the shortcode renders a password form. On successful decrypt, the placeholder is replaced with the original HTML fragment.
Example encrypting Markdown directly:
npx hugo-protector encrypt \
--text "enc" \
--password-file .password \
--shortcode-format markdown \
--prompt "team password" \
--button "open" \
--hint "announced on Slack"{{< protector
payload="BASE64_PAYLOAD"
prompt="team password"
button="open"
hint="announced on Slack"
format="markdown"
>}}
- Encrypt the fully rendered HTML you wish to ship (e.g., run
hugolocally and save the.Contentoutput). - Store the payload in your page front matter:
---
title: "Quarterly Strategy"
protector_full_page_payload: "BASE64_PAYLOAD"
protector_full_page_prompt: "Enter the shared secret"
protector_full_page_target: "#main"
---- Ensure
{{ partial "protector/full_page.html" . }}is included in the relevant layout (commonlybaseof.htmlorsingle.html). The partial renders a fullscreen overlay that injects the decrypted HTML into the element referenced byprotector_full_page_target(defaults tomain).
- Password strength matters: choose long, unique phrases and share them securely.
- Increase
--iterationsif visitors use modern hardware; lower it if decryption feels slow on mobile. - The decrypted HTML is inserted straight into the DOM. Only encrypt content you trust, or sanitize on decrypt if needed.
- The password never leaves the browser; failed attempts simply keep the form visible.
bin/hugo-protector.js— CLI entry.src/encryption.js— AES/PBKDF2 helpers.static/hugo-protector/protector.js— browser runtime that powers both shortcodes and full-page locks.layouts/shortcodes/protector.html— shortcode definition.layouts/partials/protector/full_page.html— overlay partial for.Params.protector_full_page_payload.
See docs/ARCHITECTURE.md for a deeper dive into data flow and component boundaries.