Veil turns selected HTML into authenticated ciphertext and a self-contained unlock page; deploy it to any HTTPS static host, with no application server.
Deterrence for previews and private-ish documents; not identity-based access control, and not for regulated or high-impact data.
This site is one deployment with two protected zones. One passphrase is disclosed; the other is not.
| Route | Key scope | Passphrase | Expected |
|---|---|---|---|
| /demo/ | y4le-veil-demo | open-sesame |
decrypts |
| /demo/second.html | y4le-veil-demo | same as /demo/ |
no prompt after unlocking /demo/ in this tab |
| /vault/ | y4le-veil-vault | withheld | stays locked |
Walk it in one tab, leaving “Remember this device” unchecked:
/demo/second.html; it opens with no
prompt, because both pages came from one build and share one key scope.veil verify
checks wrapper integrity, that the pages you meant to protect are
protected, that the output corresponds to its input, and that everything
decrypts.veil.js is a
single Node script; vendor it, pin the commit, record the digest.Veil is deterrence, not absolute security. It keeps selected HTML out of a plaintext deploy artifact and out of search indexes; it does not do the following.
veil verify
running in that same job cannot prove otherwise.The full threat model states the boundary in detail.
noindex robots meta. The real title exists only inside the
ciphertext.How it works covers inlining, asset omission, and unlock state.
# typed, not left in shell history read -rs VEIL_PASSPHRASE && export VEIL_PASSPHRASE # encrypt a site into a fresh output directory node veil.js ./my-site ./encrypted \ --passphrase-env VEIL_PASSPHRASE --id my-project # audit what you are about to publish node veil.js verify ./encrypted --input ./my-site \ --id my-project --passphrase-env VEIL_PASSPHRASE
Node.js 18+ and a host that serves over HTTPS; Web Crypto exists only in
secure contexts, so a protected page needs HTTPS, localhost, or
file:.
Built by two chained Veil runs over one source tree, then verified once per zone before publishing.
This page is not encrypted; read its source and you will find it. Read the source of either protected route and you will find a payload.
It is also deployed on github.io, one origin
shared with every other project site on the account; that is the situation
the boundary above warns about, and it is acceptable here only because
nothing on this site is secret. A deployment that matters gets its own
hostname.