ToughRADIUS AI Agent Development Guide
🤖 AI Agent Working Guidelines
🔍 Mandatory Requirement: Understand Existing Code Before Editing
Before touching any code, thoroughly inspect the relevant implementation and surrounding tests. Treat code search and context gathering as the very first step of every task.
Why This Matters
✅ Precise Targeting – Quickly locate existing implementations you can extend or reuse
✅ Architectural Awareness – Learn how modules collaborate before making changes
✅ Consistency – Mirror naming, data flow, and error-handling patterns already in place
✅ Risk Reduction – Avoid regressions caused by overlooking hidden dependencies
Recommended Search Workflow
Pinpoint the module: Identify packages, directories, or features involved (e.g.,
internal/radiusd/vendors).Use repository search tools: Combine
semantic_search,grep_search, andfile_searchwith precise keywords (function names, struct names, RFC identifiers, etc.).Read surrounding tests: Open the corresponding
*_test.go/ Playwright specs to understand expected behavior and edge cases.Record findings: Jot down key structs, helper functions, or patterns you must follow before implementing anything new.
Example Search Prompts
# Before adding new RADIUS vendor support
semantic_search "vendor attribute parsing" in internal/radiusd/vendors
grep_search "VendorCode" --include internal/radiusd/**
# Before adding new API endpoints
file_search "*/internal/adminapi/*routes*.go"
semantic_search "Echo middleware" in internal/webserver
# Before fixing authentication bugs
semantic_search "AuthError" in internal/radiusd
grep_search "app.GDB" --include internal/app/**
# Before large refactors
file_search "*radius_auth*.go"
list_code_usages AuthenticateUserIterative Exploration Pattern
Round 1: Macro View – Scan architecture docs (
docs/v9-architecture.md), service entry points, and top-level packages.Round 2: Detail Dive – Read concrete handler/service implementations plus related tests.
Round 3: Edge Cases – Inspect integration tests, benchmarks, or vendor-specific helpers to catch non-obvious behavior.
Always document the insights gained in your task notes or PR description so reviewers know which prior art influenced the change.
🧪 Mandatory Requirement: Continuous Verification
You MUST actively run tests throughout the development lifecycle.
Before changes: Run existing tests to establish a baseline.
During development: Run relevant tests frequently to catch regressions early.
After completion: Run the full test suite to ensure system integrity.
Never assume: "It should work" is not acceptable. Verify with
go test.
📝 Code is the Best Documentation Principle
Core Philosophy: Code IS Documentation, Documentation IS Code
We follow the "Standard Library Style" documentation approach. Just as the Go standard library is self-documenting, our codebase should be readable, understandable, and maintainable through comprehensive in-code comments, not separate Markdown files.
Correct Understanding of "Code Is Documentation":
✅ Documentation exists in the source code as comments
✅ Update comments in sync with every code change
✅ Full documentation is accessible through
go docand IDEs❌ Do not create separate Markdown documents for each module
❌ Do not generate documentation files after every development task
1. Write Documentation Directly in Code (Mandatory)
Every exported symbol (function, struct, interface, constant) must have a comprehensive comment in the source file that explains what it does, how to use it, and why it behaves that way.
Standard Library Style Checklist:
Package Comment: Every package must have a package-level comment explaining its purpose
Summary Sentence: The first sentence should be a concise summary that appears in
go docDetailed Description: Explain the behavior, side effects, and algorithm if necessary
Parameter Documentation: Use bulleted list format with types and constraints
Return Value Documentation: Clearly define what outputs are returned and when
Error Handling: Explicitly state what errors can be returned and under what conditions
Usage Examples: Provide code snippets in comments for complex APIs
Side Effects: Document any state changes, I/O operations, or concurrency implications
2. Documentation Examples
Example 1: Package-Level Documentation
// Package radiusd implements the core RADIUS protocol server supporting
// authentication (RFC 2865), accounting (RFC 2866), and RadSec (RFC 6614).
//
// This package provides concurrent request handling using goroutine pools,
// vendor-specific attribute parsing (Huawei, Cisco, Mikrotik), and integration
// with the ToughRADIUS database backend.
//
// Key components:
// - AuthServer: Handles RADIUS authentication on UDP port 1812
// - AcctServer: Handles RADIUS accounting on UDP port 1813
// - RadSecServer: Handles RADIUS over TLS on TCP port 2083
//
// Usage:
//
// authServer := NewAuthServer(config)
// go authServer.Start() // Runs in background
//
// For vendor attribute parsing, see internal/radiusd/vendors/.
package radiusdExample 2: Function Documentation
// AuthenticateUser validates user credentials against the RADIUS database.
// It checks username/password, account expiration, and session limits.
//
// The function performs the following validations in order:
// 1. User existence check
// 2. Password verification (supports PAP, CHAP, MS-CHAPv2)
// 3. Account status check (disabled/expired)
// 4. Concurrent session limit check
//
// Parameters:
// - username: User's login name (case-sensitive, max 255 chars)
// - password: Plain text password (will be hashed internally using configured algorithm)
// - nasIP: Network Access Server IP address for session tracking and NAS authorization
//
// Returns:
// - *domain.RadiusUser: User object if authentication succeeds, contains billing plan info
// - error: AuthError with metrics tag if validation fails, nil on success
//
// Common errors:
// - MetricsRadiusRejectUserNotFound: Username doesn't exist in database
// - MetricsRadiusRejectPasswordError: Password mismatch
// - MetricsRadiusRejectExpire: Account expired (ExpireTime < now)
// - MetricsRadiusRejectDisable: Account is disabled (Status != "enabled")
// - MetricsRadiusRejectConcurrent: Concurrent session limit exceeded
//
// Side effects:
// - Increments Prometheus metrics counter for auth attempts
// - Logs authentication result to zap logger with namespace "radius"
//
// Concurrency: Safe for concurrent use. Database queries use GORM connection pool.
//
// Example:
//
// user, err := AuthenticateUser("[email protected]", "secret123", "192.168.1.1")
// if err != nil {
// if errors.Is(err, ErrUserNotFound) {
// log.Error("user not found", zap.Error(err))
// }
// return err
// }
// log.Info("auth success", zap.String("username", user.Username))
func AuthenticateUser(username, password, nasIP string) (*domain.RadiusUser, error) {
// Implementation
}Example 3: Struct Documentation
// RadiusUser represents a user account in the RADIUS authentication system.
// It maps to the "radius_user" table in the database and is managed through
// the Admin API and web interface.
//
// This struct holds all user-specific configuration including authentication
// credentials, billing plan reference, account status, and session limits.
//
// Database table: radius_user
// GORM features: Auto-migration, soft delete (DeletedAt), timestamps
//
// Lifecycle:
// 1. Created via Admin API POST /api/v1/users
// 2. Authenticated during RADIUS Access-Request
// 3. Disabled/Expired based on Status and ExpireTime fields
// 4. Soft-deleted when removed (can be recovered)
//
// Concurrency: GORM handles database-level locking. Application code should
// use transactions when updating user and session data together.
type RadiusUser struct {
// ID is the auto-incrementing primary key.
// Generated by database on INSERT, immutable after creation.
ID int64 `json:"id" gorm:"primaryKey"`
// Username is the login name used for RADIUS authentication.
// Must be unique across the system (enforced by unique index).
// Format: Usually email-like (user@realm) or simple username.
// Constraints: Non-null, max 255 characters, case-sensitive.
Username string `json:"username" gorm:"uniqueIndex;not null;size:255"`
// Password is the hashed password for authentication.
// Stored in plaintext (legacy) or bcrypt hash depending on config.
// When using PAP, compared directly. For CHAP/MS-CHAPv2, must be plaintext.
// Security: Consider migrating to bcrypt for PAP-only deployments.
Password string `json:"password" gorm:"not null"`
// Status indicates the user's account status.
// Possible values: "enabled", "disabled", "expired"
// Default: "enabled"
// Authentication is rejected if Status != "enabled".
Status string `json:"status" gorm:"default:'enabled';size:20"`
// ExpireTime is the account expiration timestamp.
// If current time > ExpireTime, authentication is rejected with MetricsRadiusRejectExpire.
// Zero value means no expiration.
ExpireTime time.Time `json:"expire_time"`
// ProductId references the billing plan (radius_product table).
// Foreign key relationship (not enforced by DB, handled in application).
// Used to determine bandwidth limits, session timeout, and other policies.
ProductId int64 `json:"product_id" gorm:"index"`
// ... other fields
// CreatedAt is automatically set by GORM on INSERT.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is automatically updated by GORM on UPDATE.
UpdatedAt time.Time `json:"updated_at"`
// DeletedAt enables GORM soft delete. Non-null means record is deleted.
// Soft-deleted records are excluded from queries unless Unscoped() is used.
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
}Example 4: Interface Documentation
// Authenticator defines the interface for RADIUS authentication protocol implementations.
// Different implementations support various authentication methods defined in RFC 2865
// and related RFCs (PAP, CHAP, MS-CHAPv2, EAP).
//
// Implementations must be stateless and safe for concurrent use, as the same
// instance may be called from multiple goroutines handling different RADIUS requests.
//
// Standard implementations:
// - PAPAuthenticator: Plain-text password (RFC 2865 Section 5.2)
// - CHAPAuthenticator: Challenge-Response (RFC 2865 Section 5.3)
// - MSCHAPv2Authenticator: Microsoft CHAP version 2 (RFC 2759)
// - EAPAuthenticator: Extensible Authentication Protocol (RFC 3748)
//
// Example:
//
// var auth Authenticator = NewPAPAuthenticator()
// if valid, err := auth.CheckPassword(user, "secret123"); err != nil {
// return err
// } else if !valid {
// return ErrPasswordMismatch
// }
type Authenticator interface {
// CheckPassword verifies the provided password against the stored user credentials.
//
// The verification method depends on the authentication protocol:
// - PAP: Direct comparison or bcrypt hash verification
// - CHAP: Challenge-response validation using MD5
// - MS-CHAPv2: NT hash verification with challenge/response
//
// Parameters:
// - user: The user object retrieved from database, must not be nil
// - password: The password or response from RADIUS Access-Request packet
//
// Returns:
// - bool: True if password matches, false if password is incorrect
// - error: Non-nil if verification process fails (e.g., invalid hash format,
// missing challenge attribute). Returns nil for simple mismatch.
//
// Concurrency: Must be safe for concurrent calls.
//
// Example:
//
// valid, err := auth.CheckPassword(user, "plaintext_password")
// if err != nil {
// return fmt.Errorf("password check failed: %w", err)
// }
// if !valid {
// return ErrAuthenticationFailed
// }
CheckPassword(user *domain.RadiusUser, password string) (bool, error)
}3. Inline Comments for "Why" Not "What"
Use inline comments to explain why a specific implementation choice was made, especially for complex logic, workarounds, or optimizations. Do not explain what the code is doing if it's obvious from reading the code itself.
Good inline comments explain:
Non-obvious business logic
Performance optimizations
Workarounds for vendor quirks
Protocol-specific requirements
Edge cases and their handling
// ✅ Correct: Explain the "why" and context
// Huawei devices expect bandwidth in Kbps, but our billing plan stores it in Mbps.
// We multiply by 1024 (binary) instead of 1000 (decimal) to match Huawei's VSA spec.
// See: RFC 2548 Section 6.2 and internal/radiusd/vendors/huawei/README.md
return baseBandwidth * 1024
// ✅ Correct: Explain non-obvious behavior
// PostgreSQL GORM driver has a bug with empty IN clauses.
// Explicitly return empty result to avoid SQL syntax error.
if len(ids) == 0 {
return []domain.RadiusUser{}, nil
}
// ✅ Correct: Explain performance consideration
// Using goroutine pool instead of unlimited goroutines to prevent
// memory exhaustion under high request rate (>10k req/s observed in prod).
s.taskPool.Submit(func() { handleRequest(req) })
// ❌ Wrong: Explain the "what" (redundant)
// Multiply by 1024
return baseBandwidth * 1024
// ❌ Wrong: Obvious from code
// Set user status to enabled
user.Status = "enabled"
// ❌ Wrong: Commented-out code (use git history instead)
// return baseBandwidth * 1000 // Old calculation
return baseBandwidth * 10244. Vendor-Specific Code Must Document Protocol Details
Vendor-specific implementations must reference the authoritative specification and explain the data format, encoding rules, and any quirks or workarounds.
// ParseHuaweiInputAverageRate extracts downstream bandwidth limit from Huawei VSA attribute.
//
// Huawei VSA attribute format (based on RFC 2865 Vendor-Specific attribute):
// Vendor-Type: 11 (Huawei-Input-Average-Rate)
// Vendor-Length: Variable (typically 4-8 bytes)
// Vendor-Data: Unsigned integer representing bandwidth in Kbps
//
// Examples:
// - Value 1024 = 1 Mbps downstream limit
// - Value 10240 = 10 Mbps downstream limit
// - Value 102400 = 100 Mbps downstream limit
//
// Note: Huawei devices use binary Kbps (1024-based) not decimal (1000-based).
// This differs from Cisco which uses decimal Kbps.
//
// Parameters:
// - attr: RADIUS attribute with Vendor-ID = 2011 (Huawei)
//
// Returns:
// - int64: Bandwidth limit in Kbps, 0 if attribute is malformed
//
// References:
// - Huawei VSA Dictionary: internal/radiusd/vendors/huawei/README.md
// - RFC 2865 Section 5.26: Vendor-Specific Attribute Format
func ParseHuaweiInputAverageRate(attr *radius.Attribute) int64 {
if attr == nil || len(attr.Value) < 4 {
return 0
}
// Huawei stores bandwidth as 32-bit big-endian unsigned integer
return int64(binary.BigEndian.Uint32(attr.Value))
}Documentation Strategy: Code-First Approach
When to Write In-Code Comments (Always Required):
Every code file should be self-documenting. Comments are NOT optional.
✅ Package comment - First file in each package must have package doc
✅ All exported symbols - Functions, methods, types, constants, variables
✅ Complex algorithms - Step-by-step explanation of non-trivial logic
✅ Non-obvious design decisions - Why this approach was chosen
✅ Protocol implementations - Reference RFC numbers, vendor specs
✅ Performance-critical code - Explain optimizations and trade-offs
✅ Error conditions - What errors can occur and why
✅ Concurrency guarantees - Thread-safety, locking, goroutine usage
✅ Side effects - Database writes, I/O, metric updates, logging
When to Create Separate Markdown Documentation (Rare):
Only create separate docs when in-code comments are insufficient for the target audience.
✅ Architecture Overview (
docs/architecture.md) - High-level system design for newcomers✅ User Guides (
README.md,docs/deployment.md) - For end users, not developers✅ API Contracts (
docs/api-spec.md) - For external integrators (REST API, gRPC)✅ Protocol References (
docs/rfcs/) - Standard specifications (read-only)✅ Migration Guides (
docs/migration-v8-to-v9.md) - Breaking changes between versions❌ NOT for module documentation - Put it in the code as package/function comments
❌ NOT for feature descriptions - Describe in code comments + Git commits
❌ NOT for work summaries - Use Git history and PR descriptions
When to Update Existing Docs (Only When Necessary):
✅ Public API changes → Update code comments first, then external API docs if needed
✅ Configuration options → Update example config files and inline comments
✅ Breaking changes → Update CHANGELOG.md + migration guide
✅ Deployment process → Update deployment docs
❌ Internal refactoring → Only update code comments, no separate doc needed
❌ Bug fixes → Git commit message is enough, no doc update
❌ Performance improvements → Update inline comments if algo changed
Documentation Verification:
Use go doc to verify your documentation is accessible:
# View package documentation
go doc internal/radiusd
# View specific function documentation
go doc internal/radiusd.AuthenticateUser
# Generate HTML documentation for entire project
godoc -http=:6060
# Visit http://localhost:6060/pkg/github.com/talkincode/toughradius/Auto-Generated Documentation Rule (Strictly Prohibited)
AI Agent must NOT create separate documentation files unless explicitly requested.
The correct workflow is:
Write comprehensive in-code comments for all changes
Write clear Git commit messages explaining what and why
Update existing docs only if public API/config/deployment changed
Give brief completion confirmation in chat (1-2 sentences max)
Prohibited Actions:
❌ Auto-creating
SUMMARY.md,WORK_REPORT.md,DOCUMENTATION.mdafter task completion❌ Auto-creating module-specific doc files like
module-name-guide.md❌ Adding lengthy summaries in chat responses (multi-paragraph reports)
❌ Duplicating information that already exists in code comments or Git history
❌ Creating "documentation" as a separate deliverable for internal features
Allowed Actions:
✅ In-code comments - Always required, this IS the documentation
✅ Git commit messages - Explain what changed and why (Conventional Commits format)
✅ Brief completion note - 1-2 sentences confirming what was done
✅ Update existing docs - If public API, config, or deployment process changed
✅ Create docs when requested - User explicitly asks for a guide or tutorial
Correct Completion Response:
✅ Added Cisco vendor support with comprehensive in-code documentation and tests (98% coverage).Incorrect Completion Response:
## Cisco Vendor Implementation Summary ❌
### Overview
This document describes the implementation of Cisco vendor support...
### Architecture
The Cisco vendor module is located in...
### API Reference
...
(This is all redundant - should be in code comments and Git commits!)What Reviewers Should See:
# 1. Clear code comments (primary documentation)
$ go doc internal/radiusd/vendors/cisco
package cisco // import "github.com/talkincode/toughradius/v9/internal/radiusd/vendors/cisco"
Package cisco implements Cisco RADIUS Vendor-Specific Attribute (VSA) parsing...
func ParseCiscoAVPair(attr *radius.Attribute) map[string]string
ParseCiscoAVPair extracts key-value pairs from Cisco VSA attribute...
# 2. Git history (change log)
$ git log --oneline
a1b2c3d feat(radius): add Cisco vendor VSA parsing support
d4e5f6g test(radius): add Cisco vendor parsing tests (98% coverage)
g7h8i9j docs(radius): update vendor support list in README
# 3. No separate markdown files polluting the repo
$ ls docs/
architecture.md deployment.md api-integration.md rfcs/
# NOT: cisco-vendor-guide.md, work-summary.md, etc.Documentation Quality Standards
In-Code Documentation Must Be:
Clear - Use simple, plain language; avoid jargon unless explaining technical protocols
Complete - Document all parameters, returns, errors, side effects, concurrency
Practical - Include real-world usage examples for complex APIs
Accurate - Keep code and comments in sync (update comments when code changes)
Concise - Avoid redundant explanations of obvious code
Discoverable - Structured so
go docand IDEs can parse and display properly
Documentation Review Checklist:
Before submitting a PR, verify each exported symbol has:
Example: High-Quality In-Code Documentation
// GetUserOnlineSessions retrieves all active RADIUS sessions for a user.
//
// This function queries the accounting table for sessions with null stop time,
// indicating they are still active. It's used to enforce MaxSessions limits.
//
// Parameters:
// - username: User's login name (exact match, case-sensitive)
//
// Returns:
// - []*domain.RadiusAccounting: Slice of active session records (empty if none)
// - error: Database error if query fails (nil on success)
//
// Performance: Uses index on (username, acct_stop_time) for fast lookup.
// For users with >1000 sessions, consider pagination.
//
// Example:
// sessions, err := GetUserOnlineSessions("[email protected]")
// if err != nil {
// return fmt.Errorf("failed to check sessions: %w", err)
// }
// if len(sessions) >= maxSessions {
// return ErrMaxSessionsExceeded
// }
func GetUserOnlineSessions(username string) ([]*domain.RadiusAccounting, error) {
var sessions []*domain.RadiusAccounting
err := app.GDB().Where("username = ? AND acct_stop_time IS NULL", username).
Find(&sessions).Error
return sessions, err
}Golden Rules for "Code IS Documentation":
📝 In-code comments are THE documentation - Not supplementary, not optional
🎯 Write for readers - Future self, teammates, open-source contributors
🚫 No redundant Markdown files - Don't duplicate what's in code comments
✅ Git history is the changelog - Commit messages record what/when/why
🔍 Verify with
go doc- If it doesn't show up ingo doc, add more comments♻️ Update comments when code changes - Comments are part of the code, not separate
📚 Separate docs only for end users - Deployment guides, user manuals, API contracts
Anti-Pattern Recognition:
❌ Wrong Workflow:
1. Write code
2. Code works
3. Create DOCUMENTATION.md to explain the code
4. PR includes both code files and doc files
✅ Correct Workflow:
1. Write code WITH comprehensive comments
2. Code AND comments work together
3. Verify with `go doc package.Function`
4. PR includes only code files with excellent comments
5. Git commit message explains what/whyCore Development Principles
This project strictly follows these three core development principles. All code contributions must comply with these standards:
🧪 Test-Driven Development (TDD)
Mandatory Requirement: Write Tests First, Then Code
Before implementing any feature or fixing any bug, you MUST write a test case that reproduces the issue or defines the expected behavior.
Create Test File: If
internal/radiusd/auth.gois being modified, create/editinternal/radiusd/auth_test.go.Define Test Case: Write a test function
TestAuth_UserNotFoundthat asserts the expected failure.Run Test (Fail): Execute the test to confirm it fails (Red).
Implement Code: Write the minimum code necessary to pass the test.
Run Test (Pass): Execute the test to confirm it passes (Green).
Refactor: Clean up the code while keeping tests passing.
TDD Workflow
Red Phase - Write failing tests
# Create test file touch internal/radiusd/new_feature_test.go # Run tests (should fail) go test ./internal/radiusd/new_feature_test.go -vGreen Phase - Write minimal implementation to pass tests
# Implement feature code vim internal/radiusd/new_feature.go # Run tests again (should pass) go test ./internal/radiusd/new_feature_test.go -vRefactor Phase - Optimize code while keeping tests passing
# Continuously run tests to ensure safe refactoring go test ./... -v
🔄 Continuous Verification (Mandatory)
Do not wait until the end to run tests.
Every logical change should be followed by a test run.
If a test fails, stop and fix it immediately. Do not pile up changes on top of broken tests.
Use
go test ./...frequently to check for side effects in other packages.Verify compilation with
go build ./...to ensure no syntax errors or type mismatches were introduced.
Test Coverage Requirements
New feature code coverage must be ≥ 80%
Core RADIUS protocol module coverage must be ≥ 90%
Critical business logic must have integration tests
# Check test coverage
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out
# View coverage statistics
go test ./internal/radiusd -coverprofile=coverage.out
go tool cover -func=coverage.outTest File Organization
internal/radiusd/
├── auth_passwd_check.go # Implementation file
├── auth_passwd_check_test.go # Unit tests (same package)
├── radius_auth.go
├── radius_test.go # Integration tests
└── vendor_parse_test.go # Feature testsTest Case Naming Convention
// ✅ Correct: Clearly describe test intent
func TestAuthPasswordCheck_ValidUser_ShouldReturnSuccess(t *testing.T) {}
func TestAuthPasswordCheck_ExpiredUser_ShouldReturnError(t *testing.T) {}
func TestGetNas_UnauthorizedIP_ShouldReturnAuthError(t *testing.T) {}
// ❌ Wrong: Ambiguous
func TestAuth(t *testing.T) {}
func TestFunc1(t *testing.T) {}Table-Driven Tests
For multi-scenario testing, use table-driven approach:
func TestVendorParse(t *testing.T) {
tests := []struct {
name string
vendorCode string
input string
wantMac string
wantVlan1 int64
}{
{"Huawei VLAN", VendorHuawei, "vlan=100", "", 100},
{"Mikrotik MAC", VendorMikrotik, "mac=aa:bb:cc:dd:ee:ff", "aa:bb:cc:dd:ee:ff", 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test logic
})
}
}🔄 GitHub Workflow
Mandatory Requirement: Follow Git Flow branching model and standard PR process
Branching Strategy
main (production branch)
├── v9dev (development branch)
│ ├── feature/user-management # Feature branch
│ ├── feature/radius-vendor-cisco # Feature branch
│ ├── bugfix/session-timeout # Bug fix
│ └── hotfix/security-patch # Hotfix
└── release/v9.1.0 # Release branchStandard Development Process
1. Create Feature Branch
# Create feature branch from v9dev
git checkout v9dev
git pull origin v9dev
git checkout -b feature/add-cisco-vendor
# Branch naming convention
# feature/ - New features
# bugfix/ - Bug fixes
# hotfix/ - Hotfixes
# refactor/ - Code refactoring
# docs/ - Documentation updates2. TDD Loop Development
# 1️⃣ Write tests first
vim internal/radiusd/vendors/cisco/cisco_test.go
# 2️⃣ Run tests (red)
go test ./internal/radiusd/vendors/cisco -v
# 3️⃣ Implement feature
vim internal/radiusd/vendors/cisco/cisco.go
# 4️⃣ Run tests (green)
go test ./internal/radiusd/vendors/cisco -v
# 5️⃣ Commit atomic changes
git add internal/radiusd/vendors/cisco/
git commit -m "test: add Cisco vendor attribute parsing tests"
git commit -m "feat: implement Cisco vendor attribute parsing"3. Commit Convention (Conventional Commits)
# Format: <type>(<scope>): <subject>
git commit -m "feat(radius): add Cisco vendor support"
git commit -m "test(radius): add unit tests for Cisco attributes"
git commit -m "fix(auth): correct password validation logic"
git commit -m "docs(api): update RADIUS authentication API docs"
git commit -m "refactor(database): optimize user query performance"
git commit -m "perf(radius): reduce authentication latency by 20%"
# Type definitions
# feat: New features
# fix: Bug fixes
# test: Testing related
# docs: Documentation updates
# refactor: Code refactoring
# perf: Performance optimization
# style: Code formatting
# chore: Build/tool changes4. Create Pull Request
PR must include:
✅ All tests passing (
go test ./...)✅ Code coverage meets requirements
✅ Clear description and change summary
✅ Associated Issue number
✅ At least one code reviewer approval
PR Template:
## Change Description
Brief description of the purpose and main changes of this PR
## Change Type
- [ ] New feature
- [ ] Bug fix
- [ ] Performance optimization
- [ ] Code refactoring
- [ ] Documentation update
## Test Coverage
- [ ] Added unit tests
- [ ] Added integration tests
- [ ] Test coverage ≥ 80%
- [ ] All tests passing
## Checklist
- [ ] Code follows project conventions
- [ ] Commit messages follow Conventional Commits
- [ ] Updated relevant documentation
- [ ] No breaking changes (or documented in CHANGELOG)
## Related Issue
Closes #1235. Continuous Integration Checks
Each PR automatically triggers:
✅
go test ./...- Run all tests✅
go build- Ensure code compiles✅ Docker image build
✅ Code style checks
Release Process
# 1. Create release branch
git checkout -b release/v9.1.0 v9dev
# 2. Update version and CHANGELOG
vim VERSION
vim CHANGELOG.md
# 3. Merge to main and tag
git checkout main
git merge --no-ff release/v9.1.0
git tag -a v9.1.0 -m "Release version 9.1.0"
git push origin main --tags
# 4. Trigger GitHub Actions auto-release
# - Build AMD64/ARM64 binaries
# - Publish Docker images to DockerHub and GHCR
# - Create GitHub Release📦 Minimum Viable Product (MVP) Principle
Mandatory Requirement: Each feature must be delivered in minimum viable units
MVP Design Method
Identify Core Value
❓ What problem does this feature solve?
❓ What is the simplest implementation?
❓ What is essential vs. nice-to-have?
Feature Breakdown Example
❌ Wrong approach: Implement complete feature at once Issue #123: Add Cisco vendor support └── Includes auth, accounting, VSA attributes, config management, Web UI... ✅ Correct approach: MVP breakdown Issue #123: Add Cisco vendor basic auth support (MVP-1) ├── PR #124: Cisco VSA attribute parsing ├── PR #125: Cisco auth flow integration └── PR #126: Basic test cases Issue #130: Extend Cisco accounting features (MVP-2) └── Built on MVP-1 Issue #135: Add Cisco Web management UI (MVP-3) └── Built on MVP-1 + MVP-2MVP Delivery Standards
Each MVP must be:
✅ Independently Usable - Does not depend on incomplete features
✅ Fully Tested - Coverage meets requirements
✅ Well Documented - API docs + usage guide
✅ Demonstrable - Can run and show value
✅ Rollback-Safe - Does not break existing functionality
MVP Practice Examples
Example 1: Adding RADIUS Vendor Support
MVP-1 (Week 1): Basic attribute parsing ✅
├── vendor_cisco.go # Vendor constant definitions
├── vendor_cisco_test.go # Parsing tests
└── Support reading basic VSA attributes
MVP-2 (Week 2): Authentication integration ✅
├── auth_accept_config.go # Add Cisco case
├── auth_cisco_test.go # Auth integration tests
└── Support Cisco device auth flow
MVP-3 (Week 3): Accounting support ✅
└── Extend accounting records with Cisco-specific fields
MVP-4 (Week 4): Web management ✅
└── Admin API adds Cisco configuration UIExample 2: Performance Optimization
MVP-1: Identify bottlenecks ✅
├── Add performance test benchmarks
├── Identify hotspot functions
└── Establish performance baseline
MVP-2: Optimize database queries ✅
├── Add indexes
├── Optimize N+1 queries
└── Verify 20% performance improvement
MVP-3: Caching layer ✅ (optional)
└── Continue if performance still not meeting targetsComplete Development Workflow Example
Scenario: Adding New RADIUS Vendor Support (Cisco)
Step 1: Create Issue (Requirements Analysis)
Title: [Feature] Add Cisco RADIUS Vendor Support
## MVP-1 Scope
- [ ] Parse Cisco VSA attributes
- [ ] Unit test coverage ≥ 90%
- [ ] Documentation update
## MVP-2 Scope (Future)
- [ ] Authentication flow integration
- [ ] Accounting feature support
## Not Included
- Web management UI (MVP-3)
- Advanced configuration management (MVP-4)Step 2: TDD Development
# 1️⃣ Create branch
git checkout -b feature/cisco-vendor-mvp1 v9dev
# 2️⃣ Write tests first (red)
cat > internal/radiusd/vendors/cisco/cisco_test.go << 'EOF'
package cisco
import "testing"
func TestParseCiscoAVPair(t *testing.T) {
tests := []struct{
name string
input string
want map[string]string
}{
{"basic", "key=value", map[string]string{"key": "value"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ParseAVPair(tt.input)
// Assertion logic
})
}
}
EOF
# 3️⃣ Run tests (should fail)
go test ./internal/radiusd/vendors/cisco -v
# 4️⃣ Implement minimal code (green)
cat > internal/radiusd/vendors/cisco/cisco.go << 'EOF'
package cisco
func ParseAVPair(input string) map[string]string {
// Minimal implementation
return map[string]string{}
}
EOF
# 5️⃣ Run tests (should pass)
go test ./internal/radiusd/vendors/cisco -v
# 6️⃣ Refactor and optimize
# Improve implementation while keeping tests passing
# 7️⃣ Check coverage
go test ./internal/radiusd/vendors/cisco -coverprofile=coverage.out
go tool cover -func=coverage.out | grep totalStep 3: Commit Code
# Atomic commits
git add internal/radiusd/vendors/cisco/cisco_test.go
git commit -m "test(radius): add Cisco AVPair parsing tests"
git add internal/radiusd/vendors/cisco/cisco.go
git commit -m "feat(radius): implement Cisco AVPair parsing (MVP-1)"
git add docs/radius/cisco-vendor.md
git commit -m "docs(radius): add Cisco vendor documentation"Step 4: Create Pull Request
git push origin feature/cisco-vendor-mvp1
# Create PR on GitHub, fill in PR templateStep 5: Code Review and Merge
Wait for CI to pass
Code review feedback
Fix issues, push updates
Merge to v9dev after approval
Step 6: Plan MVP-2
Create new Issue for next MVP
Repeat the above process
Quality Gates
All code must pass before merging:
✅ Automated Checks
✅ Code Review
✅ Documentation Requirements (Code-First Approach)
✅ MVP Acceptance
Common Anti-Patterns (Prohibited)
❌ Anti-Pattern 1: Exporting APIs Without Documentation
// ❌ Wrong: Exported function with no comment
func AuthenticateUser(username, password, nasIP string) (*domain.RadiusUser, error) {
// Implementation
}
// ✅ Correct: Comprehensive API documentation
// AuthenticateUser validates user credentials against the RADIUS database.
// It checks username/password, account expiration, and session limits.
//
// Parameters:
// - username: User's login name (case-sensitive)
// - password: Plain text password (will be hashed internally)
// - nasIP: Network Access Server IP address for session tracking
//
// Returns:
// - *domain.RadiusUser: User object if authentication succeeds
// - error: AuthError with metrics tag if validation fails
func AuthenticateUser(username, password, nasIP string) (*domain.RadiusUser, error) {
// Implementation
}❌ Anti-Pattern 2: Committing Without Tests
# Wrong example
git commit -m "feat: add new feature" # No corresponding test file
# Correct approach
git commit -m "test: add tests for new feature"
git commit -m "feat: add new feature"❌ Anti-Pattern 3: Giant PRs
❌ PR #100: Complete user management system implementation
+2000 -500 lines across 50 files
✅ Split into:
PR #101: User model and database migration (MVP-1)
PR #102: User CRUD API endpoints (MVP-2)
PR #103: User management UI (MVP-3)❌ Anti-Pattern 4: Implementation Before Tests
// ❌ Wrong flow
1. Implement complete feature
2. Feature becomes complex
3. Difficult to add tests
4. Insufficient test coverage
// ✅ TDD flow
1. Write tests (define behavior)
2. Minimal implementation
3. Refactor and optimize
4. Test coverage naturally achieved❌ Anti-Pattern 5: Skipping Code Review
# ❌ Direct push to main branch
git push origin main # Rejected by protected branch
# ✅ Through PR process
git push origin feature/my-feature
# Create PR → CI checks → Code review → Merge❌ Anti-Pattern 6: Creating Redundant Documentation
# ❌ Wrong: Auto-generating summary docs after each task
feature-implementation.go # Implementation
feature-implementation-summary.md # Redundant - info should be in code comments
WORK_REPORT.md # Redundant - info should be in Git commits
# ✅ Correct: Code + Comments + Git History
feature-implementation.go # Implementation with comprehensive comments
# Git commit messages record the what/why/when
# No separate summary document needed❌ Anti-Pattern 7: Introducing CGO Dependencies
// ❌ Wrong: Importing a library that requires CGO
import "github.com/mattn/go-sqlite3"
// ✅ Correct: Using a pure Go alternative
import "github.com/glebarez/sqlite"Tool Configuration
Local Development Environment Setup
# Install Git hooks (automated testing)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "Running tests..."
export CGO_ENABLED=0
go test ./...
if [ $? -ne 0 ]; then
echo "❌ Tests failed, commit blocked"
exit 1
fi
echo "✅ Tests passed"
EOF
chmod +x .git/hooks/pre-commit
# Configure commit template
git config commit.template .gitmessage.txtRecommended VS Code Extensions
Go - Go language support
Go Test Explorer - Test visualization
Coverage Gutters - Coverage display
Conventional Commits - Commit convention helper
GitLens - Git enhancement
References
Remember: Quality over speed, Usable over perfect, Tests before code, Code is the documentation!
Documentation Hierarchy:
🥇 Code + Comprehensive Comments - First-class documentation
🥈 Git Commit History - Records what/why/when
🥉 Minimal Separate Docs - Only for architecture & external APIs
🚫 Technical Constraints
Mandatory Requirement: No CGO Dependencies
This project is designed to be cross-platform and easily deployable.
Strictly Forbidden: Introducing any library that requires
CGO_ENABLED=1.Database Drivers: Use pure Go drivers only (e.g.,
glebarez/sqliteinstead ofmattn/go-sqlite3).Build Flags: Always ensure
CGO_ENABLED=0is set in build scripts and CI configurations.
Last updated
Was this helpful?