# 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___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 `: `, 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`