feat(auth): reapply UI_PASSWORD to admin account on every startup#57
Merged
Conversation
Today seedAdmin() only writes a credential when no admin user exists, so changing UI_PASSWORD after the first run silently has no effect. This makes the env var the source of truth on every API startup: - If the stored credential's hash already verifies UI_PASSWORD, do nothing. - If it differs, replace it via internalAdapter.updatePassword and log it. - Existing first-startup and missing-credential repair branches are kept. Operational note: any UI-side password change is overwritten on the next restart unless UI_PASSWORD is updated to match. This is the supported recovery path when the admin password is forgotten — bump UI_PASSWORD, restart the container, log in. Spec: openspec/changes/update-admin-password-reset-on-startup Tests: apps/api/src/lib/seed-admin.test.ts (5 cases) Co-authored-by: Cursor <cursoragent@cursor.com>
|
🚅 Deployed to the archmax-pr-57 environment in archmax SemLayer
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2f34081. Configure here.
There was a problem hiding this comment.
No security issues found in the changed files.
Checked the requested threat surfaces against this PR:
- MCP endpoint auth: no MCP route/tool files changed; no new path bypasses token/project scoping.
- Query execution sandboxing: no
execute_query, DuckDB, or semantic-model query execution code changed. - Admin auth:
BETTER_AUTH_SECRETremains validated at startup via Zod min length, Better Auth cookie attributes remainHttpOnly, productionSecure, andSameSite=Lax; the new seed flow hashesUI_PASSWORDand does not log it. - API input validation: no Hono route handlers or request parsing paths changed.
- Environment secrets: docs and code do not commit, return, or log secret values; logs mention only username/reset status.
- Dependency exposure: no dependency manifests or lockfile changes.
Validation run:
pnpm --filter @archmax/api typecheckpnpm exec vitest run apps/api/src/lib/seed-admin.test.ts
Sent by Cursor Automation: archmax Security Review
Docker image readydocker pull ghcr.io/archmaxai/archmax:pr-57 |
The previous reconciliation collapsed "no credential row" and "credential row with a null password" into a single createAccount() call, which would have produced a duplicate credential account for the same user in the second case (caught by Cursor Bugbot on PR #57). Now: - No credential row -> createAccount (unchanged). - Credential row exists -> updatePassword via internalAdapter, regardless of whether the existing hash is null or just stale. updatePassword scopes by (userId, providerId="credential") so it updates in place. Test for the null-password case is updated to assert the in-place update (accounts stays length 1, updatePassword is called, no createAccount). Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
seedAdmin()now reconciles the admin credential againstUI_PASSWORDon every API startup instead of skipping when the user already exists. If the stored hash already verifies the currentUI_PASSWORD, it's a no-op; if it differs, the credential is replaced viainternalAdapter.updatePasswordand a log line is emitted.README.md,configuration.mdx,docker.mdx,self-hosting.mdx) are updated to describeUI_PASSWORDas authoritative on every startup and to document the rotate-by-restart workflow.Why
Previously, changing
UI_PASSWORDafter the first run had no effect — the env var silently became inert and there was no in-product way to recover a lost admin password. This makesUI_PASSWORDthe source of truth and gives operators a documented recovery path: bumpUI_PASSWORD, restart the container, log in.Operational note: any password the admin changes through the UI is overwritten on the next API restart unless
UI_PASSWORDis updated to match.OpenSpec
openspec/changes/update-admin-password-reset-on-startup/authcapability spec —Admin User Seedingis rewritten as a startup reconciliation contract with scenarios for first-seed, no-op-when-matching, reset-when-changed, and missing-credential repair.openspec validate update-admin-password-reset-on-startup --strictis green.Test plan
pnpm --filter @archmax/api typecheckpnpm --filter @archmax/api buildpnpm typecheck(workspace, 7/7 tasks)pnpm exec vitest run --project api src/lib/seed-admin.test.ts— 5/5 pass (no user, hash matches, hash differs, missing credential, null-password credential row)openspec validate update-admin-password-reset-on-startup --strictUI_PASSWORD=passwordA, log in; stop, restart withUI_PASSWORD=passwordB, confirmpasswordAno longer works andpasswordBdoes.Note: the existing
apps/api/src/routes/test-agents.integration.test.tsfailure is pre-existing onmainand unrelated to this change.Made with Cursor
Note
Medium Risk
Changes admin authentication behavior by resetting the stored admin credential to match
UI_PASSWORDon each startup, which can unexpectedly override UI-rotated passwords after restarts. Touches login/credential handling but is scoped to the seeded admin user and includes unit coverage for the main branches.Overview
Makes
UI_PASSWORDauthoritative for the admin account on every API/container startup.seedAdminnow reconciles the existing admincredentialaccount: it verifies the stored hash against the currentUI_PASSWORDand, when mismatched (or null), replaces it viainternalAdapter.updatePassword; when it matches, it becomes a no-op.Adds focused unit tests for the new reconciliation paths, and updates docs/specs to remove “initial” wording and document the rotate/recover workflow (change
UI_PASSWORD, restart) plus the operational caveat that UI-changed passwords are overwritten on restart if they differ fromUI_PASSWORD.Reviewed by Cursor Bugbot for commit 61f7e88. Bugbot is set up for automated code reviews on this repo. Configure here.