Comprehensive reference for AI coding agents working in the Tracer codebase. Read AGENTS.md first for a concise overview, then use this file for deep patterns and conventions.
cp .env.example .env # Create environment file
make up # Start PostgreSQL + Tracer via Docker Compose
make test # Run all tests
make lint # Run golangci-lint v2Health check: GET http://localhost:4020/health
Readiness: GET http://localhost:4020/ready
| Attribute | Value |
|---|---|
| Project | Real-time transaction validation and fraud prevention API |
| Language | Go (go.mod: go 1.25.7; Dockerfile builder: golang:1.26-alpine — builder image is intentionally newer) |
| Architecture | Hexagonal (Ports & Adapters) + CQRS |
| Database | PostgreSQL 16 |
| Rule Engine | Google CEL (cel-go v0.27.0) |
| Auth | lib-auth v2 (API Key + Access Manager plugin) |
| Observability | OpenTelemetry via lib-commons/v4 |
| Testing | TDD; testify + sqlmock + gomock + testcontainers + Godog (BDD) |
| License | Elastic License 2.0 |
make build # Build binary to .bin/tracer
make run # Run locally with .env config (go run cmd/app/main.go)
make clean # Remove build artifacts
make tidy # go mod tidy
make dev-setup # Install tools (golangci-lint, swag, mockgen, gosec) + set-env + tidymake test # All tests (go test -v ./...)
make test-unit # Unit tests with race detector (excludes tests/ and api/)
make test-integration # Integration tests with testcontainers (build tag: integration, -p=1)
make test-e2e # E2E BDD tests with Godog (resets Docker, build tag: e2e)
make test-all # Unit + integration
make test-bench # Benchmarks (BENCH=pattern BENCH_PKG=./path)
make check-tests # Quick coverage verification
# Single test
go test -v -run TestFunctionName ./internal/services/command/...
# Integration with specific test
make test-integration RUN=TestIntegration_PostgresRepo_Create
# Low-resource mode (CI)
make test-integration LOW_RESOURCE=1make coverage-unit # Unit coverage report (uses .ignorecoverunit for exclusions)
make coverage-integration # Integration coverage report
make coverage # All coverage targetsmake lint # golangci-lint v2 with --fix (auto-installs if missing)
make format # go fmt
make sec # gosec + govulncheck security scan
make sec SARIF=1 # SARIF output for GitHub Security tab
make quality # lint + test aggregator
make generate # go generate (mocks via mockgen)
make generate-docs # Generate Swagger/OpenAPI docs to api/ (uses swaggo/swag under the hood)
make verify-api-docs # Check annotation coverage
make validate-api-docs # Validate generated OpenAPI specmake up # Start PostgreSQL + Tracer (docker compose up -d)
make down # Stop and remove containers/volumes
make start # Start existing containers (without recreating)
make stop # Stop containers (without removing)
make restart # Stop + start
make rebuild-up # Full rebuild and restart
make logs # Tail all service logs
make logs-api # Tail tracer service only
make ps # List container statusmake migrate # Apply all pending migrations (functions first, then schema)
make migrate-down # Rollback last migration
make migrate-down-all # Rollback ALL (5s confirmation, FORCE=1 to skip)
make migrate-version # Show current version
make migrate-force VERSION=N # Force set version
make ensure-migrate # Ensure migration tool is installed
make seed # Load development seed data
make seed-down # Remove seed datamake setup-git-hooks # Install pre-commit hooks from .githooks/
make check-hooks # Verify hooks installation
make check-envs # Verify no secrets in tracked filestracer/
├── cmd/app/main.go # Entry point: loads .env, calls bootstrap.InitServers()
├── internal/
│ ├── bootstrap/ # Composition root
│ │ ├── config.go # Config struct, InitServers(), all DI wiring
│ │ ├── http_server.go # Fiber server with graceful shutdown
│ │ └── service.go # Service struct with Run()/Shutdown()
│ ├── adapters/
│ │ ├── http/in/ # Fiber handlers, routes, middleware, validation
│ │ │ ├── routes.go # Route registration with auth guard
│ │ │ ├── validation_handler.go # POST /v1/validations
│ │ │ ├── rule_handler.go # Rule CRUD + lifecycle handlers
│ │ │ ├── limit_handler.go # Limit CRUD + lifecycle handlers
│ │ │ ├── audit_event_handler.go # Audit event read handlers
│ │ │ ├── health.go # Health/readiness probes
│ │ │ ├── middleware/ # Auth (lib-auth v2: API Key + Access Manager), CORS, IP extraction
│ │ │ └── *_validation.go # Input validation per resource
│ │ ├── postgres/ # Repository implementations
│ │ │ ├── rule_repository.go # Rules CRUD
│ │ │ ├── limit_repository.go # Limits CRUD
│ │ │ ├── usage_counter_repository.go # Usage counter ops
│ │ │ ├── transaction_validation_repository.go # Validation records
│ │ │ ├── audit_event_repository.go # Audit events + hash chain
│ │ │ ├── rule_sync_repository.go # Delta queries for cache sync
│ │ │ ├── db/ # DB/Connection/TxBeginner interfaces
│ │ │ └── *_postgresql_model.go # DB ↔ domain model conversions
│ │ └── cel/ # CEL expression engine adapter
│ │ ├── adapter.go # Compile + Evaluate with cost limiting
│ │ ├── environment.go # CEL environment setup (variables, types)
│ │ └── program.go # Compiled program wrapper
│ ├── services/ # Business logic
│ │ ├── validation_service.go # Orchestrates rules + limits + audit in single tx
│ │ ├── rule_service.go # Rule facade (commands + queries)
│ │ ├── limit_service.go # Limit facade (commands + queries + usage)
│ │ ├── audit_event_service.go # Read-only audit facade
│ │ ├── transaction_validation_service.go # Validation history facade
│ │ ├── audit_writer.go # AuditWriter interface (services package)
│ │ ├── metrics.go # Prometheus-style metrics helpers
│ │ ├── command/ # Write operations
│ │ │ ├── create_rule.go, update_rule.go, activate_rule.go, ...
│ │ │ ├── create_limit.go, update_limit.go, activate_limit.go, ...
│ │ │ ├── record_audit_event.go # AuditWriter implementation
│ │ │ ├── repository.go # RuleRepository interface
│ │ │ ├── limit_repository.go # LimitRepository interface
│ │ │ ├── audit_writer.go # AuditWriter interface (command package)
│ │ │ └── rule_cache_writer.go # RuleCacheWriter interface
│ │ ├── query/ # Read operations
│ │ │ ├── evaluate_rules.go # Rule evaluation orchestration
│ │ │ ├── rule_evaluator.go # Single rule CEL evaluation
│ │ │ ├── complete_evaluator.go # Full evaluation with scope matching
│ │ │ ├── get_active_rules.go # Active rules retrieval (DB or cache)
│ │ │ ├── limit_checker.go # Limit enforcement with usage counters
│ │ │ ├── verify_audit_event.go # Hash chain verification
│ │ │ └── get_*, list_* # Standard CRUD queries
│ │ ├── cache/ # In-memory rule cache
│ │ │ ├── rule_cache.go # Thread-safe cache (sync.RWMutex)
│ │ │ ├── adapter.go # CacheAdapter → ActiveRulesRepository
│ │ │ ├── warmup.go # Initial cache population from DB
│ │ │ └── cached_rule.go # CachedRule struct (Rule + compiled program)
│ │ └── workers/ # Background workers
│ │ ├── rule_sync_worker.go # Polls DB for rule changes
│ │ └── usage_cleanup_worker.go # Cleans expired usage counters
│ ├── testhelper/ # Test helpers (e.g., time_of_day.go)
│ ├── testutil/ # Shared test utilities
│ ├── testutil_dbsuite/ # DB test suite helpers
│ └── testutil_integration/ # Integration test helpers
├── pkg/
│ ├── model/ # Domain entities (44 files)
│ │ ├── rule.go, limit.go, validation.go, scope.go
│ │ ├── audit_event.go, transaction_validation.go, transaction.go
│ │ ├── context.go, decision_maker.go, evaluation_result.go
│ │ ├── check_limits.go, scope_matcher.go, time_of_day.go
│ │ └── *_test.go # Extensive domain model tests
│ ├── constant/ # Error codes (TRC-XXXX), pagination, app constants
│ ├── clock/ # Clock interface (Real + Fixed for MOCK_TIME)
│ ├── resilience/ # Circuit breaker wrapper (sony/gobreaker)
│ ├── validation/ # Date/time validation (RFC3339)
│ ├── hash/ # SHA-256 hash chain for audit events
│ ├── logging/ # WithTrace() — enriches logger with trace/span IDs
│ ├── sanitize/ # Input sanitization helpers
│ ├── net/ # HTTP cursor pagination, IP extraction
│ ├── contextutil/ # Context value extraction
│ ├── migration/ # Function migration runner
│ └── shell/ # Makefile color/utility includes
├── tests/
│ ├── integration/ # 30 testcontainers-based API tests
│ └── end2end/ # BDD (Godog) with Gherkin features
├── migrations/ # 12 migrations + functions/ + seeds/
├── api/ # Generated Swagger docs
└── .golangci.yml # Linter config (golangci-lint v2)
| Context | Responsibility |
|---|---|
| Validation | Orchestrate validation requests, coordinate rule/limit evaluation, record audit |
| Rules | Manage rule lifecycle, compile/evaluate CEL expressions, in-memory cache |
| Limits | Manage spending limits, track usage counters, enforce thresholds |
| Audit | Immutable event log, hash chain verification, SOX/GLBA compliance |
Command layer (internal/services/command/):
| Interface | Methods | Implementations |
|---|---|---|
RuleRepository |
Create, GetByID, GetByName, ListByStatus, Update, Delete, ListActiveByScopes, UpdateStatus | postgres.Repository |
LimitRepository |
Create, GetByID, Update, UpdateStatus | postgres.LimitRepository |
AuditWriter |
RecordValidationEvent, RecordRuleEvent, RecordLimitEvent | command.RecordAuditEventCommand |
ExpressionCompiler |
Compile(ctx, expr) → (any, error) | celCompilerAdapter (wraps cel.Adapter) |
RuleCacheWriter |
UpsertRule, RemoveRule | cache.RuleCache |
Query layer (internal/services/query/):
| Interface | Methods | Implementations |
|---|---|---|
ActiveRulesRepository |
GetActiveRulesForScopes | cache.CacheAdapter, postgres.Repository |
LimitRepository |
GetByID, List | postgres.LimitRepository |
UsageCounterRepository |
GetByLimitID, IncrementOrInsert, GetByScopeAndPeriod | postgres.UsageCounterRepository |
Infrastructure (internal/adapters/postgres/db/):
| Interface | Purpose |
|---|---|
DB |
ExecContext, QueryContext, QueryRowContext |
Connection |
GetDB() → (DB, error) |
TxBeginner |
BeginTx(ctx, opts) → Tx |
Every service method starts with tracking + span:
func (s *ActivateRuleService) Execute(ctx context.Context, ruleID uuid.UUID) (*model.Rule, error) {
logger, tracer, _, _ := libCommons.NewTrackingFromContext(ctx)
ctx, span := tracer.Start(ctx, "service.rule.activate")
defer span.End()
logger = logging.WithTrace(ctx, logger)
// ... business logic ...
}- Sentinel errors in
pkg/constant/errors.gowith TRC-XXXX codes - Business errors: return directly, use
HandleSpanBusinessErrorEvent - Technical errors: wrap with
fmt.Errorf("context: %w", err), useHandleSpanError - Use
%walways, never%vfor error wrapping (enforced by errorlint) - Constructor validation: return
(*T, error), sentinel errors for nil deps
- Constructor
New*()validates invariants, returns(*T, error) - Validate-before-mutate: no partial mutations on error
- Defensive copies for slices/maps
- Private fields with validated setters for state transitions
Validate()method on every entity for persistence-boundary checks- Amounts use
shopspring/decimal(never float64) - IDs use
uuid.UUID(never string) - Timestamps: always
time.Now().UTC()
- Interfaces defined where used (command/ and query/ packages)
- PostgreSQL model structs separate from domain entities
- Squirrel SQL builder with
squirrel.Dollarplaceholder format - Cursor-based pagination (no offset)
sortByvalidated against allowlist before cursor encoding
- Fiber v2 framework
- Every handler: extract ctx, start span, parse input, call service, respond
- Response wrappers:
libHTTP.OK(),libHTTP.Created(),libHTTP.NoContent(),libHTTP.WithError() - Never use direct Fiber responses (
c.JSON(),c.Status().JSON()) - Swagger annotations required on all handlers
- Input validation in separate
*_validation.gofiles
- TDD required: write test first, then implement
- Table-driven tests with descriptive case names
gomockfor interface mocks (never manual mocks)sqlmockfor database query testing- Deterministic data:
testutil.FixedTime(),testutil.MustDeterministicUUID(seed) - Never
uuid.New()ortime.Now()in tests require.Lenbefore indexing slicesrequire.Equalfor ordered content,require.ElementsMatchfor unordered- No
defer ctrl.Finish()— go.uber.org/mock v0.3.0+ auto-registers cleanup - Build tags:
integrationfor testcontainers,e2efor BDD
- Always
WithFields(never string interpolation) - OpenTelemetry semantic conventions:
rule.id,error.message,operation - Enrich with trace context:
logger = logging.WithTrace(ctx, logger) operationfield must match span name- Log messages: "Creating rule" (start), "Rule created successfully" (success), "Failed to create rule" (error)
- API Key via
X-API-Keyheader - Constant-time comparison (
crypto/subtle) - Configurable:
API_KEY_ENABLED,API_KEY_ENABLED_ONLY_VALIDATION - Access Manager plugin:
PLUGIN_AUTH_ENABLED,PLUGIN_AUTH_ADDRESS - Public endpoints:
/health,/ready,/version,/swagger/*
Most config loaded via libCommons.SetConfigFromEnvVars(cfg) from struct tags. Exception: MOCK_TIME is read once via os.Getenv at boot (not part of Config struct).
| Category | Variables |
|---|---|
| Application | SERVER_ADDRESS (:4020), LOG_LEVEL |
| Database | DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, DB_SSL_MODE, MIGRATIONS_PATH |
| Auth | API_KEY, API_KEY_ENABLED, API_KEY_ENABLED_ONLY_VALIDATION, PLUGIN_AUTH_ADDRESS, PLUGIN_AUTH_ENABLED |
| CORS | CORS_ALLOWED_ORIGINS |
| CEL | CEL_COST_LIMIT (default: 10000) |
| Evaluation | DEFAULT_DECISION_WHEN_NO_MATCH (ALLOW|DENY), MAX_RULES_PER_REQUEST (1000) |
| Telemetry | ENABLE_TELEMETRY, OTEL_RESOURCE_SERVICE_NAME, OTEL_LIBRARY_NAME, OTEL_RESOURCE_SERVICE_VERSION, OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT, OTEL_EXPORTER_OTLP_ENDPOINT |
| Cleanup Worker | CLEANUP_WORKER_ENABLED (false), CLEANUP_INTERVAL_HOURS (24) |
| Rule Sync | RULE_SYNC_POLL_INTERVAL_SECONDS (10), RULE_SYNC_STALENESS_THRESHOLD_SECONDS (50), RULE_SYNC_OVERLAP_BUFFER_SECONDS (2) |
| Testing | MOCK_TIME (RFC3339 via os.Getenv, not in Config struct — read once at boot for deterministic integration tests) |
| Service | Image | Port |
|---|---|---|
| tracer | tracer:dev (Dockerfile.dev) |
4020 (via SERVER_ADDRESS env var) |
| tracer-postgres | postgres:16-alpine |
5432 |
Multi-stage: golang:1.26-alpine (builder) → gcr.io/distroless/static-debian12:nonroot (runtime). Exposes 4020 and 7001. Non-root user. No shell in production — use orchestrator probes.
Configuration in .golangci.yml. Key enabled linters:
errorlint— enforces%wusage, proper error assertionscontextcheck— verifies correct context propagationdepguard— blocksio/ioutil(deprecated)gocyclo— max complexity 20revive— import shadowing, empty blocks, use-anywsl_v5— whitespace conventions (empty lines before return, if, defer)staticcheck,bodyclose,prealloc,nilerr,unconvert,wastedassign
Shadow declarations of err and ok are excluded from govet shadow check.
Empty line required before: return, if, for, switch, defer, assignments after different statement types. No empty lines at block boundaries.
- lib-auth/v2 (
v2.6.0): Auth middleware, auth clientauthMiddleware.NewAuthClient(address, enabled, &logger)
- lib-commons/v4 (
v4.6.0): Common utilities, telemetry, infrastructure- Tracking:
libCommons.NewTrackingFromContext(ctx)→ logger, tracer, headerID - OpenTelemetry:
libOtel.HandleSpanError(span, "msg", err),libOtel.HandleSpanBusinessErrorEvent(span, "msg", err) - Database:
libPostgres.New(config)→ Client with primary/replica - Config:
libCommons.SetConfigFromEnvVars(cfg)— struct tag loading - HTTP:
libHTTP.OK(),libHTTP.Created(),libHTTP.WithError(),libHTTP.HandleFiberError() - Launcher:
libCommons.NewLauncher(opts...).Run()— graceful multi-service lifecycle - Logging:
libZap.New(),libLog.Loggerinterface
- Tracking:
gofiber/fiber/v2— HTTP frameworkgoogle/cel-gov0.27.0 — CEL expression engineMasterminds/squirrel— SQL query buildershopspring/decimal— Precise decimal arithmeticgoogle/uuid— UUID generationjackc/pgx/v5— PostgreSQL driversony/gobreaker— Circuit breakerbxcodec/dbresolver/v2— Primary/replica DB routinggolang-migrate/migrate/v4— Database migrationsgo-playground/validator/v10— Struct tag validation for DTOsDATA-DOG/go-sqlmock— SQL mockingtestcontainers/testcontainers-go— Container-based integration testscucumber/godog— BDD test frameworkgo.uber.org/mock— Interface mocking (gomock)
All CI uses shared workflows from LerianStudio/github-actions-shared-workflows.
make lint— linters passmake test-unit— unit tests passmake sec— no security issuesmake generate-docs— Swagger updated (if API changed)- Commit message follows conventional commits
- Add handler method to
internal/adapters/http/in/{resource}_handler.go - Add input validation in
{resource}_validation.go - Register route in
routes.go - Add Swagger annotations
- Run
make generate-docs - Write tests for handler
- Create file in
internal/services/command/{verb}_{resource}.go - Define input struct in same file
- Implement
Execute(ctx, input)with tracking + span - Wire dependencies in
internal/bootstrap/config.go - Write table-driven tests with gomock
- Create
migrations/000NNN_descriptive_name.up.sqland.down.sql - Test:
make migrate && make migrate-down && make migrate - Add indexes for join/filter columns
| Problem | Solution |
|---|---|
| Server won't start | Check make up ran, verify .env exists, check make logs |
| Migration stuck | make migrate-version to check state, make migrate-force VERSION=N |
| Integration tests fail | Ensure Docker running, try LOW_RESOURCE=1 |
| E2E tests fail | make test-e2e E2E_SKIP_RESET=1 to reuse DB |
| Rule not evaluated | Verify rule status is ACTIVE (not DRAFT) |
| Cache stale | Check RULE_SYNC_POLL_INTERVAL_SECONDS, readiness probe reports cache health |
| CEL expression error | Check CEL_COST_LIMIT, verify expression compiles at rule creation |
| Testcontainers hang | Set TESTCONTAINERS_RYUK_DISABLED=true (macOS/Docker Desktop) |
| File | Purpose |
|---|---|
AGENTS.md |
Concise agent overview (read first) |
docs/PROJECT_RULES.md |
Full architectural rules, domain model, testing standards |
docs/CODING_STANDARDS.md |
Code review patterns and best practices |
.env.example |
All environment variables with documentation |
.golangci.yml |
Linter configuration |
internal/bootstrap/config.go |
Composition root — all DI wiring |
pkg/constant/errors.go |
All error codes (TRC-XXXX) |
pkg/model/ |
Domain entities (44 files) |
api/swagger.json |
OpenAPI specification (auto-generated by swaggo — do not edit) |
api/swagger.yaml |
OpenAPI spec YAML (auto-generated by swaggo — do not edit) |
api/openapi.yaml |
OpenAPI spec via openapi-generator (auto-generated — do not edit) |
api/docs.go |
Swagger Go bindings (auto-generated by swaggo — do not edit) |
Last Updated: April 2026
Go Version: module go 1.25.7, Docker builder image golang:1.26-alpine
Migrations: 12 (000001 through 000012) + function migrations