humanify: unminifying JavaScript with an LLM that only picks names
Deobfuscate Javascript code using ChatGPT
At a glance
- What is it?
- humanify is a Rust binary that renames minified JavaScript identifiers using OpenAI, Gemini, Anthropic, OpenRouter or a local Ollama model, while oxc keeps the syntax tree intact. It renames one file per run, and it costs one LLM call per identifier.
- Who is it for?
- Adopt humanify if you already have a provider key or a local Ollama model and you want readable names in a single minified file without changing its structure. Do not adopt it as a full deobfuscator: it does not unbundle webpack output, so the README tells you to pipe through webcrack first, and it does not remove string-array or control-flow obfuscation.
- 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 64 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem humanify targets: minified names, not minified structure
Minifiers and obfuscators replace every local name with a letter. The control flow survives, the semantics survive, and the code becomes unreadable anyway. humanify attacks exactly that layer. The README describes the tool as using large language models to unminify and rename minified JavaScript, and it is explicit about the division of labour: the LLM only suggests new identifier names, while oxc does the work at the AST level so the rewritten code stays structurally identical to the input.
That boundary is the whole design. A model that rewrites source text directly can quietly drop a branch, reorder a side effect or change a comparison. humanify never lets it touch the tree. The model sees a slice of surrounding code and proposes a name; the parser and code generator handle everything else. The README's example is a `splitstring.min.js` whose parameters are `e` and `t`; after `humanify openai splitstring.min.js -o splitstring.js` they read `inputString` and `chunkSize`, and the loop body is otherwise the same code.
The audience is narrow and worth stating plainly: someone reading a single bundled or minified script who wants it to look like something a person wrote. It is not a malware-analysis framework, not a source-map recovery tool, and not a bundler.
How the renaming pipeline works, from stdin to rewritten AST
The data flow is a Unix filter. Input is a file path or `-` for stdin; output goes to stdout or to a file named by `-o`. In between, the file is parsed with oxc, and the tool walks the identifiers. For each one it collects a window of surrounding code, 500 characters by default, and sends that context to the selected provider with a request for a new name. The returned name is applied to the AST, and the code generator prints the result.
Because the naming step is per identifier, the cost model follows the identifier count, not the file size. The README states this directly: humanify makes one LLM call per identifier, and for ChatGPT-class APIs the cost roughly scales with the number of identifiers and the surrounding context window. A medium minified file of about 500 identifiers typically lands between $0.10 and $1.00 with OpenAI's small models. Gemini's free tier, Ollama and OpenRouter free models are listed as free options, with the caveat that free OpenRouter models are heavily rate limited.
The provider layer is a preset system. `humanify <openai|gemini|anthropic|ollama|openrouter|requesty> [FLAGS] <INPUT>` selects defaults for model, API key environment variable and base URL. `-m` overrides the model, `-k` overrides the key, `--base-url` overrides the endpoint, and `--context-size` changes how much surrounding code each call sees. `--json-mode` pins the strategy used to get structured output out of the model, with `ladder` as the default and alternatives including `openai-json-schema`, `anthropic-native`, `forced-tool-call`, `tool-call-and-prompt` and `prompt`. The Anthropic preset, per the README, uses Anthropic's native structured-outputs API when available and falls back to forced tool calls if the account lacks the beta.
The dependency list in `Cargo.toml` matches that story: a family of `oxc_*` crates at 0.126.0 for parsing, semantics and codegen, `reqwest` with rustls for HTTP, `tokio` for the async runtime, `clap` for the CLI. There is no cache or state directory, so a second run over the same file pays the same calls again.
Installing humanify and running it on one minified file
The README calls a pre-built binary from the latest release the preferred installation path. On macOS Apple Silicon the command below downloads the tarball, extracts it and moves the binary onto the path. The other platforms follow the same pattern with different target triples: `humanify-x86_64-apple-darwin`, `humanify-x86_64-unknown-linux-gnu`, `humanify-aarch64-unknown-linux-gnu`, and on Windows a `humanify-x86_64-pc-windows-msvc.zip` from the releases page.
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-aarch64-apple-darwin.tar.gz | tar xz
sudo mv humanify /usr/local/bin/If you would rather build it, the README gives a single Cargo command. This pulls the repository and compiles the binary, which is why the project needs no Node, npm or Python at runtime.
cargo install --git https://github.com/jehna/humanifyFor the first real run, export a key and point the tool at a minified file. The README's OpenAI section uses `OPENAI_API_KEY` and writes to `readable.js`; the default model is `gpt-5-mini` and `-m` overrides it.
export OPENAI_API_KEY=your-token
humanify openai obfuscated.js -o readable.jsIf you want to see what is happening, add the two diagnostic flags the README documents: `-v` prints the resolved configuration and identifier-level rename steps to stderr, and `--progress` shows a progress bar there. Neither changes stdout, so the rewritten file stays clean. Expect one model call per identifier, which on a large bundle is a long wait and a real bill.
To try it without paying, pull a local model and use the Ollama preset. The README recommends `qwen3.5:4b` and notes that local mode is free and private but slower and less accurate than the hosted providers.
ollama pull qwen3.5:4b
humanify ollama obfuscated.js -o readable.jsWhere humanify stops: webpack bundles, cost, and non-renaming obfuscation
The sharpest limitation is scope. The README says humanify does one job: rename identifiers in one JavaScript file in, one out. A webpack bundle is not one file in that sense. The documented answer is to run a separate deobfuscator first and pipe the result in, and the README shows exactly that with webcrack.
npx webcrack < bundle.min.js | humanify openai - -o bundle.jsThat pipe means humanify is the second stage of a two-tool workflow, not a standalone answer to an obfuscated bundle. If the code has been through string-array encoding or control-flow flattening, renaming identifiers does nothing for the parts you cannot read; the README does not claim otherwise.
Cost is the second constraint, and it is structural rather than incidental. One call per identifier with a 500-character context window means a large file is both slow and expensive, and `--context-size` trades accuracy against spend in both directions. Running the same file twice costs the same twice, because nothing is cached.
Quality is the third. Names come from a model that sees a window of code, so it can produce a plausible but wrong name, and nothing in the pipeline checks the suggestion against behaviour. The README's own framing is that local mode is less accurate, and the OpenRouter note warns that free models are heavily rate limited. On a file where a wrong name would mislead a security review, that matters more than the readability gain.
humanify versus running webcrack on its own
The obvious alternative is webcrack by itself. It is the tool the README recommends for unbundling webpack output, and it works without an API key, without a network round trip per name and without a per-identifier bill. If your goal is to split a bundle into modules and recover structure, webcrack is the stage that does it, and humanify adds nothing there.
The difference in approach is what each one treats as the problem. webcrack reverses bundler transformations by pattern: it recognises webpack's runtime shapes and undoes them deterministically. humanify does not reverse anything. It asks a model to guess names from context and then rebuilds the file through oxc. Deterministic output is reproducible and auditable; model output is neither, but it produces names like `splitString` and `chunkSize` where a deterministic pass would leave `a` and `e` in place.
In practice the two compose, which is why the README shows the pipe. Use webcrack to get a readable module graph, then run humanify over the pieces where identifier names are the last obstacle. Choosing between them is a question of whether your remaining problem is structure or names.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-07-29, which is recent enough that the project is being worked on. The release history backs that up: v3.1.1 on 2026-07-29, v3.1.0 the same day, and v3.0.1 on 2026-05-27. The v3 line is a rewrite, and the README lists what changed: a single static Rust binary with no Node, npm or Python; Unix-style stdin and stdout with `-o`; and new providers for Ollama, Anthropic and OpenRouter.
The upgrade cost sits in that rewrite. The README carries a migration note for pre-v3 users: there is no `humanify download` anymore, and you are told to use a local inference provider like Ollama instead. Anyone with scripts built around v2's command surface has to rework them against the v3 subcommand form. The provider presets also move with upstream APIs: the Anthropic preset's fallback from native structured outputs to forced tool calls exists because account-level beta access varies, and default model names such as `gpt-5-mini`, `gemini-3.1-flash-lite` and `claude-sonnet-4-6` are pinned in the tool, so a provider deprecating one is a reason to watch releases.
The licence is MIT, which permits commercial use and modification provided the copyright notice and permission notice are kept. That is a summary of the identifier, not legal advice; if you redistribute a modified binary, read the `LICENSE` file in the repository. The practical licence question is elsewhere: sending proprietary code to a hosted provider is governed by that provider's terms, and the README's Ollama path exists precisely because local inference keeps the code on your machine.
Editorial conclusion
Adopt humanify if you already have a provider key or a local Ollama model and you want readable names in a single minified file without changing its structure. Do not adopt it as a full deobfuscator: it does not unbundle webpack output, so the README tells you to pipe through webcrack first, and it does not remove string-array or control-flow obfuscation. Before committing to a provider, run it on one file with -v and --progress so you can see the resolved configuration and the per-identifier steps, and measure the call count against the token-usage note in the README, which puts a medium file of roughly 500 identifiers at $0.10 to $1.00 on OpenAI's small models.
Frequently asked questions
What does the name humanify mean in this project?
The name is the project's own coinage for making minified JavaScript readable again, and the README describes the tool as un-minifying JavaScript code using LLMs. It has no connection to the unrelated products and services that also use the word humanify.
How do I install humanify?
The README calls downloading a pre-built binary from the latest release the preferred way, with per-platform tarballs such as humanify-aarch64-apple-darwin.tar.gz and humanify-x86_64-unknown-linux-gnu.tar.gz, or a zip for Windows. You can also build it with cargo install --git https://github.com/jehna/humanify.
Which LLM providers can humanify use?
The usage line lists openai, gemini, anthropic, ollama, openrouter and requesty as presets. Each preset has a default model, such as gpt-5-mini for OpenAI and qwen3.5:4b for Ollama, and -m overrides it.
Does humanify work on a webpack bundle directly?
No. The README states that humanify does one job, renaming identifiers in one JavaScript file in and one out, and tells you to unbundle first by piping through webcrack, for example npx webcrack < bundle.min.js | humanify openai - -o bundle.js.
Can I run humanify without paying for API calls?
Yes. The README describes Ollama mode as free and private but slower and less accurate than the hosted providers, and notes that Gemini's free tier is enough for most files. Free OpenRouter models are also listed, with a warning that they are heavily rate limited.
Official sources
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.
[](https://hysenlabs.com/projects/jehna-humanify)