mutant: a programming language whose bytecode is encrypted by default
Mutant is an open-source, garbage-collected programming language that tries to enable security by default.
At a glance
- What is it?
- mutant is a garbage-collected language for security research and digital forensics whose programs compile to encrypted bytecode and run inside a VM, with a 694-builtin standard library written in pure Go and a password-gated toolchain. The encryption scheme has already been broken publicly at DEFCON, and the project publishes that write-up itself.
- Who is it for?
- mutant fits a security researcher who wants to write analysis tooling in a language whose artefacts are hard to read off disk, and who values a large built-in library for forensics over the usual ecosystem. It is the wrong choice if you need a language with broad third-party support, and the wrong choice if you treat bytecode encryption as a security boundary, since the project publishes a DEFCON write-up on breaking it.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The premise is that your artefact on disk should not be readable
mutant is described in the repository as an open source programming language for security research and digital forensics, and the README frames it as an open-source, garbage-collected programming language that tries to enable security by default. The specific mechanism is that programs compile to encrypted bytecode, run in the Mutant VM, and can be packed into standalone binaries for other platforms. The point of encrypting the bytecode is that a dropped implant or a malware sample is written as an opaque blob rather than as readable source or readable instructions, which raises the cost of static analysis. Everything else in the project follows from that goal. The audience is a researcher who writes tooling and wants the artefact to survive being copied onto a host without immediately giving away its behaviour, and the toolchain is designed so that producing a runnable artefact is the path of least resistance rather than a special export step.
Building it needs the scripts, because go install leaves you short of assets
The install section gives two ways to get a working binary, and the difference between them matters more than it looks. You can download a binary from the repository's releases, or build from source with Go:
git clone https://github.com/aoiflux/mutant
cd mutant
./scripts/build.sh --host-only # Linux, macOS, WSLOn Windows the equivalent is:
./scripts/build.ps1 -HostOnly # WindowsThe README then explains the catch with the obvious alternative. A plain `go install` also works, but its binary cannot run `mutant release`, since that command needs the runtime assets that the build scripts embed. In other words, the release packaging feature is not available from a stock install, and the way to get it is to use the project's own scripts. The module is declared as `module mutant` with Go 1.26.9, and the dependency list is long and specialised, with pure Go ports of filesystem format libraries such as libewf, libext, libfat, libhfs, libntfs, libtable, libvhdi, libxfat and libxfs, plus gopacket for network work, open-policy-agent for policy, saferwall/pe for executable parsing, gopsutil for process information and gopher-lua for Lua scripting inside the runtime.
The password model is the most interesting design decision here
Compiling and running both ask for a password, and the options table shows a deliberate refusal to accept secrets casually. With nothing specified it prompts interactively with terminal echo disabled. `--password-file <path>` is for unattended runs and is refused if the file is readable by other users. `--password-stdin` is for pipelines. `--dev` is for local development only and uses a built-in key that is not secret. `--password-insecure <v>` works but means other local users can read it from `ps`. A bare `--password` is refused since 2.6.0, and there is deliberately no environment-variable form, with the reasoning pointed at in docs/CONFIGURATION_POLICY.md. The reasoning is not hard to reconstruct: an argument on the command line is visible in the process table, and an environment variable is visible to anyone who can read the process environment of the same user, while a prompt or a protected file is not. The refusal to add an environment variable form shows the project treating its own convenience as a weaker priority than that.
Five commands cover the first session
The quick start is a short list, and it is enough to get through a first program:
mutant # start the REPL
mutant hello.mut # compile to encrypted bytecode: hello.mu
mutant hello.mu # run the bytecode
mutant test ./lib # run every *_test.mut file
mutant help # every command, and `mutant help <command>`The naming convention is worth noticing because it makes the pipeline obvious. A source file has the `.mut` extension, and compiling it produces a `.mu` file, which is the encrypted bytecode. Running the `.mu` runs the bytecode, and `mutant test ./lib` runs every `_test.mut` file under a directory. The REPL is the default with no arguments. The `mutant help` command lists every command, with `mutant help <command>` giving the detail for one, which is a better fallback than hunting through the documentation when you are still learning the vocabulary. The language is also backed by real tooling: a formatter, a test runner, a debugger, and the `mlsp` language server behind the VS Code extension.
Standalone binaries have a fixed target list and no surprises
A program can be packed into a standalone binary for another platform:
mutant release hello.mut --os windows --arch amd64The targets are enumerated, and any pair outside the list is refused. The supported set is darwin/amd64, linux/amd64, linux/386, linux/arm64, linux/arm, windows/amd64 and windows/386, with `x86` accepted as an alias for `386`. That closed list is a deliberate choice rather than a limitation of the compiler: the release process has to embed runtime assets for each target, and a fixed set means those assets are known to exist. There is also a runtime behaviour that matters for analysis work. A program runs in secure mode by default, so a hit from the runtime's security probes ends the run, and `--compat` warns and continues instead. The distinction is between a program that is stopped when the runtime notices something wrong and one that keeps going, which matters when you are running a program against a hostile or unusual host.
The standard library is the reason to look at it, with 694 builtins
The README advertises a 694-builtin standard library in pure Go with no cgo, covering data handling, networking, cryptography, detection, graphs and a deep forensic suite. The dependency list backs that up, since a pure Go standard library only stays pure if the format libraries underneath it are also pure Go, and the aoiflux ports of libewf, libext, libfat, libhfs, libntfs, libvhdi, libxfat and libxfs are exactly that. The example directories give a clearer picture of the scope than the marketing number does: automation, basics, binary, bytes, cache, collections, concurrency, crypto, data, detection, email, filesystem, forensics, graph, http, lua, macros, memory, modules, network, policy, project, registry and security. For a forensic workflow, having registry triage, memory inspection, EWF evidence container parsing and Lua scripting in the language you are writing in, rather than as a set of Python side-cars, is the practical argument.
The project's own materials say the encryption has been broken
This is the part that changes how you should read the rest of the README, and the project is upfront about it. The Featured in list includes a DEFCON AppSec Village first place winning entry, which the README links as breaking the mutant language's encryption. A separate link points at a Hackaday piece on this week in security covering tunnelcrack, mutant, and not Discord, and there is a Nullcon Goa 2022 talk on pushing security left by mutating byte code. So the encryption of the bytecode is a defence that has been publicly attacked and won against, and the project links the attack from its own README rather than hiding it. The README also points to docs/advisories for the security advisories and to SECURITY.md for reporting a vulnerability, and the release notes in CHANGELOG.md describe what changed in each version. Current release is 2.6.1, with 2.6.0 released the same day and 2.5.0 in the month before, so the project is moving quickly and shipping security-relevant changes often.
Where mutant is the wrong tool
Three limits are concrete. The first is that encrypted bytecode is obfuscation rather than a security boundary, and the project's own link to a DEFCON win over it says so. If your threat model includes an adversary who can run your binary or read your process memory, the encryption does not hold, and designing around it would be a mistake. The second is the ecosystem. This is a young language with no third-party package ecosystem to speak of, and while the built-in library is unusually broad, anything outside it is work you write yourself, so the usual argument for picking a mainstream language still holds for most projects. The third is the release cadence and the licence. The version is at 2.6.1 with several releases in quick succession, so the API is moving, and the licence is GNU AGPL v3.0 only, which covers the language implementation and the language server in the repository. AGPL is a serious constraint for anyone intending to embed the tooling in a product that is offered as a service.
The common alternative is to write the tool in Go or Rust and protect the artefact with a separate packer or obfuscator, and the difference in approach is where the protection lives. With mutant, the protection is intrinsic: the compiler emits encrypted bytecode, the VM decrypts it at load time, and there is no separate stage to configure. The cost is that you adopt the whole language, its VM, its 694 builtins and its toolchain, and you inherit its AGPL terms and its release cadence. With the packer approach, you keep a mainstream language with its libraries, its performance and its community, and the artefact protection is a step at the end of your build, which also means it can be swapped without touching the code. The other alternative is not to protect the artefact at all, which is the right answer when the threat is a curious user rather than an analyst, and it avoids the whole question.
Editorial conclusion
mutant fits a security researcher who wants to write analysis tooling in a language whose artefacts are hard to read off disk, and who values a large built-in library for forensics over the usual ecosystem. It is the wrong choice if you need a language with broad third-party support, and the wrong choice if you treat bytecode encryption as a security boundary, since the project publishes a DEFCON write-up on breaking it. Before you adopt it, read docs/COMPARISON.md and docs/CONFIGURATION_POLICY.md, and note that a plain `go install` produces a binary that cannot run `mutant release` because the runtime assets are missing.
Frequently asked questions
How do I build and install mutant?
Clone the repository and run `./scripts/build.sh --host-only` on Linux, macOS or WSL, or `./scripts/build.ps1 -HostOnly` on Windows. A plain `go install` works too, but that binary cannot run `mutant release`, because the runtime assets are embedded by the build scripts.
How do I pass a password to mutant for unattended runs?
Use `--password-file <path>` for unattended runs, which is refused if the file is readable by other users, or `--password-stdin` for pipelines. A bare `--password` is refused since 2.6.0 and there is deliberately no environment-variable form.
What file does mutant produce when it compiles a program?
A `.mut` source file compiles to encrypted bytecode in a `.mu` file. `mutant hello.mut` produces hello.mu and `mutant hello.mu` runs the bytecode.
Which platforms can mutant release build standalone binaries for?
darwin/amd64, linux/amd64, linux/386, linux/arm64, linux/arm, windows/amd64 and windows/386, with `x86` accepted as an alias for `386`. Any other os and architecture pair is refused.
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/aoiflux-mutant)Was this useful?