agents/skills/injectable/l1/go-concurrency-safety/SKILL.md
L1 supplement - audits Go-specific concurrency hazards in node client code: map iteration non-determinism, goroutine leaks, mutex ordering, panic boundaries, context cancellation.
npx skillsauth add plamentsv/plamen go-concurrency-safetyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
L1 trigger:
L1_PATTERN=trueAND target language = Go Inject Into: Every L1 depth agent working on Go code, in addition to the main skill Finding prefix:[GO-N]Status: v0.1 draft, Round 4 exemplars pending
Supplement to the main L1 skills when the target is written in Go. It codifies the Go-specific traps that turn otherwise-correct logic into production bugs. These are drawn from the Cosmos-SDK, Geth, Erigon, and CometBFT bug histories.
Go maps iterate in unspecified order. Production bug class: any consensus computation that ranges over a map[K]V and uses the order in its output will produce different results on different nodes.
Detection:
for $_, $_ := range $MAP where $MAP is declared as map[...]...Fix pattern:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
v := m[k]
// use (k, v)
}
Alternative: use btree or sort.SliceStable on a converted slice.
Tag: [GO-MAP-ITER:{loc}:{consumer}]
Goroutines that are started but never terminated. Over hours/days, a leak accumulates and eventually OOMs the node.
Common patterns:
<-ch where the sender path can return without sendinghttp.Get / network call without context timeoutfor loop without a cancellation checkDetection:
go $FUNC(...) / go func() { ... }()select inside: does it have a case <-ctx.Done()?Tag: [GO-GOROUTINE-LEAK:{spawn-loc}:{blocked-on}]
Locking order inversions cause deadlocks. In node clients, locks are often acquired across subsystem boundaries (consensus holds lock A, calls into p2p which acquires lock B; p2p holds lock B, calls into consensus which acquires lock A → deadlock).
Detection:
Lock / Unlock pairs with an early return that skips Unlock (prefer defer Unlock)Tag: [GO-LOCK-ORDER:{lock-a}:{lock-b}:{inversion-site}]
Sending on a closed channel panics. In Go, closing a channel you don't own is usually wrong.
Detection:
close(ch): who owns ch? Can another goroutine send on it after close?for _, x := range ch pattern: correct consumer side; check producer side always closes exactly onceTag: [GO-CLOSED-CHAN:{loc}]
A goroutine panic crashes the whole process (unless recovered). In RPC handlers, HTTP middleware typically recovers; in background workers, often not.
Detection:
go func() in background code: is the first line defer func() { recover() }?panic() in consensus code: should never happen; any panic inside the consensus package is a bugTag: [GO-PANIC:{loc}:{recovered}]
The context.Context pattern propagates cancellation. Every long-running operation should accept a context and check it.
Detection:
ctx and honor cancellation?context.Background() in production code is a smell — usually should be a child of a longer-lived contextTag: [GO-CTX:{loc}:{missing-propagation}]
defer Gotchasdefer in a loop: the deferred function runs when the outer function returns, not when the loop iteration ends. Can accumulate.defer with captured variables: closure captures by reference; variables may have changed by the time defer runs.defer f(x) evaluates x at defer time; defer func() { f(x) }() evaluates at call time.Tag: [GO-DEFER:{loc}:{issue}]
Go doesn't panic on integer overflow (wraps silently). In consensus math, this is dangerous.
Detection:
+, *, - on uint64 / int64 in consensus/fee/stake computationmath.AddUint64 or explicit overflow checksbig.Int is safer for unbounded arithmeticTag: [GO-OVERFLOW:{loc}:{type}]
GO-btcd signed-int transaction version (CVE-2024-34478) — transaction version treated as signed int instead of unsigned, combined with a data race in the consensus-rule check. Chain split and fund loss possible. Snyk advisory. Skill catch point: Section 8 (integer overflow) + go test -race. Every signed/unsigned mismatch on a consensus-relevant field is a finding.
Geth concurrent map iteration and map write panic (issue #17750) — map accessed from multiple goroutines without sync; Go runtime panics with fatal error: concurrent map iteration and map write. go-ethereum #17750. Skill catch point: Section 3 — for every map field on a struct shared across goroutines, verify it is protected by sync.Map, sync.RWMutex, or channel serialization.
Cosmos SDK map iteration determinism (EPIC #13039) — class-wide issue across multiple modules. Go's runtime intentionally randomizes range m for map[K]V, making every state-machine iteration over a map a potential chain-halt vector. EPIC #13039. Skill catch point: Section 1 — mandatory check.
Aptos 10/18/23 HashMap regression — performance refactor replaced a deterministic map with Rust HashMap; since Aptos uses Move VM written in Rust, this is a Rust concurrency bug by mechanism but the same class applies to Go. Referenced in consensus-safety-invariants skill.
Always run go test -race ./... and go vet ./... as part of the language-specific scanner. Race detector findings on consensus-relevant types (state, block, validator-set) are automatic High-severity candidates.
development
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline