# Bunster: compiling shell scripts into standalone Go binaries

> Bunster transpiles bash scripts into Go source and builds them into static executables, so a script can ship without a shell. It is early-stage software with a documented subset of supported features, so the question is which scripts are ready for it.

**yassinebenaid/bunster** — Compile shell scripts to static binaries.

- Repository: https://github.com/yassinebenaid/bunster
- Website: https://bunster.netlify.app
- Stars: 2,678 · Forks: 70
- Language: Go
- License: BSD-3-Clause
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/yassinebenaid-bunster

## What Bunster solves, and who it is actually for

A bash script is a dependency on a shell, on a version of that shell, and on whatever utilities it calls. Shipping one to a machine you do not control means either trusting that /bin/bash exists and behaves the same way, or bundling an interpreter. Bunster takes a third route: it reads a shell script, transpiles it into Go source, and hands that source to the Go toolchain to produce an executable. The README is explicit that this is not wrapping, contrasting it with shc, which the README describes as wrapping a script inside a binary. Bunster's output is meant to be a standalone program that does not rely on external shells on the system.

The people this is aimed at are the ones who already write deployment scripts, installers, or small automation in bash and want to distribute them as artifacts. The README also lists features that go past plain bash compatibility: a module system for splitting code across files, a builtin package manager for publishing and consuming modules, native .env file loading, embedding of static assets at compile time, and builtin flag parsing where you declare the flags you expect. Those features are the reason someone might pick Bunster over simply rewriting the script in Go. The compatibility promise is deliberately narrow: the project aims to be compatible with bash as a starting move, and additional shells are not planned until after v1.

## The pipeline: lexer, parser, IR, generator, Go toolchain

The repository layout shows the compiler split into stages, and each stage is a top-level directory: lexer/, parser/, ast/, ir/, analyser/, generator/, builder/, runtime/, stubs/, and cmd/. That structure matches the README's description of the flow. A script goes in, the lexer and parser produce an AST, the analyser inspects it, an intermediate representation is built, and the generator emits Go code. The builder then drives the Go toolchain over that generated code.

The runtime/ and stubs/ directories are the part worth noting. Because the generated program has no shell underneath it, anything a shell would normally provide at runtime has to be provided by compiled-in Go code. That is why embedding files and directories at compile time is a first-class feature rather than an add-on: the binary has to carry what it needs.

The analyser is described in the README as performing static analysis and reporting potential bugs at compile time, but it is marked wip. Treat that as a direction rather than a guarantee. The rest of the pipeline is visible in the repository, but the README does not document error message formats, diagnostic codes, or how the analyser reports findings, so there is nothing concrete to rely on there yet.

## Installing Bunster and compiling a first script

The README gives a shell installer that places the binary and adds it to your PATH. On Linux it lands at ~/.local/bin/bunster; on macOS it goes to ~/bin/bunster.

```bash
curl -f https://bunster.netlify.app/install.sh | bash
```

For a system-wide install accessible to all users, the README passes GLOBAL=1 to the same script:

```bash
curl -f https://bunster.netlify.app/install.sh | GLOBAL=1 bash
```

Homebrew is also listed as a supported route:

```sh
brew install bunster
```

The README points to the documentation site for other installation methods. Once installed, the Makefile in the repository shows the command shape the project uses for its own test script. The build target produces ./bin/bunster, and the compile target runs it against script.test.bash with an output path:

```bash
./bin/bunster build script.test.bash -o ./bin/script
```

If you want to inspect the intermediate Go rather than the final binary, the generate target writes the generated output to a directory instead:

```bash
./bin/bunster generate script.test.bash -o ./bunster-build
```

There is also an ast target, which dumps the parsed tree for a script. Running that before a build is the cheapest way to find out whether your script parses at all:

```bash
./bin/bunster ast script.test.bash
```

Those three invocations come from the repository's Makefile, which is the project's own usage of its CLI rather than documentation written for newcomers. The README does not spell out the full flag set for build, generate or ast beyond the -o output flag shown here.

## The compatibility warning is the real constraint

The README carries a warning block stating that the project is in its early stages and that only a subset of features is supported so far, linking to a page about simple commands. That single sentence should shape how you evaluate Bunster. A shell is a large surface area, and a transpiler has to implement each construct it accepts. Anything not implemented is not a degraded version of the feature; it is a compile-time failure or, worse, a behaviour difference you only notice at runtime.

The versioning section sets expectations in the same direction. Bunster follows SemVer, and the README states that full bash compatibility is targeted for the stable v1.0.0 release, with the caveat that there might be some caveats even then. Additional shells are not planned before v1. The most recent release in the repository's release list is v0.14.0, which places the project well before that stability milestone.

The practical failure mode is a script that compiles cleanly but relies on a subtlety the transpiler handles differently. Static analysis is listed as wip, so it is not a safety net you can count on. The honest way to use Bunster today is on scripts small enough that you can read the generated Go with bunster generate and check it yourself. For a thousand-line deployment script full of traps, parameter expansion edge cases and subshell tricks, this is the wrong tool, and the README's own warning says so.

## How Bunster differs from shc and from rewriting in Go

The README names shc directly as the comparison point. The difference it draws is between wrapping and compiling: shc produces a binary that still carries the script and still needs a shell to run it, while Bunster transpiles to Go and compiles that. The consequence is that Bunster's output can run on a machine with no bash installed, which is the property that matters when you are shipping into a minimal container or onto a host you do not control. The cost is that Bunster has to implement the language, which is exactly why the supported-feature subset is narrow today.

The other alternative is rewriting the script in Go by hand. That gives you the same static binary without a transpiler in the middle, and without waiting on Bunster's bash coverage. What you lose is the script itself: the shell syntax you already know, plus the features Bunster layers on top, such as the module system, the package manager, .env loading, asset embedding, and declared flag parsing. If your script is short, a manual rewrite is the lower-risk path. If it is long and you want to keep writing shell, Bunster's proposition is that you keep the syntax and get the binary. Whether that trade pays off depends entirely on how much of your script falls inside the supported subset.

## Maintenance, releases and what the BSD-3-Clause licence means here

The repository is not archived, and the last push was on 2026-04-28. The release history shows v0.12.1 in April 2025, v0.13.0 later that month, and v0.14.0 in August 2025, so the project has been cutting minor releases at a moderate pace. The README describes the project as developed and maintained by the public community and invites criticism of features, implementation, code style and documentation. There is a CODE_OF_CONDUCT.md and a SECURITY.md, and security reports go to a named maintainer by email rather than through a public issue.

Upgrade cost is tied to the versioning policy. The README states that minor releases v0.x.0 add features, code optimization and build improvements, while patch releases v0.N.x carry bug fixes and minor enhancements. Until v1.0.0, a minor bump is the one to read release notes for. There is no stated deprecation policy for the CLI or for the module format, and the README does not document rollback, so pinning a version is the only lever the material actually describes.

Licensing is BSD-3-Clause, which is permissive and imposes the usual obligation to retain the copyright notice and licence text in redistributions. The generated Go code and the compiled binary are a separate question: the README does not state what licence, if any, attaches to compiler output, and that is the thing to confirm with the maintainers before you ship a binary built from a proprietary script. This is not legal advice; read LICENSE and ask.

## Conclusion

Adopt Bunster when you have a bash script you already trust and you need to ship it as a single static executable with no shell dependency, and when you can accept that only a documented subset of bash is supported today. Do not adopt it as a drop-in replacement for a large legacy bash codebase, and do not expect it to run scripts that depend on features the project has not implemented yet. Before committing, run ./bin/bunster ast on your script to see how it parses, then build it and compare the binary's behaviour against the original script on your own inputs. Verify first that your script's constructs appear in the supported feature list, because the README states that only a subset of features is supported so far and that full bash compatibility is targeted for v1.0.0.

## FAQ

### Does Bunster just wrap my script inside a binary like shc does?

No. The README contrasts Bunster with shc explicitly, stating that Bunster does not wrap the script but compiles it to a standalone, shell-independent program. It does this by transpiling the script to Go and then compiling that Go with the Go toolchain.

### How do I install Bunster?

The README gives a curl installer, curl -f https://bunster.netlify.app/install.sh | bash, which places the binary at ~/.local/bin/bunster on Linux and ~/bin/bunster on macOS. Passing GLOBAL=1 to the same script installs it system-wide, and brew install bunster is also listed.

### Is Bunster compatible with all of bash yet?

No. The README carries a warning that the project is in its early stages and that only a subset of features is supported so far. It states that full bash compatibility is targeted for the stable v1.0.0 release, possibly with some caveats.

### What can I do with Bunster beyond running bash scripts?

The README lists a module system for splitting code across files, a builtin package manager for publishing and consuming modules, native .env file support, static asset embedding at compile time, and builtin flag parsing where you declare the flags you expect.

## Sources

- [License: BSD-3-Clause](https://github.com/yassinebenaid/bunster/blob/master/LICENSE)
- [Project website](https://bunster.netlify.app)
- [README](https://github.com/yassinebenaid/bunster/blob/master/README.md)
- [Releases](https://github.com/yassinebenaid/bunster/releases)
- [yassinebenaid/bunster on GitHub](https://github.com/yassinebenaid/bunster)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/yassinebenaid-bunster
