- web/static/htmx.min.js: replace placeholder stub with
htmx 2.0.4 (downloaded from unpkg.com). The placeholder
only logged a console warning; no HTMX features worked,
so form submissions fell back to native POSTs and the
account_row fragment was returned as a raw HTML body
rather than spliced into the table. This was the root
cause of account creation appearing to 'do nothing'.
- internal/ui/ui.go: add pgcreds_form.html to shared
template list; add PUT /accounts/{id}/pgcreds route;
reorder AccountDetailData fields so embedded PageData
does not shadow Account.
- internal/ui/handlers_accounts.go: add handleSetPGCreds
handler — encrypts the submitted password with AES-256-GCM
using the server master key before storage, validates
system-account-only constraint, re-reads and re-renders
the fragment after save. Add PGCred field population to
handleAccountDetail.
- internal/ui/ui_test.go: add tests for account creation,
role management, and PG credential handlers.
- web/templates/account_detail.html: add Postgres
Credentials card for system accounts.
- web/templates/fragments/pgcreds_form.html: new fragment
for the PG credentials form; CSRF token is supplied via
the body-level hx-headers attribute in base.html.
Security: PG password is encrypted with AES-256-GCM
(crypto.SealAESGCM) before storage; a fresh nonce is
generated per call; the plaintext is never logged or
returned in responses.
- Added failed login tracking for account lockout enforcement in `db` and `ui` layers; introduced `failed_logins` table to store attempts, window start, and attempt count.
- Updated login checks in `grpcserver/auth.go` and `ui/handlers_auth.go` to reject requests if the account is locked.
- Added immediate failure counter reset on successful login.
- Implemented username length and character set validation (F-12) and minimum password length enforcement (F-13) in shared `validate` package.
- Updated account creation and edit flows in `ui` and `grpcserver` layers to apply validation before hashing/processing.
- Added comprehensive unit tests for lockout, validation, and related edge cases.
- Updated `AUDIT.md` to mark F-08, F-12, and F-13 as fixed.
- Updated `openapi.yaml` to reflect new validation and lockout behaviors.
Security: Prevents brute-force attacks via lockout mechanism and strengthens defenses against weak and invalid input.
- ui/handlers_accounts.go (handleIssueSystemToken): call
GetSystemToken before issuing; if one exists, call
RevokeToken(existing.JTI, "rotated") before TrackToken
and SetSystemToken for the new token; mirrors the pattern
in REST handleTokenIssue and gRPC IssueServiceToken
- db/db_test.go: TestSystemTokenRotationRevokesOld verifies
the full rotation flow: old JTI revoked with reason
"rotated", new JTI tracked and active, GetSystemToken
returns the new JTI
- AUDIT.md: mark F-16 as fixed
Security: without this fix an old system token remained valid
after rotation until its natural expiry, giving a leaked or
stolen old token extra lifetime. With the revocation the old
JTI is immediately marked in token_revocation so any validator
checking revocation status rejects it.
The package-level defaultRateLimiter drained its token bucket
across all test cases, causing later tests to hit ResourceExhausted.
Move rateLimiter from a package-level var to a *grpcRateLimiter field
on Server; New() allocates a fresh instance (10 req/s, burst 10) per
server. Each test's newTestEnv() constructs its own Server, so tests
no longer share limiter state.
Production behaviour is unchanged: a single Server is constructed at
startup and lives for the process lifetime.
- Introduced `web/templates/` for HTMX-fragmented pages (`dashboard`, `accounts`, `account_detail`, `error_fragment`, etc.).
- Implemented UI routes for account CRUD, audit log display, and login/logout with CSRF protection.
- Added `internal/ui/` package for handlers, CSRF manager, session validation, and token issuance.
- Updated documentation to include new UI features and templates directory structure.
- Security: Double-submit CSRF cookies, constant-time HMAC validation, login password/Argon2id re-verification at all steps to prevent bypass.