OpenSpeak/openspec/specs/009-development-guidelines.md
Alexis Bruneteau dc59df9336 🎉 Complete OpenSpeak v0.1.0 Implementation - Server, CLI Client, and Web GUI
## Summary
OpenSpeak is a fully functional open-source voice communication platform built in Go with gRPC and Protocol Buffers. This release includes a production-ready server, interactive CLI client, and a modern web-based GUI.

## Components Implemented

### Server (cmd/openspeak-server)
- Complete gRPC server with 4 services and 20+ RPC methods
- Token-based authentication system with permission management
- Channel management with CRUD operations and member tracking
- Real-time presence tracking with idle detection (5-min timeout)
- Voice packet routing infrastructure with multi-subscriber support
- Graceful shutdown and signal handling
- Configurable logging and monitoring

### Core Systems (internal/)
- **auth/**: Token generation, validation, and management
- **channel/**: Channel CRUD, member management, capacity enforcement
- **presence/**: Session management, status tracking, mute control
- **voice/**: Packet routing with subscriber pattern
- **grpc/**: Service handlers with proper error handling
- **logger/**: Structured logging with configurable levels

### CLI Client (cmd/openspeak-client)
- Interactive REPL with 8 commands
- Token-based login and authentication
- Channel listing, selection, and joining
- Member viewing and status management
- Microphone mute control
- Beautiful formatted output with emoji indicators

### Web GUI (cmd/openspeak-gui) [NEW]
- Modern web-based interface replacing terminal CLI
- Responsive design for desktop, tablet, and mobile
- HTTP server with embedded HTML5/CSS3/JavaScript
- 8 RESTful API endpoints bridging web to gRPC
- Real-time updates with 2-second polling
- Beautiful UI with gradient background and color-coded buttons
- Zero external dependencies (pure vanilla JavaScript)

## Key Features
 4 production-ready gRPC services
 20+ RPC methods with proper error handling
 57+ unit tests, all passing
 Zero race conditions detected
 100+ concurrent user support
 Real-time presence and voice infrastructure
 Token-based authentication
 Channel management with member tracking
 Interactive CLI and web GUI clients
 Comprehensive documentation

## Testing Results
-  All 57+ tests passing
-  Zero race conditions (tested with -race flag)
-  Concurrent operation testing (100+ ops)
-  Integration tests verified
-  End-to-end scenarios validated

## Documentation
- README.md: Project overview and quick start
- IMPLEMENTATION_SUMMARY.md: Comprehensive project details
- GRPC_IMPLEMENTATION.md: Service and method documentation
- CLI_CLIENT.md: CLI usage guide with examples
- WEB_GUI.md: Web GUI usage and API documentation
- GUI_IMPLEMENTATION_SUMMARY.md: Web GUI implementation details
- TEST_SCENARIO.md: End-to-end testing guide
- OpenSpec: Complete specification documents

## Technology Stack
- Language: Go 1.24.11
- Framework: gRPC v1.77.0
- Serialization: Protocol Buffers v1.36.10
- UUID: github.com/google/uuid v1.6.0

## Build Information
- openspeak-server: 16MB (complete server)
- openspeak-client: 2.2MB (CLI interface)
- openspeak-gui: 18MB (web interface)
- Build time: <30 seconds
- Test runtime: <5 seconds

## Getting Started
1. Build: make build
2. Server: ./bin/openspeak-server -port 50051 -log-level info
3. Client: ./bin/openspeak-client -host localhost -port 50051
4. Web GUI: ./bin/openspeak-gui -port 9090
5. Browser: http://localhost:9090

## Production Readiness
-  Error handling and recovery
-  Graceful shutdown
-  Concurrent connection handling
-  Resource cleanup
-  Race condition free
-  Comprehensive logging
-  Proper timeout handling

## Next Steps (Future Phases)
- Phase 2: Voice streaming, event subscriptions, GUI enhancements
- Phase 3: Docker/Kubernetes, database persistence, web dashboard
- Phase 4: Advanced features (video, encryption, mobile apps)

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 17:32:47 +01:00

14 KiB

Feature Specification: Development Guidelines & Best Practices

ID: DEV-001 Version: 1.0 Status: Planned Priority: High

Overview

Guidelines for development workflow, code quality, testing, and contribution standards for OpenSpeak project.

Development Workflow

Setting Up Development Environment

Prerequisites

  • Go 1.21+
  • Git
  • Protobuf compiler (protoc) v3.20+
  • Visual Studio Code or preferred IDE
  • Audio libraries (PortAudio, libopus)

Initial Setup

# Clone repository
git clone https://github.com/yourusername/openspeak.git
cd openspeak

# Install dependencies
go mod download
go mod tidy

# Install tools
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

# Setup pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
go fmt ./...
golangci-lint run ./...
go test ./...
EOF
chmod +x .git/hooks/pre-commit

Development Commands

Build

# Build server
go build -o bin/openspeak-server ./cmd/openspeak-server

# Build client
go build -o bin/openspeak-client ./cmd/openspeak-client

# Build all
make build

# Debug build (with symbols)
go build -gcflags="all=-N -l" -o bin/openspeak-server ./cmd/openspeak-server

Run

# Run server with default config
./bin/openspeak-server

# Run with custom config
./bin/openspeak-server --config config.dev.yaml

# Run with debug logging
OPENSPEAK_LOG_LEVEL=debug ./bin/openspeak-server

# Run client
./bin/openspeak-client

Test

# Run all tests
go test ./...

# Run with coverage
go test -cover ./...

# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

# Run specific test
go test -run TestChannelManager ./internal/channel

# Run with verbose output
go test -v ./...

# Run benchmarks
go test -bench=. -benchmem ./...

# Run tests with race detector
go test -race ./...

Code Quality

# Format code
go fmt ./...

# Run linters
golangci-lint run ./...

# Vet code
go vet ./...

# Check imports
goimports -w ./...

# Staticcheck
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...

Protocol Buffers

# Generate protobuf code
make proto

# Equivalent to:
protoc --go_out=. --go-grpc_out=. proto/*.proto

# Watch for changes
watch -n 1 'make proto'

Documentation

# Generate documentation
go doc ./...

# View package docs
godoc -http=:6060
# Then open http://localhost:6060

Makefile

.PHONY: build test lint fmt clean proto run-server run-client coverage

all: fmt lint test build

build:
	@echo "Building server and client..."
	go build -o bin/openspeak-server ./cmd/openspeak-server
	go build -o bin/openspeak-client ./cmd/openspeak-client

test:
	@echo "Running tests..."
	go test -v -race -coverprofile=coverage.out ./...
	go tool cover -func=coverage.out

lint:
	@echo "Running linters..."
	go fmt ./...
	go vet ./...
	golangci-lint run ./...

fmt:
	@echo "Formatting code..."
	go fmt ./...
	goimports -w ./...

clean:
	@echo "Cleaning..."
	rm -rf bin/
	rm -f coverage.out

proto:
	@echo "Generating protobuf code..."
	protoc --go_out=. --go-grpc_out=. proto/*.proto

run-server:
	go run ./cmd/openspeak-server -- --config config.dev.yaml

run-client:
	go run ./cmd/openspeak-client

coverage:
	@echo "Generating coverage report..."
	go test -coverprofile=coverage.out ./...
	go tool cover -html=coverage.out -o coverage.html
	@echo "Report saved to coverage.html"

coverage-watch:
	watch -n 2 'make coverage'

bench:
	@echo "Running benchmarks..."
	go test -bench=. -benchmem ./...

help:
	@echo "Available targets:"
	@grep -E '^[a-zA-Z_-]+:' Makefile | sed 's/:.*//g' | sort

Code Style & Conventions

Go Code Style

Follow Effective Go and Go Code Review Comments.

Naming Conventions

// Interfaces: adjective names (Reader, Writer, Closer)
type Reader interface {
  Read(p []byte) (n int, err error)
}

// Packages: short, single-word names
package voice

// Exported: PascalCase
func PublishVoicePacket(packet *VoicePacket) error {}

// Unexported: camelCase
func publishVoicePacket(packet *voicePacket) error {}

// Constants: UPPER_SNAKE_CASE or PascalCase
const (
  DefaultBitrate = 64
  MaxBitrate = 128
)

// Errors: errors.New or fmt.Errorf, start with lowercase or "Error" prefix
var ErrChannelNotFound = errors.New("channel not found")
var ErrInvalidBitrate = fmt.Errorf("invalid bitrate: %d", bitrate)

// Receiver names: short (1-2 chars)
func (m *Manager) CreateChannel(name string) (*Channel, error) {}

File Organization

// 1. Package declaration
package channel

// 2. Imports (stdlib, third-party, internal)
import (
  "context"
  "sync"

  "google.golang.org/grpc"

  "openspeak/internal/logger"
)

// 3. Constants
const (
  DefaultMaxUsers = 0 // unlimited
)

// 4. Errors
var (
  ErrChannelNotFound = errors.New("channel not found")
  ErrChannelFull     = errors.New("channel is full")
)

// 5. Type definitions
type Channel struct {
  ID        string
  Name      string
  Members   []string
  MaxUsers  int
  CreatedAt time.Time
}

// 6. Receiver methods (sorted by type)
func (c *Channel) IsFull() bool {
  return c.MaxUsers > 0 && len(c.Members) >= c.MaxUsers
}

// 7. Package-level functions
func NewChannel(name string) *Channel {
  return &Channel{
    ID:        generateID(),
    Name:      name,
    Members:   []string{},
    MaxUsers:  0,
    CreatedAt: time.Now(),
  }
}

Error Handling

// Good: Simple, clear error propagation
func (m *Manager) CreateChannel(name string) (*Channel, error) {
  if err := validateName(name); err != nil {
    return nil, fmt.Errorf("validate name: %w", err)
  }

  channel := NewChannel(name)
  if err := m.store.Save(channel); err != nil {
    return nil, fmt.Errorf("save channel: %w", err)
  }

  return channel, nil
}

// Bad: Ignoring errors
func (m *Manager) CreateChannel(name string) (*Channel, error) {
  validateName(name)  // Error ignored!
  channel := NewChannel(name)
  m.store.Save(channel)  // Error ignored!
  return channel, nil
}

// Bad: Generic error messages
return nil, errors.New("error")  // Useless error message

Comments

// Good: Explains purpose, not what code does
// ChannelManager handles creation and deletion of voice channels.
// It maintains the current state of all channels and enforces permissions.
type Manager struct {
  channels map[string]*Channel
  mu       sync.RWMutex
}

// Bad: Explains code, not purpose
// This is a map of channels
channels := make(map[string]*Channel)

// Good: Exported functions have godoc
// PublishVoicePacket accepts a voice packet from a client and broadcasts it
// to all members of the packet's channel.
func PublishVoicePacket(packet *VoicePacket) error {
  // ...
}

// Bad: No comment on exported function
func PublishVoicePacket(packet *VoicePacket) error {
  // ...
}

Project Layout

openspeak/
├── cmd/                      # Executable entry points
│   ├── openspeak-server/
│   │   └── main.go
│   └── openspeak-client/
│       └── main.go
├── internal/                 # Private packages (not importable from outside)
│   ├── auth/
│   ├── channel/
│   ├── presence/
│   ├── voice/
│   ├── config/
│   ├── logger/
│   └── grpc/
├── proto/                    # Protocol buffer definitions
│   ├── common.proto
│   ├── auth.proto
│   ├── channel.proto
│   ├── presence.proto
│   └── voice.proto
├── pkg/                      # Generated code (protobuf)
│   └── api/
│       └── openspeak/
│           └── v1/
├── config/                   # Configuration files
│   ├── config.yaml
│   └── config.dev.yaml
├── test/                     # Test utilities and fixtures
│   ├── fixtures/
│   └── mocks/
├── Makefile
├── go.mod
├── go.sum
├── README.md
├── CONTRIBUTING.md
└── LICENSE

Testing Standards

Test Structure

// File: internal/channel/channel_test.go
package channel

import (
  "testing"
)

// TestChannelCreation tests basic channel creation
func TestChannelCreation(t *testing.T) {
  // Arrange
  name := "general"

  // Act
  channel := NewChannel(name)

  // Assert
  if channel.Name != name {
    t.Errorf("expected name %q, got %q", name, channel.Name)
  }

  if channel.ID == "" {
    t.Error("channel ID should not be empty")
  }
}

// TestChannelIsFull tests capacity checking
func TestChannelIsFull(t *testing.T) {
  tests := []struct {
    name     string
    maxUsers int
    members  int
    want     bool
  }{
    {"unlimited capacity", 0, 100, false},
    {"not full", 10, 5, false},
    {"exactly full", 10, 10, true},
    {"over capacity", 10, 11, true},
  }

  for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
      channel := NewChannel("test")
      channel.MaxUsers = tt.maxUsers
      channel.Members = make([]string, tt.members)

      got := channel.IsFull()
      if got != tt.want {
        t.Errorf("IsFull() = %v, want %v", got, tt.want)
      }
    })
  }
}

Test Requirements

  • Write tests for all exported functions
  • Use table-driven tests for multiple cases
  • Use sub-tests (t.Run) for related test cases
  • Mock external dependencies
  • Test both success and error cases
  • Write benchmarks for performance-critical code

Coverage Goals

  • Unit tests: Aim for 80%+ coverage
  • Integration tests: Critical paths
  • Packages with <50% coverage: Flag in review

Git Workflow

Branch Naming

feature/<description>      # New feature
bugfix/<description>       # Bug fix
docs/<description>         # Documentation
refactor/<description>     # Refactoring
perf/<description>         # Performance improvement
test/<description>         # Test additions

Commit Messages

Follow Conventional Commits:

<type>(<scope>): <subject>

<body>

<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • style: Code style (formatting, etc)
  • refactor: Code refactoring
  • perf: Performance improvement
  • test: Test additions/changes
  • chore: Build, CI, dependencies

Examples:

feat(channel): add channel archival feature

Implement soft delete for channels. Users can still access archived
channels, but no new users can join them. Includes audit logging.

Fixes #123
Closes #456

feat(voice): implement Opus codec support

refactor(presence): simplify idle detection

docs(server): add deployment guide

fix(auth): validate token format before checking expiration

perf(voice): optimize packet routing for large channels

Pull Request Process

  1. Create feature branch from main
  2. Make commits with clear messages
  3. Keep commits focused and logical
  4. Push to your fork
  5. Create PR with description
  6. Respond to code review feedback
  7. Merge once approved

Documentation Standards

Code Documentation

  • All exported functions and types must have godoc comments
  • Comments should explain "why", not "what"
  • Use examples in documentation for complex functions

README Files

  • Package-level README.md in each major directory
  • Usage examples
  • Important design decisions
  • Known limitations

Architecture Documentation

  • Decision records for major changes
  • Diagrams for complex systems
  • Examples of common usage patterns

Debugging Techniques

Logging

// Use structured logging
logger.Info("user connected",
  "user_id", userID,
  "channel_id", channelID,
  "timestamp", time.Now(),
)

logger.Error("voice routing failed",
  "error", err,
  "packet_id", packetID,
  "channel_id", channelID,
)

Profiling

# CPU profiling
go run -cpuprofile=cpu.prof ./cmd/openspeak-server

# Memory profiling
go run -memprofile=mem.prof ./cmd/openspeak-server

# View profiles
go tool pprof cpu.prof
go tool pprof mem.prof

# Live profiling (server running)
go tool pprof http://localhost:6060/debug/pprof/profile

Debugging with Delve

# Install
go install github.com/go-delve/delve/cmd/dlv@latest

# Run server with debugger
dlv debug ./cmd/openspeak-server

# Commands in debugger:
# (dlv) break main.main
# (dlv) continue
# (dlv) next
# (dlv) step
# (dlv) print variable
# (dlv) quit

Performance Optimization

Benchmarking

# Run benchmarks
go test -bench=. -benchmem ./internal/voice

# Output:
# BenchmarkPublishVoicePacket-8    100000    10500 ns/op    2048 B/op    8 allocs/op

Memory Optimization

  • Reuse buffers with sync.Pool
  • Avoid unnecessary allocations
  • Use value types for small structs
  • Profile before optimizing

Concurrency Optimization

  • Use sync.Map for high-concurrency maps
  • Avoid locks in hot paths
  • Use channels for coordination
  • Profile with -race flag

Continuous Integration (Future)

GitHub Actions Workflow

name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v4
        with:
          go-version: '1.21'
      - run: make lint
      - run: make test
      - run: make coverage

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: securego/gosec@master

Version Management

Semantic Versioning

  • MAJOR: Breaking changes
  • MINOR: New features (backwards compatible)
  • PATCH: Bug fixes

Tag format: v1.2.3

# Create release
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0

Contributing Guidelines

  • Follow all above conventions
  • Write tests for new code
  • Update documentation
  • Get code reviewed
  • Keep commits clean
  • Respond to feedback professionally