chore: Update linter configuration and CI workflow for improved code quality

cognitive committed Jan 27, 2026 at 11:35 UTC 0d708f2e87dab7554022e41d01c47bcf0f21b11a
7 files changed +370 -55
.github/workflows/ci.yml
+51 -1
@@ -7,6 +7,9 @@ on:
7 branches: ["*"]
8 workflow_dispatch:
9
10 +env:
11 + CGO_ENABLED: "0"
12 +
13 jobs:
14 test:
15 name: Test
@@ -38,13 +41,60 @@ jobs:
41 - name: Generate protobuf files
42 run: make build-protoc
43
44 + - name: Run vet
45 + run: go vet ./...
46 +
47 - name: Run tests
48 run: go test -v -race -coverprofile=coverage.out ./...
49
50 + lint:
51 + name: Lint
52 + runs-on: ubuntu-latest
53 + steps:
54 + - name: Checkout code
55 + uses: actions/checkout@v5
56 +
57 + - name: Set up Go
58 + uses: actions/setup-go@v6
59 + with:
60 + go-version: "stable"
61 + check-latest: true
62 +
63 + - name: Download dependencies
64 + run: go mod download
65 +
66 + - name: Install golangci-lint
67 + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
68 +
69 + - name: Run golangci-lint
70 + run: golangci-lint run
71 +
72 + security:
73 + name: Security
74 + runs-on: ubuntu-latest
75 + steps:
76 + - name: Checkout code
77 + uses: actions/checkout@v5
78 +
79 + - name: Set up Go
80 + uses: actions/setup-go@v6
81 + with:
82 + go-version: "stable"
83 + check-latest: true
84 +
85 + - name: Download dependencies
86 + run: go mod download
87 +
88 + - name: Install govulncheck
89 + run: go install golang.org/x/vuln/cmd/govulncheck@latest
90 +
91 + - name: Run govulncheck
92 + run: govulncheck ./...
93 +
94 build:
95 name: Build
96 runs-on: ubuntu-latest
47 - needs: test
97 + needs: [test, lint, security]
98
99 steps:
100 - name: Checkout code
.golangci.yml
+161 -50
@@ -2,46 +2,94 @@ version: "2"
2
3 run:
4 tests: true
5 - # Go 1.25+ will handle modules impeccably; ensure the linter knows the version.
5 go: "1.25"
6
7 +formatters:
8 + enable:
9 + - goimports
10 + settings:
11 + goimports:
12 + local-prefixes:
13 + - gosuda.org
14 +
15 linters:
16 default: standard
17
18 enable:
12 - # --- The Essentials (Bugs & Correctness) ---
13 - - govet # Standard vet
14 - - errcheck # Unchecked errors are fatal
15 - - staticcheck # Dominant static analysis
16 - - gochecknoglobals # Disallows global variables (improves concurrency safety)
17 - - gochecknoinits # Disallows init functions (can have concurrency implications)
18 - - gocritic # General linter with many checks, including some concurrency-related ones like deferInLoop
19 - - containedctx # Ensures context.Context is the first argument (improves context propagation)
20 -
21 - # --- The Dark Arts (Performance & Concurrency) ---
22 - - prealloc # Encourages slice pre-allocation (critical for low latency)
23 - - bodyclose # Ensures HTTP response bodies are closed
24 - - noctx # HTTP requests must have context (prevents goroutine leaks)
25 - - copyloopvar # Detects loop variable copying (Modern Go replacement for exportloopref)
26 - - intrange # Suggests using newer `for i := range n` syntax (Go 1.22+)
19 + # --- Tier 1: Bugs & Correctness ---
20 + - govet
21 + - errcheck
22 + - staticcheck
23 + - unused
24 + - gosec
25 + - errorlint
26 + - copyloopvar
27 + - nilerr
28 + - bodyclose
29 + - sqlclosecheck
30 + - rowserrcheck
31 + - durationcheck
32 + - makezero
33 + - noctx
34 +
35 + # --- Tier 2: Code Quality & Style ---
36 + - gocritic
37 + - revive
38 + - unconvert
39 + - unparam
40 + - wastedassign
41 + - misspell
42 + - whitespace
43 + - godot
44 + - goconst
45 + - dupword
46 + - usestdlibvars
47 + - testifylint
48 + - testableexamples
49 + - tparallel
50 + - usetesting
51 +
52 + # --- Tier 3: Concurrency & Safety ---
53 + - gochecknoglobals
54 + - gochecknoinits
55 + - containedctx
56 +
57 + # --- Tier 4: Performance & Modernization ---
58 + - prealloc
59 + - intrange
60 + - modernize
61 + - fatcontext
62 + - perfsprint
63 + - reassign
64 + - spancheck
65 + - mirror
66 + - recvcheck
67
68 exclusions:
69 rules:
70 - linters:
71 - errcheck
72 source: "^\\s*defer\\s+"
73 + - path: "_test\\.go"
74 + linters:
75 + - bodyclose
76 + - errcheck
77 + - gosec
78 + - noctx
79 + - wrapcheck
80 + - goconst
81 + - funlen
82 + - dupl
83 + - gochecknoglobals
84 + - text: "should have a package comment"
85 + linters: [revive]
86 + - text: "exported \\S+ \\S+ should have comment"
87 + linters: [revive]
88
89 settings:
90 errcheck:
36 - # FALSE: Allows you to explicitly ignore an error using `_ = func()`
37 - # If true, `_ = func()` is still a violation.
91 check-blank: false
39 -
40 - # FALSE: Does not force checking type assertion results (v, ok := x.(T))
92 check-type-assertions: false
42 -
43 - # List of functions to exclude from checking.
44 - # These are the most common sources of "noise" in standard Go development.
93 exclude-functions:
94 - fmt.Printf
95 - fmt.Println
@@ -49,33 +97,96 @@ linters:
97 - fmt.Fprintf
98 - fmt.Fprint
99 - fmt.Fprintln
52 - - fmt.Sprintf # Rarely fails unless OOM
53 - - os.Unsetenv # Usually safe to ignore
54 - - encoding/json.Marshal # Safe ONLY if you trust the struct tags/types
55 - - encoding/json.Unmarshal # Safe ONLY if you trust the input data
56 - - encoding/json.NewEncoder # Encoder setup
57 - - encoding/json.NewDecoder # Decoder setup
58 - - strings.Builder.WriteString # Writes to memory; usually safe
59 - - strings.Builder.Write # Writes to memory; usually safe
60 - - bytes.Buffer.Write # Writes to memory; usually safe
61 - - bytes.Buffer.WriteString # Writes to memory; usually safe
62 - - io.Copy # Standard I/O operation
63 - - context.WithTimeout # Context creation
64 - - context.WithCancel # Context creation
65 - - log.Printf # Standard logging
66 - - log.Println # Standard logging
67 - - log.Print # Standard logging
68 - - time.Now # Time retrieval
69 - - time.Sleep # Time operations
70 - - sync.Mutex.Lock # Mutex operations
71 - - sync.Mutex.Unlock # Mutex operations
72 - - sync.RWMutex.RLock # RWMutex operations
73 - - sync.RWMutex.RUnlock # RWMutex operations
74 - - atomic.AddInt64 # Atomic operations
75 - - atomic.StoreInt64 # Atomic operations
76 - - atomic.LoadInt64 # Atomic operations
77 - - rand.Read # Cryptographically secure random
78 - - crypto/rand.Read # Secure random source
100 + - fmt.Sprintf
101 + - os.Unsetenv
102 + - encoding/json.Marshal
103 + - encoding/json.Unmarshal
104 + - encoding/json.NewEncoder
105 + - encoding/json.NewDecoder
106 + - strings.Builder.WriteString
107 + - strings.Builder.Write
108 + - bytes.Buffer.Write
109 + - bytes.Buffer.WriteString
110 + - io.Copy
111 + - context.WithTimeout
112 + - context.WithCancel
113 + - log.Printf
114 + - log.Println
115 + - log.Print
116 + - time.Now
117 + - time.Sleep
118 + - sync.Mutex.Lock
119 + - sync.Mutex.Unlock
120 + - sync.RWMutex.RLock
121 + - sync.RWMutex.RUnlock
122 + - atomic.AddInt64
123 + - atomic.StoreInt64
124 + - atomic.LoadInt64
125 + - rand.Read
126 + - crypto/rand.Read
127 +
128 + gocritic:
129 + enabled-tags:
130 + - diagnostic
131 + - style
132 + - performance
133 + - experimental
134 + - opinionated
135 + disabled-checks:
136 + - hugeParam
137 + - rangeValCopy
138 +
139 + govet:
140 + enable-all: true
141 + disable:
142 + - fieldalignment
143 + settings:
144 + shadow:
145 + strict: true
146 +
147 + revive:
148 + rules:
149 + - name: blank-imports
150 + - name: context-as-argument
151 + - name: context-keys-type
152 + - name: dot-imports
153 + - name: error-return
154 + - name: error-strings
155 + - name: error-naming
156 + - name: exported
157 + disabled: true
158 + - name: if-return
159 + - name: increment-decrement
160 + - name: var-naming
161 + - name: var-declaration
162 + - name: range
163 + - name: receiver-naming
164 + - name: time-naming
165 + - name: unexported-return
166 + - name: indent-error-flow
167 + - name: errorf
168 + - name: empty-block
169 + - name: superfluous-else
170 + - name: unused-parameter
171 + - name: unreachable-code
172 + - name: redefines-builtin-id
173 +
174 + gosec:
175 + excludes:
176 + - G104
177 + - G304
178 +
179 + perfsprint:
180 + strconcat: true
181 +
182 + fatcontext:
183 + check-struct-pointers: true
184 +
185 + spancheck:
186 + checks:
187 + - end
188 + - record-error
189 + - set-status
190
191 issues:
192 max-issues-per-linter: 0
.pre-commit-config.yaml renamed
AGENTS.md new
+128
@@ -0,0 +1,128 @@
1 +# AGENTS.md
2 +
3 +Repo-specific guidance for automated agents working on Portal.
4 +
5 +## Quick Commands
6 +
7 +Build:
8 +- `make build` (all artifacts)
9 +- `make build-server` (relay server binary)
10 +- `make build-frontend` (React admin UI)
11 +- `make build-wasm` (webclient WASM)
12 +- `make build-tunnel` (portal-tunnel binaries)
13 +- `make build-protoc` (protobufs)
14 +
15 +Run:
16 +- `make run` (run `./bin/relay-server`)
17 +- `docker compose up` (full stack, relay at :4017, admin at `/admin`)
18 +
19 +Lint/Format/Test:
20 +- `make fmt` (gofmt + goimports)
21 +- `make vet` (go vet)
22 +- `make lint` (golangci-lint)
23 +- `make test` (go test -v -race ./...)
24 +- `make vuln` (govulncheck)
25 +- `make tidy` (go mod tidy + go mod verify)
26 +
27 +Single test:
28 +- `go test -v -run TestName ./path/to/pkg`
29 +
30 +Frontend dev:
31 +- `cd cmd/relay-server/frontend && npm run dev`
32 +- `cd cmd/relay-server/frontend && npm run build`
33 +- `cd cmd/relay-server/frontend && npm run lint`
34 +
35 +## Architecture (Big Picture)
36 +
37 +Portal is a relay network that connects Apps (service publishers) and Clients (service consumers) through a central relay server without decrypting payloads.
38 +
39 +Core components:
40 +- Relay server: `cmd/relay-server` (HTTP + WS relay, admin UI serving)
41 +- Relay core logic: `portal/` (lease manager, connection handlers, forwarding)
42 +- Crypto + protocols: `portal/core/` and `portal/core/proto/` (RDSEC/RDVERB)
43 +- SDK for Apps: `sdk/`
44 +- Tunnel client: `cmd/portal-tunnel/` (exposes local services)
45 +- Webclient: `cmd/webclient/` (WASM + service worker served by relay)
46 +- Admin frontend: `cmd/relay-server/frontend/` (built into `cmd/relay-server/dist/app`)
47 +
48 +## Connection Flow (High Level)
49 +
50 +1. App registers a Lease with the relay (identity, ALPN, metadata).
51 +2. Client requests connection by Lease ID or name.
52 +3. Relay forwards the request to the App and brokers the connection.
53 +4. RDSEC handshake establishes end-to-end encryption (X25519 + ChaCha20-Poly1305).
54 +5. Yamux multiplexes multiple streams over one relay connection.
55 +
56 +## Key Terms
57 +
58 +- Portal / Relay: central mediator; never decrypts payloads.
59 +- App: service publisher using SDK or tunnel to register Leases.
60 +- Client: consumer (often browser + WASM) connecting via relay.
61 +- Lease: advertising unit; one Lease maps to one public endpoint.
62 +
63 +## Where to Look
64 +
65 +- `cmd/relay-server/` (entrypoint and HTTP/WS relay)
66 +- `portal/` (core relay logic)
67 +- `portal/core/proto/` (protocol definitions)
68 +- `sdk/` (App integration)
69 +- `cmd/portal-tunnel/` (tunnel client)
70 +- `cmd/webclient/` (WASM client)
71 +- `docs/architecture.md` and `docs/glossary.md`
72 +
73 +## Repo Basics
74 +
75 +- Module: `gosuda.org/portal`
76 +- Go version: 1.25.3 (from `go.mod`)
77 +
78 +## Gosuda Go Standards
79 +
80 +Formatting & style:
81 +- Run formatting before commits (see Quick Commands).
82 +- Import order: stdlib -> external -> internal (blank-line separated).
83 +- Naming: packages lowercase single-word; interfaces as behavior verbs; errors use `Err` prefix for sentinels and `Error` suffix for types.
84 +- Context first parameter for public I/O: `func Do(ctx context.Context, ...)`.
85 +- CGo disabled: `CGO_ENABLED=0`.
86 +
87 +Static analysis & linters:
88 +- Use `go vet`, `golangci-lint`, `go test -race`, and `govulncheck` (see Quick Commands).
89 +- Linter tiers: correctness, quality, concurrency safety, and performance/modernization (configured in `.golangci.yml`).
90 +
91 +Error handling:
92 +- Wrap with `%w` and include call-site context.
93 +- Sentinel errors per package; use `errors.Is`/`errors.As`.
94 +- Use `errors.Join` for multi-error.
95 +- Never ignore errors unless explicitly excluded by errcheck.
96 +
97 +Iterators (Go 1.23+):
98 +- Signatures: `func(yield func() bool)`, `func(yield func(V) bool)`, `func(yield func(K, V) bool)`.
99 +- Always check yield return; prefer stdlib helpers like `slices.Collect` and `maps.Keys`.
100 +
101 +Context & concurrency:
102 +- Prefer `errgroup.Group` for parallel work, `SetLimit` for bounds.
103 +- No goroutines without clear exit; creator owns lifecycle.
104 +- Directional channels in signatures; only sender closes.
105 +- Avoid `time.After` in loops; use `context.WithTimeout` or `time.Ticker`.
106 +
107 +Testing:
108 +- Use race detector in normal test runs.
109 +- Use `t.Context()` in tests where applicable.
110 +- Benchmarks should use `for b.Loop() {}`.
111 +
112 +Security:
113 +- Use `govulncheck` and `go mod verify` during release workflows.
114 +- Avoid `math/rand` for security-sensitive operations.
115 +
116 +Performance:
117 +- Avoid `reflect` on hot paths; prefer generics or type switches.
118 +- Use `sync.Pool` for hot paths only.
119 +
120 +Module hygiene:
121 +- Always commit `go.mod` and `go.sum`; never commit `go.work`.
122 +- Pin toolchain version to match `go.mod` (currently 1.25.3).
123 +
124 +CI/CD:
125 +- CI runs test -> lint -> security -> build (`.github/workflows/ci.yml`).
126 +
127 +Verbalized sampling:
128 +- For non-trivial changes: sample multiple intents, explore edge cases, assess coupling, tidy first, and surface tradeoffs.
CLAUDE.md new
+1
@@ -0,0 +1 @@
1 +AGENTS.md
\ No newline at end of file
Makefile
+29 -2
@@ -1,6 +1,8 @@
1 SHELL := /bin/sh
2 +CGO_ENABLED := 0
3 +export CGO_ENABLED
4
3 -.PHONY: help run build build-wasm compress-wasm build-frontend build-tunnel build-server clean
5 +.PHONY: help fmt vet lint test vuln tidy build-go all run build build-wasm compress-wasm build-frontend build-tunnel build-server clean
6
7 .DEFAULT_GOAL := help
8
@@ -14,11 +16,36 @@ help:
16 @echo " make run - Run relay server"
17 @echo " make clean - Remove build artifacts"
18
19 +fmt:
20 + gofmt -w .
21 + goimports -w .
22 +
23 +vet:
24 + go vet ./...
25 +
26 +lint:
27 + golangci-lint run
28 +
29 +test:
30 + go test -v -race -coverprofile=coverage.out ./...
31 +
32 +vuln:
33 + govulncheck ./...
34 +
35 +tidy:
36 + go mod tidy
37 + go mod verify
38 +
39 +build-go:
40 + go build ./...
41 +
42 +all: fmt vet lint test vuln build-go build
43 +
44 run:
45 ./bin/relay-server
46
47 # Convenience target
21 -build: build-wasm build-frontend build-tunnel build-server
48 +build: build-go build-wasm build-frontend build-tunnel build-server
49
50 build-protoc:
51 protoc -I . \
go.sum
-2
@@ -40,8 +40,6 @@ golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
40 golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
41 google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
42 google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
43 -gopkg.eu.org/broccoli v1.2.2 h1:/VnfW2PzmROadiYG+hGPz6Hczp7vghiyPvjGGIN1b3o=
44 -gopkg.eu.org/broccoli v1.2.2/go.mod h1:eM8HnmLyfiQHAwqh2afErWYnAkkOvi+RXgoXBRhKMCQ=
43 gopkg.eu.org/broccoli v1.2.3 h1:Lc6+C3n24sRGoFyYxo1qz8iiXQzwB6VcN9bLjnT9vSo=
44 gopkg.eu.org/broccoli v1.2.3/go.mod h1:eM8HnmLyfiQHAwqh2afErWYnAkkOvi+RXgoXBRhKMCQ=
45 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=