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.
How work happens here
Section titled “How work happens here”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.
Build & test
Section titled “Build & test”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.
make help # list targets — this is the default goalmake build # go build -o askrelay ./cmd/askrelaymake run ARGS="--help" # go run ./cmd/askrelay, with your argsmake test # CGO_ENABLED=1 go test -race ./...make tidy # go mod tidymake lint # golangci-lint run, configured by .golangci.ymlmake overview # regenerate docs/askrelay-overview.htmlmake 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.
Sign your commits (DCO)
Section titled “Sign your commits (DCO)”Contributions are under the Developer Certificate of Origin. Sign off each commit:
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.
Conventions
Section titled “Conventions”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
Section titled “Dependencies”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.
Where the design lives
Section titled “Where the design lives”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 startGOVERNANCE.md— how decisions get madeSECURITY.md— vulnerability disclosureLICENSEandNOTICE— Apache-2.0 and the copyright notice
In docs/ — the source-of-truth design:
askrelay-architecture.md— the full designaskrelay-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 bothaskrelay-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.

