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=