This guide provides comprehensive instructions for AI coding agents working on the Auth API repository.
Language: Go 1.23+
Framework: Gin for HTTP handling
Database: PostgreSQL with GORM
Caching: Redis
Project Type: Modular REST API (Clean Architecture)
This project has OpenCode skills that give you instant context about the codebase. Always load relevant skills before exploring files manually. This saves significant time and avoids redundant discovery.
Load the project-map skill immediately. It contains the full module inventory, dependency graph, file paths, and architecture overview:
skill("project-map")
Load the relevant skill before reading source files:
| Task area | Skill to load | What it covers |
|---|---|---|
| Routes, endpoints, middleware | route-map |
All 200+ HTTP routes, auth layers, rate limits, handler mappings |
| Database models, migrations | data-model |
All 17 GORM models, fields, relationships, indexes, ER diagram |
| Auth, JWT, login, 2FA, sessions | auth-flows |
4 auth systems, token lifecycle, RBAC, session management |
| Email, templates, SMTP | email-system |
Template resolution chain, 3 rendering engines, variable pipeline |
| Admin GUI, HTMX templates | admin-gui |
HTMX GUI structure, template rendering, CRUD patterns, CSRF |
1. skill("project-map") — Understand overall structure
2. skill("auth-flows") — Deep dive into auth before modifying login
3. Read specific files as needed — Now you know exactly which files to open
Do NOT spend time grepping or globbing to discover the codebase structure when a skill already provides that information.
make dev # Hot reload development server (uses Air)
make run # Run application once
make setup # Install dependencies and Airmake test # Run all tests
make test-totp # Run TOTP test (requires TEST_TOTP_SECRET env var)go test -v ./internal/user -run TestRegister # Specific test
go test -v ./internal/user -run TestRegister -count=1 # No caching
go test -v -race ./internal/user # Race detectormake fmt # Format code (go fmt)
make lint # Run golangci-lint
make security # Run gosec + nancy vulnerability scansmake build # Build binary for current OS
make build-prod # Cross-compile for Linux (CGO_ENABLED=0)make migrate-up # Apply pending migrations
make migrate-down # Rollback last migration
make migrate-status # Show current schema
make migrate-backup # Create database backup// Standard library
import (
"context"
"fmt"
"net/http"
)
// Third-party packages
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// Internal packages
import (
"github.com/gjovanovicst/auth_api/internal/user"
"github.com/gjovanovicst/auth_api/pkg/dto"
)- Functions: CamelCase starting with verb (
NewService,GetByID) - Constants: UPPER_SNAKE_CASE for constants
- Private functions: camelCase prefix (e.g.,
validateEmail) - Exported types: PascalCase (e.g.,
UserService) - File names: snake_case (e.g.,
handler.go,repository.go)
- Use interfaces for abstraction in service/repository layers
- Define domain models in
pkg/models/with GORM tags - Use DTOs in
pkg/dto/for API request/response contracts - All struct fields must have JSON tags for API endpoints
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Email string `gorm:"unique;not null" json:"email"`
Password string `gorm:"not null" json:"-"` // Hidden from responses
CreatedAt time.Time `json:"created_at"`
}Never expose raw database errors to clients. Follow the pattern:
if err != nil {
// Log internal error if needed
c.JSON(http.StatusInternalServerError, dto.ErrorResponse{
Error: "An error occurred", // Generic message
})
return
}Use custom error types from pkg/errors/:
if err != nil {
appErr := errors.NewAppError(errors.ErrConflict, "Email already registered")
c.JSON(appErr.Code, gin.H{"error": appErr.Message})
return
}All services use constructor functions:
// Repository layer
func NewRepository(db *gorm.DB) Repository {
return &repository{db: db}
}
// Service layer
func NewService(repo Repository, cache Cache) Service {
return &service{repo: repo, cache: cache}
}
// Handler layer
func NewHandler(service Service) *Handler {
return &Handler{Service: service}
}Use go-playground/validator struct tags:
type LoginRequest struct {
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,max=128"`
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
c.JSON(http.StatusBadRequest, dto.ErrorResponse{Error: err.Error()})
return
}// Always parameterize queries (GORM handles this automatically)
var user models.User
if err := r.db.Where("email = ?", email).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil // Not found, no error
}
return nil, err
}Always document HTTP handlers with Swagger annotations:
// @Summary User login
// @Description Authenticate user with email and password
// @Tags authentication
// @Accept json
// @Produce json
// @Param request body dto.LoginRequest true "Login credentials"
// @Success 200 {object} dto.LoginResponse
// @Failure 400 {object} dto.ErrorResponse
// @Router /login [post]
func (h *Handler) Login(c *gin.Context) {
// Implementation
}After changes, regenerate docs:
make swag-initUse the log service for audit trails:
logService.CreateLog(ctx, "USER_LOGIN", "User logged in successfully",
userID, "INFO")- Repository: Data access, database queries
- Service: Business logic, validation, orchestration
- Handler: HTTP transport, request binding, response formatting
internal/
├── auth/ # Authentication domain
├── user/ # User management domain
├── social/ # OAuth2 providers
├── twofa/ # Two-factor authentication
├── log/ # Activity logging
├── middleware/ # HTTP middleware (auth, CORS, etc.)
├── database/ # Database connection & migrations
├── redis/ # Redis session management
└── config/ # Configuration loading
pkg/
├── models/ # GORM database models
├── dto/ # API request/response DTOs
├── jwt/ # JWT utilities
└── errors/ # Custom error types
- JWT tokens: 15-minute access, 720-hour refresh (configurable)
- Token blacklisting: Use Redis for logout functionality
- Password hashing: Use bcrypt (never store plaintext)
- 2FA/TOTP: Support for authenticator apps + recovery codes
- JWT validation middleware in
internal/middleware/auth.go - Extract user context:
c.Get("user_id")after auth middleware - Admin routes prepared for role-based access control
- Always validate DTOs with struct tags + validator
- Sanitize database inputs (GORM prevents SQL injection)
- Use parameterized queries only (GORM handles automatically)
Before committing:
make security # Run gosec + nancyConfiguration in .gosec.json with Medium severity threshold.
<type>(<scope>): <description>
[optional body]
[optional footer]
feat: New featurefix: Bug fixsecurity: Security patch (especially important for auth APIs)docs: Documentationrefactor: Code restructuring (no logic changes)test: Adding/updating testschore: Build, dependencies
auth, user, social, twofa, email, middleware, database, redis, log, api, models, dto, jwt
feat(auth): add JWT token blacklisting for logout
fix(middleware): prevent auth bypass with malformed tokens
security(password): enforce bcrypt cost >= 12
test(user): add registration validation tests
docs(api): update Swagger for 2FA endpoints
- Check
.envis configured with database/Redis credentials - Run
make docker-devto start PostgreSQL and Redis (optional, or use local services) - Run
make setupto install Air and dependencies
- Use
make devfor hot-reload server - Write tests alongside implementation
- Run
make fmtandmake lintbefore committing - Document HTTP endpoints with Swagger tags
make test- Ensure all tests passmake fmt- Format codemake lint- Check linting rulesmake security- Run security scansmake swag-init- Update Swagger docs (if API changed)- Follow commit message format above
- Entry point:
cmd/api/main.go(dependency injection, route setup) - Database models:
pkg/models/ - DTOs:
pkg/dto/ - Error types:
pkg/errors/errors.go - JWT utilities:
pkg/jwt/jwt.go - Configuration: Viper-based in
cmd/api/main.gowith.envfile - Swagger docs: Auto-generated in
docs/directory