Skip to content

Contributing

askrelay is Apache-2.0 and built in the open. It is one Go module — standard library first, five pinned dependencies, single-binary distribution.

Read this before you plan anything large, because it is unusual. Development is maintainer-supervised — that is decision D-22, and it is deliberate.

Implementation is not delegated to autonomous agents; that hides the reasoning the maintainer needs to see. Work proceeds one work package at a time. Each WP is divided into small logical subtasks, and for every subtask the what, the why, and the proposed code are explained and confirmed with the maintainer before it is written — never ahead of confirmation. The maintainer must understand every line that lands.

The Sonnet subagents in .claude/agents/ — askrelay-test (adversarial), askrelay-review (fresh-context), and askrelay-security (red-team on security-touching work) — are an independent quality pass, run once per work package after all its subtasks are assembled. They review; they do not implement. Their findings return to the maintainer for a fix decision.

None of that is a barrier. Design discussion and issues are welcome any time, and so is code. It means only that the work-package board in docs/askrelay-implementation-plan.md is the real roadmap, and that a work package is eligible to be picked up when everything it depends on is DONE.

Requires Go 1.26. The shipped binary builds with CGO_ENABLED=0 — a single static binary. The test target is the deliberate exception: it sets CGO_ENABLED=1, because the race detector needs cgo.

Terminal window
make help # list targets — this is the default goal
make build # go build -o askrelay ./cmd/askrelay
make run ARGS="--help" # go run ./cmd/askrelay, with your args
make test # CGO_ENABLED=1 go test -race ./...
make tidy # go mod tidy
make lint # golangci-lint run, configured by .golangci.yml
make overview # regenerate docs/askrelay-overview.html

make test and make lint must be green before you open a pull request.

CI (.github/workflows/ci.yml) runs the plain build, a -tags dev build, the static CGO_ENABLED=0 build, a cross-compile smoke over linux/arm64, darwin/arm64, and windows/amd64, then go vet, the race-enabled tests, and golangci-lint as a separate job.

Planned · WP-14

There is no release or packaging target. No release target in the Makefile, no GoReleaser config, no container publish step in CI — the cross-compile smoke builds those targets to prove they compile, and stops there. Packaging (Docker, GoReleaser, brew tap, npm wrapper) is WP-14, and it is TODO. Do not go looking for a release pipeline to hook into.

Regenerate the overview when you touch a doc

Section titled “Regenerate the overview when you touch a doc”

docs/askrelay-overview.html is generated, and must never be edited by hand. It is built from the markdown docs by docs/tools/build-overview.py.

After editing any markdown doc listed in that script — or README.md, or CLAUDE.md — run make overview and commit the regenerated HTML in the same commit as the edit. A commit that changes a source doc without its regenerated HTML leaves the two out of sync.

Contributions are under the Developer Certificate of Origin. Sign off each commit:

Terminal window
git commit -s -m "your message"

The Signed-off-by: line certifies you wrote the patch, or have the right to submit it. No copyright assignment (CLA) is asked or accepted.

Errors (T-17). Exported sentinel values, matched with errors.Is. Propagate by wrapping — fmt.Errorf("<pkg>: …: %w", err). Return a zero value or an error, never both. Panic only for unrecoverable faults. The pure core — internal/a2a, internal/envelope, internal/gate — returns errors and never logs; only boundaries log. Errors that cross to a remote party are sanitized before they leave.

Logging (T-15/T-18). slog only. Never log message content, secrets, keys, tokens, or PII — ids and shapes only. Structured fields, not formatted strings. Log at boundaries. Security refusals log at Warn, without the offending content.

Dependencies enter only at the implementation plan’s §3 pins, and only when a work package first imports them. go.mod carries five direct requirements and is kept minimal on purpose:

Module Version
github.com/modelcontextprotocol/go-sdk v1.6.1
modernc.org/sqlite v1.54.0
github.com/coder/websocket v1.8.15
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0

Adding a sixth is a decision, not a convenience — raise it before you write code that needs one.

Two places, and the distinction matters when you go looking.

At the repository root — the project files:

  • CONTRIBUTING.md — ground rules, DCO, and where to start
  • GOVERNANCE.md — how decisions get made
  • SECURITY.md — vulnerability disclosure
  • LICENSE and NOTICE — Apache-2.0 and the copyright notice

In docs/ — the source-of-truth design:

  • askrelay-architecture.md — the full design
  • askrelay-implementation-plan.md — work packages, dependency pins (§3), technical decisions (T-xx), and the risk register. It wins over the architecture doc wherever the two disagree.
  • phase2-decision-log.md — every product decision (D-xx) with its verdict and rationale, binding on both
  • askrelay-system-map.md — the network and architecture picture

Those docs, not this site, are authoritative for why the code is shaped the way it is; this site documents how to use and run it. And above all of them: the Go code under cmd/ and internal/ is what actually runs. A doc that contradicts the code is a bug in the doc.

Verified against askrelay 2e019ea on . Anything newer than that commit is not reflected here.