The runner log endpoint returned the stored log only for succeeded and failed
runs, so a stopped build showed an empty log. Serve it for stopped runs too:
the build coordinator stores the log regardless of how the run ended.
Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/builders/... ./pkg/service/builders/...
- CI_DATABASE_TYPE=turso go test -parallel 1 -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/builders/...
- golangci-lint run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/builders/... ./pkg/service/builders/...
Every /v2/*path request dispatched through the dig container: distribution.All
called factory.Initialize(c, digCon) and each factory ran digCon.Invoke to get
its handler. dig containers are not safe for concurrent use, so concurrent
pulls and pushes raced on the container's internal maps and crashed the
process with "concurrent map read and map write".
Split the factory contract into Prepare, which resolves the handler once at
startup, and Initialize, which only dispatches the request. handlers.
InitializeDistribution runs distribution.Prepare before registering the route,
so request goroutines no longer touch dig.
Verification:
- go build ./pkg/server/... ./pkg/app/...
- CI_DATABASE_TYPE=turso go test -parallel 1 ./pkg/server/handlers/...
- golangci-lint run ./pkg/server/handlers/...
- Sent 200 concurrent and then 180 mixed concurrent /v2 requests to a freshly
built server: the process kept serving and the log had no fatal error.
A sync listed the branches of every repository it saw, one API call per
repository per page. For an account with a few hundred repositories that
alone exhausts the GitHub rate limit, and the result was stale the moment a
branch was pushed.
Store the provider's push time instead, and read branches only when a build
form asks for them:
- the daemon reconciles repositories and owners only; github, gitlab and
gitea no longer page through branches, diffBranch is gone, and the
code_repository_branches table and model are dropped from the baseline
- CodeRepository gains pushed_at, filled from github pushed_at, gitlab
last_activity_at (falling back to updated_at) and gitea updated_at, and
the list orders by it so "most recently active" means the provider's view
rather than our own row's updated_at, which every sync touched
- ListCodeRepositoryBranches and GetCodeRepositoryBranch resolve the
binding, the decrypted token and the repository, then query the provider,
so branch names are always current; a repository id is user input and is
now checked against the caller's binding instead of exposing another
account's repository
- the list response also stops filling updated_at from created_at
Existing installations need the new column and the dropped table applied by
hand, as documented for the baseline migration edit.
Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/dal/repository/coderepo/... ./pkg/service/coderepos/... ./pkg/server/handlers/coderepos/... ./pkg/background/daemon/coderepo/...
- make gormgen
- make swagen
PUT /api/v1/coderepos/{provider}/credential returned a 500 and logged
"http: panic serving ...: Bad field type *string": validator's max tag
panics when it is combined with required_if on a pointer field. A token
request leaves ssh_key, username and password nil, required_if passes
because the condition is false, the chain moves on to max, and max receives
the nil *string without dereferencing it.
Enforce the size limits in the service instead, next to the required checks
that already lived there, and keep required_if on the request so an
incomplete body is still rejected before it reaches the service. Swagger no
longer advertises maxLength for these fields.
Add a binding test that drives the three credential shapes through
server.WrapRequest, which is the path the panic was reached on.
Verification:
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/coderepos/... ./pkg/service/coderepos/...
- golangci-lint run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/api/... ./pkg/service/coderepos/... ./pkg/server/handlers/coderepos/...
- make swagen
Users had no way to supply their own access token: the only credential in
play was the OAuth token captured at login, which expires and which the
builders then copied into their own table. The clone credential table
existed but had no write path, so every lookup fell back to that token.
Give each provider an explicit credential:
- GET/PUT/DELETE /api/v1/coderepos/{provider}/credential plus
POST .../credential/validate; the read endpoint returns a masked token and
"has" flags, never the secret
- UpsertCodeRepositoryCredential validates a token against the provider,
resolves the account id from it, ensures the third-party binding exists
(409 when the account already belongs to another user) and seals
token/ssh_key/password before storing them
- only token credentials can list repositories: ssh and username/password
are clone credentials, so validate reports validated=false for them and
no sync is queued
- ListCodeRepositoryProviders returns the configured providers joined with
the caller's binding, credential and last sync state, so a provider that
is configured but not linked yet is still offered by the UI
- fix the AccountID and UpdatedAt mistakes in the user3rdparty response,
which dereferenced a nil pointer and reported the creation time twice
- regenerate the coderepos and user repository mocks and the operator
swagger spec
Verification:
- make gormgen
- make swagen
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/coderepos/... ./pkg/server/handlers/coderepos/...
Sigma shipped three overlapping ways to operate a remote server: the
embedded MCP server, sigma-cli, and the sigma-api-operator skill. All
three wrapped the same HTTP API, yet each covered a different subset:
user CRUD existed only in the CLI, system-* only in MCP, and builders,
daemons, webhooks, validators, code repositories and tokens were
reachable only through raw HTTP. MCP was disabled by default and still
carried 1246 lines of production code plus 606 lines of tests to
maintain that partially overlapping surface.
Keep two surfaces instead: sigma-cli for execution and the skill for
API discovery.
- delete pkg/server/mcp and its mcp-go dependency, which also drops the
jsonschema-go, jsonschema/v6 and uritemplate/v3 transitives
- drop ConfigurationMCP, its defaults and the authz/audit skipper
bypasses that existed only to let the MCP endpoint bypass both
- remove the mcp section from conf/config*.yaml, docs/configuration.md,
and the Helm chart values and configmap
- replace docs/mcp.md with docs/cli.md covering sigma-cli login, global
flags, per-resource commands, jq composition and agent usage
- make sigma-cli the preferred path in the sigma-api-operator skill and
rebuild its examples around the resources the CLI does not wrap yet,
using the paths and request schemas from swagger.yaml
Lint was already red on dev for an unrelated reason: golangci-lint
v2.13.0 vendors golang.org/x/tools v0.49.0, which only decodes export
data version 4, so every import of a Go 1.27.2 standard library package
failed typecheck with "export data version 5 is greater than maximum
supported version 4". Bump to v2.15.0 (x/tools v0.52.0) and fix the two
modernize findings it reports for Go 1.27's strings.CutLast.
- .github/workflows/lint.yml: golangci-lint v2.13.0 -> v2.15.0, and
document the export data constraint next to the pin
- pkg/authz/resolver.go, pkg/logger/logger.go: replace LastIndex slicing
with strings.CutLast
- bump github.com/docker/cli v29.9.0 -> v29.9.1
BREAKING CHANGE: the `mcp` configuration section and the /api/v1/mcp
endpoint are gone. Use sigma-cli or the REST API for automation.
Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- golangci-lint v2.15.0 run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./... (0 issues)
- golangci-lint config verify
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/authz/... ./pkg/logger/... ./cmd/cli/... ./pkg/app/... ./pkg/background/build/...
- go test -run TestConfiguration -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/config/...
- go mod tidy
- make swagen
- helm template sigma ./deploy/sigma
- bun run docs:build
- make build
- make build-cli
Remove the unused Wrap/WrapHandle helpers and the HandlerFunc type; route
registration now passes handler method values directly. Drop the
unreachable application/x-www-form-urlencoded and multipart/form-data
branches in bindBody since every bound endpoint is JSON, and merge
adapter.go into request.go. Add tests for the query/param mapping error
paths, the nil-validator path, and non-JSON bodies, raising the package
coverage to 100%.
Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/...
- go tool cover -func: pkg/server 100.0% of statements
Reject self status or role changes with 403 in the user update handler,
and make the user service persist role updates so administrators can still
manage other users' roles.
Verification:
- go build -tags "..." ./pkg/service/users/... ./pkg/server/handlers/users/...
Fix and complete Swagger annotations for the HTTP handlers so the
generated OpenAPI spec matches the registered routes and request
types:
- Correct path parameter types from number/int64 to string, since all
resource IDs are UUID strings.
- Fix wrong router paths and methods (builders, coderepos, tags,
webhooks, users, validators) and add missing path parameters.
- Fix request/response schema references (builder runner list, oauth2
callback, webhook get/log) and a few summary/description typos.
- Add annotations for previously undocumented endpoints: artifacts,
analytics, builder runner actions, and user self/signup/recovery.
- Rename the users and coderepos path parameters to user_id/repo_id and
align the route registrations and request struct tags accordingly;
mark BuilderRunnerItem.Log as swaggertype string so swag can parse it.
- Regenerate tools/skill/sigma-api-operator/swagger.yaml. The
distribution handlers are intentionally left undocumented.
Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/users/ ./pkg/server/handlers/coderepos/ -run TestInitialize -count=1
- go tool swag fmt && go tool swag init --output tools/skill/sigma-api-operator --outputTypes yaml
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/... ./pkg/api/...
- gofmt -l pkg/server/handlers pkg/api
Replace `errors.New(utils.UnwrapJoinedErrors(err))` with the original `err`
in slog error logging so the logged value keeps its error chain for
`errors.Is`/`errors.As` classification. Also drop the duplicated `err`
attribute in the namespace handlers.
Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/...
Replace context.Background() with t.Context() so test contexts are
canceled when the test ends, which surfaces leaked background work. The
pkg/storage suites are intentionally left unchanged because they run
manually against cloud resources.
Verification:
- go vet ./...
Thread the caller context through ClientsFactory.New and
Clients.AuthToken so proxied blob and manifest requests keep
cancellation and trace propagation instead of starting from
context.Background(). Regenerate the mock and update callers and tests.
Document the intentionally background contexts in the one-shot builder
CLI.
Verification:
- go generate ./pkg/distribution/clients/... (no diff)
- go vet ./...
Adopt modern Go and stdlib idioms surfaced by the modernize analyzer:
promote embedded fields in struct literals, and replace hand-written
equivalents with maps.Copy, min, atomic.Int64, new(expr) and
slices.SortStableFunc.
Verification:
- go fix -diff ./... (no remaining changes)
- go vet ./...
pkg/validators is the gin request-validation layer: it registers gin's
binding.Validator and is used by the HTTP handlers, so move it to
pkg/server/validators where the rest of the HTTP layer lives.
The only non-server consumer was pkg/background/daemon, which called
validators.Initialize() even though the worker never uses gin binding
validation. Drop that call so the worker does not depend on the server
package; the server still initializes validators through
handlers.Initialize.
Move every pkg/service/<domain>/*_mocks.go into pkg/service/<domain>/mocks/
(package mocks) so generated mocks are no longer part of the packages under
test and stop depressing their coverage. Update the //go:generate directives
and the tests that referenced the mocks.
Replace every `// <Identifier> ...` placeholder doc comment with a real
description of the declared symbol. 276 comments in 71 files were rewritten:
149 top-level placeholders first, then a follow-up scan caught another 127
indented ones (interface methods and const blocks) that a column-anchored
match had missed.
Comments now open with the declared identifier and record the actual
contract - behaviour, side effects, error/panic conditions, units, HTTP
endpoints, lock keys and cache prefixes - instead of restating the
signature. Three placeholders carried a stale identifier and were corrected
to the declared symbol: ConfigurationDatabasePostgresql, GetImageRef and
Initialize.
No functional change: every modified line is a comment.
Verification:
- grep '^\s*//\s+\S+\s+\.\.\.\s*$' over all *.go returns no matches
- gofmt -l over the changed files prints nothing
- CGO_ENABLED=1 go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./... exits 0
- git diff HEAD -U0 filtered to non-comment changed lines is empty
Rename the cache module package name from `cacher` to `cache` across all files, including:
- Update all internal package declarations
- Fix import aliases in dependent modules
- Correct mockgen package configuration
- Update cache interface and error type references
This aligns the package naming with the module directory structure and improves consistency across the codebase.
Add a cache-backed failed-login throttler for Basic auth. Once a username
reaches auth.loginRateLimit.maxFailures failures within window, each
subsequent authentication attempt is delayed by delay (capped at 5s)
instead of being rejected; the counter is cleared on a successful login.
- add pkg/infra/ratelimit backed by pkg/infra/cache (inmemory/redis) with
Count/Incr/Reset and Prometheus metrics for failures, delayed attempts,
and cache errors
- wire ConfigurationAuthLoginRateLimit with defaults (5 failures, 5m window,
1s delay) and document it in conf/config.yaml
- integrate the limiter into the authn middleware, counting all Basic
failures including /v2/, and honor request context cancellation
- add unit tests for threshold behavior, delay, and cancellation
- mark the login rate limit item complete in TODO.md
Replace all usages of standard encoding/json with encoding/json/v2 and jsontext
where applicable, update decoding logic to use json.UnmarshalRead and
json.MarshalWrite instead of manual encoder/decoder instances.
Verification:
go test -race ./pkg/... ./cmd/...
Replace the per-type GC rule, runner, and record tables with shared
daemon_gc_rules, daemon_gc_runners, and daemon_gc_records tables.
Store the GC daemon type on rules and use a unified resource field for
tag names, repository names, and artifact/blob digests. Keep existing
service and HTTP APIs intact by mapping the shared persistence models to
the existing type-specific contracts.
Regenerate GORM queries and mocks, and update GC repository tests.
Remove the unused method parameter from the shared distribution test
context
helper and align import grouping in related handler tests.
Verification:
make lint-go
Add isolated GoMock-based tests for repository catalog, tag listing,
blob
downloads, manifest referrers, and blob upload lifecycle handlers.
Generate the Authorizer mock used by distribution handler tests and
retain its
go:generate directive.
Convert service constructors into dig provider functions and let the service
bootstrap layer call Provide directly. This keeps dig container usage at the
composition boundary instead of passing the container into each service package.
Register API handlers explicitly from the handlers package and remove the blank
imports that previously triggered handler init functions from cmd/imports.go.
Handler packages now expose Initialize functions that are invoked by the root
handler registration flow.
Also update related tests and document the analytics service interface methods.
Verification:
go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/... ./pkg/app/bootstrap
go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/... ./pkg/app/server ./pkg/app/distribution ./cmd/...
Rename domain-specific service interfaces to package-local Service interfaces
and update handler, MCP, and test dependencies to use the new names.
Merge service parameter structs into implementation structs where possible,
including token service construction, and add explicit DI registration helpers
for token service wiring.
Also simplify authorizer dependency construction and regenerate service mocks
with stable mock type names.
Verification:
CI_DATABASE_TYPE=turso go test -p 1 -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/...
go test -p 1 -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/mcp ./pkg/server/handlers/...
go test -p 1 -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/token ./pkg/server/middlewares/authn ./pkg/background/buildrunner ./pkg/app/bootstrap
golangci-lint run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/... ./pkg/server/mcp ./pkg/server/handlers/... ./pkg/authz
golangci-lint run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/token ./pkg/server/middlewares/authn ./pkg/background/buildrunner ./pkg/app/bootstrap
git diff --check -- pkg/service pkg/server pkg/authz
git diff --check -- pkg/service/token pkg/app/bootstrap pkg/background/buildrunner pkg/server/middlewares/authn
Add complete test suites covering helper utilities, authorization logic, core tooling, and business functionality for the MCP server implementation.
Verification:
go test -tags "test" ./pkg/server/mcp/...
Add route registration tests for the major handler packages and behavior tests
for analytics and validator endpoints.
Rewrite the reusable artifact, tag, user authentication, webhook, and
distribution health tests using the current Gin, service mock, and dependency
injection patterns. Remove the migrated Echo and legacy testkit.DB test code.
Verification:
go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/...
golangci-lint run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/handlers/...
git diff --check -- pkg/server/handlers