COMMAND LINE | ONE FILE, NO DEPENDENCIES

One file, one line,
and the bench is in your terminal.

hh is the tools API with the ceremony removed: `hh lock taxes.pdf` instead of a curl invocation with three flags you have to look up. It is a single POSIX shell script — no bash, no jq, no python, nothing to keep updated — and it is generated from the same contract as the API itself, so it can never offer a tool the server does not have or miss one it does.

curl -fsSL https://hushhh.cc/install.sh | sh

It downloads one file to ~/.local/bin/hh, checks it against a checksum this page was built with, and makes it executable. No sudo, nothing else touched, and it tells you if that directory is not on your PATH instead of leaving you with a command not found.

Read it first. It is served as plain text for exactly that reason, and so is the installer: piping a URL into a shell should be a decision, not a leap. install.sh · hh

Or skip the installer entirely — it is one file and it is not a secret:

curl -fsSL https://hushhh.cc/hh -o ~/.local/bin/hh && chmod +x ~/.local/bin/hh
[ The commands ]
hh pdf-unlock statement.pdfRemove PDF passwords · page · api
hh pdf-merge cover.pdf body.pdf appendix.pdfJoin several into one · page · api
hh pdf-split report.pdf --mode keep --range 1-3,8Extract or remove pages · page · api
hh pdf-rotate scan.pdf --angle 90 --range 2-endTurn pages the right way up · page · api
hh webp photo.png --quality 80WebP, JPG, PNG, AVIF · page · api
hh diff old.conf new.conf --format unifiedEvery difference, marked · page · api
hh lock taxes.pdfLock and unlock with a password · page · api
hh pass --mode words --words 5Strong passwords and passphrases · page · api
hh hash ubuntu.iso --expected 3a1f…MD5, SHA-1, SHA-256 · page · api

hh b6bf4943 · `hh --version` prints a fingerprint of the contract it was built from. When this page says a different one, the client is older than the API.

[ Using it ]

Every command takes a file and writes the result next to it, named after what happened to it: report.pdf becomes report-unlocked.pdf. It never overwrites without --force. `hh <command> --help` prints that command's flags with what each one means, and `-o -` sends the result to standard output so it can go into a pipe.

hh pdf-split report.pdf --mode keep --range 1-3\nhh file-hash ubuntu.iso -o -\nhh pdf-unlock --help
[ Passwords ]

A password is never put in the URL and never passed as an argument to curl: it goes in a header, written to a temporary config file that is deleted when the command ends. A query string is recorded by proxies and by your own shell history, and an argument is visible in `ps` to everyone else on the machine. If a command needs a password and you did not give one, it asks at the terminal with the echo off — --password and --password-stdin are there for scripts, which have no terminal to ask at.

[ Pointing it somewhere else ]

Set HUSHHH_HOST to run against another instance: HUSHHH_HOST=http://localhost:3000 hh hash file.iso. That is also how the test suite drives it, against a server it started itself.

[ Uninstalling ]

It is one file. Delete it, or run the installer with --uninstall.

curl -fsSL https://hushhh.cc/install.sh | sh -s -- --uninstall
[ What gets sent ]

The file. That is the difference between this and the tool pages, where the work happens in your browser and nothing leaves the machine. Here the work happens on the server, so the bytes travel — over HTTPS, held in memory for the length of the request, never written to disk, to a log or to a database. If that trade is not one you want to make for a particular file, the page for that tool does the same job without it.

The same one thing is counted as on the pages: which tool ran, whether it worked, and an IP address with its last part zeroed. Not the file, not its name, not its size, not the result.

[ FAQ ]

Frequently Asked Questions

Q: Why a shell script and not a real package?

Because the machine you want it on already has sh and curl, and probably does not have node, python or brew set up the way a package would need. One file also means the whole thing is readable in a minute before you run it, which matters more for something installed with a pipe into a shell than any amount of packaging would.

Q: Is `curl | sh` not a bad idea?

It is a bad idea when you cannot see what you are running. Both files are served as plain text so you can read them first, the installer verifies the client against a checksum built into it, it asks for no privileges, and everything it writes is one file in your own home directory. If you would rather not pipe it, download hh yourself and chmod +x it — that is the whole install.

Q: Does it work offline?

No, and it cannot: the work happens on the server. The tool pages do work offline, because there the work happens in your browser. Two different answers because they are two different things.

Q: How do I update it?

Run the installer again. `hh --version` prints the version it was built from, which is a fingerprint of the API contract — if it does not match the one on this page, there is something new.

Q: Windows?

Under WSL or Git Bash it works, since both have sh and curl. There is no native PowerShell client; the API is plain HTTP and Invoke-WebRequest does the same job in one line.