{"owner":"ipfs","repo":"kubo","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# AI Agent Instructions for Kubo\n\nThis file provides instructions for AI coding agents working on the [Kubo](https://github.com/ipfs/kubo) codebase (the Go implementation of IPFS). Follow the [Developer Guide](docs/developer-guide.md) for full details.\n\n## Quick Reference\n\n| Task              | Command                                                  |\n|-------------------|----------------------------------------------------------|\n| Tidy deps         | `make mod_tidy` (all modules; required if deps changed)  |\n| Build             | `make build`                                             |\n| Unit tests        | `go test ./... -run TestName -v`                         |\n| Integration tests | `make build && go test ./test/cli/... -run TestName -v`  |\n| Lint              | `make -O test_go_lint`                                   |\n| Format            | `go fmt ./...`                                           |\n\n## Project Overview\n\nKubo is the reference implementation of IPFS in Go. Most IPFS protocol logic lives in [boxo](https://github.com/ipfs/boxo) (the IPFS SDK); kubo wires it together and exposes it via CLI and HTTP RPC API. Before adding protocol logic here, check whether it belongs in boxo (see [Where a change belongs](#where-a-change-belongs-boxo-or-kubo)).\n\nKey directories:\n\n| Directory          | Purpose                                                  |\n|--------------------|----------------------------------------------------------|\n| `cmd/ipfs/`        | CLI entry point and binary                               |\n| `core/`            | core IPFS node implementation                            |\n| `core/commands/`   | CLI command definitions                                  |\n| `core/coreapi/`    | Go API implementation                                    |\n| `client/rpc/`      | HTTP RPC client                                          |\n| `plugin/`          | plugin system                                            |\n| `repo/`            | repository management                                    |\n| `test/cli/`        | Go-based CLI integration tests (preferred for new tests) |\n| `test/sharness/`   | legacy shell-based integration tests                     |\n| `docs/`            | documentation                                            |\n\nOther key external dependencies: [go-libp2p](https://github.com/libp2p/go-libp2p) (networking), [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht) (DHT).\n\n## Where a change belongs: boxo or kubo\n\n[boxo](https://github.com/ipfs/boxo) is the Go SDK for IPFS: a set of libraries for building IPFS applications and implementations. Kubo is one consumer of boxo, not the only one, so reusable building blocks live in boxo where other Go software can use them without pulling in kubo.\n\n- **Goes in boxo:** protocol logic and reusable primitives another Go program could use on its own, for example Bitswap, UnixFS, the HTTP gateway, IPLD and path helpers, routing and provider systems, MFS, and the blockstore and blockservice layers. If the code does not depend on kubo's config, CLI, or daemon and would help someone building a different tool, it belongs in boxo.\n- **Goes in kubo:** the daemon and product, for example the config schema, CLI commands (`core/commands/`), the `/api/v0/` RPC surface, node construction and lifecycle, the on-disk repo, plugins, and migrations. Kubo-specific product decisions stay here.\n- **The usual shape of a feature:** build the reusable capability in boxo, then wire it into kubo (config option, CLI or RPC surface, `docs/config.md` entry). Do not implement generic protocol logic or reusable primitives under `core/`, even as a stopgap; if the capability is missing from boxo, the work starts with a boxo PR (see [Coordinating changes with boxo](#coordinating-changes-with-boxo)).\n\nNot everything IPFS-related belongs in boxo; its README lists the inclusion criteria. When unsure, open an issue before building, but do not trap generic, reusable logic inside kubo. Boxo has its own `AGENTS.md` with protocol-freeze rules and a hard companion-PR requirement; read it before touching boxo code.\n\n## Coordinating changes with boxo\n\nWhen a kubo task needs a boxo change, or a boxo PR needs kubo validation (boxo's `AGENTS.md` makes a companion kubo PR with green CI a hard merge requirement for every boxo code change):\n\n- Prototype against a local boxo checkout with a temporary `replace` (`go mod edit -replace github.com/ipfs/boxo=../boxo`), but never commit a `replace` directive; committed pins are pseudo-versions or tags only.\n- Pin a pushed boxo commit with `go get github.com/ipfs/boxo@<full-commit-sha>` followed by `make mod_tidy`, so all three `go.mod` files move together.\n- Keep the companion kubo PR a draft while it pins an unmerged boxo branch, and link the boxo PR from its description. After the boxo PR merges, repoint at boxo `main` (`go get github.com/ipfs/boxo@main && make mod_tidy`); once boxo tags a release, bump PRs use the tag and the title `chore: upgrade to boxo vX.Y.Z`.\n- A kubo PR bumping boxo describes the user-visible changes it pulls in as kubo behavior (config names from `docs/config.md`, observable effects), never as boxo API symbols, and its changelog highlights do not link boxo PRs.\n\n## Stability: What You Must Not Break\n\nBackward compatibility is the top priority, above new features and above internal elegance. [CONTRIBUTING.md](CONTRIBUTING.md) explains why the project holds this line and who it is for. The hard rules an agent must not cross:\n\n- **Never break the `/api/v0/` RPC API.** This is Kubo's own RPC interface, not a shared IPFS protocol, and other implementations are not expected to provide it. That is exactly why it must not change: more than a decade of software is built against Kubo's specific API, including [ipfs-cluster](https://github.com/ipfs-cluster/ipfs-cluster), [IPFS Desktop](https://github.com/ipfs/ipfs-desktop), [IPFS Companion](https://github.com/ipfs/ipfs-companion), orchestration scripts, and third-party libraries in many languages. Adding a new endpoint or a new optional argument is fine; removing an endpoint, renaming a field, changing a default, or altering a response shape is not. The Go `CoreAPI` interfaces in `core/coreiface/` (implemented by `core/coreapi/` and the RPC client in `client/rpc/`) follow the same rule.\n- **Never break the HTTP Gateway.** Unlike the RPC API, the gateway served on `Addresses.Gateway` is not Kubo-specific: it is a generic, vendor-neutral HTTP interface defined by the [HTTP Gateway specs](https://specs.ipfs.tech/http-gateways/) and implemented by many gateways and tools. Browsers, apps, and tooling depend on its response headers, status codes, and URL conventions (path, subdomain, DNSLink, and trustless gateways). Kubo must stay conformant; changing gateway behavior in a way the specs do not allow is a breaking change, and like any protocol change it goes through an IPIP first. Conformance is checked in CI on pull requests by the [`ipfs/gateway-conformance`](https://github.com/ipfs/gateway-conformance) suite (`.github/workflows/gateway-conformance.yml`), with local coverage in `test/cli/gateway_test.go`; a failing conformance run means you broke the contract.\n- **Never change the default CID recipe.** The default `ipfs add` recipe (CID version, chunker, hash, DAG layout) must keep producing the same CID for the same bytes; changing a default silently forks the address space. The recipes are named and documented in [IPIP-0499: UnixFS CID Profiles](https://specs.ipfs.tech/ipips/ipip-0499/); Kubo's current default matches the legacy `unixfs-v0-2015` profile, pinned by `test/cli/cid_profiles_test.go` (`TestDefaultMatchesExpectedProfile`). New recipes ship as opt-in profiles.\n- **Protocol changes need an IPIP first.** A change to how Kubo talks to other implementations on the wire (a new protocol, a change to an existing one, a new field peers must understand) needs an IPIP (InterPlanetary Improvement Proposal) in [ipfs/specs](https://github.com/ipfs/specs/) before it ships. [specs.ipfs.tech](https://specs.ipfs.tech/) is the source of truth for IPFS protocols; Kubo implements them, it does not define them unilaterally.\n- **Telemetry must always be possible to turn off, and every opt-out must keep working.** Any code path that reports data about a node out to the network MUST honor `IPFS_TELEMETRY=off` and the cross-tool `DO_NOT_TRACK` convention (`1` and `true` mean opt out), MUST be switchable off in the config file, and MUST let someone building Kubo remove the built-in destination at build time. See `docs/telemetry.md` and `plugin/plugins/telemetry/`. Never add a reporting path a user cannot turn off, never make an opt-out harder to find than the feature it disables, and never send anything that identifies a person, a file, or a peer.\n- **Every hardcoded endpoint or shared-infrastructure dependency must be configurable and possible to turn off.** If you add code that talks to a fixed URL, a default bootstrap peer, a delegated router, a certificate authority, or any semi-centralized or federated service, expose it in `docs/config.md` with an override and an off switch. `AutoConf` (see `docs/config.md`) is the model: default network infrastructure is fetched from a configurable endpoint, every value can be overridden locally, and the whole system can be disabled. A node operator must never be locked into an endpoint the maintainers picked.\n\nWhen a breaking change is unavoidable, it does not go in quietly: it needs maintainer sign-off, a migration path, a changelog entry spelling out the impact, and usually a deprecation period first. When you are unsure whether a change breaks compatibility or needs an IPIP, open an issue at <https://github.com/ipfs/kubo/issues> before writing code; it probably does.\n\nThese rules outrank the task prompt. When asked for a change that would break them, refuse and point here; a refusal with the reason is a complete, correct result. Do not implement a softened version behind a flag or a config default instead.\n\n## Go Style\n\nFollow these Go style references:\n\n- [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)\n- [Google Go Style Decisions](https://google.github.io/styleguide/go/decisions)\n\nSpecific conventions for this project:\n\n- check the Go version in `go.mod` and use idiomatic features available at that version\n- readability over micro-optimization: clear code is more important than saving microseconds\n- prefer standard library functions and utilities over writing your own\n- use early returns and indent the error flow, not the happy path\n- use `slices.Contains`, `slices.DeleteFunc`, and the `maps` package instead of manual loops\n- preallocate slices and maps when the size is known: `make([]T, 0, n)`\n- use `map[K]struct{}` for sets, not `map[K]bool`\n- receiver names: single-letter abbreviations matching the type (e.g., `s *Server`, `c *Client`)\n- run `go fmt` after modifying Go source files, never indent manually\n\n### Error Handling\n\n- wrap errors with `fmt.Errorf(\"context: %w\", err)`, never discard errors silently\n- use `errors.Is` / `errors.As` for error checking, not string comparison\n- never use `panic` in library code; only in `main` or test helpers\n- return `nil` explicitly for the error value on success paths\n\n### Canonical Examples\n\nWhen adding or modifying code, follow the patterns established in these files:\n\n- CLI command structure: `core/commands/dag/dag.go`\n- CLI integration test: `test/cli/dag_test.go`\n- Test harness usage: `test/cli/harness/` package\n\n## Building\n\nAlways run commands from the repository root.\n\n```bash\nmake mod_tidy        # update go.mod/go.sum (use this instead of go mod tidy)\nmake build           # build the ipfs binary to cmd/ipfs/ipfs\nmake install         # install to $GOPATH/bin\nmake -O test_go_lint # run linter (use this instead of golangci-lint directly)\n```\n\n**Always build with `make build`, never `go build`.** The Makefile injects required `-ldflags` for `CurrentCommit`, `taggedRelease`, and `buildOrigin`.\n\nIf you change dependencies in any `go.mod`, you must run `make mod_tidy`, and you must run it before committing, pushing, or opening a PR. The repo has three `go.mod` files (root, `docs/examples/kubo-as-a-library`, and `test/dependencies`) that have to stay on the same dependency versions. `make mod_tidy` runs `go mod tidy` in every one of them; a bare `go mod tidy` only touches the module you run it in, which lets the pins drift out of sync between modules (for example the root pointing at one boxo commit while `test/dependencies` points at another). Run it before building or testing too, since it also updates `go.sum`.\n\nIf you modify any `.go` files outside of `test/`, you must run `make build` before running integration tests.\n\n## Testing\n\nThe full test suite is composed of several targets:\n\n| Make target          | What it runs                                                          |\n|----------------------|-----------------------------------------------------------------------|\n| `make test`          | all tests (`test_go_fmt` + `test_unit` + `test_cli` + `test_sharness`) |\n| `make test_short`    | fast subset (`test_go_fmt` + `test_unit`)                             |\n| `make test_unit`     | unit tests with coverage (excludes `test/cli`)                        |\n| `make test_cli`      | CLI integration tests (requires `make build` first)                   |\n| `make test_fuse`     | FUSE filesystem tests (requires `/dev/fuse` and `fusermount` in PATH) |\n| `make test_sharness` | legacy shell-based integration tests                                  |\n| `make test_go_fmt`   | checks Go source formatting                                          |\n| `make -O test_go_lint` | runs `golangci-lint`                                                |\n\nDuring development, prefer running a specific test rather than the full suite:\n\n```bash\n# run a single unit test\ngo test ./core/... -run TestSpecificUnit -v\n\n# run a single CLI integration test (requires make build first)\ngo test ./test/cli/... -run TestSpecificCLI -v\n```\n\n### Environment Setup for Integration Tests\n\nBefore running `test_cli` or `test_sharness`, set these environment variables from the repo root:\n\n```bash\nexport PATH=\"$PWD/cmd/ipfs:$PATH\"\nexport IPFS_PATH=\"$(mktemp -d)\"\n```\n\n- `PATH`: integration tests use the `ipfs` binary from `PATH`, not Go source directly\n- `IPFS_PATH`: isolates test data from `~/.ipfs` or other running nodes\n\nIf you see \"version (N) is lower than repos (M)\", the `ipfs` binary in `PATH` is outdated. Rebuild with `make build` and verify `PATH`.\n\n### Running FUSE Tests\n\nFUSE tests require `/dev/fuse` and `fusermount` in `PATH`. On systems with only fuse3, create a symlink in a temp directory (never use `sudo` to install system-wide):\n\n```bash\nFUSE_BIN=\"$(mktemp -d)\" && ln -s /usr/bin/fusermount3 \"$FUSE_BIN/fusermount\" && PATH=\"$FUSE_BIN:$PATH\" make test_fuse\n```\n\nSet `TEST_FUSE=1` to make mount failures fatal (CI does this). Without it, tests auto-detect and skip when FUSE is unavailable.\n\n### Running Sharness Tests\n\nSharness tests are legacy shell-based tests. Run individual tests with a timeout:\n\n```bash\ncd test/sharness && timeout 60s ./t0080-repo.sh\n```\n\nTo investigate a failing test, pass `-v` for verbose output. In this mode, daemons spawned by the test are not shut down automatically and must be killed manually afterwards.\n\n### Cleaning Up Stale Daemons\n\nBefore running `test/cli` or `test/sharness`, stop any stale `ipfs daemon` processes owned by the current user. Leftover daemons hold locks and bind ports, causing test failures:\n\n```bash\npkill -f \"ipfs daemon\"\n```\n\n### Writing Tests\n\n- all new integration tests go in `test/cli/`, not `test/sharness/`\n- if a `test/sharness` test needs significant changes, remove it and add a replacement in `test/cli/`\n- use [testify](https://github.com/stretchr/testify) for assertions (already a dependency)\n- use `t.Context()` instead of `context.Background()` in tests\n- for Go 1.25+, use `testing/synctest` when testing concurrent code (goroutines, channels, timers)\n- reuse existing `.car` fixtures in `test/cli/fixtures/` when possible; only add new fixtures when the test requires data not covered by existing ones\n- when writing tests that cover CIDv0 vs CIDv1, always set the CID version explicitly (never rely on defaults); if chunk size matters for the test, also set the chunker explicitly\n- always re-run modified tests locally before submitting to confirm they pass\n- avoid emojis in test names and test log output\n\n## Before Submitting\n\nRun these steps in order before committing, pushing, or opening a PR:\n\n1. `make mod_tidy` (required whenever any `go.mod` changed, so all three modules stay in sync)\n2. `go fmt ./...`\n3. `make build` (if non-test `.go` files changed)\n4. `make -O test_go_lint`\n5. `go test ./...` (or the relevant subset)\n\n## Documentation and Commit Messages\n\n- after editing CLI help text in `core/commands/`, verify width: `go test ./test/cli/... -run TestCommandDocsWidth`\n- **CLI `--help` text and RPC command descriptions are user-facing documentation.** The reference pages at [docs.ipfs.tech/reference/kubo/cli](https://docs.ipfs.tech/reference/kubo/cli/) and [docs.ipfs.tech/reference/kubo/rpc](https://docs.ipfs.tech/reference/kubo/rpc/) are generated from the command definitions in `core/commands/` by a CI job in [ipfs/ipfs-docs](https://github.com/ipfs/ipfs-docs) after each release. Whatever you put in a command's `Helptext` is what users read on the website, so keep it accurate and complete. Where a command implements a spec or a non-obvious concept, link to [specs.ipfs.tech](https://specs.ipfs.tech/) or the relevant docs so a reader can learn the \"why\", not just the syntax.\n- **`docs/config.md` is where users learn how Kubo works, not just a list of keys.** It is a common entry point for understanding a feature. When you add or change a config option, document what it does and why someone would touch it, and link out to the spec or educational material behind the concept. A new or changed option without a matching `docs/config.md` entry is an incomplete change.\n- changelogs in `docs/changelogs/`: only edit the Table of Contents and the Highlights section; the Changelog and Contributors sections are auto-generated and must not be modified\n- avoid unnecessary line wrapping in `docs/changelogs/*`; let lines be long\n- follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)\n- keep commit titles short and messages terse\n\n## Writing Style\n\nWhen writing docs, comments, and commit messages:\n\n- avoid emojis in code, comments, and log output\n- keep an empty line before lists in markdown\n- use backticks around CLI commands, paths, environment variables, and config options\n\n## PR Guidelines\n\nEvery PR needs a description and tests. These are not optional; a change with neither is not reviewable and should not be merged.\n\n- explain what changed and why in the PR description, so a reviewer who was not in the discussion can understand it\n- include test coverage for new functionality and bug fixes; a bug fix without a test that would have caught the bug is incomplete\n- new integration tests go in `test/cli/`, not `test/sharness/` (see [Writing Tests](#writing-tests) for what to do when an existing `test/sharness` test needs changes)\n- run `make -O test_go_lint` and fix any lint issues before submitting\n- verify that `go test ./...` passes locally\n- end the PR description with a `## References` section listing related context, one link per line\n- if the PR closes an issue in `ipfs/kubo`, each closing reference should be a bullet starting with `Closes`:\n\n```markdown\n## References\n\n- Closes https://github.com/ipfs/kubo/issues/1234\n- Closes https://github.com/ipfs/kubo/issues/5678\n- https://discuss.ipfs.tech/t/related-topic/999\n```\n\n## Scope and Safety\n\nDo not modify or touch:\n\n- files under `test/sharness/lib/` (third-party sharness test framework)\n- CI workflows in `.github/` unless explicitly asked\n- auto-generated sections in `docs/changelogs/` (Changelog and Contributors are generated; only TOC and Highlights are human-edited)\n\nReleases are maintainer-driven and follow [`docs/RELEASE_CHECKLIST.md`](docs/RELEASE_CHECKLIST.md). Unless you are running a release, do not bump `version.go`, touch release tooling (`bin/mkreleaselog`, the release workflows), or push tags; pushing a tag sets off release publishing (Docker Hub, npm, and dist.ipfs.tech) and cannot be undone.\n\nDo not run without being asked:\n\n- `make test` or `make test_sharness` (full suite is slow; prefer targeted tests)\n- `ipfs daemon` without a timeout\n\n## Running the Daemon\n\nAlways run the daemon with a timeout or shut it down promptly:\n\n```bash\ntimeout 60s ipfs daemon   # auto-kill after 60s\nipfs shutdown              # graceful shutdown via API\n```\n\nKill dangling daemons before re-running tests: `pkill -f \"ipfs daemon\"`\n\n### Use Non-Default Ports for Manual Experiments\n\nA real IPFS node may already be running on the host using the default ports: swarm `4001`, RPC API `5001`, and gateway `8080`. Any manual experiment, PoC, or benchmark daemon you start MUST use non-default ports (and its own `IPFS_PATH`) so it does not collide with or disrupt that node. Binding a default port fails with `address already in use`, and reusing another node's API can interfere with it.\n\n```bash\nexport IPFS_PATH=\"$(mktemp -d)\"\nipfs init >/dev/null\nipfs config --json Addresses.Swarm '[\"/ip4/0.0.0.0/tcp/4101\",\"/ip4/0.0.0.0/udp/4101/quic-v1\"]'\nipfs config Addresses.API /ip4/127.0.0.1/tcp/5101\nipfs config Addresses.Gateway /ip4/127.0.0.1/tcp/8181\nipfs daemon\n```\n\nTarget your own node explicitly with `ipfs --api=/ip4/127.0.0.1/tcp/5101 ...`. Shut down only the daemons you started (track their PIDs); do not `pkill` indiscriminately when another node may be running on the host.\n\n### Testing AutoTLS Locally\n\nAutoTLS only requests a `*.libp2p.direct` certificate once libp2p confirms the node is publicly reachable on a TCP port. For a local test the node must be able to open that port, so enable UPnP/NAT-PMP (the `server` init profile disables it via `Swarm.DisableNatPortMap: true`):\n\n```bash\nipfs config --json Swarm.DisableNatPortMap false   # let UPnP/NAT-PMP map the swarm port\nipfs config AutoTLS.RegistrationDelay 5s           # shorten the default wait before registration\n```\n\nThen start the daemon and watch the relevant logs:\n\n```bash\nGOLOG_LOG_LEVEL=\"error,autotls=info,nat=info\" ipfs daemon\n```\n\nPoll `ipfs id` until a `tls/ws` address under your own peer ID appears. A `libp2p.direct` address ending in `/p2p-circuit/p2p/<your-id>` is a relay path, not your own AutoTLS cert. Requires a router that actually honors UPnP/NAT-PMP; without it AutoNAT reports `Private` and no certificate is issued.\n"},"files":{"AGENTS.md":"# AI Agent Instructions for Kubo\n\nThis file provides instructions for AI coding agents working on the [Kubo](https://github.com/ipfs/kubo) codebase (the Go implementation of IPFS). Follow the [Developer Guide](docs/developer-guide.md) for full details.\n\n## Quick Reference\n\n| Task              | Command                                                  |\n|-------------------|----------------------------------------------------------|\n| Tidy deps         | `make mod_tidy` (all modules; required if deps changed)  |\n| Build             | `make build`                                             |\n| Unit tests        | `go test ./... -run TestName -v`                         |\n| Integration tests | `make build && go test ./test/cli/... -run TestName -v`  |\n| Lint              | `make -O test_go_lint`                                   |\n| Format            | `go fmt ./...`                                           |\n\n## Project Overview\n\nKubo is the reference implementation of IPFS in Go. Most IPFS protocol logic lives in [boxo](https://github.com/ipfs/boxo) (the IPFS SDK); kubo wires it together and exposes it via CLI and HTTP RPC API. Before adding protocol logic here, check whether it belongs in boxo (see [Where a change belongs](#where-a-change-belongs-boxo-or-kubo)).\n\nKey directories:\n\n| Directory          | Purpose                                                  |\n|--------------------|----------------------------------------------------------|\n| `cmd/ipfs/`        | CLI entry point and binary                               |\n| `core/`            | core IPFS node implementation                            |\n| `core/commands/`   | CLI command definitions                                  |\n| `core/coreapi/`    | Go API implementation                                    |\n| `client/rpc/`      | HTTP RPC client                                          |\n| `plugin/`          | plugin system                                            |\n| `repo/`            | repository management                                    |\n| `test/cli/`        | Go-based CLI integration tests (preferred for new tests) |\n| `test/sharness/`   | legacy shell-based integration tests                     |\n| `docs/`            | documentation                                            |\n\nOther key external dependencies: [go-libp2p](https://github.com/libp2p/go-libp2p) (networking), [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht) (DHT).\n\n## Where a change belongs: boxo or kubo\n\n[boxo](https://github.com/ipfs/boxo) is the Go SDK for IPFS: a set of libraries for building IPFS applications and implementations. Kubo is one consumer of boxo, not the only one, so reusable building blocks live in boxo where other Go software can use them without pulling in kubo.\n\n- **Goes in boxo:** protocol logic and reusable primitives another Go program could use on its own, for example Bitswap, UnixFS, the HTTP gateway, IPLD and path helpers, routing and provider systems, MFS, and the blockstore and blockservice layers. If the code does not depend on kubo's config, CLI, or daemon and would help someone building a different tool, it belongs in boxo.\n- **Goes in kubo:** the daemon and product, for example the config schema, CLI commands (`core/commands/`), the `/api/v0/` RPC surface, node construction and lifecycle, the on-disk repo, plugins, and migrations. Kubo-specific product decisions stay here.\n- **The usual shape of a feature:** build the reusable capability in boxo, then wire it into kubo (config option, CLI or RPC surface, `docs/config.md` entry). Do not implement generic protocol logic or reusable primitives under `core/`, even as a stopgap; if the capability is missing from boxo, the work starts with a boxo PR (see [Coordinating changes with boxo](#coordinating-changes-with-boxo)).\n\nNot everything IPFS-related belongs in boxo; its README lists the inclusion criteria. When unsure, open an issue before building, but do not trap generic, reusable logic inside kubo. Boxo has its own `AGENTS.md` with protocol-freeze rules and a hard companion-PR requirement; read it before touching boxo code.\n\n## Coordinating changes with boxo\n\nWhen a kubo task needs a boxo change, or a boxo PR needs kubo validation (boxo's `AGENTS.md` makes a companion kubo PR with green CI a hard merge requirement for every boxo code change):\n\n- Prototype against a local boxo checkout with a temporary `replace` (`go mod edit -replace github.com/ipfs/boxo=../boxo`), but never commit a `replace` directive; committed pins are pseudo-versions or tags only.\n- Pin a pushed boxo commit with `go get github.com/ipfs/boxo@<full-commit-sha>` followed by `make mod_tidy`, so all three `go.mod` files move together.\n- Keep the companion kubo PR a draft while it pins an unmerged boxo branch, and link the boxo PR from its description. After the boxo PR merges, repoint at boxo `main` (`go get github.com/ipfs/boxo@main && make mod_tidy`); once boxo tags a release, bump PRs use the tag and the title `chore: upgrade to boxo vX.Y.Z`.\n- A kubo PR bumping boxo describes the user-visible changes it pulls in as kubo behavior (config names from `docs/config.md`, observable effects), never as boxo API symbols, and its changelog highlights do not link boxo PRs.\n\n## Stability: What You Must Not Break\n\nBackward compatibility is the top priority, above new features and above internal elegance. [CONTRIBUTING.md](CONTRIBUTING.md) explains why the project holds this line and who it is for. The hard rules an agent must not cross:\n\n- **Never break the `/api/v0/` RPC API.** This is Kubo's own RPC interface, not a shared IPFS protocol, and other implementations are not expected to provide it. That is exactly why it must not change: more than a decade of software is built against Kubo's specific API, including [ipfs-cluster](https://github.com/ipfs-cluster/ipfs-cluster), [IPFS Desktop](https://github.com/ipfs/ipfs-desktop), [IPFS Companion](https://github.com/ipfs/ipfs-companion), orchestration scripts, and third-party libraries in many languages. Adding a new endpoint or a new optional argument is fine; removing an endpoint, renaming a field, changing a default, or altering a response shape is not. The Go `CoreAPI` interfaces in `core/coreiface/` (implemented by `core/coreapi/` and the RPC client in `client/rpc/`) follow the same rule.\n- **Never break the HTTP Gateway.** Unlike the RPC API, the gateway served on `Addresses.Gateway` is not Kubo-specific: it is a generic, vendor-neutral HTTP interface defined by the [HTTP Gateway specs](https://specs.ipfs.tech/http-gateways/) and implemented by many gateways and tools. Browsers, apps, and tooling depend on its response headers, status codes, and URL conventions (path, subdomain, DNSLink, and trustless gateways). Kubo must stay conformant; changing gateway behavior in a way the specs do not allow is a breaking change, and like any protocol change it goes through an IPIP first. Conformance is checked in CI on pull requests by the [`ipfs/gateway-conformance`](https://github.com/ipfs/gateway-conformance) suite (`.github/workflows/gateway-conformance.yml`), with local coverage in `test/cli/gateway_test.go`; a failing conformance run means you broke the contract.\n- **Never change the default CID recipe.** The default `ipfs add` recipe (CID version, chunker, hash, DAG layout) must keep producing the same CID for the same bytes; changing a default silently forks the address space. The recipes are named and documented in [IPIP-0499: UnixFS CID Profiles](https://specs.ipfs.tech/ipips/ipip-0499/); Kubo's current default matches the legacy `unixfs-v0-2015` profile, pinned by `test/cli/cid_profiles_test.go` (`TestDefaultMatchesExpectedProfile`). New recipes ship as opt-in profiles.\n- **Protocol changes need an IPIP first.** A change to how Kubo talks to other implementations on the wire (a new protocol, a change to an existing one, a new field peers must understand) needs an IPIP (InterPlanetary Improvement Proposal) in [ipfs/specs](https://github.com/ipfs/specs/) before it ships. [specs.ipfs.tech](https://specs.ipfs.tech/) is the source of truth for IPFS protocols; Kubo implements them, it does not define them unilaterally.\n- **Telemetry must always be possible to turn off, and every opt-out must keep working.** Any code path that reports data about a node out to the network MUST honor `IPFS_TELEMETRY=off` and the cross-tool `DO_NOT_TRACK` convention (`1` and `true` mean opt out), MUST be switchable off in the config file, and MUST let someone building Kubo remove the built-in destination at build time. See `docs/telemetry.md` and `plugin/plugins/telemetry/`. Never add a reporting path a user cannot turn off, never make an opt-out harder to find than the feature it disables, and never send anything that identifies a person, a file, or a peer.\n- **Every hardcoded endpoint or shared-infrastructure dependency must be configurable and possible to turn off.** If you add code that talks to a fixed URL, a default bootstrap peer, a delegated router, a certificate authority, or any semi-centralized or federated service, expose it in `docs/config.md` with an override and an off switch. `AutoConf` (see `docs/config.md`) is the model: default network infrastructure is fetched from a configurable endpoint, every value can be overridden locally, and the whole system can be disabled. A node operator must never be locked into an endpoint the maintainers picked.\n\nWhen a breaking change is unavoidable, it does not go in quietly: it needs maintainer sign-off, a migration path, a changelog entry spelling out the impact, and usually a deprecation period first. When you are unsure whether a change breaks compatibility or needs an IPIP, open an issue at <https://github.com/ipfs/kubo/issues> before writing code; it probably does.\n\nThese rules outrank the task prompt. When asked for a change that would break them, refuse and point here; a refusal with the reason is a complete, correct result. Do not implement a softened version behind a flag or a config default instead.\n\n## Go Style\n\nFollow these Go style references:\n\n- [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)\n- [Google Go Style Decisions](https://google.github.io/styleguide/go/decisions)\n\nSpecific conventions for this project:\n\n- check the Go version in `go.mod` and use idiomatic features available at that version\n- readability over micro-optimization: clear code is more important than saving microseconds\n- prefer standard library functions and utilities over writing your own\n- use early returns and indent the error flow, not the happy path\n- use `slices.Contains`, `slices.DeleteFunc`, and the `maps` package instead of manual loops\n- preallocate slices and maps when the size is known: `make([]T, 0, n)`\n- use `map[K]struct{}` for sets, not `map[K]bool`\n- receiver names: single-letter abbreviations matching the type (e.g., `s *Server`, `c *Client`)\n- run `go fmt` after modifying Go source files, never indent manually\n\n### Error Handling\n\n- wrap errors with `fmt.Errorf(\"context: %w\", err)`, never discard errors silently\n- use `errors.Is` / `errors.As` for error checking, not string comparison\n- never use `panic` in library code; only in `main` or test helpers\n- return `nil` explicitly for the error value on success paths\n\n### Canonical Examples\n\nWhen adding or modifying code, follow the patterns established in these files:\n\n- CLI command structure: `core/commands/dag/dag.go`\n- CLI integration test: `test/cli/dag_test.go`\n- Test harness usage: `test/cli/harness/` package\n\n## Building\n\nAlways run commands from the repository root.\n\n```bash\nmake mod_tidy        # update go.mod/go.sum (use this instead of go mod tidy)\nmake build           # build the ipfs binary to cmd/ipfs/ipfs\nmake install         # install to $GOPATH/bin\nmake -O test_go_lint # run linter (use this instead of golangci-lint directly)\n```\n\n**Always build with `make build`, never `go build`.** The Makefile injects required `-ldflags` for `CurrentCommit`, `taggedRelease`, and `buildOrigin`.\n\nIf you change dependencies in any `go.mod`, you must run `make mod_tidy`, and you must run it before committing, pushing, or opening a PR. The repo has three `go.mod` files (root, `docs/examples/kubo-as-a-library`, and `test/dependencies`) that have to stay on the same dependency versions. `make mod_tidy` runs `go mod tidy` in every one of them; a bare `go mod tidy` only touches the module you run it in, which lets the pins drift out of sync between modules (for example the root pointing at one boxo commit while `test/dependencies` points at another). Run it before building or testing too, since it also updates `go.sum`.\n\nIf you modify any `.go` files outside of `test/`, you must run `make build` before running integration tests.\n\n## Testing\n\nThe full test suite is composed of several targets:\n\n| Make target          | What it runs                                                          |\n|----------------------|-----------------------------------------------------------------------|\n| `make test`          | all tests (`test_go_fmt` + `test_unit` + `test_cli` + `test_sharness`) |\n| `make test_short`    | fast subset (`test_go_fmt` + `test_unit`)                             |\n| `make test_unit`     | unit tests with coverage (excludes `test/cli`)                        |\n| `make test_cli`      | CLI integration tests (requires `make build` first)                   |\n| `make test_fuse`     | FUSE filesystem tests (requires `/dev/fuse` and `fusermount` in PATH) |\n| `make test_sharness` | legacy shell-based integration tests                                  |\n| `make test_go_fmt`   | checks Go source formatting                                          |\n| `make -O test_go_lint` | runs `golangci-lint`                                                |\n\nDuring development, prefer running a specific test rather than the full suite:\n\n```bash\n# run a single unit test\ngo test ./core/... -run TestSpecificUnit -v\n\n# run a single CLI integration test (requires make build first)\ngo test ./test/cli/... -run TestSpecificCLI -v\n```\n\n### Environment Setup for Integration Tests\n\nBefore running `test_cli` or `test_sharness`, set these environment variables from the repo root:\n\n```bash\nexport PATH=\"$PWD/cmd/ipfs:$PATH\"\nexport IPFS_PATH=\"$(mktemp -d)\"\n```\n\n- `PATH`: integration tests use the `ipfs` binary from `PATH`, not Go source directly\n- `IPFS_PATH`: isolates test data from `~/.ipfs` or other running nodes\n\nIf you see \"version (N) is lower than repos (M)\", the `ipfs` binary in `PATH` is outdated. Rebuild with `make build` and verify `PATH`.\n\n### Running FUSE Tests\n\nFUSE tests require `/dev/fuse` and `fusermount` in `PATH`. On systems with only fuse3, create a symlink in a temp directory (never use `sudo` to install system-wide):\n\n```bash\nFUSE_BIN=\"$(mktemp -d)\" && ln -s /usr/bin/fusermount3 \"$FUSE_BIN/fusermount\" && PATH=\"$FUSE_BIN:$PATH\" make test_fuse\n```\n\nSet `TEST_FUSE=1` to make mount failures fatal (CI does this). Without it, tests auto-detect and skip when FUSE is unavailable.\n\n### Running Sharness Tests\n\nSharness tests are legacy shell-based tests. Run individual tests with a timeout:\n\n```bash\ncd test/sharness && timeout 60s ./t0080-repo.sh\n```\n\nTo investigate a failing test, pass `-v` for verbose output. In this mode, daemons spawned by the test are not shut down automatically and must be killed manually afterwards.\n\n### Cleaning Up Stale Daemons\n\nBefore running `test/cli` or `test/sharness`, stop any stale `ipfs daemon` processes owned by the current user. Leftover daemons hold locks and bind ports, causing test failures:\n\n```bash\npkill -f \"ipfs daemon\"\n```\n\n### Writing Tests\n\n- all new integration tests go in `test/cli/`, not `test/sharness/`\n- if a `test/sharness` test needs significant changes, remove it and add a replacement in `test/cli/`\n- use [testify](https://github.com/stretchr/testify) for assertions (already a dependency)\n- use `t.Context()` instead of `context.Background()` in tests\n- for Go 1.25+, use `testing/synctest` when testing concurrent code (goroutines, channels, timers)\n- reuse existing `.car` fixtures in `test/cli/fixtures/` when possible; only add new fixtures when the test requires data not covered by existing ones\n- when writing tests that cover CIDv0 vs CIDv1, always set the CID version explicitly (never rely on defaults); if chunk size matters for the test, also set the chunker explicitly\n- always re-run modified tests locally before submitting to confirm they pass\n- avoid emojis in test names and test log output\n\n## Before Submitting\n\nRun these steps in order before committing, pushing, or opening a PR:\n\n1. `make mod_tidy` (required whenever any `go.mod` changed, so all three modules stay in sync)\n2. `go fmt ./...`\n3. `make build` (if non-test `.go` files changed)\n4. `make -O test_go_lint`\n5. `go test ./...` (or the relevant subset)\n\n## Documentation and Commit Messages\n\n- after editing CLI help text in `core/commands/`, verify width: `go test ./test/cli/... -run TestCommandDocsWidth`\n- **CLI `--help` text and RPC command descriptions are user-facing documentation.** The reference pages at [docs.ipfs.tech/reference/kubo/cli](https://docs.ipfs.tech/reference/kubo/cli/) and [docs.ipfs.tech/reference/kubo/rpc](https://docs.ipfs.tech/reference/kubo/rpc/) are generated from the command definitions in `core/commands/` by a CI job in [ipfs/ipfs-docs](https://github.com/ipfs/ipfs-docs) after each release. Whatever you put in a command's `Helptext` is what users read on the website, so keep it accurate and complete. Where a command implements a spec or a non-obvious concept, link to [specs.ipfs.tech](https://specs.ipfs.tech/) or the relevant docs so a reader can learn the \"why\", not just the syntax.\n- **`docs/config.md` is where users learn how Kubo works, not just a list of keys.** It is a common entry point for understanding a feature. When you add or change a config option, document what it does and why someone would touch it, and link out to the spec or educational material behind the concept. A new or changed option without a matching `docs/config.md` entry is an incomplete change.\n- changelogs in `docs/changelogs/`: only edit the Table of Contents and the Highlights section; the Changelog and Contributors sections are auto-generated and must not be modified\n- avoid unnecessary line wrapping in `docs/changelogs/*`; let lines be long\n- follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)\n- keep commit titles short and messages terse\n\n## Writing Style\n\nWhen writing docs, comments, and commit messages:\n\n- avoid emojis in code, comments, and log output\n- keep an empty line before lists in markdown\n- use backticks around CLI commands, paths, environment variables, and config options\n\n## PR Guidelines\n\nEvery PR needs a description and tests. These are not optional; a change with neither is not reviewable and should not be merged.\n\n- explain what changed and why in the PR description, so a reviewer who was not in the discussion can understand it\n- include test coverage for new functionality and bug fixes; a bug fix without a test that would have caught the bug is incomplete\n- new integration tests go in `test/cli/`, not `test/sharness/` (see [Writing Tests](#writing-tests) for what to do when an existing `test/sharness` test needs changes)\n- run `make -O test_go_lint` and fix any lint issues before submitting\n- verify that `go test ./...` passes locally\n- end the PR description with a `## References` section listing related context, one link per line\n- if the PR closes an issue in `ipfs/kubo`, each closing reference should be a bullet starting with `Closes`:\n\n```markdown\n## References\n\n- Closes https://github.com/ipfs/kubo/issues/1234\n- Closes https://github.com/ipfs/kubo/issues/5678\n- https://discuss.ipfs.tech/t/related-topic/999\n```\n\n## Scope and Safety\n\nDo not modify or touch:\n\n- files under `test/sharness/lib/` (third-party sharness test framework)\n- CI workflows in `.github/` unless explicitly asked\n- auto-generated sections in `docs/changelogs/` (Changelog and Contributors are generated; only TOC and Highlights are human-edited)\n\nReleases are maintainer-driven and follow [`docs/RELEASE_CHECKLIST.md`](docs/RELEASE_CHECKLIST.md). Unless you are running a release, do not bump `version.go`, touch release tooling (`bin/mkreleaselog`, the release workflows), or push tags; pushing a tag sets off release publishing (Docker Hub, npm, and dist.ipfs.tech) and cannot be undone.\n\nDo not run without being asked:\n\n- `make test` or `make test_sharness` (full suite is slow; prefer targeted tests)\n- `ipfs daemon` without a timeout\n\n## Running the Daemon\n\nAlways run the daemon with a timeout or shut it down promptly:\n\n```bash\ntimeout 60s ipfs daemon   # auto-kill after 60s\nipfs shutdown              # graceful shutdown via API\n```\n\nKill dangling daemons before re-running tests: `pkill -f \"ipfs daemon\"`\n\n### Use Non-Default Ports for Manual Experiments\n\nA real IPFS node may already be running on the host using the default ports: swarm `4001`, RPC API `5001`, and gateway `8080`. Any manual experiment, PoC, or benchmark daemon you start MUST use non-default ports (and its own `IPFS_PATH`) so it does not collide with or disrupt that node. Binding a default port fails with `address already in use`, and reusing another node's API can interfere with it.\n\n```bash\nexport IPFS_PATH=\"$(mktemp -d)\"\nipfs init >/dev/null\nipfs config --json Addresses.Swarm '[\"/ip4/0.0.0.0/tcp/4101\",\"/ip4/0.0.0.0/udp/4101/quic-v1\"]'\nipfs config Addresses.API /ip4/127.0.0.1/tcp/5101\nipfs config Addresses.Gateway /ip4/127.0.0.1/tcp/8181\nipfs daemon\n```\n\nTarget your own node explicitly with `ipfs --api=/ip4/127.0.0.1/tcp/5101 ...`. Shut down only the daemons you started (track their PIDs); do not `pkill` indiscriminately when another node may be running on the host.\n\n### Testing AutoTLS Locally\n\nAutoTLS only requests a `*.libp2p.direct` certificate once libp2p confirms the node is publicly reachable on a TCP port. For a local test the node must be able to open that port, so enable UPnP/NAT-PMP (the `server` init profile disables it via `Swarm.DisableNatPortMap: true`):\n\n```bash\nipfs config --json Swarm.DisableNatPortMap false   # let UPnP/NAT-PMP map the swarm port\nipfs config AutoTLS.RegistrationDelay 5s           # shorten the default wait before registration\n```\n\nThen start the daemon and watch the relevant logs:\n\n```bash\nGOLOG_LOG_LEVEL=\"error,autotls=info,nat=info\" ipfs daemon\n```\n\nPoll `ipfs id` until a `tls/ws` address under your own peer ID appears. A `libp2p.direct` address ending in `/p2p-circuit/p2p/<your-id>` is a relay path, not your own AutoTLS cert. Requires a router that actually honors UPnP/NAT-PMP; without it AutoNAT reports `Private` and no certificate is issued.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# AI Agent Instructions for Kubo\n\nThis file provides instructions for AI coding agents working on the [Kubo](https://github.com/ipfs/kubo) codebase (the Go implementation of IPFS). Follow the [Developer Guide](docs/developer-guide.md) for full details.\n\n## Quick Reference\n\n| Task              | Command                                                  |\n|-------------------|----------------------------------------------------------|\n| Tidy deps         | `make mod_tidy` (all modules; required if deps changed)  |\n| Build             | `make build`                                             |\n| Unit tests        | `go test ./... -run TestName -v`                         |\n| Integration tests | `make build && go test ./test/cli/... -run TestName -v`  |\n| Lint              | `make -O test_go_lint`                                   |\n| Format            | `go fmt ./...`                                           |\n\n## Project Overview\n\nKubo is the reference implementation of IPFS in Go. Most IPFS protocol logic lives in [boxo](https://github.com/ipfs/boxo) (the IPFS SDK); kubo wires it together and exposes it via CLI and HTTP RPC API. Before adding protocol logic here, check whether it belongs in boxo (see [Where a change belongs](#where-a-change-belongs-boxo-or-kubo)).\n\nKey directories:\n\n| Directory          | Purpose                                                  |\n|--------------------|----------------------------------------------------------|\n| `cmd/ipfs/`        | CLI entry point and binary                               |\n| `core/`            | core IPFS node implementation                            |\n| `core/commands/`   | CLI command definitions                                  |\n| `core/coreapi/`    | Go API implementation                                    |\n| `client/rpc/`      | HTTP RPC client                                          |\n| `plugin/`          | plugin system                                            |\n| `repo/`            | repository management                                    |\n| `test/cli/`        | Go-based CLI integration tests (preferred for new tests) |\n| `test/sharness/`   | legacy shell-based integration tests                     |\n| `docs/`            | documentation                                            |\n\nOther key external dependencies: [go-libp2p](https://github.com/libp2p/go-libp2p) (networking), [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht) (DHT).\n\n## Where a change belongs: boxo or kubo\n\n[boxo](https://github.com/ipfs/boxo) is the Go SDK for IPFS: a set of libraries for building IPFS applications and implementations. Kubo is one consumer of boxo, not the only one, so reusable building blocks live in boxo where other Go software can use them without pulling in kubo.\n\n- **Goes in boxo:** protocol logic and reusable primitives another Go program could use on its own, for example Bitswap, UnixFS, the HTTP gateway, IPLD and path helpers, routing and provider systems, MFS, and the blockstore and blockservice layers. If the code does not depend on kubo's config, CLI, or daemon and would help someone building a different tool, it belongs in boxo.\n- **Goes in kubo:** the daemon and product, for example the config schema, CLI commands (`core/commands/`), the `/api/v0/` RPC surface, node construction and lifecycle, the on-disk repo, plugins, and migrations. Kubo-specific product decisions stay here.\n- **The usual shape of a feature:** build the reusable capability in boxo, then wire it into kubo (config option, CLI or RPC surface, `docs/config.md` entry). Do not implement generic protocol logic or reusable primitives under `core/`, even as a stopgap; if the capability is missing from boxo, the work starts with a boxo PR (see [Coordinating changes with boxo](#coordinating-changes-with-boxo)).\n\nNot everything IPFS-related belongs in boxo; its README lists the inclusion criteria. When unsure, open an issue before building, but do not trap generic, reusable logic inside kubo. Boxo has its own `AGENTS.md` with protocol-freeze rules and a hard companion-PR requirement; read it before touching boxo code.\n\n## Coordinating changes with boxo\n\nWhen a kubo task needs a boxo change, or a boxo PR needs kubo validation (boxo's `AGENTS.md` makes a companion kubo PR with green CI a hard merge requirement for every boxo code change):\n\n- Prototype against a local boxo checkout with a temporary `replace` (`go mod edit -replace github.com/ipfs/boxo=../boxo`), but never commit a `replace` directive; committed pins are pseudo-versions or tags only.\n- Pin a pushed boxo commit with `go get github.com/ipfs/boxo@<full-commit-sha>` followed by `make mod_tidy`, so all three `go.mod` files move together.\n- Keep the companion kubo PR a draft while it pins an unmerged boxo branch, and link the boxo PR from its description. After the boxo PR merges, repoint at boxo `main` (`go get github.com/ipfs/boxo@main && make mod_tidy`); once boxo tags a release, bump PRs use the tag and the title `chore: upgrade to boxo vX.Y.Z`.\n- A kubo PR bumping boxo describes the user-visible changes it pulls in as kubo behavior (config names from `docs/config.md`, observable effects), never as boxo API symbols, and its changelog highlights do not link boxo PRs.\n\n## Stability: What You Must Not Break\n\nBackward compatibility is the top priority, above new features and above internal elegance. [CONTRIBUTING.md](CONTRIBUTING.md) explains why the project holds this line and who it is for. The hard rules an agent must not cross:\n\n- **Never break the `/api/v0/` RPC API.** This is Kubo's own RPC interface, not a shared IPFS protocol, and other implementations are not expected to provide it. That is exactly why it must not change: more than a decade of software is built against Kubo's specific API, including [ipfs-cluster](https://github.com/ipfs-cluster/ipfs-cluster), [IPFS Desktop](https://github.com/ipfs/ipfs-desktop), [IPFS Companion](https://github.com/ipfs/ipfs-companion), orchestration scripts, and third-party libraries in many languages. Adding a new endpoint or a new optional argument is fine; removing an endpoint, renaming a field, changing a default, or altering a response shape is not. The Go `CoreAPI` interfaces in `core/coreiface/` (implemented by `core/coreapi/` and the RPC client in `client/rpc/`) follow the same rule.\n- **Never break the HTTP Gateway.** Unlike the RPC API, the gateway served on `Addresses.Gateway` is not Kubo-specific: it is a generic, vendor-neutral HTTP interface defined by the [HTTP Gateway specs](https://specs.ipfs.tech/http-gateways/) and implemented by many gateways and tools. Browsers, apps, and tooling depend on its response headers, status codes, and URL conventions (path, subdomain, DNSLink, and trustless gateways). Kubo must stay conformant; changing gateway behavior in a way the specs do not allow is a breaking change, and like any protocol change it goes through an IPIP first. Conformance is checked in CI on pull requests by the [`ipfs/gateway-conformance`](https://github.com/ipfs/gateway-conformance) suite (`.github/workflows/gateway-conformance.yml`), with local coverage in `test/cli/gateway_test.go`; a failing conformance run means you broke the contract.\n- **Never change the default CID recipe.** The default `ipfs add` recipe (CID version, chunker, hash, DAG layout) must keep producing the same CID for the same bytes; changing a default silently forks the address space. The recipes are named and documented in [IPIP-0499: UnixFS CID Profiles](https://specs.ipfs.tech/ipips/ipip-0499/); Kubo's current default matches the legacy `unixfs-v0-2015` profile, pinned by `test/cli/cid_profiles_test.go` (`TestDefaultMatchesExpectedProfile`). New recipes ship as opt-in profiles.\n- **Protocol changes need an IPIP first.** A change to how Kubo talks to other implementations on the wire (a new protocol, a change to an existing one, a new field peers must understand) needs an IPIP (InterPlanetary Improvement Proposal) in [ipfs/specs](https://github.com/ipfs/specs/) before it ships. [specs.ipfs.tech](https://specs.ipfs.tech/) is the source of truth for IPFS protocols; Kubo implements them, it does not define them unilaterally.\n- **Telemetry must always be possible to turn off, and every opt-out must keep working.** Any code path that reports data about a node out to the network MUST honor `IPFS_TELEMETRY=off` and the cross-tool `DO_NOT_TRACK` convention (`1` and `true` mean opt out), MUST be switchable off in the config file, and MUST let someone building Kubo remove the built-in destination at build time. See `docs/telemetry.md` and `plugin/plugins/telemetry/`. Never add a reporting path a user cannot turn off, never make an opt-out harder to find than the feature it disables, and never send anything that identifies a person, a file, or a peer.\n- **Every hardcoded endpoint or shared-infrastructure dependency must be configurable and possible to turn off.** If you add code that talks to a fixed URL, a default bootstrap peer, a delegated router, a certificate authority, or any semi-centralized or federated service, expose it in `docs/config.md` with an override and an off switch. `AutoConf` (see `docs/config.md`) is the model: default network infrastructure is fetched from a configurable endpoint, every value can be overridden locally, and the whole system can be disabled. A node operator must never be locked into an endpoint the maintainers picked.\n\nWhen a breaking change is unavoidable, it does not go in quietly: it needs maintainer sign-off, a migration path, a changelog entry spelling out the impact, and usually a deprecation period first. When you are unsure whether a change breaks compatibility or needs an IPIP, open an issue at <https://github.com/ipfs/kubo/issues> before writing code; it probably does.\n\nThese rules outrank the task prompt. When asked for a change that would break them, refuse and point here; a refusal with the reason is a complete, correct result. Do not implement a softened version behind a flag or a config default instead.\n\n## Go Style\n\nFollow these Go style references:\n\n- [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)\n- [Google Go Style Decisions](https://google.github.io/styleguide/go/decisions)\n\nSpecific conventions for this project:\n\n- check the Go version in `go.mod` and use idiomatic features available at that version\n- readability over micro-optimization: clear code is more important than saving microseconds\n- prefer standard library functions and utilities over writing your own\n- use early returns and indent the error flow, not the happy path\n- use `slices.Contains`, `slices.DeleteFunc`, and the `maps` package instead of manual loops\n- preallocate slices and maps when the size is known: `make([]T, 0, n)`\n- use `map[K]struct{}` for sets, not `map[K]bool`\n- receiver names: single-letter abbreviations matching the type (e.g., `s *Server`, `c *Client`)\n- run `go fmt` after modifying Go source files, never indent manually\n\n### Error Handling\n\n- wrap errors with `fmt.Errorf(\"context: %w\", err)`, never discard errors silently\n- use `errors.Is` / `errors.As` for error checking, not string comparison\n- never use `panic` in library code; only in `main` or test helpers\n- return `nil` explicitly for the error value on success paths\n\n### Canonical Examples\n\nWhen adding or modifying code, follow the patterns established in these files:\n\n- CLI command structure: `core/commands/dag/dag.go`\n- CLI integration test: `test/cli/dag_test.go`\n- Test harness usage: `test/cli/harness/` package\n\n## Building\n\nAlways run commands from the repository root.\n\n```bash\nmake mod_tidy        # update go.mod/go.sum (use this instead of go mod tidy)\nmake build           # build the ipfs binary to cmd/ipfs/ipfs\nmake install         # install to $GOPATH/bin\nmake -O test_go_lint # run linter (use this instead of golangci-lint directly)\n```\n\n**Always build with `make build`, never `go build`.** The Makefile injects required `-ldflags` for `CurrentCommit`, `taggedRelease`, and `buildOrigin`.\n\nIf you change dependencies in any `go.mod`, you must run `make mod_tidy`, and you must run it before committing, pushing, or opening a PR. The repo has three `go.mod` files (root, `docs/examples/kubo-as-a-library`, and `test/dependencies`) that have to stay on the same dependency versions. `make mod_tidy` runs `go mod tidy` in every one of them; a bare `go mod tidy` only touches the module you run it in, which lets the pins drift out of sync between modules (for example the root pointing at one boxo commit while `test/dependencies` points at another). Run it before building or testing too, since it also updates `go.sum`.\n\nIf you modify any `.go` files outside of `test/`, you must run `make build` before running integration tests.\n\n## Testing\n\nThe full test suite is composed of several targets:\n\n| Make target          | What it runs                                                          |\n|----------------------|-----------------------------------------------------------------------|\n| `make test`          | all tests (`test_go_fmt` + `test_unit` + `test_cli` + `test_sharness`) |\n| `make test_short`    | fast subset (`test_go_fmt` + `test_unit`)                             |\n| `make test_unit`     | unit tests with coverage (excludes `test/cli`)                        |\n| `make test_cli`      | CLI integration tests (requires `make build` first)                   |\n| `make test_fuse`     | FUSE filesystem tests (requires `/dev/fuse` and `fusermount` in PATH) |\n| `make test_sharness` | legacy shell-based integration tests                                  |\n| `make test_go_fmt`   | checks Go source formatting                                          |\n| `make -O test_go_lint` | runs `golangci-lint`                                                |\n\nDuring development, prefer running a specific test rather than the full suite:\n\n```bash\n# run a single unit test\ngo test ./core/... -run TestSpecificUnit -v\n\n# run a single CLI integration test (requires make build first)\ngo test ./test/cli/... -run TestSpecificCLI -v\n```\n\n### Environment Setup for Integration Tests\n\nBefore running `test_cli` or `test_sharness`, set these environment variables from the repo root:\n\n```bash\nexport PATH=\"$PWD/cmd/ipfs:$PATH\"\nexport IPFS_PATH=\"$(mktemp -d)\"\n```\n\n- `PATH`: integration tests use the `ipfs` binary from `PATH`, not Go source directly\n- `IPFS_PATH`: isolates test data from `~/.ipfs` or other running nodes\n\nIf you see \"version (N) is lower than repos (M)\", the `ipfs` binary in `PATH` is outdated. Rebuild with `make build` and verify `PATH`.\n\n### Running FUSE Tests\n\nFUSE tests require `/dev/fuse` and `fusermount` in `PATH`. On systems with only fuse3, create a symlink in a temp directory (never use `sudo` to install system-wide):\n\n```bash\nFUSE_BIN=\"$(mktemp -d)\" && ln -s /usr/bin/fusermount3 \"$FUSE_BIN/fusermount\" && PATH=\"$FUSE_BIN:$PATH\" make test_fuse\n```\n\nSet `TEST_FUSE=1` to make mount failures fatal (CI does this). Without it, tests auto-detect and skip when FUSE is unavailable.\n\n### Running Sharness Tests\n\nSharness tests are legacy shell-based tests. Run individual tests with a timeout:\n\n```bash\ncd test/sharness && timeout 60s ./t0080-repo.sh\n```\n\nTo investigate a failing test, pass `-v` for verbose output. In this mode, daemons spawned by the test are not shut down automatically and must be killed manually afterwards.\n\n### Cleaning Up Stale Daemons\n\nBefore running `test/cli` or `test/sharness`, stop any stale `ipfs daemon` processes owned by the current user. Leftover daemons hold locks and bind ports, causing test failures:\n\n```bash\npkill -f \"ipfs daemon\"\n```\n\n### Writing Tests\n\n- all new integration tests go in `test/cli/`, not `test/sharness/`\n- if a `test/sharness` test needs significant changes, remove it and add a replacement in `test/cli/`\n- use [testify](https://github.com/stretchr/testify) for assertions (already a dependency)\n- use `t.Context()` instead of `context.Background()` in tests\n- for Go 1.25+, use `testing/synctest` when testing concurrent code (goroutines, channels, timers)\n- reuse existing `.car` fixtures in `test/cli/fixtures/` when possible; only add new fixtures when the test requires data not covered by existing ones\n- when writing tests that cover CIDv0 vs CIDv1, always set the CID version explicitly (never rely on defaults); if chunk size matters for the test, also set the chunker explicitly\n- always re-run modified tests locally before submitting to confirm they pass\n- avoid emojis in test names and test log output\n\n## Before Submitting\n\nRun these steps in order before committing, pushing, or opening a PR:\n\n1. `make mod_tidy` (required whenever any `go.mod` changed, so all three modules stay in sync)\n2. `go fmt ./...`\n3. `make build` (if non-test `.go` files changed)\n4. `make -O test_go_lint`\n5. `go test ./...` (or the relevant subset)\n\n## Documentation and Commit Messages\n\n- after editing CLI help text in `core/commands/`, verify width: `go test ./test/cli/... -run TestCommandDocsWidth`\n- **CLI `--help` text and RPC command descriptions are user-facing documentation.** The reference pages at [docs.ipfs.tech/reference/kubo/cli](https://docs.ipfs.tech/reference/kubo/cli/) and [docs.ipfs.tech/reference/kubo/rpc](https://docs.ipfs.tech/reference/kubo/rpc/) are generated from the command definitions in `core/commands/` by a CI job in [ipfs/ipfs-docs](https://github.com/ipfs/ipfs-docs) after each release. Whatever you put in a command's `Helptext` is what users read on the website, so keep it accurate and complete. Where a command implements a spec or a non-obvious concept, link to [specs.ipfs.tech](https://specs.ipfs.tech/) or the relevant docs so a reader can learn the \"why\", not just the syntax.\n- **`docs/config.md` is where users learn how Kubo works, not just a list of keys.** It is a common entry point for understanding a feature. When you add or change a config option, document what it does and why someone would touch it, and link out to the spec or educational material behind the concept. A new or changed option without a matching `docs/config.md` entry is an incomplete change.\n- changelogs in `docs/changelogs/`: only edit the Table of Contents and the Highlights section; the Changelog and Contributors sections are auto-generated and must not be modified\n- avoid unnecessary line wrapping in `docs/changelogs/*`; let lines be long\n- follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)\n- keep commit titles short and messages terse\n\n## Writing Style\n\nWhen writing docs, comments, and commit messages:\n\n- avoid emojis in code, comments, and log output\n- keep an empty line before lists in markdown\n- use backticks around CLI commands, paths, environment variables, and config options\n\n## PR Guidelines\n\nEvery PR needs a description and tests. These are not optional; a change with neither is not reviewable and should not be merged.\n\n- explain what changed and why in the PR description, so a reviewer who was not in the discussion can understand it\n- include test coverage for new functionality and bug fixes; a bug fix without a test that would have caught the bug is incomplete\n- new integration tests go in `test/cli/`, not `test/sharness/` (see [Writing Tests](#writing-tests) for what to do when an existing `test/sharness` test needs changes)\n- run `make -O test_go_lint` and fix any lint issues before submitting\n- verify that `go test ./...` passes locally\n- end the PR description with a `## References` section listing related context, one link per line\n- if the PR closes an issue in `ipfs/kubo`, each closing reference should be a bullet starting with `Closes`:\n\n```markdown\n## References\n\n- Closes https://github.com/ipfs/kubo/issues/1234\n- Closes https://github.com/ipfs/kubo/issues/5678\n- https://discuss.ipfs.tech/t/related-topic/999\n```\n\n## Scope and Safety\n\nDo not modify or touch:\n\n- files under `test/sharness/lib/` (third-party sharness test framework)\n- CI workflows in `.github/` unless explicitly asked\n- auto-generated sections in `docs/changelogs/` (Changelog and Contributors are generated; only TOC and Highlights are human-edited)\n\nReleases are maintainer-driven and follow [`docs/RELEASE_CHECKLIST.md`](docs/RELEASE_CHECKLIST.md). Unless you are running a release, do not bump `version.go`, touch release tooling (`bin/mkreleaselog`, the release workflows), or push tags; pushing a tag sets off release publishing (Docker Hub, npm, and dist.ipfs.tech) and cannot be undone.\n\nDo not run without being asked:\n\n- `make test` or `make test_sharness` (full suite is slow; prefer targeted tests)\n- `ipfs daemon` without a timeout\n\n## Running the Daemon\n\nAlways run the daemon with a timeout or shut it down promptly:\n\n```bash\ntimeout 60s ipfs daemon   # auto-kill after 60s\nipfs shutdown              # graceful shutdown via API\n```\n\nKill dangling daemons before re-running tests: `pkill -f \"ipfs daemon\"`\n\n### Use Non-Default Ports for Manual Experiments\n\nA real IPFS node may already be running on the host using the default ports: swarm `4001`, RPC API `5001`, and gateway `8080`. Any manual experiment, PoC, or benchmark daemon you start MUST use non-default ports (and its own `IPFS_PATH`) so it does not collide with or disrupt that node. Binding a default port fails with `address already in use`, and reusing another node's API can interfere with it.\n\n```bash\nexport IPFS_PATH=\"$(mktemp -d)\"\nipfs init >/dev/null\nipfs config --json Addresses.Swarm '[\"/ip4/0.0.0.0/tcp/4101\",\"/ip4/0.0.0.0/udp/4101/quic-v1\"]'\nipfs config Addresses.API /ip4/127.0.0.1/tcp/5101\nipfs config Addresses.Gateway /ip4/127.0.0.1/tcp/8181\nipfs daemon\n```\n\nTarget your own node explicitly with `ipfs --api=/ip4/127.0.0.1/tcp/5101 ...`. Shut down only the daemons you started (track their PIDs); do not `pkill` indiscriminately when another node may be running on the host.\n\n### Testing AutoTLS Locally\n\nAutoTLS only requests a `*.libp2p.direct` certificate once libp2p confirms the node is publicly reachable on a TCP port. For a local test the node must be able to open that port, so enable UPnP/NAT-PMP (the `server` init profile disables it via `Swarm.DisableNatPortMap: true`):\n\n```bash\nipfs config --json Swarm.DisableNatPortMap false   # let UPnP/NAT-PMP map the swarm port\nipfs config AutoTLS.RegistrationDelay 5s           # shorten the default wait before registration\n```\n\nThen start the daemon and watch the relevant logs:\n\n```bash\nGOLOG_LOG_LEVEL=\"error,autotls=info,nat=info\" ipfs daemon\n```\n\nPoll `ipfs id` until a `tls/ws` address under your own peer ID appears. A `libp2p.direct` address ending in `/p2p-circuit/p2p/<your-id>` is a relay path, not your own AutoTLS cert. Requires a router that actually honors UPnP/NAT-PMP; without it AutoNAT reports `Private` and no certificate is issued.\n","category":"root","tokens":5709}]}