Skip to content

release: Solidity support + admin password reset + login-lock management#77

Merged
dvcdsys merged 10 commits into
mainfrom
develop
Jun 5, 2026
Merged

release: Solidity support + admin password reset + login-lock management#77
dvcdsys merged 10 commits into
mainfrom
develop

Conversation

@dvcdsys
Copy link
Copy Markdown
Owner

@dvcdsys dvcdsys commented Jun 5, 2026

What

Release PR — promote develop to main. Bundles three merged feature
branches since the last release:

Why

Cuts the next server release. Each change landed via its own reviewed PR
into develop; this PR is the integration gate to main before tagging.

How

  • Admin password reset and login-lock management both go through the
    generated OpenAPI mux and enforce Admin-level auth per the
    docs/AUTH_REVIEW.md matrix (gating tests in auth_test.go,
    loginlocks_admin_test.go).
  • Solidity support is a data-driven registry entry; node kinds verified
    against the real gotreesitter grammar, with a strict all-kinds
    regression test.
  • openapi.gen.go regenerated (no hand-edits); doc/openapi.yaml is the
    source of truth.

Type of change

  • Bug fix
  • New feature

Checklist

  • go test ./... green on server (chunker, langdetect, httpapi auth/locks, users)
  • go build ./... green on server + CLI
  • OpenAPI regenerated via make openapi-gen (no manual edits to gen.go)
  • No secrets or API keys committed

Release follow-up (after merge)

  1. cd server && make scout-cuda → verify 0 HIGH/CRITICAL
  2. Bump server/cmd/cix-server/version.go
  3. Tag server/vX.Y.Z → GitHub Actions builds & pushes images
  4. Update Portainer stack tag and redeploy

🤖 Generated with Claude Code

dvcdsys and others added 10 commits June 5, 2026 14:58
Add POST /api/v1/admin/users/{id}/reset-password (admin only). Mirrors
the invite flow: an admin sets a new temporary password, the server flags
the user must_change_password=1 and revokes the target's existing
sessions, so on next login the existing ChangePasswordPage forces a new
password (same as first login). Any admin may reset any user, including
another admin — a reset neither demotes nor disables anyone, so no
last-admin guard applies.

- users.AdminResetPassword: sibling of UpdatePassword that SETS the flag
  instead of clearing it.
- Handler gates on mustBeAdmin, validates new_password (>=8), revokes
  sessions via Sessions.DeleteAllForUser, returns the updated user.
- Dashboard: ResetPasswordDialog (cloned from InviteUserDialog) wired into
  the users table, plus the useResetUserPassword hook.
- Documented in doc/openapi.yaml (source of truth).

Mounted directly in router.go, matching the existing embedding-provider
admin routes — the committed openapi.gen.go predates the pinned
oapi-codegen, so it is not regenerated here. The follow-up commit
reconciles that drift and moves these routes onto the generated mux.

Purely additive: no DB migration, no changed/removed endpoints or
behavior; the dashboard ships inside the server binary so there is no
version skew. Backward-compatible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…he generated mux

The committed internal/httpapi/openapi/openapi.gen.go had drifted from the
pinned oapi-codegen (v2.7.0): it predated the embedding-provider endpoints,
which were added to doc/openapi.yaml but mounted directly in router.go as a
stopgap. The previous commit added the password-reset endpoint the same way.

Regenerate gen.go from the spec so the generated mux owns all of them, and
drop the direct mounts (keeping both would double-register the routes and
panic chi at startup):

- TestEmbeddingProvider and ResetUserPassword now take their path params
  from the generated wrapper (kind/id) instead of chi.URLParam; chi import
  dropped from auth.go, swapped for the openapi pkg in admin_embeddings.go.
- router.go no longer hand-mounts the embedding-provider or reset-password
  routes — HandlerFromMux registers them from the spec.
- The embedded /openapi.json blob now lists these endpoints (previously it
  under-reported the API in Swagger UI / docs).

Incidental churn: regeneration also reorders some struct fields and adds
.Valid() enum helpers that the stale file lacked. `make openapi-check` is
green again. No wire/behavior change — same routes, methods, and shapes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-depth)

The ≥8 length check previously lived only in the HTTP handler. Move the
policy behind a shared users.MinPasswordLength constant and also enforce it
in the AdminResetPassword service method, so a non-HTTP caller can't set a
too-short password. The handler now references the constant instead of a
literal. Adds service-level tests for the set-flag and reject-short paths.

Pre-existing Create/UpdatePassword keep their empty-only checks (untouched
to avoid affecting bootstrap and other flows); hardening those uniformly is
a separate concern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(server): admin-initiated password reset
The login limiter (loginlimiter.go) blocks brute-force attempts per IP and
per (IP, email), but the only way to lift a lock early was a server restart
(which wipes all in-memory state). A legitimate user who fat-fingered their
password from a shared office IP was stuck for the full 15-minute window with
no admin recourse.

Add observability + targeted clear to the limiter and an admin surface:

- loginLimiter.locks() snapshots every counter currently at/over its limit
  (prune-on-read so the maps don't grow); clearIP/clearKey lift one lock.
- GET /api/v1/admin/login-locks lists active locks; POST .../reset clears the
  exact row the admin picked (type=ip | ip_email). Both Admin-only, idempotent
  reset returns 204.
- Dashboard "Login security" admin module: table of active locks with a
  per-row Reset button, 15s polling, empty state.

Spec-first: doc/openapi.yaml + regenerated openapi.gen.go. Gating tests cover
non-admin 403, the full lock→list→reset→unthrottled cycle, and validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses review feedback on the login-lock admin feature:

- ResetLoginLock now emits an explicit structured audit line (actor id/email,
  lock type/ip/email) on every clear. Lifting a lock weakens the brute-force
  defence, so it deserves its own audit record, not just the generic request
  log. Nil-guarded; actor fields blank under AuthDisabled.
- clearKey delegates to reset instead of duplicating the delete, so the key
  derivation lives in one place and the two paths can't drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(server): admin UI to view and reset login rate-limit locks
Solidity (`.sol`) files were detected as unknown language and indexed
as plain text via sliding window — no symbols, no contract/function
navigation, no `cix def` / `cix refs`. Repos cloned by the server-side
`repoindexer` skipped `.sol` files entirely because their language
resolved to "".

Fix on three sides:

- `server/internal/langdetect`: map `.sol` → "solidity"
- `server/internal/chunker`: register `grammars.SolidityLanguage` with
  node kinds for contract / library (class), interface / struct / enum /
  event (type), and function / modifier / constructor / fallback-receive
  (function). Tree-sitter grammar was already shipped by gotreesitter.
- `cli/internal/discovery`: mirror `.sol` → "solidity" in the CLI's own
  extension map so locally-discovered files reach the server with the
  right language tag instead of "".

Tests:
- new `TestChunkFile_Solidity` verifies contract / function symbols are
  extracted with correct kind and parent linkage
- `TestRegistry_NodeNamesMatchAST` fixture extended with a Solidity
  snippet so node names stay in sync with the grammar
- langdetect test gets a `Token.sol → solidity` case

Verified on a sample contract: 10 symbols extracted (contract, library,
interface, struct, enum, event, function, modifier, method) where
before everything came out as a single `block` chunk with no symbol
name.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR review feedback:

1. doc/LANGUAGES.md was missed in the original PR. Bump default set
   header from (30) -> (31) and add the `solidity` row to the table.

2. TestRegistry_NodeNamesMatchAST's `matched := false` logic passes if
   any *one* of a language's configured kinds appears in the AST -- so
   a future grammar rename of `modifier_definition` (or any of the
   other 8 Solidity kinds beyond contract/function) would slip through.
   Reviewer flagged this as a low-priority shared limitation; rather
   than reshape the generic test for every language, add a focused
   `TestRegistry_SolidityAllNodeKindsPresent` that parses a contract
   exercising all 10 advertised node kinds and fails if any is missing.
   Also widen the Solidity fixture in the generic test for parity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(chunker): add Solidity language support
@dvcdsys dvcdsys merged commit d5d7e2b into main Jun 5, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant