Development Guide¶
Welcome to the ORES contributor experience. This guide covers everything you need to set up a local development environment, run tests, and build all ORES artifacts.
Prerequisites¶
| Tool | Version | Purpose |
|---|---|---|
| Go | 1.25+ | All Go code |
| Task | 3.x | Task runner (replaces make) |
| buf | 1.x | Protobuf code generation |
| golangci-lint | latest | Linting (bundled via go tool) |
golangci-lint is bundled
golangci-lint is configured as a go tool in go.mod — you do not need to install it separately.
Install tools¶
Clone and Build¶
This produces bin/ores (CLI) and bin/oresd (daemon).
Available Tasks¶
Run task with no arguments to see all tasks, or use any of the following:
| Task | Description |
|---|---|
task build | Build CLI (bin/ores) and daemon (bin/oresd) |
task build:wasm | Build WASM module (bin/ores.wasm) |
task test | Run all tests with race detector and coverage |
task test:short | Run tests without race detector (faster) |
task lint | Run golangci-lint |
task generate | Regenerate protobuf code with buf generate |
task clean | Remove bin/, dist/, gen/, coverage.txt |
Running tests¶
This runs go test -race -coverprofile=coverage.txt ./.... View coverage in your browser:
Running lint¶
Lint must pass
golangci-lint is configured in .golangci.yml. All rules must pass with zero findings before a PR is merged.
Regenerating protobuf¶
This runs buf generate using the configuration in buf.gen.yaml and buf.yaml. Generated files land in gen/proto/ores/v1/.
When to regenerate
You only need to run this if you modify .proto files in api/proto/.
Project Structure¶
ores/
├── api/
│ └── proto/ores/v1/ # Protobuf service definition
├── bin/ # Build output (gitignored)
├── cmd/
│ ├── ores/ # CLI entry point
│ └── oresd/ # Daemon entry point
├── gen/
│ └── proto/ores/v1/ # Generated protobuf Go code (gitignored)
├── pkg/
│ ├── engine/ # Pipeline orchestrator
│ ├── explain/ # Explanation builder
│ ├── model/ # Scoring model and confidence
│ ├── score/ # Core request/response types
│ ├── signals/ # Signal interface, registry, NormalizedSignal
│ │ └── parsers/ # Per-signal-type parsers
│ └── wasm/ # WASM entry point
├── buf.gen.yaml # buf code generation config
├── buf.yaml # buf module config
├── go.mod # Go module definition
├── Taskfile.yml # Task definitions
└── .golangci.yml # Linter configuration
Package Responsibilities¶
pkg/score- Defines the core types:
EvaluationRequest,EvaluationResult,Explanation,Factor,Label, andLabelForScore. This package has no dependencies on other ORES packages — it is the shared type language. pkg/signals- Defines the
Signalinterface, theNormalizedSignaltype (map[string]float64), and theRegistrythat maps signal names to their implementations. Does not contain any specific signal parsers. pkg/signals/parsers- Contains the eight built-in signal parser implementations (
CVSS,EPSS,NIST,Asset,ThreatIntel,BlastRadius,Patch,Compliance) and theRegisterAllfunction that registers them with aRegistry. pkg/model- Implements the weighted composite scoring model. Accepts a slice of
NormalizedSignalvalues, applies dimension scoring functions, and returns aScoreResultwith per-dimension contributions. Also implements confidence calculation (CalculateConfidence). The model version string is defined here. pkg/explain- Builds the
score.Explanationfrom model output and signal metadata. Maps dimension names to contributing signals and generates human-readable reasoning strings. pkg/engine- Wires together
pkg/signals,pkg/signals/parsers,pkg/model, andpkg/explaininto a singleEvaluatecall. This is the public API for library consumers. cmd/ores- The
oresCLI. Implementsevaluate,signals, andversionsubcommands using cobra. Reads input from file or stdin (JSON and YAML), calls the engine, and writes results in JSON, YAML, or table format. cmd/oresd- The
oresddaemon. Implements theOresServiceConnectRPC handler, wraps it with audit-logging middleware, and runs an HTTP server with health/readiness probes. Handles graceful shutdown on SIGINT/SIGTERM. pkg/wasm- The WASM entry point. Reads JSON from stdin, calls the engine, writes JSON to stdout. Built with
GOOS=wasip1 GOARCH=wasm.
Coding Standards¶
Logging¶
Use log/slog in cmd/ code. Never use fmt.Print* or the log package for application logging.
// Good
slog.Info("evaluation complete", "score", result.Score, "label", result.Label)
// Bad
fmt.Printf("evaluation complete: score=%d\n", result.Score)
log.Printf("evaluation complete: score=%d", result.Score)
Error handling¶
Always wrap errors with context at call sites. Return errors from functions — do not log.Fatal in library code.
// Good
if err := engine.Evaluate(ctx, req); err != nil {
return fmt.Errorf("evaluating request: %w", err)
}
// Bad
if err := engine.Evaluate(ctx, req); err != nil {
log.Fatal(err)
}
Interfaces¶
All external dependencies (e.g., the engine passed to HTTP handlers) must be referenced through interfaces for testability.
Tests¶
Table-driven tests using github.com/stretchr/testify (assert and require). Test files live alongside the code they test.
func TestLabelForScore(t *testing.T) {
t.Parallel()
tests := []struct {
name string
score int
want Label
}{
{"critical", 95, LabelCritical},
{"high", 75, LabelHigh},
{"medium", 50, LabelMedium},
{"low", 25, LabelLow},
{"info", 5, LabelInfo},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
assert.Equal(t, tc.want, LabelForScore(tc.score))
})
}
}
Linting¶
All code must pass golangci-lint run ./... with zero findings.
Devcontainer¶
A .devcontainer/devcontainer.json is included for VS Code and GitHub Codespaces. It sets up Go 1.25, Task, and buf automatically.
One-click setup
Open the repository in VS Code and choose "Reopen in Container" to get a fully configured environment with all tools pre-installed.
Making a Change¶
-
Create a feature branch from
main: -
Make your changes.
-
Run tests and lint:
-
Commit and open a pull request against
main.
Full PR process
See CONTRIBUTING.md for the complete pull request process and code review guidelines.