pht is a zero-dependency Node.js CLI that publishes password-encrypted, self-contained HTML shares. It encrypts on your machine and uploads only ciphertext to pastehtml.dev. A recipient opens a file share with pht open, or opens an HTML page share in a browser.
Node.js 20 or newer is required.
From Git:
npm i -g github.com/clankercode/phtFrom a clone, symlinked onto PATH:
git clone https://github.com/clankercode/pht.git
cd pht
mkdir -p ~/.local/bin
ln -s "$PWD/pht.mjs" ~/.local/bin/pht
ln -s "$PWD/pht-seal" ~/.local/bin/pht-seal
ln -s "$PWD/pht-open" ~/.local/bin/pht-openpht-seal and pht-open are wrappers around pht seal and pht open.
HTML page shares also need staticrypt 3.5.4:
npm i -g staticrypt@3.5.4The password is read from PHT_PASSWORD, or from STATICRYPT_PASSWORD if that is unset. It is never accepted as an argument and it is never printed.
export PHT_PASSWORD="$(openssl rand -base64 24)"
pht seal notes.txt
pht seal photo.jpg --webp=80
pht seal page.html
pht open https://pastehtml.dev/p/TOKEN/raw ./receivedpht seal <file> [--title T] [--webp[=quality]] [--page] [--keep-update-token] encrypts, publishes, and prints live_url, raw_url, and the SHA-256 of the sealed plaintext. Non-HTML files use file mode. .html and .htm files use page mode, and --page forces page mode for any file. --webp converts an image with cwebp, or with python3 and Pillow when cwebp is unavailable. The default quality is 80.
pht open <live_url|raw_url|token> [outdir] fetches https://pastehtml.dev/p/<token>/raw and decrypts it. It accepts a pastehtml live URL, a www URL, a https://<token>.pastehtml.dev URL, a raw URL, or a bare token. File mode writes the recorded filename after the SHA-256 matches. Page mode writes page.html. An existing output file is left untouched.
pht seal notes.txt --keep-update-token--keep-update-token writes the paste host's update token to .pht-update-token in the current directory, mode 600. The token is not printed.
The sealed HTML must be at most 1,900,000 bytes. A size failure reports the actual size. File shares of large images can be reduced with --webp. Pastes are permanent. This CLI does not update or delete them.
- The password is the secret. File mode derives an AES-256-GCM key with PBKDF2-SHA256, a random 16-byte salt, and 600,000 iterations. Page mode uses staticrypt 3.5.4: PBKDF2 SHA-1 for 1,000 rounds, then SHA-256 for 14,000, then SHA-256 for 585,000, an HMAC-SHA256 check, and AES-256-CBC. A short or guessable password can be attacked offline by anyone who copies the paste.
- Only ciphertext is uploaded. Filename, MIME type, size, and checksum sit inside the encrypted payload in file mode. The host, the network, and anyone who learns the URL can keep that ciphertext forever. Links are unlisted, not private, and pastes cannot be deleted.
- File mode decrypts locally.
pht openchecks the AES-GCM authentication tag and the embedded SHA-256 before it writes a file. A changed page fails closed. The password stays in this process. - Page mode in a browser trusts the page's JavaScript. staticrypt decrypts in the page you loaded. If that HTML is replaced, the replacement script can read the password as it is typed. Prefer
pht openwhen you do not want to type the password into a browser. Check that the URL you open is the URL you were given. - The update token overwrites a paste. Whoever holds it can replace the ciphertext at that URL through the paste host.
phtstores it only when--keep-update-tokenis set, mode600, and never prints it. Do not commit it or send it next to the link. Replacing a page share changes the JavaScript a browser will run. - Decrypted output is untrusted data. Names from the payload are reduced to a single basename. The CLI refuses control characters, path separators that survive that reduction, symlink outputs, and existing files.
- This is not a place for long-term credentials. Use a strong random password, at least 24 bytes, and a normal secret channel for keys you cannot afford to store as public ciphertext.
Exit codes are 0 for success, 1 for usage, network, tool, format, or size errors, 2 for a checksum mismatch, and 3 for a wrong password or failed authentication.
File mode is one HTML page with a pht:v2 comment. The comment holds a JSON header and base64url ciphertext. The header is the version, algorithm, KDF, iteration count, salt, and IV. That header is authenticated as GCM additional data. The plaintext starts with a four-byte metadata length, then JSON (filename, originalFilename, mime, size, sha256), then the original bytes. The visible page says only encrypted payload; open with pht open.
Page mode is a staticrypt 3.5.4 page. pht open decrypts that exact version. Set PHT_STATICRYPT_BIN if staticrypt is not on PATH.
npm testTests use node:test and do not need network access. The staticrypt interoperability test uses an installed 3.5.4 binary or npx, and skips when neither can run. Set PHT_NETWORK_TEST=1 to publish and open one tiny non-secret test paste.
Unlicense OR CC0-1.0. See LICENSE-UNLICENSE and LICENSE-CC0.