Go binary & build repository by @burrowers. AI agent rules & system prompts with 5.8k stars.
# garble
Obfuscates Go code by wrapping the Go toolchain via `go build -toolexec=garble`.
The top-level process does CLI/setup; sub-processes wrap each `compile`/`asm`/`link`
call to transform source before the real tool runs. Read `CONTRIBUTING.md` for the
architecture (toolexec model, caching, terminology) and `README.md` for design goals
(deterministic, reproducible, reversible).
## Layout
- Top-level `*.go`: CLI (`main.go`), the AST/type transformer (`transformer.go`),
name hashing (`hash.go`), reflection handling (`reflect*.go`), caches
(`cache_*.go`), runtime/linker patching (`runtime_patch.go`).
- `internal/`: `literals` (string obfuscation), `ctrlflow`, `linker`, `ssa2ast`,
`asthelper`.
- `bundled_xtools.go`: patched copies of x/tools packages; see below before touching.
- `go_std_tables.go` is generated by `scripts/gen_go_std_tables.go` (`go generate`).
## Testing
- `go test ./...` does real builds and is slow (30s+). Use `go test -short` and
`go test -run Script/<name>` to target one `testdata/script/<name>.txtar`.
- Integration tests are testscript `.txtar` files under `testdata/script/`. Most
behavior is tested there, not in Go unit tests.
- Every change that alters behavior (feature, flag, bug) needs a test.
- Tests must be deterministic โ avoid randomness so failures reproduce; prefer
fixed seeds/inputs.
- Test cost matters: keep builds minimal (smallest package that triggers the bug,
reuse caches, avoid full std rebuilds and `-debugdir` outside `debugdir.txtar`).
- New Go version support, dependency bumps, and CI tuning are routine recurring work.
- garble rejects Go toolchains newer than it supports, including `devel` builds.
If the `go` in `$PATH` is too new, run tests via `GOTOOLCHAIN` with the latest
bugfix release of the newest supported major version, such as
`GOTOOLCHAIN=go1.27.0 go test -short` as of August 2026.
## Bundled x/tools packages
`bundled_xtools.go` holds modified copies of x/tools packages, needed so that
`typeutil.Hasher` does not hash struct field tags, given that tags do not affect type
identity. typeutil imports the internal `typeparams` package, so that is bundled too,
with the import replaced by the bundled `typeparams_` names. Each package's code is
prefixed with its name and introduced by a `NOTE(garble)` comment naming its origin.
Our patches are marked `// NOTE(garble)`. They are applied by an agent following the
steps below, not regenerated by a tool. We also drop any API which is not reachable
from `typeutil_Hasher`. Line 1 records the x/tools version the file was bundled from.
Bumping `golang.org/x/tools` requires re-syncing the file. Never overwrite it with
raw `go tool bundle` output, as the pruning and patches would have to be redone.
Instead:
* `go build -o /tmp/bundle golang.org/x/tools/cmd/bundle` after the bump; use this
new binary throughout, as older ones fail to load packages of a newer Go release
* bundle both packages at the old version via a throwaway module requiring it and
blank-importing them, e.g. `/tmp/bundle -o /tmp/old_typeutil.go
golang.org/x/tools/go/types/typeutil`, and at the new version from this repository
* diff old against new to see what upstream changed; apply only the hunks that touch
code we still keep
* diff `bundled_xtools.go` against the new bundles to confirm the only differences are
the removed API and our `NOTE(garble)` patches, then bump the version in line 1
* `go build ./...` and check `staticcheck` reports no unused code
## Commits
- Subject: lowercase, imperative, no trailing period, concise. Optionally prefixed
with an area: `testdata:`, `CI:`, `internal/literals:`, `README:`, `scripts:`, `all:`.
- Body explains *why*, not just what. For perf changes, include benchstat tables or
before/after timings. For bug fixes, paste the failing build/test output and end
with `Fixes #NNN.`. Credit external contributors by name when relevant.
- Keep prose tight โ say what matters and stop. No restating, no padding.
## Bug-fix workflow
Split into two commits: first a regression test that passes against the broken code
(asserting current wrong behavior or with a TODO), then the fix flipping the test to
assert correct behavior. Skip this for panics or hangs.