# 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 is `boratanrikulu/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
//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

```bash
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.
