Skip to content

Feat/db#9

Merged
lwlee2608 merged 5 commits intomainfrom
feat/db
Feb 6, 2026
Merged

Feat/db#9
lwlee2608 merged 5 commits intomainfrom
feat/db

Conversation

@lwlee2608
Copy link
Collaborator

@lwlee2608 lwlee2608 commented Feb 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • User management system with role-based access (Admin, Staff, Student)
    • Secure password hashing and authentication
  • Tests

    • Added password validation and verification tests
  • Chores

    • Updated build configuration and dependencies
    • Added database connection pooling and migration infrastructure

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

Walkthrough

The changes establish a complete database infrastructure layer for the application. They add PostgreSQL connection pooling configuration with schema support, database migration management using Goose, SQLC-based code generation for type-safe SQL operations, an initial user schema with role-based access control, password hashing utilities using bcrypt, and the necessary configuration for SQLC code generation. These changes enable structured database access patterns and proper initialization flows.

Sequence Diagram(s)

sequenceDiagram
    actor App as Application
    participant Init as InitDB Function
    participant PG as PostgreSQL
    participant Pool as pgxpool.Pool
    
    App->>Init: InitDB(ctx, url, schema)
    Init->>Init: Parse pgxpool.Config
    Init->>Init: Set MaxConns=10, MinConns=2
    Init->>Init: Register AfterConnect Hook
    Init->>PG: CreatePool with config
    PG->>Pool: Initialize pool
    Init->>Pool: pool.Ping(ctx)
    Pool->>PG: Test connectivity
    PG-->>Pool: Response
    Pool-->>Init: Success
    Init-->>App: Return *pgxpool.Pool
Loading
sequenceDiagram
    actor App as Application
    participant Migrate as RunMigrations
    participant DB as SQL Connection
    participant PG as PostgreSQL
    participant Goose as Goose
    
    App->>Migrate: RunMigrations(dbURL, schema)
    Migrate->>DB: sql.Open(pgx driver)
    DB->>PG: Connect
    PG-->>DB: Connected
    Migrate->>DB: ensureSchemaExists(schema)
    DB->>PG: CREATE SCHEMA IF NOT EXISTS
    DB->>PG: SET search_path TO schema
    PG-->>DB: Confirmed
    Migrate->>Goose: SetDialect(PostgreSQL)
    Migrate->>Goose: SetBaseFS(embedded migrations)
    Goose->>PG: goose.Up(ctx, db)
    PG->>PG: Execute 0001_create_users.sql
    PG-->>Goose: Migrations applied
    Migrate-->>App: Return success or error
Loading
sequenceDiagram
    actor App as Application
    participant Querier as SQLC Querier
    participant Pool as pgxpool.Pool
    participant PG as PostgreSQL
    participant Models as User Models
    
    App->>Querier: GetUserByUsername(ctx, username)
    Querier->>Pool: db.QueryRow(ctx, sql, username)
    Pool->>PG: Execute parameterized query
    PG->>PG: SELECT * FROM users WHERE username = $1
    PG-->>Pool: Return row
    Pool->>Models: row.Scan into User struct
    Models-->>Querier: Populate ID, Username, Role, etc.
    Querier-->>App: Return User or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~40 minutes

Poem

🐰 A database sanctuary now takes shape,
With pools of pgx and schemas agape,
Migrations hop through, users take their place,
SQLC generates code at a rapid pace,
Passwords hashed strong with bcrypt's embrace!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/db' is vague and does not clearly convey what specific database functionality was added. While it indicates a database feature, it lacks specificity about the actual changes (connection pooling, migrations, user queries, etc.). Consider a more descriptive title like 'Add database layer with PostgreSQL pooling, migrations, and user queries' to clearly communicate the scope of changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/db

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@go.mod`:
- Line 46: The go.mod currently pins github.com/quic-go/quic-go to v0.54.0 which
has known high-severity vulnerabilities; update the module version to v0.57.0 by
changing the dependency entry for github.com/quic-go/quic-go to v0.57.0, then
run `go get github.com/quic-go/quic-go@v0.57.0` (or `go mod tidy`) to update
go.sum and resolve transitive changes, rebuild and run tests to ensure no
breakages from the quic-go upgrade.

In `@internal/db/db.go`:
- Around line 28-29: The code assigns to
poolConfig.ConnConfig.RuntimeParams["search_path"] without ensuring
RuntimeParams is initialized, which can panic; update the schema handling in
internal/db/db.go to check if poolConfig.ConnConfig.RuntimeParams is nil and if
so allocate a new map before setting the "search_path" key (i.e., guard and
initialize RuntimeParams on poolConfig.ConnConfig then set
RuntimeParams["search_path"] = schema), referencing poolConfig and
ConnConfig.RuntimeParams to locate the change.

In `@internal/db/migration.go`:
- Around line 53-71: The migration code uses sql.DB with SET search_path but
because sql.DB is a pooled client the SET only affects a single connection;
ensureSchemaExists (and the migration runner) must force all migration
statements to use the same connection: before setting search_path and running
goose.Up, constrain the pool (e.g., call db.SetMaxOpenConns(1) and
db.SetMaxIdleConns(1)), perform the schema creation, SET search_path
(setPathQuery) and run migrations, then restore the original pool settings;
alternatively open a dedicated single connection (db.Conn(ctx)) and execute
ensureSchemaExists and goose.Up on that connection so the search_path persists
for the migration session.

In `@internal/db/migrations/0001_create_users.sql`:
- Around line 18-19: Remove the hardcoded default admin password in the
migration that inserts into the users table; instead modify the migration logic
so it does not bake a known password into the INSERT INTO users (username,
password_hash, role) VALUES (...) statement—either omit creating the root user
in this SQL migration and require creation via a startup/provisioning script or
have the deployment process set a hashed password from an environment-provided
value (read at startup and inserted by application code), referencing the users
table and the password_hash column so you update any code paths that relied on
this seed to expect creation via env-driven provisioning or an explicit
admin-creation routine.

In `@internal/users/password_test.go`:
- Around line 39-49: TestCheckPasswordWithMigrationHash uses a hardcoded initial
password ("changeme") and migrationHash; update the repository to avoid relying
on a production default by (1) documenting in README/DEPLOYMENT that the initial
admin password must be rotated immediately, and (2) changing the seed/config
that uses CheckPassword to take the initial admin password from an environment
variable (e.g., INITIAL_ADMIN_PASSWORD) instead of hardcoding "changeme"; keep
TestCheckPasswordWithMigrationHash by either reading that test value from a
test-only env var or clearly marking the test as validating the static
migrationHash only (no runtime auth use).
🧹 Nitpick comments (13)
Makefile (2)

14-15: Renaming installdep looks good.

Clean rename. The .PHONY list on line 10 still references generate but doesn't list dep — consider adding it so make dep always runs even if a file named dep exists.

Suggested fix
-.PHONY: all build build-server build-agent clean test generate docker docker-server docker-agent docker-all protoc protoc-gen run run-agent generate-certs
+.PHONY: all build build-server build-agent clean test generate dep docker docker-server docker-agent docker-all protoc protoc-gen run run-agent generate-certs

30-31: generate no longer depends on dep; sqlc must be pre-installed.

If a developer runs make generate on a fresh checkout without sqlc on $PATH, it will fail with a cryptic "command not found". Consider either restoring the dependency (generate: dep) or adding a guard:

generate:
	`@command` -v sqlc >/dev/null 2>&1 || { echo "sqlc not found. Run 'make dep' first."; exit 1; }
	sqlc generate
internal/users/password.go (1)

13-18: Be aware of bcrypt's 72-byte input limit.

bcrypt.GenerateFromPassword silently truncates passwords longer than 72 bytes. If long passwords or passphrases are expected, a common mitigation is to pre-hash the input (e.g., SHA-256) before passing it to bcrypt. For most use cases the 72-byte limit is fine — just flagging for awareness.

internal/users/password_test.go (1)

15-20: Redundant length check.

Line 19 (len(hash) > 0) is already covered by assert.NotEmpty on line 15. You can drop one of them.

internal/db/queries/users.sql (2)

9-11: ListUsers has no pagination — this won't scale.

Consider adding LIMIT and OFFSET (or cursor-based pagination) parameters now, before callers depend on the unbounded signature. Even a reasonable default limit would help.

-- name: ListUsers :many
SELECT * FROM users
ORDER BY created_at DESC
LIMIT $1 OFFSET $2;

1-11: Consider using explicit column lists instead of SELECT *.

While sqlc regenerates code on schema changes (mitigating the usual fragility concern), explicit columns make the queries self-documenting and avoid accidentally exposing sensitive fields (e.g., password_hash) in list endpoints.

internal/db/migrations/0001_create_users.sql (2)

7-16: Redundant index on username.

The UNIQUE constraint on line 9 already creates a unique index on username. The explicit CREATE INDEX idx_users_username on line 16 adds a second, non-unique index on the same column, which wastes storage and slows writes with no query benefit.

Proposed fix — remove the redundant index
-CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);

And in the down migration:

-DROP INDEX IF EXISTS idx_users_username;

12-13: Prefer TIMESTAMPTZ over TIMESTAMP.

TIMESTAMP (without time zone) discards timezone information, which can lead to ambiguous times across different server/client timezone settings. TIMESTAMPTZ is the recommended default for PostgreSQL.

Proposed fix
-    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
-    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
+    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
internal/db/migration.go (1)

37-46: goose.SetBaseFS and goose.SetDialect mutate global state.

These are package-level setters in goose v3. If any other part of the application (or tests) calls RunMigrations concurrently, there's a data race. This is unlikely to be a problem today since migrations run once at startup, but worth noting as a caveat if the function is ever reused in parallel test suites.

internal/db/db.go (2)

12-15: Config struct is defined but never used.

InitDB takes url and schema as separate arguments. Either use Config as the parameter type or remove the dead struct.


23-25: Pool sizing is hardcoded.

MaxConns=10 and MinConns=2 are reasonable defaults but should be configurable (e.g., via Config or environment) for production tuning.

internal/db/sqlc/users.sql.go (1)

52-55: ListUsers returns password_hash and has no pagination.

Two observations for the source queries (changes should go in internal/db/queries/users.sql, not this generated file):

  1. All three queries select password_hash. Ensure callers never serialize the User struct directly to API responses, or add a separate query/view that omits sensitive fields.
  2. ListUsers has no LIMIT/OFFSET or cursor — this will degrade as the user count grows.
internal/db/sqlc/models.go (1)

57-64: PasswordHash carries a json:"password_hash" tag — risk of accidental serialization.

Since this is generated code, the fix belongs in the API/handler layer: use a separate response DTO that omits sensitive fields rather than marshaling User directly. This complements the observation on users.sql.go.

github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

quic-go v0.54.0 vulnerabilities GO-2025-4017 GO-2025-4233

💡 Result:

quic-go v0.54.0 is vulnerable to both GO-2025-4017 and GO-2025-4233:

  • GO-2025-4017 (aliases: CVE-2025-59530 / GHSA-47m2-4cr7-mhcw)

    • Affected: github.com/quic-go/quic-go v0.50.0–v0.54.0 (inclusive), plus older branches as noted. v0.54.0 is affected.
    • Impact: remote peer can trigger a panic / process crash (DoS) during handshake by sending a premature HANDSHAKE_DONE.
    • Fix: upgrade to v0.54.1 (or v0.55.0+).
      Sources: [1], [2]
  • GO-2025-4233 (aliases: CVE-2025-64702 / GHSA-g754-hx8w-x2g6)

    • Affected: github.com/quic-go/quic-go/http3 before v0.57.0. If you use the http3 package with quic-go v0.54.0, you’re affected.
    • Impact: memory exhaustion (DoS) via HTTP/3 QPACK header expansion (decoded headers can massively exceed encoded size).
    • Fix: upgrade to v0.57.0 (or later).
      Sources: [3], [4]

Practical remediation for a project on quic-go v0.54.0: upgrade at least to v0.57.0 to address both issues (since it is ≥ v0.54.1 and includes the http3 fix at v0.57.0). [1], [3]

Sources

  1. Go Vulnerability DB: GO-2025-4017 (affected versions / fixed in v0.54.1) — https://pkg.go.dev/vuln/GO-2025-4017
  2. GitHub Advisory: GHSA-47m2-4cr7-mhcw (affected <=0.54.0, patched v0.54.1/v0.55.0) — GHSA-47m2-4cr7-mhcw
  3. Go Vulnerability DB: GO-2025-4233 (affected before v0.57.0) — https://pkg.go.dev/vuln/GO-2025-4233
  4. GitHub Advisory: GHSA-g754-hx8w-x2g6 (patched v0.57.0; QPACK expansion DoS details) — GHSA-g754-hx8w-x2g6

Bump quic-go from v0.54.0 to v0.57.0 to address known HIGH-severity vulnerabilities.

v0.54.0 contains two unpatched DoS vulnerabilities:

  • GO-2025-4017 (CVE-2025-59530): remote peer can crash the process during QUIC handshake (fixed in v0.54.1)
  • GO-2025-4233 (CVE-2025-64702): HTTP/3 QPACK header expansion enables memory exhaustion (fixed in v0.57.0)

Upgrade to v0.57.0 to address both issues.

🧰 Tools
🪛 OSV Scanner (2.3.2)

[HIGH] 46-46: github.com/quic-go/quic-go 0.54.0: Panic occurs when queuing undecryptable packets after handshake completion in github.com/quic-go/quic-go

(GO-2025-4017)


[HIGH] 46-46: github.com/quic-go/quic-go 0.54.0: HTTP/3 QPACK Header Expansion DoS in github.com/quic-go/quic-go

(GO-2025-4233)


[HIGH] 46-46: github.com/quic-go/quic-go 0.54.0: quic-go: Panic occurs when queuing undecryptable packets after handshake completion

(GHSA-47m2-4cr7-mhcw)


[HIGH] 46-46: github.com/quic-go/quic-go 0.54.0: quic-go HTTP/3 QPACK Header Expansion DoS

(GHSA-g754-hx8w-x2g6)

🤖 Prompt for AI Agents
In `@go.mod` at line 46, The go.mod currently pins github.com/quic-go/quic-go to
v0.54.0 which has known high-severity vulnerabilities; update the module version
to v0.57.0 by changing the dependency entry for github.com/quic-go/quic-go to
v0.57.0, then run `go get github.com/quic-go/quic-go@v0.57.0` (or `go mod tidy`)
to update go.sum and resolve transitive changes, rebuild and run tests to ensure
no breakages from the quic-go upgrade.

Comment on lines +28 to +29
if schema != "" {
poolConfig.ConnConfig.RuntimeParams["search_path"] = schema
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n internal/db/db.go

Repository: EternisAI/silo-proxy

Length of output: 2155


🏁 Script executed:

rg -n 'RuntimeParams' --type go -B 2 -A 2

Repository: EternisAI/silo-proxy

Length of output: 455


🏁 Script executed:

rg -n 'ParseConfig' --type go -B 3 -A 3

Repository: EternisAI/silo-proxy

Length of output: 432


🌐 Web query:

pgxpool.ParseConfig RuntimeParams initialization pgx golang

💡 Result:

In pgx v5, pgxpool.ParseConfig returns a *pgxpool.Config that contains ConnConfig *pgx.ConnConfig (which embeds pgconn.Config). You set session defaults via ConnConfig.RuntimeParams (e.g. search_path, application_name).[1][2]

cfg, err := pgxpool.ParseConfig(connString)
if err != nil { /* handle */ }

// RuntimeParams is a map; ensure it’s initialized before writing to it.
if cfg.ConnConfig.RuntimeParams == nil {
    cfg.ConnConfig.RuntimeParams = map[string]string{}
}

cfg.ConnConfig.RuntimeParams["application_name"] = "my-service"
cfg.ConnConfig.RuntimeParams["search_path"] = "my_schema,public"

pool, err := pgxpool.NewWithConfig(ctx, cfg)

Notes:

  • RuntimeParams are “run-time parameters to set on connection as session default values”.[3]
  • Configs must be created by ParseConfig (manual initialization can panic later when connecting).[2]

Sources: [1] https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool [2] https://pkg.go.dev/github.com/jackc/pgx/v5 [3] https://pkg.go.dev/github.com/jackc/pgx/v5/pgconn


Potential nil map panic on line 29 — RuntimeParams may not be initialized.

pgxpool.ParseConfig initializes RuntimeParams only if the connection string contains runtime parameters. If the map is nil, the direct assignment will panic.

Proposed fix — guard with nil check
 	if schema != "" {
+		if poolConfig.ConnConfig.RuntimeParams == nil {
+			poolConfig.ConnConfig.RuntimeParams = make(map[string]string)
+		}
 		poolConfig.ConnConfig.RuntimeParams["search_path"] = schema
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if schema != "" {
poolConfig.ConnConfig.RuntimeParams["search_path"] = schema
if schema != "" {
if poolConfig.ConnConfig.RuntimeParams == nil {
poolConfig.ConnConfig.RuntimeParams = make(map[string]string)
}
poolConfig.ConnConfig.RuntimeParams["search_path"] = schema
🤖 Prompt for AI Agents
In `@internal/db/db.go` around lines 28 - 29, The code assigns to
poolConfig.ConnConfig.RuntimeParams["search_path"] without ensuring
RuntimeParams is initialized, which can panic; update the schema handling in
internal/db/db.go to check if poolConfig.ConnConfig.RuntimeParams is nil and if
so allocate a new map before setting the "search_path" key (i.e., guard and
initialize RuntimeParams on poolConfig.ConnConfig then set
RuntimeParams["search_path"] = schema), referencing poolConfig and
ConnConfig.RuntimeParams to locate the change.

Comment on lines +53 to +71
func ensureSchemaExists(db *sql.DB, schema string) error {
// Create schema if it doesn't exist
query := "CREATE SCHEMA IF NOT EXISTS " + pgx.Identifier{schema}.Sanitize()
_, err := db.Exec(query)
if err != nil {
return err
}
slog.Info("Schema is ready", "schema", schema)

// Set search_path to the schema to ensure migrations run there
setPathQuery := "SET search_path TO " + pgx.Identifier{schema}.Sanitize()
_, err = db.Exec(setPathQuery)
if err != nil {
return err
}
slog.Info("Set search_path", "schema", schema)

return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

SET search_path may not stick for the migration — sql.DB pools connections.

db.Exec("SET search_path TO ...") applies to whichever connection the pool hands out. Subsequent calls from goose.Up may acquire a different connection that still has the default search_path. This can cause migrations to run in the wrong schema.

A straightforward fix is to constrain the pool to a single connection so all statements share it:

Proposed fix
 func RunMigrations(dbURL string, schema string) error {
 	slog.Info("Running database migrations...")
 
 	if schema == "" {
 		schema = "public"
 	}
 
 	db, err := sql.Open("pgx", dbURL)
 	if err != nil {
 		return err
 	}
 	defer db.Close()
 
+	// Force a single connection so SET search_path persists across
+	// all statements issued by goose.
+	db.SetMaxOpenConns(1)
+
 	if err := ensureSchemaExists(db, schema); err != nil {
 		return err
 	}
🤖 Prompt for AI Agents
In `@internal/db/migration.go` around lines 53 - 71, The migration code uses
sql.DB with SET search_path but because sql.DB is a pooled client the SET only
affects a single connection; ensureSchemaExists (and the migration runner) must
force all migration statements to use the same connection: before setting
search_path and running goose.Up, constrain the pool (e.g., call
db.SetMaxOpenConns(1) and db.SetMaxIdleConns(1)), perform the schema creation,
SET search_path (setPathQuery) and run migrations, then restore the original
pool settings; alternatively open a dedicated single connection (db.Conn(ctx))
and execute ensureSchemaExists and goose.Up on that connection so the
search_path persists for the migration session.

Comment on lines +18 to +19
-- Default root user with bcrypt hashed password: 'changeme'
INSERT INTO users (username, password_hash, role) VALUES ('root', '$2a$10$uejoNCSLZ9YkKOZriLlSGeg0pm/nuGVS3nRuSPyYuk/Z7HJHKBhGO', 'Admin');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded default admin password is a security risk.

The root user seed with a well-known password (changeme) will ship into every deployment. If not rotated immediately, this is a trivially exploitable admin credential. Consider requiring the password to be set via an environment variable at startup or through a separate provisioning step rather than baking it into the migration.

🤖 Prompt for AI Agents
In `@internal/db/migrations/0001_create_users.sql` around lines 18 - 19, Remove
the hardcoded default admin password in the migration that inserts into the
users table; instead modify the migration logic so it does not bake a known
password into the INSERT INTO users (username, password_hash, role) VALUES (...)
statement—either omit creating the root user in this SQL migration and require
creation via a startup/provisioning script or have the deployment process set a
hashed password from an environment-provided value (read at startup and inserted
by application code), referencing the users table and the password_hash column
so you update any code paths that relied on this seed to expect creation via
env-driven provisioning or an explicit admin-creation routine.

Comment on lines +39 to +49
func TestCheckPasswordWithMigrationHash(t *testing.T) {
// This test verifies the hardcoded hash in the migration works
// The hash was generated with password "changeme"
migrationHash := "$2a$10$uejoNCSLZ9YkKOZriLlSGeg0pm/nuGVS3nRuSPyYuk/Z7HJHKBhGO"

// Should match the expected password
assert.True(t, CheckPassword("changeme", migrationHash))

// Should not match other passwords
assert.False(t, CheckPassword("root", migrationHash))
assert.False(t, CheckPassword("admin", migrationHash))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Good practice testing the migration hash — ensure "changeme" is changed before production.

The test validates the hardcoded migration hash, which is great for catching accidental changes to the seed data. Make sure the default password "changeme" is documented as requiring immediate rotation after first deployment, or consider using an environment variable for the initial admin password instead.

🤖 Prompt for AI Agents
In `@internal/users/password_test.go` around lines 39 - 49,
TestCheckPasswordWithMigrationHash uses a hardcoded initial password
("changeme") and migrationHash; update the repository to avoid relying on a
production default by (1) documenting in README/DEPLOYMENT that the initial
admin password must be rotated immediately, and (2) changing the seed/config
that uses CheckPassword to take the initial admin password from an environment
variable (e.g., INITIAL_ADMIN_PASSWORD) instead of hardcoding "changeme"; keep
TestCheckPasswordWithMigrationHash by either reading that test value from a
test-only env var or clearly marking the test as validating the static
migrationHash only (no runtime auth use).

@lwlee2608 lwlee2608 merged commit 56c53e3 into main Feb 6, 2026
3 checks passed
@lwlee2608 lwlee2608 deleted the feat/db branch February 6, 2026 06:27
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