Comprehensive review covering: - Chunked upload implementation critique with fixes - API design improvements (batch ops, pagination, error format) - Error handling & observability (tracing, structured logging) - Reliability patterns (retry, circuit breaker, health checks) - AI & automation friendliness (idempotency, error codes) - Security considerations (audit logging, token scopes) - Performance optimizations (caching, compression) - Phased implementation roadmap
26 KiB
Gitea Architecture Review & Suggestions
Reviewer: Senior Architect Date: January 2026 Scope: Full codebase review with focus on API, services, and recent chunked upload implementation
Executive Summary
Gitea demonstrates a well-designed, pragmatic architecture suitable for a mature self-hosted Git platform. The codebase follows clean layered patterns (routers → services → models) with good separation of concerns. However, there are opportunities to improve operational observability, reliability, developer experience, and AI/automation friendliness.
This document presents findings organized by priority and effort level.
Table of Contents
- Chunked Upload Implementation Review
- API Design Improvements
- Error Handling & Observability
- Reliability & Resilience
- Developer Experience
- AI & Automation Friendliness
- Security Considerations
- Performance Optimizations
- Implementation Roadmap
1. Chunked Upload Implementation Review
What Was Done Well
- Clean separation: model (
upload_session.go), service (chunked.go), handler (upload.go) - Resumable uploads with session status tracking
- Automatic cleanup via cron job
- Follows existing Gitea patterns for API handlers and models
Issues to Address
1.1 Missing Chunk Verification (HIGH)
Problem: No checksum verification for uploaded chunks. Corrupted chunks will produce corrupted files.
Suggestion:
// Add to UploadSession model
type UploadSession struct {
// ... existing fields
ChecksumAlgorithm string `xorm:"DEFAULT 'sha256'"` // sha256, md5, none
ExpectedChecksum string `xorm:""` // Full file checksum
}
// Add to chunk upload
type ChunkMetadata struct {
ChunkNumber int64 `json:"chunk_number"`
Checksum string `json:"checksum"` // Chunk checksum
Size int64 `json:"size"`
}
1.2 No Concurrent Upload Protection (MEDIUM)
Problem: Same chunk can be uploaded multiple times concurrently, causing race conditions.
Suggestion:
// Add file locking or atomic operations
func SaveChunk(ctx context.Context, session *UploadSession, chunkNum int64, ...) error {
lockPath := session.GetChunkPath(chunkNum) + ".lock"
lock, err := acquireFileLock(lockPath)
if err != nil {
return fmt.Errorf("chunk %d is being uploaded by another request", chunkNum)
}
defer lock.Release()
// ... save chunk
}
1.3 Missing Progress Webhook (MEDIUM)
Problem: No way to notify external systems of upload progress.
Suggestion:
// Emit webhook on upload milestones
if session.ChunksReceived % 10 == 0 || session.IsComplete() {
notify_service.UploadProgress(ctx, session)
}
1.4 No Rate Limiting (MEDIUM)
Problem: Unlimited chunk uploads could exhaust server resources.
Suggestion:
// Add rate limiting middleware for upload endpoints
m.Group("/uploads", func() {
// ... routes
}, rateLimit(100, time.Minute)) // 100 chunks per minute per user
1.5 Temp File Cleanup on Failure (LOW)
Problem: If AssembleChunks fails partway through, temp files may not be cleaned up.
Suggestion:
func AssembleChunks(...) (*Attachment, error) {
defer func() {
if r := recover(); r != nil {
cleanupTempFiles(session)
panic(r)
}
}()
// ... existing logic
}
2. API Design Improvements
2.1 Standardized Error Response Format (HIGH)
Current State: Error responses vary in format across endpoints.
Suggestion: Adopt RFC 7807 Problem Details format:
// modules/structs/error.go
type APIError struct {
Type string `json:"type"` // URI reference
Title string `json:"title"` // Short summary
Status int `json:"status"` // HTTP status code
Detail string `json:"detail"` // Explanation
Instance string `json:"instance,omitempty"` // Request URI
Code string `json:"code"` // Machine-readable code
Errors []ValidationError `json:"errors,omitempty"` // Field-level errors
}
// Example response:
{
"type": "https://gitea.io/errors/validation",
"title": "Validation Failed",
"status": 422,
"detail": "The provided repository name is invalid",
"code": "ERR_VALIDATION_FAILED",
"errors": [
{"field": "name", "message": "must be alphanumeric", "code": "ERR_INVALID_CHARS"}
]
}
2.2 Batch Operations API (HIGH)
Current State: No batch endpoints for bulk operations.
Suggestion: Add batch endpoints for common operations:
POST /api/v1/repos/{owner}/{repo}/issues/batch
POST /api/v1/repos/{owner}/{repo}/labels/batch
POST /api/v1/admin/users/batch
// Example batch request
{
"operations": [
{"action": "close", "issue_ids": [1, 2, 3]},
{"action": "add_label", "issue_ids": [1, 2], "label_id": 5}
]
}
// Example batch response
{
"results": [
{"index": 0, "success": true, "affected": 3},
{"index": 1, "success": false, "error": {"code": "ERR_NOT_FOUND", "detail": "Label 5 not found"}}
],
"summary": {"total": 2, "succeeded": 1, "failed": 1}
}
2.3 Cursor-Based Pagination (MEDIUM)
Current State: Offset-based pagination can be slow for large datasets.
Suggestion: Add cursor-based pagination option:
GET /api/v1/repos/{owner}/{repo}/issues?cursor=eyJpZCI6MTAwfQ&limit=50
type PaginatedResponse struct {
Data []any `json:"data"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
Total int64 `json:"total,omitempty"` // Optional, expensive to compute
}
2.4 Field Selection (GraphQL-lite) (MEDIUM)
Current State: All fields returned on every request.
Suggestion: Add field selection parameter:
GET /api/v1/repos/{owner}/{repo}?fields=id,name,description,stars_count
// Reduces payload and database load
func filterFields(obj any, fields []string) map[string]any {
// Return only requested fields
}
2.5 API Versioning Header (LOW)
Current State: No API version negotiation.
Suggestion: Add version header support:
Accept: application/vnd.gitea.v2+json
X-Gitea-API-Version: 2
3. Error Handling & Observability
3.1 Centralized Error Catalog (HIGH)
Current State: 100+ custom error types scattered across packages.
Suggestion: Create centralized error registry:
// modules/errors/catalog.go
package errors
type ErrorCode string
const (
// Authentication errors (1xxx)
ErrAuthRequired ErrorCode = "ERR_1001"
ErrInvalidToken ErrorCode = "ERR_1002"
ErrTokenExpired ErrorCode = "ERR_1003"
// Authorization errors (2xxx)
ErrForbidden ErrorCode = "ERR_2001"
ErrNotRepoMember ErrorCode = "ERR_2002"
// Resource errors (3xxx)
ErrUserNotFound ErrorCode = "ERR_3001"
ErrRepoNotFound ErrorCode = "ERR_3002"
ErrIssueNotFound ErrorCode = "ERR_3003"
// Validation errors (4xxx)
ErrInvalidInput ErrorCode = "ERR_4001"
ErrNameTooLong ErrorCode = "ERR_4002"
// Upload errors (5xxx)
ErrUploadSessionExpired ErrorCode = "ERR_5001"
ErrChunkMissing ErrorCode = "ERR_5002"
ErrFileTooLarge ErrorCode = "ERR_5003"
)
var errorMessages = map[ErrorCode]string{
ErrAuthRequired: "Authentication required",
ErrInvalidToken: "Invalid or malformed token",
// ...
}
func (e ErrorCode) Error() string { return errorMessages[e] }
func (e ErrorCode) HTTPStatus() int { /* return appropriate status */ }
3.2 Request Tracing (HIGH)
Current State: No correlation IDs for request tracing.
Suggestion: Add OpenTelemetry integration:
// modules/web/middleware/tracing.go
func TracingMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
traceID := r.Header.Get("X-Request-ID")
if traceID == "" {
traceID = uuid.New().String()
}
ctx := context.WithValue(r.Context(), TraceIDKey, traceID)
w.Header().Set("X-Request-ID", traceID)
// Add to structured logging
log := log.WithField("trace_id", traceID)
ctx = context.WithValue(ctx, LoggerKey, log)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
3.3 Structured Logging (MEDIUM)
Current State: Mix of structured and unstructured logging.
Suggestion: Standardize on JSON structured logs:
// All log entries should include:
{
"timestamp": "2024-01-08T12:00:00Z",
"level": "info",
"trace_id": "abc-123",
"user_id": 42,
"repo_id": 100,
"action": "create_issue",
"duration_ms": 45,
"message": "Issue created successfully"
}
3.4 Metrics Endpoint Enhancement (MEDIUM)
Current State: Basic Prometheus metrics.
Suggestion: Add business metrics:
// Metrics to add:
gitea_api_requests_total{method, endpoint, status}
gitea_api_request_duration_seconds{method, endpoint}
gitea_upload_sessions_active
gitea_upload_bytes_total
gitea_webhook_deliveries_total{status, hook_type}
gitea_webhook_delivery_duration_seconds
gitea_repository_operations_total{operation}
4. Reliability & Resilience
4.1 Webhook Retry with Exponential Backoff (HIGH)
Current State: Limited retry logic visible.
Suggestion:
// services/webhook/deliver.go
type RetryConfig struct {
MaxRetries int // Default: 5
InitialBackoff time.Duration // Default: 1s
MaxBackoff time.Duration // Default: 5m
BackoffFactor float64 // Default: 2.0
}
func DeliverWithRetry(ctx context.Context, task *HookTask, cfg RetryConfig) error {
var lastErr error
backoff := cfg.InitialBackoff
for attempt := 0; attempt <= cfg.MaxRetries; attempt++ {
err := Deliver(ctx, task)
if err == nil {
return nil
}
lastErr = err
// Log retry
log.Warn("Webhook delivery failed, retrying",
"task_id", task.ID,
"attempt", attempt+1,
"next_backoff", backoff)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(backoff):
}
backoff = time.Duration(float64(backoff) * cfg.BackoffFactor)
if backoff > cfg.MaxBackoff {
backoff = cfg.MaxBackoff
}
}
// Move to dead letter queue
if err := moveToDeadLetterQueue(ctx, task, lastErr); err != nil {
log.Error("Failed to move to DLQ", "task_id", task.ID, "error", err)
}
return fmt.Errorf("webhook delivery failed after %d attempts: %w", cfg.MaxRetries, lastErr)
}
4.2 Circuit Breaker for External Services (MEDIUM)
Current State: No circuit breaker pattern.
Suggestion:
// modules/circuitbreaker/breaker.go
type CircuitBreaker struct {
name string
maxFailures int
resetTimeout time.Duration
state State // Closed, Open, HalfOpen
failures int
lastFailure time.Time
}
func (cb *CircuitBreaker) Execute(fn func() error) error {
if cb.state == Open {
if time.Since(cb.lastFailure) > cb.resetTimeout {
cb.state = HalfOpen
} else {
return ErrCircuitOpen
}
}
err := fn()
if err != nil {
cb.recordFailure()
return err
}
cb.recordSuccess()
return nil
}
// Usage for webhook delivery, OAuth providers, etc.
var webhookBreaker = NewCircuitBreaker("webhook", 5, time.Minute)
4.3 Graceful Degradation (MEDIUM)
Current State: Services fail hard on dependency failures.
Suggestion: Add fallback behaviors:
// Example: Search indexer fallback
func SearchIssues(ctx context.Context, query string) ([]Issue, error) {
if indexer.IsHealthy() {
return indexer.Search(ctx, query)
}
// Fallback to database search (slower but works)
log.Warn("Search indexer unavailable, falling back to DB search")
return db.SearchIssuesByTitle(ctx, query)
}
4.4 Health Check Endpoints (LOW)
Current State: Basic health check.
Suggestion: Add detailed health checks:
GET /api/v1/health
GET /api/v1/health/live # Kubernetes liveness
GET /api/v1/health/ready # Kubernetes readiness
{
"status": "healthy",
"version": "1.24.0",
"checks": {
"database": {"status": "up", "latency_ms": 2},
"redis": {"status": "up", "latency_ms": 1},
"search_indexer": {"status": "degraded", "message": "High latency"},
"storage": {"status": "up", "free_space_gb": 450}
}
}
5. Developer Experience
5.1 OpenAPI Spec Improvements (HIGH)
Current State: Swagger docs generated from code comments.
Suggestion: Enhance with examples and better descriptions:
// swagger:operation POST /repos/{owner}/{repo}/issues repository createIssue
// ---
// summary: Create an issue
// description: |
// Creates a new issue in the repository. The authenticated user must have
// write access to the repository.
//
// ## Example
// ```json
// {"title": "Bug report", "body": "Description here", "labels": [1, 2]}
// ```
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateIssueOption"
// example:
// title: "Bug: Login fails"
// body: "When I try to login..."
// labels: [1, 2]
// responses:
// "201":
// description: Issue created
// schema:
// "$ref": "#/definitions/Issue"
// examples:
// application/json:
// id: 1
// title: "Bug: Login fails"
// state: "open"
5.2 SDK Generation (MEDIUM)
Current State: No official SDK libraries.
Suggestion: Generate SDKs from OpenAPI spec:
# Generate SDKs
openapi-generator generate -i api/v1/swagger.json -g python -o sdk/python
openapi-generator generate -i api/v1/swagger.json -g typescript-axios -o sdk/typescript
openapi-generator generate -i api/v1/swagger.json -g go -o sdk/go
5.3 CLI Tool Enhancement (MEDIUM)
Current State: Basic CLI for Git operations.
Suggestion: Add API CLI tool:
# Proposed gitea-cli commands
gitea-cli auth login --server https://gitea.example.com
gitea-cli repo list
gitea-cli repo create myrepo --private
gitea-cli issue create --title "Bug" --body "Description"
gitea-cli release create v1.0.0 --attach ./binary.zip
gitea-cli upload --chunked ./large-file.zip # Uses chunked upload
5.4 Webhook Debugging Tools (LOW)
Current State: Limited visibility into webhook delivery.
Suggestion: Add webhook inspection endpoints:
GET /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries
GET /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{delivery_id}
POST /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{delivery_id}/redeliver
6. AI & Automation Friendliness
6.1 Machine-Readable Error Codes (HIGH)
Current State: Error messages are human-readable strings.
Suggestion: Always include machine-readable codes:
{
"code": "ERR_REPO_ARCHIVED",
"message": "Repository is archived and cannot be modified",
"documentation_url": "https://docs.gitea.io/errors/ERR_REPO_ARCHIVED"
}
6.2 Idempotency Keys (HIGH)
Current State: No idempotency support for POST requests.
Suggestion: Add idempotency key header:
// Header: Idempotency-Key: <client-generated-uuid>
func IdempotencyMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" && r.Method != "PUT" {
next.ServeHTTP(w, r)
return
}
key := r.Header.Get("Idempotency-Key")
if key == "" {
next.ServeHTTP(w, r)
return
}
// Check cache
if cached, found := idempotencyCache.Get(key); found {
w.Header().Set("X-Idempotency-Replayed", "true")
writeResponse(w, cached)
return
}
// Capture response
rec := httptest.NewRecorder()
next.ServeHTTP(rec, r)
// Cache successful responses
if rec.Code >= 200 && rec.Code < 300 {
idempotencyCache.Set(key, rec, 24*time.Hour)
}
copyResponse(w, rec)
})
}
}
6.3 Webhook Event Catalog (MEDIUM)
Current State: Events documented but not discoverable via API.
Suggestion: Add event discovery endpoint:
GET /api/v1/events
{
"events": [
{
"name": "push",
"description": "Triggered when commits are pushed",
"payload_schema": {"$ref": "#/definitions/PushPayload"},
"example": {...}
},
{
"name": "issues",
"description": "Triggered for issue events",
"actions": ["opened", "closed", "edited", "labeled"],
"payload_schema": {"$ref": "#/definitions/IssuePayload"}
}
]
}
6.4 Async Operation Pattern (MEDIUM)
Current State: Long operations block HTTP requests.
Suggestion: Add async operation pattern for long-running tasks:
POST /api/v1/repos/{owner}/{repo}/mirror-sync
Response: 202 Accepted
{
"operation_id": "op_abc123",
"status": "pending",
"status_url": "/api/v1/operations/op_abc123",
"estimated_completion": "2024-01-08T12:05:00Z"
}
GET /api/v1/operations/op_abc123
{
"operation_id": "op_abc123",
"status": "running",
"progress": 45,
"started_at": "2024-01-08T12:00:00Z"
}
6.5 Rate Limit Headers (MEDIUM)
Current State: Limited rate limit visibility.
Suggestion: Add standard rate limit headers:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4990
X-RateLimit-Reset: 1704720000
X-RateLimit-Resource: api
6.6 Conditional Requests (LOW)
Current State: Limited ETag/If-Match support.
Suggestion: Add conditional request support:
GET /api/v1/repos/owner/repo
Response Headers:
ETag: "abc123"
Last-Modified: Wed, 08 Jan 2024 12:00:00 GMT
GET /api/v1/repos/owner/repo
Request Headers:
If-None-Match: "abc123"
Response: 304 Not Modified
PUT /api/v1/repos/owner/repo
Request Headers:
If-Match: "abc123" # Prevents lost updates
7. Security Considerations
7.1 Audit Logging (HIGH)
Current State: Limited audit trail.
Suggestion: Add comprehensive audit logging:
type AuditEvent struct {
ID int64 `json:"id"`
Timestamp time.Time `json:"timestamp"`
ActorID int64 `json:"actor_id"`
ActorIP string `json:"actor_ip"`
Action string `json:"action"` // "repo.create", "user.delete"
Resource string `json:"resource"` // "repository", "user"
ResourceID int64 `json:"resource_id"`
OldValue string `json:"old_value,omitempty"` // JSON
NewValue string `json:"new_value,omitempty"` // JSON
UserAgent string `json:"user_agent"`
TraceID string `json:"trace_id"`
}
// Store in separate audit_log table
// Expose via admin API: GET /api/v1/admin/audit-log
7.2 Token Scopes Enhancement (MEDIUM)
Current State: Basic token scopes.
Suggestion: Add granular scopes:
repo:read # Read repository data
repo:write # Push to repository
repo:delete # Delete repository
issues:read # Read issues
issues:write # Create/edit issues
releases:read # Read releases
releases:write # Create releases
releases:upload # Upload release assets (for CI)
admin:users # Manage users
admin:repos # Manage all repos
webhook:manage # Manage webhooks
7.3 Request Signing (LOW)
Current State: Token-based auth only.
Suggestion: Add HMAC request signing for webhooks:
// For high-security integrations
signature := hmac.New(sha256.New, []byte(secret))
signature.Write([]byte(timestamp + "." + requestBody))
expectedSig := hex.EncodeToString(signature.Sum(nil))
// Header: X-Gitea-Signature: sha256=<signature>
// Header: X-Gitea-Timestamp: <unix-timestamp>
8. Performance Optimizations
8.1 Response Compression (HIGH)
Current State: Gzip available but not always used.
Suggestion: Ensure compression for all JSON responses:
// Middleware to compress responses > 1KB
func CompressionMiddleware(next http.Handler) http.Handler {
return gziphandler.GzipHandler(next)
}
8.2 Database Query Optimization (MEDIUM)
Current State: Some N+1 query patterns.
Suggestion: Add eager loading for common associations:
// Instead of loading issues then loading users one by one
issues, _ := GetIssues(ctx, opts)
for _, issue := range issues {
issue.Poster, _ = GetUser(issue.PosterID) // N+1!
}
// Use eager loading
issues, _ := GetIssuesWithUsers(ctx, opts) // Single JOIN query
8.3 Caching Layer (MEDIUM)
Current State: Limited caching.
Suggestion: Add Redis/memory caching for hot paths:
// Cache frequently accessed data
func GetRepository(ctx context.Context, id int64) (*Repository, error) {
cacheKey := fmt.Sprintf("repo:%d", id)
if cached, found := cache.Get(cacheKey); found {
return cached.(*Repository), nil
}
repo, err := db.GetRepository(ctx, id)
if err != nil {
return nil, err
}
cache.Set(cacheKey, repo, 5*time.Minute)
return repo, nil
}
8.4 Connection Pooling Tuning (LOW)
Current State: Default connection pool settings.
Suggestion: Add configurable pool settings:
[database]
MAX_OPEN_CONNS = 100
MAX_IDLE_CONNS = 10
CONN_MAX_LIFETIME = 1h
CONN_MAX_IDLE_TIME = 10m
9. Implementation Roadmap
Phase 1: Quick Wins (1-2 weeks)
| Item | Priority | Effort | Impact |
|---|---|---|---|
| Add request tracing (X-Request-ID) | HIGH | Low | High |
| Add rate limit headers | MEDIUM | Low | Medium |
| Add chunk checksum verification | HIGH | Low | High |
| Standardize error response format | HIGH | Medium | High |
Phase 2: Foundation (2-4 weeks)
| Item | Priority | Effort | Impact |
|---|---|---|---|
| Centralized error catalog | HIGH | Medium | High |
| Webhook retry with backoff | HIGH | Medium | High |
| Idempotency key support | HIGH | Medium | High |
| Audit logging | HIGH | Medium | High |
| Health check endpoints | LOW | Low | Medium |
Phase 3: Enhancement (4-8 weeks)
| Item | Priority | Effort | Impact |
|---|---|---|---|
| Batch operations API | HIGH | High | High |
| Cursor-based pagination | MEDIUM | Medium | Medium |
| SDK generation pipeline | MEDIUM | Medium | Medium |
| Circuit breaker pattern | MEDIUM | Medium | Medium |
| Event discovery endpoint | MEDIUM | Low | Medium |
Phase 4: Advanced (8+ weeks)
| Item | Priority | Effort | Impact |
|---|---|---|---|
| Async operation pattern | MEDIUM | High | Medium |
| Field selection (GraphQL-lite) | MEDIUM | High | Medium |
| API versioning | LOW | High | Low |
| Full OpenTelemetry integration | MEDIUM | High | High |
Appendix A: Chunked Upload Quick Fixes
// 1. Add checksum to chunk upload (upload.go)
func UploadChunk(ctx *context.APIContext) {
// Get expected checksum from header
expectedChecksum := ctx.Req.Header.Get("X-Chunk-Checksum")
// ... save chunk ...
// Verify checksum if provided
if expectedChecksum != "" {
actualChecksum := sha256sum(chunkData)
if actualChecksum != expectedChecksum {
ctx.APIError(http.StatusBadRequest, api.APIError{
Code: "ERR_CHECKSUM_MISMATCH",
Detail: "Chunk checksum verification failed",
})
return
}
}
}
// 2. Add final file checksum verification (chunked.go)
func AssembleChunks(...) (*Attachment, error) {
// ... assemble chunks ...
// Verify final checksum if expected
if session.ExpectedChecksum != "" {
actualChecksum := computeFileChecksum(finalPath)
if actualChecksum != session.ExpectedChecksum {
return nil, fmt.Errorf("file checksum mismatch: expected %s, got %s",
session.ExpectedChecksum, actualChecksum)
}
}
// ... create attachment ...
}
Appendix B: Sample Error Catalog
// modules/errors/codes.go
package errors
// Error codes organized by category
const (
// 1xxx - Authentication
ErrCodeAuthRequired = "ERR_1001"
ErrCodeInvalidToken = "ERR_1002"
ErrCodeTokenExpired = "ERR_1003"
ErrCodeInvalidPassword = "ERR_1004"
ErrCode2FARequired = "ERR_1005"
// 2xxx - Authorization
ErrCodeForbidden = "ERR_2001"
ErrCodeNotRepoMember = "ERR_2002"
ErrCodeNotOrgMember = "ERR_2003"
ErrCodeInsufficientPerm = "ERR_2004"
// 3xxx - Resource Not Found
ErrCodeUserNotFound = "ERR_3001"
ErrCodeRepoNotFound = "ERR_3002"
ErrCodeIssueNotFound = "ERR_3003"
ErrCodeReleaseNotFound = "ERR_3004"
ErrCodeBranchNotFound = "ERR_3005"
// 4xxx - Validation
ErrCodeInvalidInput = "ERR_4001"
ErrCodeNameTooLong = "ERR_4002"
ErrCodeInvalidEmail = "ERR_4003"
ErrCodeDuplicateName = "ERR_4004"
// 5xxx - Upload
ErrCodeSessionExpired = "ERR_5001"
ErrCodeChunkMissing = "ERR_5002"
ErrCodeFileTooLarge = "ERR_5003"
ErrCodeChecksumMismatch = "ERR_5004"
ErrCodeInvalidFileType = "ERR_5005"
// 6xxx - Rate Limiting
ErrCodeRateLimited = "ERR_6001"
ErrCodeTooManyRequests = "ERR_6002"
// 7xxx - Server
ErrCodeInternal = "ERR_7001"
ErrCodeServiceDown = "ERR_7002"
ErrCodeTimeout = "ERR_7003"
)
Document prepared for discussion. All suggestions should be evaluated against project priorities and resource constraints.