Minder is an open-source supply chain security platform that helps development teams build more secure software and prove their security posture. It enables proactive security policy management across repositories and artifacts, with features for continuous security enforcement, artifact attestation, and dependency management.
Key Technologies:
- Language: Go 1.24+
- Protocol Buffers: gRPC for API communication, REST via grpc-gateway
- Database: PostgreSQL with sqlc for type-safe SQL
- Message Queue: NATS JetStream for event-driven architecture
- Authentication: Keycloak (OAuth2/OIDC), JWT tokens
- Authorization: OpenFGA (relationship-based access control)
- Frontend CLI: Cobra framework with Bubble Tea TUI components
- Observability: OpenTelemetry, Prometheus metrics, zerolog
- Security: Sigstore for artifact signing/verification
Minder consists of:
- minder-server: gRPC/REST API server (control plane)
- minder CLI: Command-line interface for users
- reminder: Background service for scheduled tasks
- Control Plane (
internal/controlplane/): API handlers and business logic - Engine (
internal/engine/): Policy evaluation and enforcement - Providers (
internal/providers/): Integration with GitHub, GitLab, container registries - Datasources (
internal/datasources/): REST API data fetching and ingestion
.
βββ cmd/ # Main applications
β βββ cli/ # minder CLI tool
β βββ server/ # minder-server (gRPC/REST API)
β βββ dev/ # Development utilities
β βββ reminder/ # Scheduled task service
βββ internal/ # Private application code
β βββ controlplane/ # API handlers and orchestration
β βββ engine/ # Policy evaluation engine
β βββ providers/ # GitHub, GitLab, container registry integrations
β βββ datasources/ # REST API datasources
β βββ db/ # Generated SQLC database code
β βββ auth/ # Authentication (Keycloak, JWT)
β βββ authz/ # Authorization (OpenFGA)
β βββ events/ # Event handling (NATS)
β βββ entities/ # Entity management
β βββ reconcilers/ # State reconciliation
β βββ crypto/ # Cryptographic operations
βββ pkg/ # Public library code
β βββ api/ # Generated protobuf/OpenAPI code
β βββ profiles/ # Security profiles
β βββ ruletypes/ # Rule type definitions
β βββ mindpak/ # Package management
β βββ engine/ # Engine interfaces
β βββ providers/ # Provider interfaces
βββ proto/ # Protocol buffer definitions
βββ database/ # Database layer
β βββ migrations/ # SQL migrations (golang-migrate)
β βββ query/ # SQLC query definitions
β βββ schema/ # Database schema
βββ deployment/ # Kubernetes and Helm charts
βββ docs/ # Documentation (Docusaurus)
βββ examples/ # Example configurations
βββ .mk/ # Makefile includes
Required tools:
- Go 1.24+
- Docker & Docker Compose
- OpenSSL (for key generation)
Build tools (installed via make bootstrap):
- ko (for container builds)
- buf (Protocol Buffer compilation)
- sqlc (SQL code generation)
- golangci-lint (linting)
- gotestfmt (test output formatting)
- protoc plugins (grpc-gateway, protoc-gen-go, etc.)
- mockgen (mock generation)
- yq (YAML processing)
- fga (OpenFGA CLI)
- helm-docs (Helm documentation)
Runtime services (via Docker):
- PostgreSQL (database)
- Keycloak (authentication)
- NATS (message queue)
Before building or running Minder, install all build dependencies and initialize configuration:
# Install build tools and initialize configuration
make bootstrapThis command will:
- Install all Go-based build tools (sqlc, protoc plugins, mockgen, etc.)
- Create
config.yamlandserver-config.yamlfrom example templates (if they don't exist) - Generate encryption keys in
.ssh/directory for token signing
Note: Run make bootstrap once after cloning the repository. You may need to run it again if build tool versions change.
# Build both CLI and server binaries to ./bin/
make build
# Clean build artifacts
make cleanCode generation is critical and must be run after changes to:
# Run all code generation (protobuf, sqlc, mocks, OpenAPI)
make gen
# Individual generators:
make buf # Generate protobuf code from proto/
make sqlc # Generate Go code from database/query/*.sql
make mock # Generate mocks using mockgen
make oapi # Generate OpenAPI client code
make cli-docs # Generate CLI documentationWhen to regenerate:
- After modifying
.protofiles βmake buf - After modifying
.sqlfiles indatabase/query/βmake sqlc - After adding database migrations β
make sqlc - After modifying interfaces that need mocks β
make mock - After changing CLI commands β
make cli-docs
Minder uses PostgreSQL with golang-migrate for migrations and sqlc for type-safe queries.
# Start local database
make run-docker
# Run migrations
make migrateup
# Rollback migrations
make migratedown
# After adding queries to database/query/*.sql, regenerate Go code
make sqlcDatabase conventions:
- Migrations in
database/migrations/numbered sequentially - Queries in
database/query/*.sqlwith sqlc annotations - Generated code in
internal/db/ - Always use prepared statements via sqlc
- Use transactions for multi-statement operations
# Run all tests with verbose output
make test
# Run tests silently (errors only)
make test-silent
# Run tests with coverage
make cover
# Coverage in silent mode
make test-cover-silent
# Lint Go code
make lint-go
# Lint protobuf definitions
make lint-buf
# Run all linters
make lintTesting conventions:
- Test files:
*_test.goalongside source files - Use
testify/assertfor assertions - Use
testify/requirefor setup/prerequisites - Use
testify/mockorgo.uber.org/mockfor mocking - Use
testify/suitefor integration tests - Mock database queries via
database/mock/store.go - Parallel tests encouraged with
t.Parallel() - Coverage excludes: auto-generated code (db, proto, mocks)
# Start all services (server, Postgres, Keycloak, NATS)
make run-docker
# Configure GitHub OAuth for Keycloak
make KC_GITHUB_CLIENT_ID=<id> KC_GITHUB_CLIENT_SECRET=<secret> github-login
# Use CLI against local server (requires config.yaml)
cp config/config.yaml.example config.yaml
minder auth login # defaults to localhost:8090
# Use CLI against hosted instance
minder auth login --grpc-host api.custcodian.devConfiguration:
- Server config:
server-config.yaml(fromconfig/server-config.yaml.example) - CLI config:
config.yaml(fromconfig/config.yaml.example) - Environment-specific configs in
config/directory - Use
MINDER_CONFIGenv var to specify config file
- Logging: Use
github.com/rs/zerolog(NOT standardlogpackage) - Error handling: Wrap errors with context using
fmt.Errorfwith%w - Naming: Follow Go conventions (PascalCase exports, camelCase private)
- Package structure: Follow standard Go project layout
- Comments: Document all exported functions, types, and packages
- Linting: All code must pass
make lintbefore commit - Cyclomatic complexity: Keep functions under complexity 15
- Line length: Max 120 characters (enforced by linter)
- Definitions in
proto/minder/v1/ - Use buf for linting and generation
- Follow buf style guide
- All RPCs must have
RpcOptionswith appropriate authorization - Use
google.api.annotationsfor REST mapping - Validate inputs with
buf.validate.validateconstraints
- Use sqlc for all database access
- Write queries in
database/query/*.sql - Use named parameters:
@param_name - Prefer
RETURNING *for INSERT/UPDATE operations - Use transactions via
db.WithTransaction() - Never use raw SQL strings in code
- All timestamps should be
timestamptz
Follow Chris Beams' guide:
- Separate subject from body with blank line
- Limit subject to 50 characters
- Capitalize subject line
- No period at end of subject
- Use imperative mood ("Add feature" not "Added feature")
- Explain what and why in body, not how
Example:
Add secret scanning remediation support
Implements automatic remediation for secret scanning alerts by
enabling the feature through the GitHub API when profiles are
evaluated. This ensures repositories stay compliant with security
policies without manual intervention.
// Good: Wrap errors with context
if err != nil {
return fmt.Errorf("failed to process entity %s: %w", entityID, err)
}
// Bad: Bare return
if err != nil {
return err
}// Good: Use zerolog with structured fields
zerolog.Ctx(ctx).Info().
Str("entity_id", entityID).
Str("rule_type", ruleType).
Msg("evaluating rule")
// Bad: Use standard log
log.Printf("evaluating rule for %s", entityID)// Good: Use sqlc-generated queries
repo, err := s.store.GetRepositoryByID(ctx, repoID)
if err != nil {
return fmt.Errorf("failed to get repository: %w", err)
}
// Good: Use transactions
err := s.store.WithTransaction(func(qtx db.ExtendedQuerier) error {
// Multiple operations
return nil
})// Use mockgen-generated mocks
mockStore := mockdb.NewMockStore(ctrl)
mockStore.EXPECT().
GetRepositoryByID(gomock.Any(), repoID).
Return(db.Repository{}, nil)// Always validate authorization
if err := s.authz.CheckAuthorization(ctx, req); err != nil {
return nil, status.Errorf(codes.PermissionDenied, "unauthorized")
}
// Use proper status codes
if errors.Is(err, sql.ErrNoRows) {
return nil, status.Error(codes.NotFound, "resource not found")
}- Define RPC in
proto/minder/v1/minder.proto - Add
RpcOptionswith authorization relation - Run
make bufto generate Go code - Implement handler in
internal/controlplane/handlers_*.go - Add authorization check in handler
- Add tests in
internal/controlplane/*_test.go - Update documentation
- Create migration files manually in
database/migrations/(numbered sequentially) - Write UP and DOWN SQL in
database/migrations/ - Add queries in
database/query/foo.sql - Run
make sqlcto generate Go code - Use generated functions in
internal/db/ - Write tests using mock store
- Define rule type schema (YAML or JSON)
- Implement evaluator in
internal/engine/eval/ - Add remediation handler in
internal/engine/actions/remediate/ - Add alert handler in
internal/engine/actions/alert/ - Register rule type in engine
- Add integration tests
- Document in
docs/
- Implement provider interface in
internal/providers/ - Add OAuth configuration in provider
- Implement entity fetching (repos, artifacts, PRs)
- Add provider registration in control plane
- Add CLI commands in
cmd/cli/app/provider/ - Add integration tests
- Document provider-specific features
- Server logs: Check Docker Compose output
- Database: Use
psqlto inspect database directly - gRPC: Use
grpcurlfor manual API testing - Events: Check NATS JetStream logs for event flow
- Auth: Check Keycloak admin console at
localhost:8081
"Permission denied" errors: Check OpenFGA authorization model and user roles
Database migration errors: Ensure migrations are sequentially numbered and reversible
gRPC connection refused: Ensure server is running and config points to correct host:port
Token expired: Run minder auth login to refresh authentication
Code generation out of sync: Run make gen to regenerate all code
make help # Show all available targets
make bootstrap # Install build tools and initialize configuration (run once)
make build # Build CLI and server binaries
make gen # Run all code generators
make test # Run all tests with verbose output
make cover # Run tests with coverage report
make lint # Run all linters
make clean # Clean generated files and binaries
make run-docker # Start all services locally
make migrateup # Apply database migrations
make migratedown # Rollback one migration
make cli-docs # Generate CLI documentation- Documentation: https://mindersec.github.io/
- API Reference: https://mindersec.github.io/ref/api
- Proto Reference: https://mindersec.github.io/ref/proto
- CLI Reference: https://mindersec.github.io/ref/cli/minder
- Rules & Profiles: https://github.com/mindersec/minder-rules-and-profiles
- Discord: https://discord.gg/RkzVuTp3WK
- Contributing: See CONTRIBUTING.md
- Never commit secrets, API keys, or tokens
- Use
.envfiles for local secrets (already in.gitignore) - All API endpoints require authentication and authorization
- Implement rate limiting for public-facing APIs
- Validate all user inputs with protobuf validators
- Use parameterized queries (sqlc) to prevent SQL injection
- Follow SLSA Build Level 3 practices for releases
- Sign all release artifacts with Sigstore
- Always run
make genafter modifying.protoor.sqlfiles - Check authorization in all gRPC handlers before business logic
- Use zerolog for logging, never standard library
log - Write tests for new features before marking work complete
- Follow the commit message guidelines strictly
- Update documentation in
docs/for user-facing changes - Mock external dependencies in tests (GitHub API, etc.)
- Use
internal/dbfor database access, never raw SQL - Check
make lintpasses before considering code complete - When adding dependencies, run
go mod tidyand commitgo.mod/go.sum