CLI tool
robinmoisson/staticrypt avatar
robinmoisson/staticrypt

The share link breaks unless you keep the salt file, and WebCrypto needs HTTPS

Password protect a static HTML page, decrypted in-browser in JS with no dependency. No server logic needed.

8,075 stars502 forksHTMLMIT

At a glance

What is it?
StatiCrypt turns a static HTML file into a self decrypting password prompt: AES-256 with WebCrypto, decryption in the browser, no server logic anywhere. The costs are that WebCrypto only exists in HTTPS or localhost contexts, a --share link depends on a salt file that lands in your working directory, and recursive mode encrypts only the HTML and copies everything else through untouched.
Who is it for?
Take StatiCrypt when you need to publish one HTML file behind a long password on Netlify or GitHub Pages and you cannot add server logic. Leave it when the page pulls in stylesheets, scripts or images, because recursive mode copies every non-HTML file through untouched, and leave it if the page will be served over plain HTTP, since v3 is built on WebCrypto and the project states that only v2 works outside HTTPS and localhost.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 131 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • robinmoisson/staticrypt8,075 ★ · +32
Star history
Repository2026-09-222026-11-14
robinmoisson/staticrypt8k8.1k

32 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

WebCrypto is the constraint that decides where this can run

StatiCrypt encrypts your HTML with AES-256 using WebCrypto and decrypts it in JavaScript on the client side, which is the whole reason it needs no back-end. WebCrypto has one requirement that shapes every deployment decision: it is only available in HTTPS or localhost contexts.

The project is explicit about what that means. Version 3 is the version built on WebCrypto, and the migration note says that if you need to use it over HTTP you will have to stay on v2. So a page served from a bare IP address over http will not decrypt, because the crypto primitive simply is not exposed there.

That is a real narrowing of the use case. Static hosting with a certificate is the target, and Netlify and GitHub pages are the named examples. Anything that has to run inside a closed network over plain HTTP belongs on v2 or on a different tool.

npm install staticrypt, then encrypt one file

The CLI installs from npm:

bash
npm install staticrypt

From there you can run it with npx, or install it globally so the command is available anywhere:

bash
npm install -g staticrypt

The simplest invocation encrypts a file and writes an encrypted/ directory beside it:

bash
staticrypt test.html

Running it with no password argument prompts you, and the project points out that the prompt keeps the password out of your shell history, which is why the -p form is presented as the alternative rather than the default. -d changes the output directory. The password can also arrive through the STATICRYPT_PASSWORD environment variable, and .env files are supported, which is how you wire it into a build script without the password appearing in the command line at all.

For a first check, encrypt one small file, open the result over https, and confirm the prompt appears and the content comes back. Everything else in the CLI is variations on that.

--share puts a hashed password in the fragment and depends on the salt

The share feature generates a link that decrypts itself, which is the cleverest thing here and the easiest to misuse. You pass --share with a URL and get the same URL with a fragment appended:

bash
staticrypt test.html --share https://example.com/encrypted.html
# => https://example.com/encrypted.html#staticrypt_pwd=5bfbf1343c7257cd7be23ecd74bb37fa2c76d041042654f358b6255baeab898f

The fragment holds the hashed password, which is why the file appears to open without a prompt. Two consequences follow. First, that fragment is a bearer token: anyone who receives the link can read the file, so it belongs in a channel you control rather than in a public post. Second, the hash only resolves while the salt stays the same, which is why the project insists you keep .staticrypt.json and warns that re-encrypting will invalidate the link.

--share-remember adds &remember_me so one link can auto-decrypt several pages, and you can append that parameter by hand if you did not ask for it at encryption time.

The salt file appears in your working directory on every run

Every CLI example creates a .staticrypt.json file in the directory you run from, and the project is careful to say three things about it: it is not secret, you do not need to protect it, and you can prevent it being written by passing --config set to false, as a string.

Its only job is the salt, and the salt is what makes --share links and Remember-me survive across pages and across deployments. For a build pipeline the project gives two ways to pin it. Commit the config file, generating one locally with:

bash
staticrypt --salt

or hardcode the salt in the CI command itself:

bash
staticrypt test.html --salt 12345678901234567890123456789012

The second option keeps the salt in your CI script and out of the repository, which is the tidier arrangement if you would rather not commit a file whose only job is to be identical forever. Either way, changing it silently breaks every auto-decrypt link you have already distributed.

Recursive mode encrypts the HTML and copies everything else

Passing several files at once writes them into an encrypted directory:

bash
staticrypt test_A.html test_B.html
staticrypt dir_to_encrypt -r
staticrypt dir_to_encrypt/* -r

The middle form keeps the directory name inside the output path, and the last form drops it, with -r still picking up subdirectories. Non-HTML files are copied as-is from the input directory, and the project says this is deliberate so that you can overwrite the input directory with the encrypted one.

That convenience is also the limitation. Assets referenced by your page, stylesheets, scripts, images, fonts, all arrive in the encrypted directory unchanged and readable, and the same applies to anything fetched at runtime. The password prompt gates the HTML document and nothing else. For a self-contained HTML file that is exactly what you want; for a site with a stylesheet, the CSS is public and the page you are protecting is only the part the browser already renders.

To encrypt a directory in place, combine the wildcard form with the output flag pointed back at the input.

The manifest says 3.5.4 while the newest tag is v3.5.0

Two version numbers disagree. The newest GitHub release is v3.5.0, published on 2024-04-17, after v3.4.0 in February 2024 and v3.3.0 in April 2023. The package.json records version 3.5.4.

So installing from npm can give you a build that no release tag points at, and reading the release notes to find out what changed will not describe what you installed. That is a small thing to know before you pin a version in a lockfile, and it is not unusual in a project whose releases are cut less often than its commits. The last push to the default branch, main, was on 2026-07-06, well after the last tag, so the gap is in both directions.

The runtime floor is explicit, though. The manifest declares:

json
"engines": {
    "node": ">=16.0.0"
},

which is the package's declared Node.js floor.

Two dependencies, and a yargs range spanning eight majors

The whole runtime dependency list is two entries: dotenv at ^16.0.3, which is what reads the .env files the password examples rely on, and yargs for argument parsing. The yargs range is the odd one:

json
"yargs": ">=10.0.3 <=17.7.2"

That accepts every yargs from 10.0.3 to 17.7.2, a span of eight major versions, so two clean installs of the same StatiCrypt version can end up with materially different argument parsing behaviour. If you deploy StatiCrypt in a build pipeline, the lockfile is doing the real work here, not the range.

Everything else about the package is conventional. The bin entry maps the staticrypt command to cli/index.js, the published files are limited to /cli and /lib, the test script is Node's own runner, and husky plus prettier handle commit formatting:

bash
node --test --test-reporter=spec test/*.test.js

Licensing is MIT, which is permissive enough that the encrypted pages you produce carry no obligation back to the author.

Editorial conclusion

Take StatiCrypt when you need to publish one HTML file behind a long password on Netlify or GitHub Pages and you cannot add server logic. Leave it when the page pulls in stylesheets, scripts or images, because recursive mode copies every non-HTML file through untouched, and leave it if the page will be served over plain HTTP, since v3 is built on WebCrypto and the project states that only v2 works outside HTTPS and localhost. Verify four things. Run npm install staticrypt and then npx staticrypt on a file you open over https, because that is the only way to confirm decryption works in the browser you actually care about. Compare the installed version against the tag list, since package.json says 3.5.4 while the newest GitHub release is v3.5.0. Keep .staticrypt.json if you plan to use --share, because re-encrypting under a new salt invalidates every link you already handed out. And read the yargs range before you trust a lockfile, since the manifest accepts anything from 10.0.3 up to 17.7.2.

Frequently asked questions

How do I encrypt my HTML code?

StatiCrypt does it with AES-256 through WebCrypto and decrypts the result in the browser, so the output is a static page with a password prompt that you can upload anywhere without server logic. Install it with npm install staticrypt and run npx staticrypt on your file.

Does StatiCrypt work over plain HTTP?

Not in v3. The project states that v3 uses WebCrypto, which is only available in HTTPS or localhost contexts, and that if you need to use it in HTTP you have to use v2. The migration guide from v2 to v3 is in MIGRATING.md.

What is the .staticrypt.json file for?

It holds the salt and is created in the current directory by the CLI. The project says it is not secret and does not need protecting. Keeping it means the salt stays the same between runs, which is what makes --share links and Remember-me survive a re-encryption. Pass --config set to false to stop it being written.

Does StatiCrypt encrypt CSS, JavaScript and images?

No. When you give it a directory with -r, the HTML files are encrypted and the non-HTML files are copied as-is from the input directory. That is also why the project says you can overwrite the input directory with the encrypted one.

How do I decrypt a StatiCrypt file from the command line?

Add --decrypt, which works on files you encrypted earlier. The -r recursive flag and the -d directory option behave as they do when encrypting, and the output directory defaults to decrypted.

What version does the StatiCrypt package.json declare?

The manifest records version 3.5.4, while the newest GitHub release tag is v3.5.0 from 2024-04-17. The package declares node >=16.0.0 in its engines field and depends only on dotenv and yargs.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. robinmoisson/staticrypt on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/robinmoisson-staticrypt.svg)](https://hysenlabs.com/projects/robinmoisson-staticrypt)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/robinmoisson-staticrypt.svg)](https://hysenlabs.com/projects/robinmoisson-staticrypt)

Was this useful?