Capture SSL/TLS plaintext with eBPFโno MITM proxy or custom CA installation. Supports Linux and Android on x86_64 and arm64.
# AGENTS.md โ eCapture repository guide
This is the canonical repository guide for coding agents. Tool-specific files
such as `CLAUDE.md` and `.github/copilot-instructions.md` are compatibility
entry points, not separate sources of project truth.
## How to use this guide
- These instructions apply to the entire repository unless a deeper
`AGENTS.md` narrows them.
- For runtime behavior, trust source code and tests. For build behavior, trust
`go.mod`, `Makefile`, `variables.mk`, `functions.mk`, and current workflows.
For maintained E2E behavior, trust `test/e2e/README.md` and its harness.
- For architecture/runtime/output work, read `docs/agent/architecture.md` and
`docs/agent/output-pipeline.md`.
- For probes, ABI, CLI config, or generated protocols, read
`docs/agent/probe-development.md`.
- For build, formatting, lint, tests, E2E, or CI, read
`docs/agent/build-test.md`.
- Some older prose may lag the code or build files. This guide and current
implementation win on factual conflicts.
- Keep this file and the relevant detailed guide synchronized when a change
invalidates a command, path, invariant, or known limitation.
## Product priorities
- TLS capture is the project's core focus. Give the TLS, GoTLS, and GnuTLS
modules high priority, including support for OpenSSL, BoringSSL, Go TLS, and
GnuTLS. Protect their correctness, compatibility, and end-to-end behavior
when planning changes, reviews, and verification.
- Bash, Zsh, MySQL, and other non-core modules have lower priority. Maintain
them, but prioritize core TLS functionality when work or coverage must be
allocated.
- In user-facing material such as a promotional article, devote roughly 80%
of the attention to the core TLS modules and 20% to non-core modules. This
is an emphasis guideline, not a fixed quota for code or tests.
### Optional machine-local context
If `AGENTS.local.md` exists at the repository root, read it before choosing an
execution environment or running build, test, cross-compilation, or remote-host
operations. It may refine *where* commands run, but it must not override this
repository's safety, supported-platform, or verification requirements.
- `AGENTS.local.md` is optional and must remain uncommitted.
- Never require another contributor or a cloud agent to have it.
- Never copy its machine details into commits, logs, issues, or pull requests.
- Start from `AGENTS.local.example.md`; do not store credentials in either
file.
## Non-negotiable platform boundary
eCapture may be edited anywhere, but compilation, linking, linting, tests,
eBPF generation, and execution require Linux. Android artifacts are also
built on a Linux host.
- Runtime targets: Linux on `x86_64`, `aarch64`, or `loongarch64`; Android on
`x86_64` or `aarch64`.
- Minimum kernels: x86_64 >= 4.18; aarch64 >= 5.5; Linux loongarch64 >= 6.6.
- Loading probes requires root or the capabilities documented in
`docs/minimum-privileges.md`. Maintained E2E harnesses have stricter root
requirements.
- macOS and Windows are editing, formatting, and read-only inspection
environments. Do not run `make`, Go builds/tests, lint, eBPF generation, or
E2E there.
- Before using a remote Linux builder, verify its OS, architecture, checkout
path, branch, commit, worktree, submodules, and synchronization state. Never
assume local edits are present remotely or overwrite remote work.
- New or changed build/test scripts must reject unsupported OS, architecture,
and kernel combinations before doing eBPF work.
- If the required Linux/root environment is unavailable, report exactly what
was not run; never claim an unexecuted build or runtime passed.
## Fast mental model
eCapture attaches uprobes, kprobes, and TC programs, reads perf/ring-buffer
events, decodes their binary ABI in Go, and routes validated events to output
handlers and writers.
```text
main.go -> cli.Start() -> Cobra RunE -> runProbe()
-> config.Validate() -> factory.CreateProbe()
-> Probe.Initialize() / Start()
-> embedded eBPF asset + ebpfmanager
-> perf/ringbuf sample -> EventDecoder.Decode()
-> optional module-private EventPreprocessor
-> events.Dispatcher -> text/keylog/pcapng handler -> ByteSink
-> low-latency pcapng -> decrypt SDK -> text ByteSink
-> typed eCaptureQ EVENT publisher
```
`runProbe` owns signal handling and runtime reload orchestration. A concrete
probe owns its eBPF manager, attachments, maps/decoders, and mode-specific
handlers. `base.BaseProbe` owns shared logging, the default text handler,
readers, dispatch, reorder support, and common shutdown behavior.
See `docs/agent/architecture.md` for the CLI/probe matrix, repository map, and
package ownership.
## Critical cross-file invariants
- CLI imports trigger each probe's `register.go` `init()`. A seemingly unused
import may be required for factory registration.
- Treat attachment and event plumbing as one atomic change:
```text
C SEC section <-> manager.Probe.Section
C BPF function <-> EbpfFuncName
target symbol/offset <-> AttachToFuncName / UAddress
C map identifier <-> manager.Map.Name / decoder registration
C global variable <-> manager.ConstantEditor
C event layout <-> Go event struct / DecodeFromBytes
```
- Preserve C/Go event field order, signedness, widths, byte order, arrays, and
padding. Validate lengths before slicing. Perf samples may contain trailing
alignment padding, so do not identify variants by exact length alone.
- `Configuration.Validate()` may run more than once and must be idempotent.
- Concrete probes must follow BaseProbe lifecycle ordering. Track every reader;
custom reader loops must use `TrackReader` and `GoReaderLoop` equivalents so
shutdown can unblock and join them before closing handlers.
- Dispatcher fan-out is synchronous and unordered. Multiple readers may call
one handler concurrently; handlers/writers must be concurrency-safe and
tolerate idempotent close. Perf reorder is per map/reader, not global.
- Module-private records such as OpenSSL connection metadata are consumed
before output dispatch; they enrich captured output events instead of
becoming standalone text or typed events.
- `--logaddr` is operational logging only. For TLS, GoTLS, and GnuTLS,
`--eventaddr` is the primary captured-event destination for text (hook or
pcapng source), keylog, and pcapng. eCaptureQ is an additive typed publisher,
not a ByteSink.
- Linux production tags are `linux,netgo,ebpfassets,dynamic`; Android uses
`ecap_android,netgo,ebpfassets,dynamic`. Preserve CO-RE and non-CO-RE paths.
- Never hand-edit or commit generated `bytecode/*.o`, `bytecode/*.d`,
`.build/`, `assets/ebpf_probe_<target>_<goarch>_bindata.go`, generated LoongArch
`vmlinux.h`, `bin/ecapture`, or `coverage.out`. Regenerate Protobuf and
stringer outputs from their sources.
## Task guides and verification
Before changing a probe, event ABI, build flow, or test harness, read the
corresponding detailed guide listed above. At minimum:
- Documentation-only changes: verify paths, commands, links, and source facts.
- Go logic: format touched files, run targeted tests, and complete full race
plus production-tag lint gates when Linux is available.
- Event decoders/ABI: add byte-level malformed, short, and padded-sample tests.
- eBPF C or manager setup: clean-build both CO-RE and non-CO-RE variants for
affected architectures.
- CLI/output/capture modes: run the relevant strict E2E case on a real
Linux/Android kernel.
- OpenSSL/BoringSSL mappings: test mapping, both asset variants, and the real
library version.
- CI changes: validate least privilege and trusted/untrusted workflow
boundaries.
Canonical commands, prerequisites, build races, E2E semantics, and the full
verification matrix live in `docs/agent/build-test.md`.
## Code conventions
- Module/import path: `github.com/gojue/ecapture/v2`; preserve the `/v2`
suffix in new imports.
- Keep changes focused and follow the nearest working implementation. Probe
implementations are not perfectly uniform, so do not copy one blindly.
- Do not add files unless necessary for the requested change. Prefer reusing
existing code, scripts, and tests, and keep changes as small as practical.
Avoid unrelated refactors and unnecessary helper layers.
- In probe/domain lifecycle code, prefer structured constructors and codes in
`internal/errors`, adding context where useful. Preserve normal `%w`
wrapping at boundaries where that is the local pattern.
- Use `internal/logger` inside probes/runtime code and zerolog in the CLI
layer. Do not add ad-hoc `fmt.Println` or standard `log` calls for runtime
logging.
- Format only touched files: `gofmt`/`goimports` for Go and the repository's
exact clang-format style for C. `make format` is a broad C-only target.
- Keep eBPF verifier limits, bounded loops, stack use, helper availability,
and old-kernel compatibility in mind.
- Preserve the `github.com/google/gopacket` replacement in `go.mod` unless a
maintainer explicitly directs otherwise.
- Root `LICENSE` is Apache-2.0. Preserve each file's existing SPDX header;
some imported or kernel-facing headers use other compatible licenses. Do
not propagate stale prose that says AGPL, and leave license changes to a
maintainer-directed task.
## Safety, Git, and release boundaries
- Captured logs, commands, SQL, keylogs, pcap/pcapng files, and test artifacts
may contain secrets. Never commit or paste them unredacted.
- Preserve unrelated worktree changes. Do not stage, commit, push, open a PR,
publish, tag, or release unless the user explicitly requests it.
- Name new branches with a conventional purpose prefix such as `feat/`,
`bugfix/`, `docs/`, `test/`, or `chore/`, followed by a concise kebab-case
description.
- The default branch is `master`. If asked to commit, follow
`<package>: <what changed>`, keep the subject <= 70 characters, and explain
why in the body as documented in `CONTRIBUTING.md`. Write the entire commit
message in English unless the user explicitly requests a specific other
language.
- Write GitHub pull request titles and bodies in English unless the user
explicitly requests a specific other language.
- Keep ordinary fork-PR build/test jobs read-only and secret-free. Keep
scanner-specific write permissions narrowly scoped. Any trusted write-back
workflow must not execute untrusted fork code or its artifacts.
- Do not weaken security checks, verifier failures, or E2E assertions merely
to make CI green.
## Known state and traps
- GnuTLS uses versioned C assets selected from the detected library patch
release; keep the supported version-to-asset table synchronized with
`kern/gnutls_*_kern.c`.
- Runtime reload reattaches process-lifetime output dependencies and creates a
fresh probe context, but retains the original factory probe type; it is not
a cross-probe hot-swap facility.
- Shared serializable probe fields are applied through
`internal/config.CommonConfig` for every Cobra command and HTTP config
factory. Keep process-lifetime output dependencies out of that copy path.
- HTTP reload distinguishes omitted captured-event destination/rotation fields
from explicit empty/zero values. Omitted fields inherit from the active
generation, while runtime logger and typed eCaptureQ dependencies are
reattached separately.
- Probe registration currently discards duplicate-registration errors.
- Every probe uses the shared bytecode selector. `--btf=0` resolves kernel BTF
once per process, while explicit modes fail when the linked asset set cannot
satisfy them. Generated non-CO-RE objects use `*_kern_noncore.o`.
- `pkg/event_processor` and `internal/output/encoders` are not in the current
production CLI event path.
- `TARGET_ARCH` selects the build artifact architecture (`amd64`, `arm64`,
or `loong64`). `CROSS_ARCH` is a compatible alias used only when
`TARGET_ARCH` is unset.
- Non-CO-RE compilation must retain Kbuild's `-fms-extensions` support for
tagged anonymous fields in Linux 6.19+ headers; dropping it breaks field
access and structure layout on Linux 7.x.
LoongArch CO-RE needs the same support for generated runtime BTF headers.
x86 non-CO-RE headers also require `CC_USING_FENTRY` when function tracing
is configured; do not enable native `-pg`/`-mfentry` instrumentation for BPF.
- `make build` is not Go-only. Switching architecture, Android, or asset modes
still requires a clean serial validation even though libpcap, bytecode, Go
intermediates, and generated asset names are architecture-isolated. The
`assets`, `assets_core`, and `assets_noncore` targets for one architecture
write the same generated asset; do not request them together in one parallel
make invocation.
- Linux release builds share an Ubuntu 24.04 x86_64 job. Loong64 joins the
same release entry point and PR architecture matrix; only amd64/arm64
produce Android artifacts. Loong64 is cross-built with `TARGET_ARCH=loong64`
and the repository's LLVM 22.1.8 wrappers. Do not substitute `loong64` for the
`loongarch64-linux-gnu` C target or `loongarch` Kbuild architecture.
- Native Linux/loong64 production builds also support Debian 13+ with Clang/
LLVM 22.1.8, versioned LLD 22 linking and native multiarch libraries.
The Clang driver remains Go's external linker entry point.
`builder/init_env.sh` prepares dependencies and kernel inputs. Native CO-RE may use the running kernel's
BTF; non-CO-RE still requires prepared LoongArch kernel headers. Building
against packaged headers does not establish non-CO-RE compatibility with a
different running kernel. See `docs/loong64-support.md`.
- The dedicated LoongArch E2E job shares `e2e.yml` push/PR/manual triggers,
but only trusted repository refs enter the host. It builds CO-RE at the event
SHA with LLVM 22.1.8 and runs OpenSSL/GoTLS directly from that checkout through
a fixed sudo Shell entry point. Harness output streams live to Actions.
Fork PRs retain hosted checks. Root-owned hooks enforce trusted refs and
clean job processes, caches and owned TC state; there is no separate E2E unit.
## Focused references
- Architecture and runtime: `docs/agent/architecture.md`
- Probe and ABI development: `docs/agent/probe-development.md`
- Build and verification: `docs/agent/build-test.md`
- User-facing behavior: `README.md`, `README-zh_Hans.md`
- Build sources: `Makefile`, `variables.mk`, `functions.mk`, `builder/init_env.sh`
- E2E: `test/e2e/README.md`, `test/e2e/QUICK_REFERENCE.md`
- Privileges/security: `docs/minimum-privileges.md`, `SECURITY.md`
- Event forwarding: `docs/event-forward-api.md`, `pkg/ecaptureq/README.md`
- Generated/config docs: `protobuf/README.md`, `protobuf/PROTOCOLS.md`, `docs/remote-config-update-api.md`
- GitHub Copilot PR agent: `.github/agents/pr-agent.agent.md`