Skip to content

Contributing

Eric Fitzgerald edited this page Apr 8, 2026 · 6 revisions

Contributing to TMI

Thank you for your interest in contributing to TMI (Threat Modeling Improved)! This guide will help you understand the contribution process, code standards, and best practices.

Table of Contents

Getting Started

Before You Begin

  1. Read the documentation

  2. Set up your development environment

    # Fork and clone repository
    git clone https://github.com/YOUR_USERNAME/tmi.git
    cd tmi
    
    # Add upstream remote
    git remote add upstream https://github.com/ericfitz/tmi.git
    
    # Start development environment
    make start-dev

    For the web app:

    git clone https://github.com/YOUR_USERNAME/tmi-ux.git
    cd tmi-ux
    
    git remote add upstream https://github.com/ericfitz/tmi-ux.git
    
    pnpm install
    pnpm run dev
  3. Find something to work on

    • Check Issues
    • Look for good-first-issue or help-wanted labels
    • Review the Roadmap

Types of Contributions

We welcome various types of contributions:

  • Bug Fixes - Fix reported bugs or issues you find
  • New Features - Add new functionality
  • Documentation - Improve or add documentation
  • Tests - Add or improve test coverage
  • Performance - Optimize existing code
  • Refactoring - Improve code quality
  • Examples - Add usage examples or tutorials

Development Workflow

1. Create a Feature Branch

# Update your fork
git fetch upstream
git checkout main
git merge upstream/main

# Create feature branch
git checkout -b feature/your-feature-name

# Or for bug fixes
git checkout -b fix/bug-description

2. Make Your Changes

Follow these guidelines:

For Server Changes (Go)

# Make your changes
vim api/your_file.go

# Run linter
make lint

# Run unit tests
make test-unit

# Run integration tests
make test-integration

# Build server
make build-server

For Web App Changes (Angular 21 / Vitest / Playwright)

cd tmi-ux

# Make your changes
vim src/app/your-component.ts

# Run linter (ESLint with auto-fix)
pnpm run lint

# Run all linters (ESLint + Stylelint for SCSS)
pnpm run lint:all

# Run unit tests (Vitest)
pnpm run test

# Run unit tests in watch mode
pnpm run test:watch

# Run E2E tests (Playwright)
pnpm run test:e2e

3. Commit Your Changes

Follow conventional commit format:

git add .
git commit -m "type(scope): description"

Commit Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation only
  • style: Code style changes (formatting, etc.)
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Build process or auxiliary tool changes

Examples:

git commit -m "feat(api): add bulk threat creation endpoint"
git commit -m "fix(auth): correct OAuth callback URL handling"
git commit -m "docs(readme): update installation instructions"
git commit -m "test(integration): add authorization tests"

Automatic Semantic Versioning

TMI uses automatic semantic versioning based on conventional commit types. Commits to the main branch trigger a post-commit hook that updates the version automatically.

Version Format: MAJOR.MINOR.PATCH (optionally with a prerelease suffix, e.g. 1.4.0-rc.0)

  • MAJOR: Incremented for breaking changes
  • MINOR: Incremented on feat: commits (server only; in tmi-ux, minor bumps occur only when a release branch merges into main)
  • PATCH: Incremented on all other commits (fix:, refactor:, docs:, etc.)

How It Works:

The post-commit hook runs only on the main branch. When you commit to main, it automatically:

  1. Checks whether production-relevant files were modified (Go source in api/, auth/, cmd/server/, internal/, or files in static/)
  2. Reads the commit message and parses the conventional commit type
  3. Updates version: feat: increments MINOR (resets PATCH), others increment PATCH
  4. Updates .version file and api/version.go
  5. Amends the commit with version changes

On feature branches (e.g. dev/*, feature/*, fix/*), the hook is a no-op.

Example Progression (server, on main branch):

# Starting at 1.4.0
git commit -m "fix(api): correct JWT validation"     # → 1.4.1 (patch++)
git commit -m "refactor(auth): simplify login flow"  # → 1.4.2 (patch++)
git commit -m "feat(api): add user deletion endpoint" # → 1.5.0 (minor++, patch=0)
git commit -m "docs: update API documentation"       # → 1.5.1 (patch++)
git commit -m "feat(websocket): add heartbeat"       # → 1.6.0 (minor++, patch=0)

Version Storage:

  • .version - JSON file tracking current version state including major, minor, patch, and optional prerelease fields (tracked in git)
  • api/version.go - Go variables set at build time via -ldflags
  • package.json (tmi-ux) - The version field is updated by the tmi-ux post-commit hook

Checking Version:

# View version file
cat .version

# Query running server
curl http://localhost:8080/ | jq '.service.build'
# Output: "1.4.0-abc1234"

Troubleshooting:

  • Hook not running? Ensure executable: chmod +x .git/hooks/post-commit
  • Version not updating? Verify jq is installed: which jq
  • Files out of sync? Make any commit to trigger sync: git commit --allow-empty -m "chore: sync version files"

4. Push Changes

# Push to your fork
git push origin feature/your-feature-name

5. Create Pull Request

  1. Go to https://github.com/ericfitz/tmi
  2. Click "New Pull Request"
  3. Select your fork and branch
  4. Fill out the PR template

Code Standards

Go Code Standards

Formatting

# Format code
gofmt -w .

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

Style Guidelines

Naming Conventions:

// Good
type ThreatModel struct { }
func GetThreatModel(id string) (*ThreatModel, error) { }
var threatModelStore = NewStore[ThreatModel]()

// Bad
type threat_model struct { }
func get_threat_model(ID string) (*ThreatModel, error) { }
var ThreatModelStore = NewStore[ThreatModel]()  // unexported globals

Error Handling:

// Good - comprehensive error handling
func CreateThreatModel(data ThreatModel) error {
    if err := validate(data); err != nil {
        return fmt.Errorf("validation failed: %w", err)
    }

    if err := db.Save(&data); err != nil {
        return fmt.Errorf("failed to save threat model: %w", err)
    }

    return nil
}

// Bad - swallowing errors
func CreateThreatModel(data ThreatModel) error {
    validate(data)
    db.Save(&data)
    return nil
}

Logging:

// Use structured logging via slogging
logger := slogging.Get()
logger.Info("Created threat model",
    slog.String("threat_model_id", tmID),
    slog.String("user_id", userID),
)

// Not this
logger.Info("Created threat model " + tmID + " for user " + userID)

TypeScript/Angular Standards

Formatting

# Format code
pnpm run format

# Check formatting
pnpm run format:check

# Lint
pnpm run lint:all

Style Guidelines

Naming Conventions:

// Classes - PascalCase
export class ThreatModelService { }

// Interfaces - PascalCase with 'I' prefix (optional)
export interface ThreatModel { }
export interface IThreatModel { }  // also acceptable

// Functions/Methods - camelCase
getThreatModels(): Observable<ThreatModel[]> { }

// Constants - UPPER_SNAKE_CASE
const MAX_THREAT_COUNT = 100;

// Properties - camelCase
private threatModels: ThreatModel[];

RxJS Best Practices:

// Good - proper subscription handling
export class ThreatModelComponent implements OnInit, OnDestroy {
  private destroy$ = new Subject<void>();

  ngOnInit() {
    this.apiService.getThreatModels()
      .pipe(takeUntil(this.destroy$))
      .subscribe(tms => this.threatModels = tms);
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

// Bad - memory leaks
export class ThreatModelComponent implements OnInit {
  ngOnInit() {
    this.apiService.getThreatModels()
      .subscribe(tms => this.threatModels = tms);  // Never unsubscribed!
  }
}

Component Structure (Angular 21 standalone components):

@Component({
  selector: 'app-threat-model',
  templateUrl: './threat-model.component.html',
  styleUrl: './threat-model.component.scss',
  standalone: true,
  imports: [CommonModule, TranslocoModule]
})
export class ThreatModelComponent implements OnInit {
  // 1. Public properties
  threatModels: ThreatModel[];

  // 2. Private properties
  private destroy$ = new Subject<void>();

  // 3. Constructor (or use inject() for signal-based DI)
  constructor(
    private apiService: ApiService,
    private logger: LoggerService
  ) { }

  // 4. Lifecycle hooks
  ngOnInit(): void {
    this.loadThreatModels();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  // 5. Public methods
  loadThreatModels(): void {
    // Implementation
  }

  // 6. Private methods
  private handleError(error: Error): void {
    // Implementation
  }
}

Documentation Standards

Code Comments

Go:

// ThreatModel represents a threat modeling project
// containing diagrams, threats, and security analysis.
type ThreatModel struct {
    ID          string    `json:"id"`
    Name        string    `json:"name"`
    Description *string   `json:"description,omitempty"`
}

// CreateThreatModel creates a new threat model with the given data.
// It validates the input, assigns ownership to the authenticated user,
// and persists to the database.
//
// Returns the created threat model or an error.
func CreateThreatModel(c *gin.Context) {
    // Implementation
}

TypeScript:

/**
 * Service for managing threat model operations.
 *
 * Provides methods for CRUD operations on threat models
 * and handles communication with the TMI API.
 */
@Injectable({providedIn: 'root'})
export class ThreatModelService {
  /**
   * Retrieves all threat models accessible to the current user.
   *
   * @returns Observable of threat model array
   * @throws Error if API call fails
   */
  getThreatModels(): Observable<ThreatModel[]> {
    // Implementation
  }
}

README and Documentation

  • Keep documentation up to date with code changes
  • Add code examples for new features
  • Update API documentation when endpoints change
  • Include migration guides for breaking changes

Documentation Structure

The TMI documentation is organized by audience and purpose across multiple repositories:

TMI-UX Local Documentation (tmi-ux/docs/)

Reference Documentation (reference/):

Directory Contents
reference/architecture/ overview.md, validation.md, violations.md (all resolved), service-provisioning.md, naming-conventions.md, session-management.md, autosave-data-modeling.md, dfd-change-propagation/
reference/libraries/ x6-complete-guide.md - Comprehensive X6 graph library documentation
reference/features/ collaborative-editing.md, dfd-user-interaction-guide.md
reference/security/ headers.md - HTTP security header implementation

Developer Documentation (developer/):

Directory Contents
developer/setup/ environment-configuration.md, core-services.md, import-constants.md
developer/testing/ testing-utilities.md - Testing utilities and patterns
developer/features/ validation-framework.md

AI Agent Documentation (agent/):

  • collaboration-participant-list-ux-design.md - Collaboration UX design
  • dfd-integration-testing-approach.md - DFD integration testing strategy
  • dfd-integration-test-plan.md - DFD integration test plan
  • developers-guide-antvx6-graphing-library.md - X6 developer guide
  • interesting-x6-events.txt - X6 event samples
  • pdf-report-diagram-rendering-design.md - PDF diagram rendering design

Supporting Files (in docs/reference/):

  • authentication-component-architecture.png - Authentication component architecture diagram
  • authentication-flow.png - OAuth authentication flow diagram

TMI Server Local Documentation (tmi/docs/)

  • docs/developer/ - Development documentation (setup, testing, integration)
  • docs/operator/ - Operations documentation (deployment, database, monitoring)
  • docs/reference/ - Reference materials (API specs, schemas, architecture)

Wiki Documentation (GitHub wiki)

  • Getting Started - End-user guides for threat modeling
  • Deployment - Operator deployment guides
  • Operation - SRE/DevOps running TMI
  • Development - Contributor documentation
  • API Reference - Technical API documentation

Documentation Conventions

  • File Naming: Use kebab-case.md naming convention (lowercase with hyphens, except README.md)
  • Cross-References: Maintain links between related documents
  • Audience-Focused: Each section serves a specific audience (developers, operators, end-users, AI agents)
  • Verification: All file references should be verified to exist before committing

Quick Navigation for New Developers

  1. Start with CLAUDE.md in the repository root for AI assistant guidance and project overview
  2. Read the Architecture-and-Design guide
  3. Review Service Provisioning Standards (docs/reference/architecture/service-provisioning.md)
  4. Check Environment Configuration (docs/developer/setup/environment-configuration.md)

When Adding New Documentation

  1. Choose the appropriate location based on primary audience
  2. Use descriptive, hyphenated filenames
  3. Update README files in parent directories
  4. Add cross-references to related documents
  5. Verify all internal links before committing

Testing Requirements

Required Tests

All contributions must include appropriate tests:

1. Unit Tests

Server (Go):

func TestCreateThreatModel(t *testing.T) {
    // Test your function
}

Web App (TypeScript):

describe('ThreatModelService', () => {
  it('should create threat model', () => {
    // Test your service
  });
});

2. Integration Tests

For server changes that affect database or API:

func TestDatabaseThreatModelIntegration(t *testing.T) {
    suite := SetupIntegrationTest(t)
    defer suite.TeardownIntegrationTest(t)
    // Test with real database
}

3. API Tests

For new endpoints, add Postman tests:

pm.test("Status code is 201", function () {
    pm.response.to.have.status(201);
});

4. E2E Tests (if applicable)

For UI changes (using Playwright):

import { test, expect } from '@playwright/test';

test('should create new threat model', async ({ page }) => {
  await page.goto('/threat-models');
  await page.getByTestId('create-button').click();
  // Test the flow
});

Test Coverage

  • Aim for 80%+ code coverage on new code
  • All bug fixes must include a regression test
  • Test both happy path and error cases

Running Tests

# Server (Go)
make test-unit            # Go unit tests
make test-integration     # Integration tests with PostgreSQL
make test-api             # Postman/Newman API tests

# Web App (Angular / Vitest / Playwright)
cd tmi-ux
pnpm run test             # Unit tests (Vitest)
pnpm run test:coverage    # Unit tests with coverage report
pnpm run test:e2e         # E2E tests (Playwright)
pnpm run test:e2e:ui      # E2E tests with Playwright UI

Pull Request Process

1. PR Template

Fill out all sections of the PR template:

## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] API tests pass (if applicable)
- [ ] E2E tests pass (if applicable)

## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Code is commented where needed
- [ ] Documentation updated
- [ ] Tests added/updated
- [ ] All tests pass locally

2. Before Submitting

Ensure your PR:

  • Passes all checks

    # Server
    make lint
    make test-unit
    make test-integration
    make build-server
    
    # Web App
    cd tmi-ux
    pnpm run lint:all
    pnpm run test
    pnpm run build
  • Has no merge conflicts

    git fetch upstream
    git merge upstream/main
    # Resolve any conflicts
  • Has clear commit messages

  • Includes tests

  • Updates documentation

3. PR Size

Keep PRs focused and manageable:

  • Ideal: < 400 lines changed
  • Maximum: < 1000 lines changed
  • Large changes should be split into multiple PRs

4. Draft PRs

Use draft PRs for:

  • Work in progress
  • Getting early feedback
  • Discussing approach
Mark PR as "Draft" when creating it
Convert to "Ready for review" when complete

Code Review Guidelines

For Contributors

Responding to Feedback:

  • Be open to suggestions
  • Ask questions if unclear
  • Make requested changes promptly
  • Explain your reasoning when disagreeing

Handling Requests:

# Make requested changes
git add .
git commit -m "refactor: address review comments"
git push origin feature/your-feature-name

# Force push if rebasing/squashing
git push --force-with-lease origin feature/your-feature-name

For Reviewers

Review Focus Areas:

  1. Correctness - Does it work as intended?
  2. Tests - Adequate test coverage?
  3. Design - Fits architecture?
  4. Performance - Any bottlenecks?
  5. Security - Any vulnerabilities?
  6. Documentation - Clear and complete?

Giving Feedback:

# Good feedback
"Consider using a map here for O(1) lookup instead of iterating the array"

"This could cause a race condition if two users update simultaneously.
Consider using a transaction or optimistic locking."

# Less helpful
"This is wrong"
"Do it differently"

Community Guidelines

Code of Conduct

We follow the Contributor Covenant:

  • Be respectful - Treat everyone with respect
  • Be inclusive - Welcome all contributors
  • Be constructive - Provide helpful feedback
  • Be professional - Keep discussions on-topic

Communication Channels

  • GitHub Issues - Bug reports and feature requests
  • GitHub Discussions - Questions and general discussion
  • Pull Requests - Code review and technical discussions

Asking Questions

Before asking:

  1. Check existing documentation
  2. Search closed issues
  3. Review pull requests

When asking:

  • Provide context
  • Include error messages
  • Share relevant code snippets
  • Describe what you've tried

Reporting Bugs

Use this template:

## Bug Description
Clear description of the bug

## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. See error

## Expected Behavior
What should happen

## Actual Behavior
What actually happens

## Environment
- OS: [e.g. macOS 13.0]
- Browser: [e.g. Chrome 120]
- TMI Version: [e.g. 0.9.0]

## Additional Context
Screenshots, logs, etc.

Suggesting Features

Use this template:

## Feature Description
Clear description of the feature

## Use Case
Why is this needed? Who will use it?

## Proposed Solution
How should it work?

## Alternatives Considered
What other approaches did you consider?

## Additional Context
Mockups, examples, etc.

Recognition

Contributors are recognized in:

  • Release notes
  • Contributors list
  • Project README

Significant contributions may lead to:

  • Maintainer status
  • Core team membership
  • Speaking opportunities

AI-Assisted Development

TMI supports AI-assisted development through detailed context documentation. The docs/agent/ directory contains context and implementation guidance for AI coding agents working on this project, including architecture proposals, implementation plans, design documents, and technical context.

Agent Documentation Contents

The agent documentation directory includes:

Design and Planning Documents:

  • collaboration-participant-list-ux-design.md - UX design for collaboration participant list feature
  • pdf-report-diagram-rendering-design.md - Design document for pre-rendered diagram storage to enable full diagram inclusion in PDF reports

Testing Documentation:

  • dfd-integration-testing-approach.md - Integration testing approach for DFD services
  • dfd-integration-test-plan.md - Comprehensive integration test plan for DFD graph component

Implementation Guides:

  • developers-guide-antvx6-graphing-library.md - Developer guide for working with the AntV X6 graphing library

Reference Information:

  • interesting-x6-events.txt - Log samples of X6 graph events showing actual event data

Related Documentation

When working with AI tools on TMI, also reference:

  • Architecture: docs/reference/architecture/ - Architecture overview, service provisioning standards, naming conventions, session management
  • Developer Docs: docs/developer/ - Environment configuration, testing utilities, core services setup
  • Technical References: docs/reference/ - X6 complete guide, collaborative editing implementation, security headers

Authentication Context

When working with TMI authentication:

  1. Primary Flow: TMI uses OAuth 2.0 Authorization Code flow with PKCE (RFC 7636)
  2. TMI Provider: Built-in development OAuth provider (idp=tmi) for testing
  3. JWT Tokens: Authentication uses JWT tokens with specific claim structure
  4. Multi-Provider: Supports Google, GitHub, and Microsoft OAuth providers

Project Context Files

  • CLAUDE.md: Project-specific instructions in the repository root
  • OpenAPI Spec: docs/reference/apis/tmi-openapi.json for API structure
  • Architecture Docs: Architecture-and-Design for system understanding

AI Development Best Practices

  • Reference the OpenAPI specification for endpoint behavior
  • Use structured logging with github.com/ericfitz/tmi/internal/slogging
  • Follow the testing patterns in existing test files
  • Always use Make targets (never run go test or go run directly)

Usage Guidelines for AI Agents

  1. Implementation Plans: Use docs/agent/ for storing implementation plans, progress tracking, and design documents
  2. Context Documents: Store technical context and background information there
  3. Testing Strategies: Document testing approaches and plans
  4. Research Notes: Keep research findings and technical analysis there
  5. Update Directory Contents: AI assistants should update the directory README when adding or removing files

Getting Help

Need help contributing?

Quick Reference

Common Commands

# Server Development (Go 1.26.x)
make start-dev          # Start dev environment (DB + Redis + server)
make test-unit          # Run unit tests
make test-integration   # Run integration tests (PostgreSQL)
make test-api           # Run Postman/Newman API tests
make lint               # Run golangci-lint
make build-server       # Build binary
make status             # Check status of all services

# Web App Development (Angular 21.x / Vitest 4.x / Playwright)
cd tmi-ux
pnpm run dev            # Start dev server (ng serve --open)
pnpm run test           # Run unit tests (Vitest)
pnpm run test:coverage  # Run unit tests with coverage
pnpm run test:e2e       # Run E2E tests (Playwright)
pnpm run lint:all       # Run all linters (ESLint + Stylelint)
pnpm run format         # Format code (Prettier)
pnpm run build          # Build for production

# Git Workflow (default branch is 'main')
git checkout -b feature/name  # Create branch from main
git add .                     # Stage changes
git commit -m "msg"           # Commit
git push origin feature/name  # Push

Style Checklist

  • ✅ Go code formatted with gofmt
  • ✅ TypeScript/HTML formatted with Prettier
  • ✅ Linters pass
  • ✅ Tests pass
  • ✅ Documentation updated
  • ✅ Commit messages follow convention
  • ✅ No merge conflicts

Next Steps

Ready to contribute?

  1. Set up your development environment: Getting-Started-with-Development
  2. Understand the architecture: Architecture-and-Design
  3. Review the testing guide: Testing
  4. Find an issue to work on
  5. Create your first PR!

Thank you for contributing to TMI!

Clone this wiki locally