/go-concurrency-safety
L1 supplement - audits Go-specific concurrency hazards in node client code: map iteration non-determinism, goroutine leaks, mutex ordering, panic boundaries, context cancellation.
$ npx -y skills add PlamenTSV/plamen --skill go-concurrency-safety --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/go-concurrency-safety
Context preview
The summary Claude sees to decide when to auto-load this skill.
L1 supplement - audits Go-specific concurrency hazards in node client code: map iteration non-determinism, goroutine leaks, mutex ordering, panic boundaries, context cancellation.
SKILL.md
go-concurrency-safety.SKILL.mdname: "go-concurrency-safety"
description: "L1 supplement - audits Go-specific concurrency hazards in node client code: map iteration non-determinism, goroutine leaks, mutex ordering, panic boundaries, context cancellation."
Injectable Skill: Go Concurrency Safety
> **L1 trigger**: `L1_PATTERN=true` AND 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
When This Skill Activates
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.
1. Map Iteration Non-Determinism
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**:
- Ast-grep `for $_, $_ := range $MAP` where `$MAP` is declared as `map[...]...`
- For each hit in a consensus-critical path, check if the iteration result affects any stored state, signed message, or hash input
**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}]`
2. Goroutine Leaks
Goroutines that are started but never terminated. Over hours/days, a leak accumulates and eventually OOMs the node.
**Common patterns**:
- Goroutine blocked on `<-ch` where the sender path can return without sending
- Goroutine blocked on `http.Get` / network call without context timeout
- Goroutine in a `for` loop without a cancellation check
- Goroutine spawned inside a handler that outlives the handler's parent context
**Detection**:
- Ast-grep `go $FUNC(...)` / `go func() { ... }()`
- For each, trace the function body: is there a termination condition? Is it guaranteed to fire?
- Check every `select` inside: does it have a `case <-ctx.Done()`?
Tag: `[GO-GOROUTINE-LEAK:{spawn-loc}:{blocked-on}]`
3. Mutex Deadlock Risk
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**:
- For every function that acquires 2+ mutexes (directly or via called functions), determine the lock order
- Look for lock order inversions across the same two locks
- Look for `Lock` / `Unlock` pairs with an early return that skips Unlock (prefer `defer Unlock`)
Tag: `[GO-LOCK-ORDER:{lock-a}:{lock-b}:{inversion-site}]`
4. Channel Send on Closed Channel
Sending on a closed channel panics. In Go, closing a channel you don't own is usually wrong.
**Detection**:
- Every `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 once
Tag: `[GO-CLOSED-CHAN:{loc}]`
5. Panic Boundaries
A goroutine panic crashes the whole process (unless recovered). In RPC handlers, HTTP middleware typically recovers; in background workers, often not.
**Detection**:
- For each `go func()` in background code: is the first line `defer func() { recover() }`?
- For each handler that calls user input-parsing code: is there a recover boundary?
- `panic()` in consensus code: should never happen; any `panic` inside the consensus package is a bug
Tag: `[GO-PANIC:{loc}:{recovered}]`
6. Context Cancellation Propagation
The `context.Context` pattern propagates cancellation. Every long-running operation should accept a context and check it.
**Detection**:
- Every function in a hot path that does I/O: does it take `ctx` and honor cancellation?
- Every goroutine in a pool: does it use the pool's context for shutdown?
- `context.Background()` in production code is a smell — usually should be a child of a longer-lived context
Tag: `[GO-CTX:{loc}:{missing-propagation}]`
7. `defer` Gotchas
- `defer` 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}]`
8. Integer Overflow
Go doesn't panic on integer overflow (wraps silently). In consensus math, this is dangerous.
**Detection**:
- Every `+`, `*`, `-` on `uint64` / `int64` in consensus/fee/stake computation
- Use `math.AddUint64` or explicit overflow checks
- `big.Int` is safer for unbounded arithmetic
Tag: `[GO-OVERFLOW:{loc}:{type}]`
Output schema
- **Language**: Go
- **Bug class prefix**: `GO-`
- **Preferred evidence tags**: Same as parent skill
Known bug exemplars (v0.2 — Round 4 verified)
1. **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](https://security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMBTCSUITEBTCDBLOCKCHAIN-6808762). **Skill catch point**: Section 8 (integer overflow) + `go test -race`. Every signed/unsigned mismatch on a consensus-relevant field is a finding.
2. **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](https://github.com/ethereum/go-ethereum/issues/17750). **Skill catch point**: Section 3 — for every map field on a struct shared acr
Read more
name: "go-concurrency-safety" description: "L1 supplement - audits Go-specific concurrency hazards in node client code: map iteration non-determinism, goroutine leaks, mutex ordering, panic boundaries, context cancellation."
Injectable Skill: Go Concurrency Safety
> **L1 trigger**: `L1_PATTERN=true` AND 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
When This Skill Activates
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.
1. Map Iteration Non-Determinism
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**:
- Ast-grep `for $_, $_ := range $MAP` where `$MAP` is declared as `map[...]...`
- For each hit in a consensus-critical path, check if the iteration result affects any stored state, signed message, or hash input
**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}]`
2. Goroutine Leaks
Goroutines that are started but never terminated. Over hours/days, a leak accumulates and eventually OOMs the node.
**Common patterns**:
- Goroutine blocked on `<-ch` where the sender path can return without sending
- Goroutine blocked on `http.Get` / network call without context timeout
- Goroutine in a `for` loop without a cancellation check
- Goroutine spawned inside a handler that outlives the handler's parent context
**Detection**:
- Ast-grep `go $FUNC(...)` / `go func() { ... }()`
- For each, trace the function body: is there a termination condition? Is it guaranteed to fire?
- Check every `select` inside: does it have a `case <-ctx.Done()`?
Tag: `[GO-GOROUTINE-LEAK:{spawn-loc}:{blocked-on}]`
3. Mutex Deadlock Risk
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**:
- For every function that acquires 2+ mutexes (directly or via called functions), determine the lock order
- Look for lock order inversions across the same two locks
- Look for `Lock` / `Unlock` pairs with an early return that skips Unlock (prefer `defer Unlock`)
Tag: `[GO-LOCK-ORDER:{lock-a}:{lock-b}:{inversion-site}]`
4. Channel Send on Closed Channel
Sending on a closed channel panics. In Go, closing a channel you don't own is usually wrong.
**Detection**:
- Every `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 once
Tag: `[GO-CLOSED-CHAN:{loc}]`
5. Panic Boundaries
A goroutine panic crashes the whole process (unless recovered). In RPC handlers, HTTP middleware typically recovers; in background workers, often not.
**Detection**:
- For each `go func()` in background code: is the first line `defer func() { recover() }`?
- For each handler that calls user input-parsing code: is there a recover boundary?
- `panic()` in consensus code: should never happen; any `panic` inside the consensus package is a bug
Tag: `[GO-PANIC:{loc}:{recovered}]`
6. Context Cancellation Propagation
The `context.Context` pattern propagates cancellation. Every long-running operation should accept a context and check it.
**Detection**:
- Every function in a hot path that does I/O: does it take `ctx` and honor cancellation?
- Every goroutine in a pool: does it use the pool's context for shutdown?
- `context.Background()` in production code is a smell — usually should be a child of a longer-lived context
Tag: `[GO-CTX:{loc}:{missing-propagation}]`
7. `defer` Gotchas
- `defer` 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}]`
8. Integer Overflow
Go doesn't panic on integer overflow (wraps silently). In consensus math, this is dangerous.
**Detection**:
- Every `+`, `*`, `-` on `uint64` / `int64` in consensus/fee/stake computation
- Use `math.AddUint64` or explicit overflow checks
- `big.Int` is safer for unbounded arithmetic
Tag: `[GO-OVERFLOW:{loc}:{type}]`
Output schema
- **Language**: Go
- **Bug class prefix**: `GO-`
- **Preferred evidence tags**: Same as parent skill
Known bug exemplars (v0.2 — Round 4 verified)
1. **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](https://security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMBTCSUITEBTCDBLOCKCHAIN-6808762). **Skill catch point**: Section 8 (integer overflow) + `go test -race`. Every signed/unsigned mismatch on a consensus-relevant field is a finding.
2. **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](https://github.com/ethereum/go-ethereum/issues/17750). **Skill catch point**: Section 3 — for every map field on a struct shared acr
Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.
Repo: PlamenTSV/plamen
Other skills on plamen.
- /ability-analysis
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth agents, depth agents
Open skill - /bit-shift-safety
Trigger Pattern Always (Aptos Move) - Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case
Open skill - /centralization-risk
Trigger Protocol has privileged roles (admin, operator, governance, resource account owner) - Covers Single points of failure, privilege escalation, external governance dependen...
Open skill - /cross-chain-timing
Trigger Pattern wormhole|layerzero|ccip|bridge|cross_chain|vaa|guardian|emitter|relay|remote_chain|payload|nonce.sequence - Inject Into Breadth agents, depth-external
Open skill - /dependency-audit
Trigger EXTERNAL_LIB flag detected (protocol uses third-party Move dependencies) - Used by Breadth agents, depth-external
Open skill - /economic-design-audit
Trigger Pattern MONETARY_PARAMETER flag (required) - Inject Into Breadth agents (merged via M4 hierarchy)
Open skill

