Instruction file imported from zapstore/zsp (
.cursor/rules/go.mdc). Copyright stays with the author.
Go Conventions
Style
- Standard library style. Run
gofmt. No third-party linters beyondgo vet. - Use
internal/for private packages. Public API surface should be minimal. - Prefer flat package structure within
internal/— one package per concern, not deep nesting. - Reference existing patterns in the same project before inventing new ones.
Error Handling
- Wrap errors with
fmt.Errorf("context: %w", err)— always add context. - Return errors; don't panic. Panics are only acceptable for programmer bugs (unreachable code).
- Use
errors.Is/errors.Asfor sentinel and typed error checks. - Define sentinel errors as package-level
var ErrFoo = errors.New("foo").
Testing
- Table-driven tests. Name subtests clearly.
- Test files live next to the code they test (
foo_test.gobesidefoo.go). - Use
testdata/for fixtures. - No test frameworks — standard
testingpackage only.
Dependencies
- Prefer the standard library. Add a dependency only when it saves significant complexity.
- All projects use
github.com/nbd-wtf/go-nostrfor Nostr operations. - Pin dependency versions via
go.sum. Rungo mod tidyafter changes.
Concurrency
- Use
context.Contextfor cancellation. Pass it as the first parameter. - Prefer
sync.WaitGrouporerrgroup.Groupover bare goroutines. - No goroutine leaks — every goroutine must have a clear shutdown path.
- Use channels for communication, mutexes for state protection. Don't mix.
Naming
- Short, clear names.
srcnotsourceManager.cfgnotconfiguration. - Interfaces describe behavior:
Signer,Publisher, notISignerInterface. - Acronyms are all-caps:
URL,HTTP,APK,ID.
Project Layout
main.go— entry point, minimal logic, delegates tointernal/.internal/— all business logic, one package per domain.testdata/— test fixtures, config examples.Makefile— build commands where applicable.
Build
- Use
-ldflagsfor version injection at build time. - Support
go install module@latestwith embedded build info fallback. - CGo is acceptable where needed (e.g. SQLite) but prefer pure Go when possible.
make(defaultbuildtarget) produces a single binary named after the project at the repo root.make allcross-compiles for all supported platforms intodist/as<binary>-<os>-<arch>.- Always pass
-trimpath -ldflags '-s -w'for reproducible, stripped binaries.
