126 Commits
Author SHA1 Message Date
Tosone 42600b95f5 feat(web): chart the build duration of a repository builder
Show how long the recent builds took above the run list: the chart reads the
last 100 runs of the builder, plots the elapsed time of the finished ones and
marks the latest duration in the header. It is drawn as plain svg so no chart
dependency is needed, and hovering a point shows the tag, the duration and
the time of that build.

Verification:
- bunx tsc --noEmit
- bun run lint
- bun run build
2026-10-28 11:09:38 +08:00
Tosone 14a7952ea6 fix(web): stop the builder page from rewriting its URL on back
The page listened to popstate and called navigate(backTo) with an empty
`back_to`, and react-router resolves an empty path to the current pathname
with an empty search, pushing a new entry. Pressing back therefore stayed on
the builder page and dropped every query parameter. The mount effect that
re-pushed the current query had the same problem: it added a duplicate
history entry.

Drop both; `back_to` is still used by the Cancel button, and the browser back
button now undoes the navigation that opened the page.

Verification:
- bunx tsc --noEmit
- bun run lint
- bun run build
2026-10-28 11:09:38 +08:00
Tosone e5032fb087 feat(web): merge the builder and runs tabs of a repository
A repository exposed two tabs that only differed by whether a builder was
configured: "Builder" opened the setup form and "Runs" opened the build
list. Merge them into one "Builder" tab: it shows the setup form while the
repository has no builder and the run list once a builder exists.

- add RepositoryTabs, the tab bar shared by the repository pages
- add pages/Builder/index.tsx, which picks the setup form or the run list
  from the repository builder and swaps to the run list after a save
- render the setup form inside the repository chrome when embedded
- point the configure/update/back links at the merged route and keep
  /repository/runners as an alias for existing bookmarks

Verification:
- bunx tsc --noEmit
- bun run lint
- bun run build
2026-10-28 11:09:37 +08:00
Tosone 833597b871 refactor(web): drop the builder shortcut from the namespace menu
The builder belongs to a repository, so the namespace level entry between
the members and the daemon task sections duplicated the repository tab.
Remove the section entry, its daemon gate and the builder specific link,
and stop mapping the builder route to the removed section.

Verification:
- bunx tsc --noEmit
- bun run lint
- bun run build
2026-10-28 11:09:37 +08:00
Tosone 2b47c02f4e refactor(web): import lodash helpers from lodash-es
The web app only uses `size` and `orderBy`, and `lodash` is the CommonJS
build that the bundler cannot tree-shake. Import the helpers by name from
`lodash-es` instead, drop `lodash` and declare `@types/lodash-es` (it was
only reachable through bytemd before).

Verification:
- bunx tsc --noEmit
- bun run lint
- bun run build
2026-10-28 11:09:37 +08:00
Tosone fb4ae986d1 build(deps): bump go and web dependencies
Update Go modules and web packages to their latest available patch and
minor releases.

Go:
- github.com/aws/aws-sdk-go-v2 v1.47.3 -> v1.47.4
- github.com/aws/aws-sdk-go-v2/config v1.33.9 -> v1.33.11
- github.com/aws/aws-sdk-go-v2/service/s3 v1.114.3 -> v1.114.5
- github.com/aws/smithy-go v1.28.5 -> v1.28.6
- github.com/go-redsync/redsync/v4 v4.18.0 -> v4.18.1
- github.com/tencentyun/cos-go-sdk-v5 v0.7.75 -> v0.7.76
- transitive: aws-sdk-go-v2 internals, eventstream, credentials, sso,
  ssooidc, sts and signin

Web:
- @base-ui/react 1.8.0 -> 1.9.0
- @tanstack/react-query 5.104.1 -> 5.105.0
- axios 1.20.0 -> 1.21.0
- cron-parser 5.10.1 -> 5.10.2
- human-format 1.2.1 -> 1.4.0
- lucide-react 1.52.0 -> 1.62.0
- react / react-dom 19.3.0 -> 19.3.1
- shadcn 4.21.3 -> 4.23.0
- zod 4.6.5 -> 4.6.7
- zustand 5.0.15 -> 5.0.16
- @types/node 26.6.4 -> 26.6.5
- autoprefixer 10.6.1 -> 10.6.2
- oxlint 1.87.0 -> 1.90.0
- postcss 8.5.29 -> 8.5.32
- postcss-import 17.0.0 -> 17.0.1
- vite 8.3.3 -> 8.3.5

Verification:
- go mod tidy
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- bun run lint
- bun run build
2026-10-27 22:55:34 +08:00
Tosone 0f31d58990 chore(scripts): quote the rs3 healthcheck so shellcheck stops warning
The healthcheck was written in single quotes, which shellcheck flags as
"expressions don't expand in single quotes" even though deferring the
expansion to the container is exactly what is wanted. Keep the same value
with escaped double quotes: the backslash stops the host shell from touching
${RS3_PORT}, and the container expands it at run time.

Verification:
- bash -n scripts/run_rs3.sh
- docker run with the rendered --health-cmd resolves RS3_PORT inside the container
2026-10-27 22:47:53 +08:00
Tosone 2da4de8fb8 chore(conf): enable the builder daemon in the development config
conf/config-dev.yaml mirrored the production default, so a local run had no
builder entry in the menu and no way to create builds without editing the
file. Enable it there: the development config is the one used when running
against a local builder.

Verification:
- helm/conf not affected; loaded by `go run . server -c conf/config-dev.yaml`
2026-10-27 22:47:53 +08:00
Tosone a1051f53c2 feat(web): show the provider push time in the code repository list
The list offered an Oci Repo Count column nobody acted on, an action column
whose only entry opened the setup builder — reachable from the repository
tabs now — and a timestamp that read as nine hours old because the API sends
UTC wall-clock strings and dayjs parsed them as local time.

- replace the count and action columns with the last update time, rendered
  through RelativeTime so the timezone is handled once
- sort by pushed_at descending and page twenty at a time, with the page size
  as a constant instead of the global settings value
- drop the forced 100rem page width: the app layout already sizes the content
  column, so the pages overflowed it and their justify-between rows pushed
  the actions past the right edge
- give the sync button the same gutter as the namespace page's create button,
  and let the pagination component own its container

Verification:
- cd web && npx tsc --noEmit
- cd web && npx oxlint
- cd web && npx vite build
2026-10-27 22:47:50 +08:00
Tosone 64e4de5702 feat(web): rework the setup builder form
The form mixed three interaction styles: popovers with a cmdk search list,
plain selects and hand-styled inputs. Picking a provider could leave two
options looking highlighted, an empty list opened an empty popup that
rendered as a thin grey line, and several fields had no dark mode support.

Make every picker a Select and calm the form down:

- provider, organization, repository and branch — plus the cron and merge
  branch pickers — are Selects now; the popup is only rendered when there is
  something to choose, and the branch pickers refresh their list when opened
  because branches are no longer synced
- provider options show their display name, an unavailable provider disables
  the organization and repository pickers, and its hint opens the credential
  dialog in place instead of navigating away
- the OCI repository picker lists the repository name without the namespace
  prefix, the way the repository is referred to elsewhere
- the section descriptions move into info tooltips next to the headings
- clone depth uses the shared input, round-trips through
  code_repository_clone_depth, clamps to a non-negative integer and explains
  that 0 clones the full history
- the self-managed repository block uses the shared inputs, keeps the token
  masked and gives the fields room to breathe
- CommandItem gets a pointer cursor, which fixes every cmdk list, and the
  setup page drops the forced 100rem width that pushed it past the viewport

Verification:
- cd web && npx tsc --noEmit
- cd web && npx oxlint
- cd web && npx vite build
2026-10-27 22:47:47 +08:00
Tosone 7039914b3a feat(web): add a builder tab on repository pages and drop duplicate navigation
The repository pages carried their own tab strip, and the sidebar repeated
the same entries under the repository node, so the same navigation existed
in two places and neither was complete: the Setup builder page was only
reachable from the code repository list, which no longer links to it.

- add /namespaces/:namespace/repository/builder rendering the setup builder,
  and turn the repository tabs into Summary | Builder | Runs | Tag list; the
  former "Builder" tab, the runner list, becomes "Runs"
- remove the repository level from the sidebar: its entries are the page
  tabs now, and "Repository list" stays highlighted while browsing into a
  repository
- drop NamespaceTabs, the duplicate header strip on the namespace pages
- resolve entity ids as opaque UUID strings: interface fields are strings
  now and the pages that parsed ids with parseInt stopped truncating them
  (a UUIDv7 starts with "1a1...", so parseInt returned 1)
- add useNamespaceId: a page opened without ?namespace_id — a bookmark or a
  hand written URL — looks the id up from the namespace in the route instead
  of requesting /namespaces/null or /namespaces//
- the tag page: title is Tags, its search box matches the namespace one, and
  it skips the request while the namespace is unresolved

Verification:
- cd web && npx tsc --noEmit
- cd web && npx oxlint
- cd web && npx vite build
2026-10-27 22:47:33 +08:00
Tosone 5c05104720 feat(coderepos): read branches on demand and track provider push time
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
2026-10-27 22:47:28 +08:00
Tosone 36c36f73f8 fix(daemon): list GitHub repositories with ListByAuthenticatedUser
Repositories.List is deprecated: with an empty user it forwards to
ListByAuthenticatedUser anyway, so the call was a wrapper around the API we
want, and it was silenced with a stale "nolint: staticcheck" that the
enabled linters no longer need.

Call ListByAuthenticatedUser directly. Both hit GET /user/repos and keep the
same ListOptions pagination, so the returned repositories and the existing
"owned by this account" filter are unchanged.

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- golangci-lint run --enable staticcheck --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/background/...
2026-10-27 21:33:36 +08:00
Tosone ef9fa992fc fix(web): widen the credential type select so options are not clipped
The credential dialog used a w-fit trigger, so the popup inherited the width
of the currently selected label ("Access token"), and the explicit min-w-0
on the popup removed the 9rem floor that select-content normally keeps. The
longest option, "Username and password", was therefore clipped by the
popup's overflow-x-hidden.

Let the trigger span the dialog and keep the default min width of the popup.

Verification:
- cd web && npx tsc --noEmit
- cd web && npx oxlint
- cd web && npx vite build
2026-10-27 21:30:35 +08:00
Tosone 93de2dac54 fix(coderepos): stop the credential validator from panicking
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
2026-10-27 21:26:39 +08:00
Tosone ce7d1c6b6e feat(web): manage code repository credentials from settings
The code repository entry sat next to namespaces even though it holds no
content: it only configures the source builds clone from. Its provider
submenu came from the OAuth2 login flags, so turning OAuth off left an
empty menu entry and a blank page, and there was no way to supply an access
token at all.

- move the entry into the Settings group and drop the provider submenu; the
  providers are now cards on the page, so listing them twice only added a
  third level
- rebuild /coderepos as one card per provider showing the linked account,
  the credential type and the last sync result, with actions to add or
  update a credential, resync, clear it, or open the repository list
- add CredentialDialog for the token, SSH key and username/password types,
  with the OAuth binding offered as an alternative that still needs a token
  for syncing
- read the sync state of /coderepos/{provider} from the providers response
  instead of issuing a second request, and point the builder setup provider
  combobox at the credential settings when nothing is configured
- extend ICodeRepositoryProviderItem with the new fields and add the
  zh-CN/en-US strings

Verification:
- cd web && npx tsc --noEmit
- cd web && npx oxlint
- cd web && npx vite build
2026-10-27 21:07:53 +08:00
Tosone 76e1086035 refactor(builder): encrypt builder secrets with AES-GCM
The authorization token, signing key and SCM credentials handed to a build
runner were encrypted with AES-CTR, which has no integrity protection: a
tampered environment value decrypted into garbage that downstream code
would only fail on much later.

Use crypt.Seal/crypt.Open for those values. The key stays the same
per-build key derived from the builder and runner IDs, and every secret is
sealed once up front instead of inline in the env list, so a failure is
reported before the runner starts.

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/background/build/...
2026-10-27 21:07:49 +08:00
Tosone 48102fe383 feat(daemon): sync code repositories with stored credentials
The daemon read the OAuth access token straight from user_3rdparty, so a
sync that ran more than two hours after the last login failed with a 401
and the reason was never surfaced: the status row said Failed with an empty
message, and nobody could tell that a token had expired.

- load and decrypt the credential, and fail with a message that tells the
  user what to do when it is missing or cannot be opened
- reject non-token credentials for syncing, since listing repositories is an
  API call that an SSH key or username/password cannot make
- resolve the provider base URL from scm.providers and pass it to the
  GitLab and Gitea clients instead of letting them default to the hosted
  endpoints
- truncate the stored error message to the column size, so a long provider
  error does not fail the status update that is supposed to explain it

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
2026-10-27 21:07:46 +08:00
Tosone 9322b7e79e feat(builders): resolve clone credentials from the credential store
Creating a builder fell back to the login OAuth token whenever the clone
credential lookup found nothing, which copied a login credential into
builders.scm_token and then into the build pod environment. The same block
logged a real database error and carried on, producing a builder with no
credentials at all.

Read only the credential store now, and fail loudly instead:

- decryptedCredential loads the row, rejects type none, and opens the sealed
  token, ssh key and password before they are handed to the builder
- a missing credential returns 400 with an actionable message instead of
  silently creating a credential-less builder, and other lookup errors are
  returned rather than logged

Verification:
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/builders/...
2026-10-27 21:07:43 +08:00
Tosone 5ca747ddd7 refactor(oauth2): stop persisting OAuth tokens on login
The callback stored the access and refresh token on user_3rdparty and
re-issued the code repository sync on every login, which only worked
because the login refreshed an expiring token. Credentials now live in the
clone credential table, so the tokens have no reader left.

- drop the token update on repeated login and the token fields from both
  binding-creation paths; Oauth2UserInfo no longer carries them
- stop publishing a sync task when a binding is created: there is no
  credential at that point, so the task could only report a failure
- derive the GitLab and Gitea OAuth endpoints from scm.providers[].baseUrl
  instead of hardcoding gitlab.com, and point Gitea at its own
  /login/oauth endpoints and redirect callback

Verification:
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/oauth2/...
2026-10-27 21:07:40 +08:00
Tosone a80519d86f feat(coderepos): configure a per-provider SCM credential
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/...
2026-10-27 21:07:37 +08:00
Tosone 3cb76aae80 feat(bootstrap): generate the credential master key on first start
auth.credentialKey is optional, so a deployment must work without operator
input. Generate a random 32-byte key on first start and persist it in the
settings table, which keeps the credential service able to decrypt values
written by an earlier process.

- run the task right after the services are registered, before signing
- take a distributed lock around the check-then-create and re-read inside
  it, otherwise two replicas starting together would generate different
  keys and leave half the credentials unreadable
- when auth.credentialKey is set it is authoritative: only its length is
  validated and nothing is written to the settings table

Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/app/bootstrap/...
2026-10-27 21:07:31 +08:00
Tosone 391c380cb5 feat(dal): store SCM credentials in code_repository_clone_credentials
user_3rdparty held the OAuth access and refresh token issued at login and
three different consumers read the access token from it: the code
repository daemon to list repositories, the builder to clone, and login to
issue a JWT. Access tokens expire (GitLab after two hours) and refresh
tokens were written but never read, so syncing and building silently broke
a couple of hours after the user last signed in.

Split identity from credentials:

- drop token and refresh_token from user_3rdparty, leaving a pure identity
  binding, and stop auto-updating cr_last_update_timestamp so unrelated
  writes no longer fake a sync
- make code_repository_clone_credentials usable: one live row per
  third-party account through a (user_3rdparty_id, deleted_at) unique
  constraint, wider token/password columns for the sealed values, and
  UpsertCredential/DeleteCredential built on clause.OnConflict
- add UserRepository.UpsertUser3rdPartyByAccountID so a user who only ever
  pastes a token still gets a binding, without duplicating rows when two
  requests race

The baseline 0001 migration is edited in place for all four dialects
instead of adding an increment: fresh installs get the final schema, while
existing installations have to apply the constraint manually (documented in
the migration design notes).

Verification:
- make gormgen
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/dal/repository/coderepo/... ./pkg/dal/repository/user/...
2026-10-27 21:07:28 +08:00
Tosone c293d6b49e feat(service): seal SCM credentials at rest with a dedicated service
Every consumer that needs an access token would otherwise have to resolve
the master key itself and remember to hash it before calling crypt. Keep
that knowledge in one place:

- service/credential resolves the key once at construction time:
  auth.credentialKey when configured, otherwise the value the credential
  bootstrap task persists in the settings table
- it fails fast when neither exists or when the configured key is shorter
  than 32 bytes, because storing credentials that can never be decrypted
  is worse than refusing to start
- Seal/Open delegate to crypt.Seal/crypt.Open so stored values are
  authenticated

Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/credential/...
2026-10-27 21:07:23 +08:00
Tosone 642b2d576c feat(config): add scm provider configuration and credential master key
The source-code providers a deployment can use were implied by
auth.oauth2.*, so disabling OAuth login also disabled code repository sync
and every build that clones source code, and there was nowhere to configure
a self-hosted GitLab or Gitea at all: the gitea client was created with an
empty base URL.

Make provider capability explicit and independent from login:

- add scm.providers[] with provider and baseUrl; github and gitlab default
  to their hosted endpoints, gitea and other self-hosted instances require
  a base URL and are skipped with a warning when it is missing
- derive the provider list from the enabled OAuth2 providers when
  scm.providers is empty, so existing configurations keep working
- expose Configuration.ResolveScmProviders and Configuration.ScmProvider
- add auth.credentialKey for the master key that encrypts SCM credentials
  at rest, plus the credential.master_key settings key used as fallback

conf/config.yaml, both configuration docs and the Helm chart (values and
configmap) document the two new sections.

Verification:
- go test -run "TestConfigurationScm|TestConfigurationResolveScm" -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/config/...
- helm template test ./deploy/sigma
2026-10-27 21:07:20 +08:00
Tosone 867a26a23c feat(crypt): add authenticated AES-GCM seal/open helpers
Encrypt/Decrypt are AES-CTR without a MAC, so a stored ciphertext can be
altered without detection. The only current caller derives a per-build key
from the builder and runner IDs, which is fine for the short-lived build
environment but cannot be used for values that outlive a process.

Add Seal/Open on top of AES-256-GCM (base64(nonce|ciphertext|tag)) next to
the existing helpers instead of changing them: the builder image still
decrypts the CTR format with crypt.Decrypt. The key is derived with
sha256 like Encrypt, so operators only deal with one key convention.

Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/utils/crypt/...
2026-10-27 21:07:15 +08:00
Tosone 187f5c4d97 refactor(server)!: remove the embedded MCP server
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
2026-10-21 19:33:07 +08:00
Tosone 940ab15dd4 build(deps): bump grype, syft, buildkit and other dependencies
Update direct Go module dependencies to their latest patch/minor
releases, including security and bug fixes across registry,
scanning, storage and build backends. Align the grype, syft,
buildkit, cosign and swagger-ui versions pinned in the Dockerfile
with their upstream releases.

Direct upgrades:
- github.com/alicebob/miniredis/v2 v2.39.0 -> v2.40.0
- github.com/anchore/grype v0.120.0 -> v0.121.0
- github.com/anchore/syft v1.54.0 -> v1.55.0
- github.com/aws/aws-sdk-go-v2 v1.47.1 -> v1.47.3
- github.com/aws/aws-sdk-go-v2/config v1.33.6 -> v1.33.9
- github.com/aws/aws-sdk-go-v2/service/s3 v1.114.0 -> v1.114.3
- github.com/aws/smithy-go v1.28.2 -> v1.28.5
- github.com/docker/cli v29.8.2+incompatible -> v29.9.2+incompatible
- github.com/mark3labs/mcp-go v1.1.1 -> v1.2.1
- github.com/moby/buildkit v0.33.1 -> v0.34.1
- github.com/pressly/goose/v3 v3.28.0 -> v3.29.0
- github.com/prometheus/client_golang v1.24.1 -> v1.25.0
- github.com/sigstore/cosign/v3 v3.1.3 -> v3.1.4
- go.podman.io/podman/v6 v6.1.3 -> v6.1.4
- golang.org/x/crypto v0.57.0 -> v0.58.0
- golang.org/x/net v0.59.0 -> v0.61.0
- golang.org/x/sync v0.23.0 -> v0.24.0
- turso.tech/database/tursogo v0.8.2 -> v0.8.3

Dockerfile:
- build/Dockerfile: SYFT_VERSION 1.54.0 -> 1.55.0, GRYPE_VERSION
  0.120.0 -> 0.121.0, COSIGN_VERSION v3.1.3 -> v3.1.4,
  SWAGGER_UI_VERSION 5.33.1 -> 5.33.2, BUILDKIT_VERSION
  v0.33.1-rootless -> v0.34.1-rootless

Notable transitive bumps: grpc 1.83.2 -> 1.84.0, prometheus/common
0.70.1 -> 0.72.0, sigstore/sigstore 1.10.8 -> v1.11.1,
sigstore/sigstore-go v1.2.2 -> v1.3.0, rekor v1.5.3 -> v1.5.4,
go-openapi v0.x -> v1.0.0 across analysis/spec/validate,
google.golang.org/api v0.294.0 -> v0.301.0,
klauspost/compress 1.20.0 -> 1.20.1, and the podman buildah/storage
modules.

cloud.google.com/go/auth resolves to the pseudo-version
v0.24.1-0.20261001053825-dbc26066f70a because the cosign KMS/GCS
dependency chain requires a commit that has no tagged release yet.

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)
- go mod tidy
2026-10-21 19:32:35 +08:00
Tosone 10269798be refactor(web): migrate legacy accent colors to design tokens
The legacy pages still used scattered Tailwind colors (purple, blue,
indigo) as interactive accents, so the app drifted away from the single
neutral `--primary` accent of the design system. Replace them with tokens:

- purple primary buttons -> bg-primary / text-primary-foreground
- gray secondary buttons -> bg-secondary
- indigo active tabs -> border-primary / text-foreground
- purple and indigo focus rings -> focus:ring-ring
- blue switches -> peer-checked:bg-primary, with the knob switched to
  primary-foreground while checked so it stays visible in dark mode
- blue checkboxes -> accent-primary
- blue links and the Dockerfile editor selection -> text-primary and
  selection:bg-foreground

Verification:
- cd web && npx oxlint
- cd web && bun run build
2026-10-11 09:31:17 +08:00
Tosone 3e4f4c8564 fix(web): use the background token for page shells
The legacy page shells hardcoded `bg-white` without a dark variant, so
the whole page rendered white in dark mode. Use the `bg-background`
token, matching the pages that were already migrated.

Verification:
- cd web && npx oxlint
- cd web && bun run build
2026-10-11 09:31:09 +08:00
Tosone 4d29fb868c docs: redesign the documentation homepage
Rebuild the VitePress home page around the sigma brand: a compact
one-line hero with a terminal-style rotating tagline and a copyable
install command, a six-tile capability grid, and new Vue sections for a
quick start terminal, the architecture diagram, and a closing CTA.

Apply the logo's warm orange palette with dark-mode-aware accents, load
Nunito for all Latin text from the Fontsource jsDelivr CDN, and align
every section gutter with the hero and feature grid so the content shares
one width. Keep the hero title, the Architecture lead and the closing CTA
on a single line where the viewport allows.

Verification:
- cd docs && npx vitepress build
- checked light/dark and mobile layouts in headless Chrome
2026-10-10 19:37:45 +08:00
Tosone 7b98fc29c0 refactor(server): simplify handler wrapping and request binding
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
2026-10-07 21:07:06 +08:00
Tosone ffe28bb78e refactor(api): rename UserStatus Deactive to Inactive
"Deactive" is not a valid English word; the standard antonym of "Active"
is "Inactive". Restore the enum value, regenerate the go-enum output,
update the CLI test expectation, sync the generated swagger enum and
x-enum-varnames, and fix the web UI request payload and label.

Note: persisted rows and API clients using the old "Deactive" value must
switch to "Inactive"; no data migration is included.

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/api/enums/... ./cmd/cli/...
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/validators/...
2026-10-07 20:56:24 +08:00
Tosone 00735de3b1 feat(web): use Nunito as the primary font
Load Nunito (variable, normal and italic) from Fontsource and put it
first in the `--font-sans` stack, keeping Geist as a fallback so
characters missing from Nunito still render.

Also bump the sidebar `sigma` wordmark from `text-sm` to `text-lg` so
the wordmark reads more clearly next to the 32px logo mark.

Verification:
- bun run lint
- bun run build
2026-10-06 23:28:43 +08:00
Tosone 7c2c45884a build(deps): bump go and web dependencies
Update Go modules and web packages to their latest available versions.

Go:
- github.com/go-git/go-git/v6 v6.0.0-alpha.5 -> v6.0.0-beta.1
- github.com/redis/go-redis/v9 and redisotel v9.22.0 -> v9.23.0
- github.com/tidwall/gjson v1.19.0 -> v1.20.0
- turso.tech/database/tursogo v0.8.1 -> v0.8.2
- transitive: go-billy/v6 beta.1, ProtonMail/go-crypto 1.5.2,
  pjbgf/sha1cd 0.7.0, go.uber.org/atomic 1.12.0

Web:
- @fontsource-variable/nunito 5.3.0 (new)
- @vitejs/plugin-react 5.2.0 -> 6.1.2
- @tanstack/react-query 5.104.0 -> 5.104.1
- cron-parser 4.9.0 -> 5.10.1
- lucide-react 1.49.0 -> 1.52.0
- shadcn 4.21.1 -> 4.21.3
- vite 8.3.2 -> 8.3.3
- oxlint 1.86.0 -> 1.87.0
- postcss 8.5.28 -> 8.5.29
- postcss-import 16.2.0 -> 17.0.0
- postcss-nesting 13.0.2 -> 14.0.3
- @types/node 25.9.9 -> 26.6.4

Adapt to the cron-parser v5 API, where the default `parseExpression`
export was replaced by the named `CronExpressionParser.parse`.

Verification:
- go mod tidy
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- bun run lint
- bun run build
2026-10-06 23:28:41 +08:00
Tosone 14df8a87d4 style(web): sort imports into third-party and local groups
Reorder imports across web/src into two blocks separated by a blank line:
third-party packages first, then this repository's @/ and relative modules,
each sorted alphabetically. Document the convention in AGENTS.md.

Verification:
- web: `npx tsc --noEmit`, `npx oxlint`, `npx vite build`
2026-10-04 19:59:27 +08:00
Tosone c5851c81d2 build(deps): bump grype, otel and other dependencies
Upgrade github.com/anchore/grype to v0.120.0, github.com/aws/smithy-go to
v1.28.2, github.com/lmittmann/tint to v1.2.1, and the go.opentelemetry.io
modules (otel, otel/sdk, otel/trace, otlptracegrpc, otlptracehttp, otelgin,
otelhttp) to v1.47.0/v0.72.0, together with the transitive bumps they pull
in. Install tzdata in the alpine runtime image so named timezones are
available at runtime.

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
2026-10-04 19:59:22 +08:00
Tosone cb982ec37c feat(config): add email for the initial administrator account
Populate auth.admin.email in the example configurations, docs and Helm
values so the bootstrap sets the initial administrator's email address.

Verification:
- parsed conf/config*.yaml and confirmed auth.admin.email is read
2026-10-04 17:07:50 +08:00
Tosone 01e1c6f94f build(deps): bump cosign to v3.1.3
Update github.com/sigstore/cosign from v2.6.5 to v3.1.3 and switch the
import path in the signing bootstrap to the v3 module. go.sum also picks
up the transitive lib/pq and go.step.sm/crypto bumps.

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "..." ./pkg/app/bootstrap/...
2026-10-04 16:43:15 +08:00
Tosone 4cbddc95ab style(web): organize code repository imports 2026-10-04 16:35:32 +08:00
Tosone 2a61be0cbf fix(web): keep namespace form select popups at trigger width
Drop the shared minimum popup width on the create and update namespace
visibility and size-limit unit selects so each popup matches its trigger.

Verification:
- web: `npx tsc --noEmit`, `npx oxlint`, `npx vite build`
2026-10-04 16:35:32 +08:00
Tosone f4ccd565f6 feat(web): add icons to daemon task actions
Add a SquarePen icon for Update and a Play icon for Run in the four GC
action menus, and tidy the page formatting.

Verification:
- web: `npx tsc --noEmit`, `npx oxlint`, `npx vite build`
2026-10-04 16:35:23 +08:00
Tosone ce781cdff7 feat(web): manage user status and role in settings
- right align the namespace count and status columns, and rename the
  namespace header to "Namespace count"
- render last login and created at with RelativeTime
- replace the inline update button with a centered three-dot menu whose
  Deactive action calls PUT /users/{id} to deactivate the user
- drop the status field from the update dialog, disable the role select
  for the signed in user, and omit role/status when updating self

Verification:
- web: `npx tsc --noEmit`, `npx oxlint`, `npx vite build`
2026-10-04 16:35:17 +08:00
Tosone bbd7308153 fix(web): refine select and dropdown menu interactions
Open select popups below the trigger instead of covering it, show a
pointer cursor on select options, give options more vertical room, and
use a not-allowed cursor for disabled selects and menu items.

Verification:
- web: `npx tsc --noEmit`, `npx oxlint`, `npx vite build`
2026-10-04 16:35:11 +08:00
Tosone 7ad608c2f8 feat(handler): protect a user's own status and role
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/...
2026-10-04 16:35:04 +08:00
Tosone d2f02d1db4 refactor(api): rename user status inactive to deactive
Rename the UserStatus enum value Inactive to Deactive, regenerate the
go-enum output, update the CLI test expectation and sync the generated
swagger enum and x-enum-varnames.

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go test -tags "..." ./cmd/cli/... ./pkg/server/validators/...
2026-10-04 16:34:58 +08:00
Tosone b8ef92eba1 feat(web): restructure sidebar menu and polish user list
Rebuild the sidebar menu as a context-driven hierarchy so every
collapsible group has real children:

- add a Home entry and list namespaces under Namespaces, merging the
  former hot-namespace group; the active namespace is always shown and
  expanded even when it is not hot
- nest the namespace sections (summary, repositories, members, daemon
  tasks, webhooks) and the active repository sections (summary, tags,
  builder) under the current branch, using SidebarMenuSub so children
  share a vertical guide line
- expose the enabled code repository providers (github, gitlab) under
  Code Repository and fall back to a plain link when none is enabled
- drive expansion and highlighting from the route only, highlighting the
  selected leaf instead of its container rows
- add a Base UI collapsible primitive, align the sidebar menu metrics
  with the content lists (h-9/text-sm items, h-8 sub items, sidebar-accent
  active state), use a neutral gray for the sidebar text both when idle
  and selected, and drop the unused --sidebar-active token
- add the menu.allNamespaces message

Also polish the settings user list: un-bold the header and username, show
last login and created at with RelativeTime, and replace the inline update
button with a centered three-dot dropdown.

Verification:
- web: `npx tsc --noEmit`
- web: `npx oxlint`
- web: `npx vite build`
2026-10-04 12:03:21 +08:00
Tosone 8389a3080c build(docker): bump build tool versions
Update the pinned tool versions in the builder image:
- buildkit v0.33.0-rootless -> v0.33.1-rootless
- swagger-ui 5.33.0 -> 5.33.1
- syft 1.51.1 -> 1.54.0
- grype 0.118.0 -> 0.120.0
- cosign v2.6.1 -> v3.1.3
2026-10-04 09:03:15 +08:00
Tosone 3958a6b61d docs(swagger): align handler API annotations with implementation
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
2026-10-03 21:51:37 +08:00
Tosone 20af519812 fix(handler): log original error instead of rebuilding it
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/...
2026-10-03 21:14:08 +08:00