Loading repository data…
Loading repository data…
miniex / repository
Rust HTML/CSS/JS obfuscator - library + CLI that renders identically in the browser but resists reading and scraping. oxc-powered AST engine, WASM.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
쓰까(ssukka) is Busan dialect (부산 사투리) for "mix it up" (섞어).
HTML obfuscation library and CLI for Rust. Renders identically in browsers but is hard for humans to read.
@keyframes and their animation / animation-name references are renamed too<div> becomes <DiV> (skipped inside <svg>/<math>, whose names are case-sensitive).foo becomes .\66\6f\6f\xHH / \uXXXX / \u{..} (strict-mode-safe)These change the DOM, output size, runtime cost, or accessibility, so they are off by default:
--honeypots N) - inject invisible trap links, fake form fields, and decoy article-like prose blocks (dense, link-free filler in content-like class names, so an extractor starved of the real body by --structural harvests the decoy instead - data poisoning). Hidden from layout and assistive tech, and removed on load by an injected script, so no-JS bulk crawlers take the bait while JS clients get a clean DOM. Vocabulary: editable ssukka_core/assets/honeypot/*.txt.--structural) - move visible text out of the static markup into a data- attribute, restored client-side by an injected script. The encoding is polymorphic (per-build random attribute name + XOR key + byte order), so no single static decoder recipe works across builds. Resists static scrapers (curl / readability extractors that don't run JS) while rendering identically. Warning: breaks no-JS, SEO, and degrades accessibility.--js-ast, powered by oxc):
--mangle) - scope-aware renaming of local JS bindings (never globals, so cross-script / inline-handler references stay intact).--poison-names) - rename local bindings to plausible-but-misleading words (cursor, vertex, ...) instead of short ones, so an LLM "clean this up" pass anchors on names it keeps rather than re-deriving the originals. Each name is unique and avoids every identifier already in the script, so nothing is shadowed.--js-string-encoding array) - hoist string literals into a per-build shuffled character pool, decoded by (offset-shifted) index at runtime. Uses no atob / String.fromCharCode / TextDecoder, so hook-based deobfuscators have nothing to latch onto (a tool that executes the decoder still recovers the strings). Tune with --reserved-strings <s> (keep listed strings readable) and --string-array-threshold <0..1> (encode only a fraction).--property-keys) - convert object-literal keys to computed string keys ({foo: 1} becomes {["foo"]: 1}), so with the string array the key names are hoisted and encoded too instead of sitting in plain sight.Every AST transform re-parses its own output and is discarded if it would emit invalid JavaScript; on a parse failure the engine falls back to the token-based path. The transforms are semantics-preserving by construction (verified by executing obfuscated output under Node).
ssukka raises the cost of reading and scraping a page; it is not a security boundary. Anything the browser can render, a determined adversary with a headless browser can recover. Use it to deter casual copying and cheap bulk scraping, not to protect secrets.
Modern LLM-based deobfuscators can reverse simple identifier renaming and string encoding, so the strongest configurations layer transforms (string array + mangling + structural + honeypots) and lean on structural/visual approaches rather than renaming alone. Where renaming is used, --poison-names turns it from a no-op (an LLM just re-derives meaningful names) into a trap that anchors the cleanup pass on misleading names. Benchmark the presets with cargo bench, and measure extraction resistance with two differential harnesses: cargo test --test efficacy (naive/visibility-blind tier) and cargo test --test extraction (readability tier via the dom_smoothie dev-dependency). --structural drops recall to ~0 against both, cosmetic transforms are friction only (~1.0), and --structural + --honeypots poisons the naive tier - it harvests decoy filler instead of the body, while the readability tier drops the hidden decoys.
ssukka performs no network I/O at runtime - verified with strace (zero socket/connect syscalls). All dependencies (lol_html, lightningcss, oxc, rand) are pure Rust and compile from source. External resource inlining (--inline-local-resources) reads local files only and never fetches over the network.
A Cargo workspace; the engine does no I/O, and each front-end is a thin crate over it:
ssukka_core engine (lib): offline, forbid(unsafe). all transforms live here
ssukka facade lib (re-exports ssukka_core) + the CLI binary
ssukka-proxy serve-time reverse proxy (binary, skeleton)
ssukka-wasm wasm-bindgen bindings (cdylib, built for wasm32 only)
The engine is a streaming pipeline built on lol_html:
Input HTML
-> Pass 0 (optional): inline local CSS/JS
-> Pass 1: analyze -> SymbolMap
-> Pass 2: transform HTML / CSS / JS
-> Output HTML
cargo install ssukka # CLI
cargo install ssukka-proxy # serve-time reverse proxy
Or build from source:
git clone https://github.com/miniex/ssukka.git
cd ssukka
cargo build --release
# Basic
ssukka -i input.html -o output.html
# stdin/stdout
cat input.html | ssukka > output.html
# With options
ssukka -i input.html -o output.html --seed 42 --no-rename --no-minify-css
| Flag | Description |
|---|---|
-i, --input <FILE> | Input HTML file (default: stdin) |
-o, --output <FILE> | Output file (default: stdout) |
--seed <N> | Seed for deterministic output |
--no-rename | Disable class/ID renaming |
--no-minify-css | Disable CSS minification |
--no-minify-js | Disable JS minification |
--no-encode-entities | Disable entity encoding |
--no-shuffle-attrs | Disable attribute reordering |
--no-randomize-case | Disable tag case randomization |
--comment-split | Split long words with empty comments (anti-regex-scraper) |
--js-string-encoding <none|escapes|array> | JS string strategy (default: escapes) |
--honeypots <N> | Inject N invisible decoy nodes (scraper traps) |
| ` |
--dead-code) - opaque-predicate-guarded junk that never executes; predicate and body shapes vary per build so they aren't a fixed signature.--cff) - rewrite straight-line statement sequences (the top level and function/arrow block bodies) into a while/switch state machine with shuffled cases, a random state variable, and label-based transitions (var bindings hoisted). Correct-by-construction: a block is flattened only when every statement is an expression statement or a simple var declaration - no control flow, let/const, or declarations to preserve - so semantics hold; anything else is left intact.--vm) - a stronger alternative to --cff over the same blocks: lift the statements into an op table of arrow-function thunks plus an XOR-encoded bytecode array, run by a generic dispatch loop, so the execution order lives in data (the bytecode) instead of JS control flow. Same correct-by-construction subset; arrow thunks keep this/arguments. Adds a small per-statement closure cost; when both are set, --vm wins.--mba) - replace integer literals with equivalent bitwise/arithmetic expressions (5 becomes a (3^6)-style form), so a static or LLM cleanup pass has to do the arithmetic to read the constant. Exact under JS int32 semantics.--opaque-predicates) - wrap top-level expression statements in always-true guards (if(<opaque>){ stmt }) built from bitwise identities that hold for every input, so real code sits behind a condition the analyzer must evaluate. Skips declarations and directives.--domain-lock <hosts>, --lock-expiry <unix-secs>) - inject a guard that crashes the script (unbounded recursion) when run off an allowed host (or its subdomains) or past an expiry, so the obfuscated code can't be lifted to another site or kept working forever. A no-op on the allowed domain before expiry; a deterrent, not DRM (an attacker who strips the guard defeats it).--self-defending) - inject a check that disables console if the script was beautified or tampered with (deters casual beautify-and-run; a deobfuscator that strips the guard defeats it).--polymorphic) - vary which transforms run (and how) on every invocation, so identical input yields structurally different output each time (signature/cache evasion). With --js-ast it also deepens the JS chain per build, stacking a random mix of the (semantics-preserving) AST transforms - and chaining is where LLM cleanup degrades. Ignored when --seed is set.--watermark <N>) - embed a build/recipient id as invisible zero-width characters in the body text, so a scraped or leaked copy can be traced. The id is scattered as redundant copies across multiple text nodes and recovered by majority vote, so it survives copy-paste and partial deletion (truncation, copying one section). Renders invisibly; may affect screen readers. Reversed only by a Unicode normalizer that strips the zero-width class.--ai-opt-out) - inject standards-aligned <meta> opt-out tags into <head>: legacy robots: noai, W3C TDMRep (tdm-reservation, the EU CDSM Art.4 / AI Act rights-reservation lane), and a best-effort IETF AIPREF Content-Usage: train-ai=n (http-equiv). The canonical AIPREF/TDMRep transports - an HTTP Content-Usage response header, a robots.txt rule, and /.well-known/tdmrep.json - are exposed as library helpers (ssukka::ai_opt_out::{meta_block, robots_txt, content_usage_header, well_known_tdmrep_json}) for edge/server deployment. Pass --tdm-policy <url> to also link a machine-readable usage policy via a tdm-policy meta (implies --ai-opt-out). A legally recognized signal that bulk crawlers widely ignore on its own; pair it with the in-content deterrents above.--comment-split) - insert empty comments inside long runs (about one per 6 chars, so long words and space-free scripts like CJK are fragmented throughout) so naive regex/substring scrapers see broken text, while browsers, screen readers, find-in-page, and content extractors read it intact. Flow content only (never <title> or other RCDATA).ssukka-wasm crate (wasm/).