quarto-lock

Cryptographic password protection for static Quarto sites

quarto-lock logo

Protect rendered Quarto output without a server

quarto-lock is a Quarto extension that encrypts a rendered website or book at build time and decrypts it in the visitor’s browser after a shared password is entered.

It is designed for static hosting such as GitHub Pages: no application server, database, login service, or server-side session is required.

NoteCurrent release — v0.1.0

The first public release is v0.1.0.

View the v0.1.0 release notes

TipTry the live protected demo

Demo: https://lsbjordao.github.io/quarto-lock-demo/

Password: quarto-lock-demo

The demo intentionally uses a private Quarto source repository and a separate public repository containing only the encrypted build output.

How it works

Quarto source
*.qmd / data / notebooks / assets
        |
        | quarto render
        v
rendered _site
        |
        | quarto-lock post-render
        | PBKDF2-HMAC-SHA-256
        | AES-256-GCM
        v
encrypted static output
lock shell + *.qlock ciphertext
        |
        | static hosting
        v
visitor enters shared password
        |
        v
browser-side decryption

The core deployment principle is:

Secrets enter the build; only ciphertext leaves the build.

Quick start

Install the extension in the Quarto project you want to protect:

quarto add lsbjordao/quarto-lock

Enable it in _quarto.yml:

filters:
  - quarto-lock

Render with a strong shared password:

export QUARTO_LOCK_PASSWORD='use-a-long-strong-passphrase'
quarto render

The password is used to derive the encryption key during the locking step. It is not written into the generated HTML, JavaScript, or encrypted payloads.

To inspect the locked build locally, serve the already-rendered directory directly:

python3 -m http.server 3073 -d _site

Then open http://127.0.0.1:3073/.

WarningDo not use quarto preview after locking

quarto preview can trigger another render and recreate clear output in _site. To inspect an encrypted build, run a full quarto render and then serve _site directly.

What gets protected?

quarto-lock protects local rendered resources, including:

  • HTML pages;
  • CSS and JavaScript;
  • images and fonts;
  • Quarto search indexes and JSON files;
  • PDFs, ZIP files and other local downloads.

The public protected host receives the minimal lock shell, the browser runtime and encrypted *.qlock payloads.

External resources such as CDNs, remote images and remote APIs remain external and are not encrypted by quarto-lock.

Cryptography

Version 0.1.0 uses:

  • PBKDF2-HMAC-SHA-256 for password-based key derivation;
  • 600,000 iterations by default;
  • AES-256-GCM for authenticated encryption;
  • a random 128-bit salt per build;
  • a unique random 96-bit IV per protected file;
  • the browser Web Crypto API for client-side cryptography.

After a successful unlock, the derived AES key — not the password — is kept in sessionStorage, so navigation in the same browser tab remains unlocked. A new build receives a new salt/build id, so an old session key cannot unlock newly deployed content.

Static lock, not user authentication

quarto-lock is intentionally a lock, not an identity system.

It does not provide individual accounts, roles, MFA, audit logs, revocation lists, password recovery or server-side authorization. Anyone who can download the site can also download the ciphertext and attempt offline password guessing, so password strength matters.

Once content has been successfully decrypted in a visitor’s browser, that visitor can copy or save it.

For highly sensitive or regulated material that requires identity, roles or revocation, use server-side authentication instead.

Publish on GitHub Pages

Two deployment patterns are supported:

Situation Recommended pattern
Quarto source may be public Same repository → render → encrypted Pages artifact
Private repo with Pages support Same private repository → GitHub Pages
Private source + public static hosting Private source repo → Actions → separate public encrypted-output repo

The live demo uses the third pattern:

quarto-lock-demo-source       PRIVATE
Quarto source / data / scripts
            |
            | GitHub Actions
            | quarto render
            | quarto-lock
            v
quarto-lock-demo              PUBLIC
encrypted build output only
            |
            | GitHub Pages
            v
https://lsbjordao.github.io/quarto-lock-demo/

The public output repository does not receive .qmd files, private datasets, notebooks, build scripts or passwords.

The complete walkthrough — including the fine-grained PAT, PAGES_DEPLOY_TOKEN, GitHub Actions workflow, anti-leak checks and billing notes — is in Publishing.

Customize or translate the lock screen

Every user-facing string can be changed without editing the extension source. Configuration can come from environment variables or a local .env file.

Example:

QUARTO_LOCK_LANG=pt-BR
QUARTO_LOCK_TITLE="Área reservada"
QUARTO_LOCK_MESSAGE="Digite a senha para abrir este conteúdo."
QUARTO_LOCK_PASSWORD_LABEL="Senha"
QUARTO_LOCK_BUTTON_LABEL="Entrar"
QUARTO_LOCK_FOOTER="Protegido por Quarto Lock"
QUARTO_LOCK_ERROR_INCORRECT="Senha incorreta."

Environment variables and GitHub Actions values take precedence over .env, and .env takes precedence over the built-in defaults.

See Customize UI for the complete configuration reference.

License

MIT