gobee — README
gobee — README
Source: https://github.com/boratanrikulu/gobee (fetched via gh api on 2026-05-22)
Metadata: MIT, Go, 25 stars at fetch time, created 2026-05-01, language Go, topics: bpf, cilium-ebpf, ebpf, golang, kernel, linux, transpiler. Author: Bora Tanrıkulu me@bora.sh.
Note: user-provided URL was
github.com/boratanrikulu/gobeeq(404). The correct repo for Bora's Go-to-BPF transpiler isboratanrikulu/gobee.
gobee
Write your BPF programs in Go, not C. gobee transpiles a strict subset of Go into BPF C, generates typed Go bindings for the userspace side, and gates loads against the running kernel.
The Go ecosystem has solid userspace tooling for BPF. The kernel side has always ended with "now write your program in C." Aya brought eBPF to Rust by writing a new BPF backend in rustc. gobee gets there a different way: by transpiling to C and reusing clang's mature backend.
A Go file in, a BPF program out
A tracepoint that streams every execve to userspace via a ringbuf:
//go:build ignore
package main
import "github.com/boratanrikulu/gobee/bpf"
//bpf:license GPL
type Event struct {
Pid uint32
Comm [16]byte
}
var Events = bpf.RingBuf[Event]{
MaxEntries: 4096,
}
//bpf:section tracepoint/syscalls/sys_enter_execve
func OnExec(ctx *bpf.ExecveEnterCtx) bpf.TpReturn {
e, ok := Events.Reserve()
if !ok {
return bpf.TpOk
}
e.Pid = bpf.GetCurrentPid()
bpf.GetTaskComm(&e.Comm)
Events.Submit(e)
return bpf.TpOk
}
func main() {}
gobee translate --bindings-dir ./bpf ./bpf/src produces both .bpf.c and typed bindings, plus a sourcemap (events.bpf.c.map) so verifier errors map back to Go lines and a typed bindings file (bpf/events_bindings.go) so the userspace driver writes objs.Events, objs.AttachOnExec(), and decodes ringbuf payloads straight into bpf.Event instead of stringly-typed coll.Programs["..."] lookups.
The C is readable on purpose. If gobee emits something weird, you can see it. For tracepoints + kprobes + XDP combined into one binary, see example/sysmon/.
How it compares
| gobee | C + clang + bpf2go | Aya (Rust) | bpftrace | BCC | |
|---|---|---|---|---|---|
| Kernel-side language | Go subset | C | Rust | DSL | C |
| Userspace integration | typed Go bindings + cilium/ebpf | bpf2go | aya-runtime | none | python |
| CO-RE | ✅ via clang | ✅ | ✅ via LLVM | ✅ | ✅ |
| Helper coverage | 200 typed Go wrappers | full (write C) | full | limited | full (write C) |
| Verifier error → source | ✅ Go file:line:col | ❌ raw C | ✅ Rust file:line | ❌ | partial |
| Kernel-version gate at load | ✅ via bpfvet | manual | manual | n/a | runtime |
| Toolchain deps | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| Generated artifact | .bpf.o + Go binary |
.bpf.o + Go binary |
.bpf.o + Rust binary |
JIT | JIT |
What's supported today
| Surface | Coverage |
|---|---|
| Program types (8) | XDP, tracepoint, kprobe/kretprobe, uprobe/uretprobe, sock_ops, TC, cgroup_skb, LSM |
| Map types (19) | array, hash, lru_hash, per-CPU variants, bloom_filter, lpm_trie, ringbuf, perf_event_array, prog_array, queue, stack, sk/task/inode storage, devmap/cpumap/xskmap |
| BPF helpers | ~200 typed Go stubs from libbpf v1.5.0 headers; subset exercised in real-kernel CI |
| CO-RE | auto-detected; BPF_CORE_READ for kernel-internal struct fields, direct ctx->field for UAPI BPF context structs |
| User-defined helpers | top-level Go funcs without //bpf:section emitted as static __always_inline |
| Verifier error → Go source | auto-annotated inside Load<Stem>; *ebpf.VerifierError includes → counter.go:18:5 markers |
| Sourcemap sidecar | <stem>.bpf.c.map written next to every .bpf.c |
| Kernel-version gate | bpfvet runs at load time; fails fast with bpf program needs kernel >= 5.8, host is 5.4 instead of EINVAL |
| Cross-arch | Linux arm64 + amd64 |
What gobee won't do
- Replace clang (clang's BPF backend gives CO-RE, BTF, verifier-friendly codegen for free — multi-year reimplementation cost).
- Replace
cilium/ebpf(generated bindings sit on top of it). - Hide BPF (Go subset maps 1:1 to BPF C idioms).
- Run clang for you (compile/embed/load remain user-owned; same pattern as bpf2go).
Why transpile, not generate BPF directly
gc, the Go compiler, has no LLVM-based BPF backend. Adding one is a multi-year compiler project. rustc is built on LLVM and that's why Aya works. So gobee emits C and reuses clang's BPF backend, which gives us mature codegen, BTF, and CO-RE relocations for free.
Quickstart
go install github.com/boratanrikulu/gobee/cmd/gobee@latest
cd example/helloworld
make build # gobee translate, clang, go build
sudo ./helloworld eth0
Inspirations
- Solod: the Go-to-C transpiler that proved this pattern works.
- Aya: the Rust eBPF framework whose ergonomics gobee chases.
License
MIT.