Skip to content

codex: database foundation#2

Merged
ECWireless merged 3 commits into
mainfrom
codex/database-foundation
May 29, 2026
Merged

codex: database foundation#2
ECWireless merged 3 commits into
mainfrom
codex/database-foundation

Conversation

@ECWireless
Copy link
Copy Markdown
Member

@ECWireless ECWireless commented May 29, 2026

This pull request introduces a full database migration and local development workflow using Drizzle ORM, adds essential database-related scripts and configuration, and improves documentation and environment setup. The changes establish a robust foundation for database management, including schema migration, local resets, and production deployment automation.

Database schema and migration setup:

  • Added initial Drizzle migration SQL (drizzle/0000_gorgeous_mephisto.sql) defining all core tables, types, indices, and constraints for the application’s data model.
  • Introduced Drizzle migration metadata (drizzle/meta/_journal.json) to track migration state.
  • Added Drizzle configuration file (drizzle.config.ts) to define migration settings and load environment variables.
  • Implemented a script (scripts/reset-local-db.mjs) to safely reset and migrate the local database, with safeguards against destructive actions on production databases.

Workflow automation and scripts:

  • Added a GitHub Actions workflow (.github/workflows/database.yml) to check for committed migrations on pull requests and automatically apply migrations to production on pushes to main.

Local development and package management:

  • Updated package.json to include Drizzle and Neon dependencies, and added scripts for generating, migrating, resetting, and inspecting the database. [1] [2]
  • Added a database access module (src/db/index.ts) using Drizzle ORM and Neon for serverless Postgres connections.

Documentation and environment configuration:

  • Expanded README.md with detailed instructions for database commands, deployment workflow, environment variable usage, and security notes.
  • Updated .env.example to document the required ENCRYPTION_KEY format for local development and production.

Other improvements:

  • Integrated Vercel Analytics into the application layout for production usage tracking. [1] [2]

Summary by CodeRabbit

  • New Features

    • Added database infrastructure with PostgreSQL and Neon integration
    • Introduced encryption for sensitive data fields
    • Implemented audit logging system
  • Documentation

    • Updated README with database commands, security guidance, and deployment instructions
  • Chores

    • Added automated database workflow and local reset utilities
    • Integrated analytics tracking

Review Change Stack

Copilot AI review requested due to automatic review settings May 29, 2026 17:00
@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
raidguild-accounting Ready Ready Preview, Comment May 29, 2026 5:30pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@ECWireless, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 30 minutes and 50 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 55c057ab-9602-4010-a63f-f305ad7902a9

📥 Commits

Reviewing files that changed from the base of the PR and between f7b67a6 and 9c80822.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • .env.example
  • .github/workflows/database.yml
  • README.md
  • drizzle/0000_gorgeous_mephisto.sql
  • drizzle/0001_damp_wiccan.sql
  • drizzle/meta/0001_snapshot.json
  • drizzle/meta/_journal.json
  • package.json
  • src/db/index.ts
  • src/db/schema.ts
  • src/lib/audit.ts
  • src/lib/encryption.ts
📝 Walkthrough

Walkthrough

This PR introduces a complete database infrastructure layer for the accounting system. It defines a PostgreSQL schema with nine tables (users, audit logs, roles, entities, ledger entries, quarters, raids, treasury accounts), implements Drizzle ORM with Neon serverless connectivity, adds field-level AES-256-GCM encryption utilities, provides local database reset tooling, and establishes GitHub Actions CI/CD for migration verification and production deployment.

Changes

Database Infrastructure

Layer / File(s) Summary
Database schema definition
src/db/schema.ts, drizzle/0000_gorgeous_mephisto.sql, drizzle/meta/*
Drizzle ORM TypeScript schema defines seven enums (audit_action, entity_type, ledger_category, ledger_source, quarter_status, treasury_account_type, verification_status) and nine tables (app_users, audit_events, cleric_roles, entities, entity_addresses, ledger_entries, quarters, raids, treasury_accounts) with typed columns, defaults, indexes, unique constraints, and foreign key relationships; SQL migration and snapshot metadata generated.
ORM connection and Drizzle configuration
src/db/index.ts, drizzle.config.ts
Drizzle Kit configured for PostgreSQL with schema input, migration output, and DATABASE_URL from environment; getDb() implements lazy-loaded cached singleton using Neon serverless client; Db type exported for typing database instances.
Field encryption and audit logging
src/lib/encryption.ts, src/lib/audit.ts
AES-256-GCM field encryption module supports keyring parsing with optional key-id rotation format, exports encryptField/decryptField with base64-encoded payloads; writeAuditEvent utility logs actor, action, subject, summary, and metadata to auditEvents table via getDb().
Local database operations
scripts/reset-local-db.mjs, package.json
Node.js script validates DATABASE_URL points to localhost, drops/recreates public schema, and runs migrations; enforces protection of system databases; supports --dry-run flag; npm scripts added for db:generate, db:migrate, db:reset:local, db:studio.
Migration CI/CD and documentation
.github/workflows/database.yml, README.md, .env.example
GitHub Actions workflow validates committed migrations in PRs and applies migrations to production via DATABASE_URL secret; README documents Drizzle commands, Neon Postgres and Drizzle ORM in tech stack, encryption key requirements, and deployment ordering with Vercel; .env.example comment describes ENCRYPTION_KEY format.
App integration
src/app/layout.tsx
Vercel Analytics component added to root layout body for usage tracking.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A database dream, now take the stage,
With tables vast and keyrings safe,
Ledgers locked with cipher's might,
Neon shines through cryptic night,
Migrations flow to production's grace,
As audits trace the accounting's face!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'codex: database foundation' directly reflects the main objective of the PR: introducing a complete database foundation using Drizzle ORM, schema, migrations, and supporting infrastructure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/database-foundation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR lays down the project’s database foundation by introducing a Drizzle ORM schema + initial migration set, adding local DB reset tooling, and automating migration checks/apply via GitHub Actions. It also introduces server-side encryption/auditing helpers and updates documentation and dependencies to support the new workflow.

Changes:

  • Added Drizzle schema definitions, generated migration SQL, and Drizzle CLI config/metadata.
  • Added local database reset script and a GitHub Actions workflow to verify/apply migrations.
  • Introduced initial server-side encryption + audit logging utilities and integrated Vercel Analytics.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/lib/encryption.ts Adds AES-256-GCM helpers for storing encrypted JSON payloads.
src/lib/audit.ts Adds helper to insert audit events into the database.
src/db/schema.ts Defines the initial Postgres schema (tables, enums, indexes) in Drizzle.
src/db/index.ts Creates a Neon + Drizzle DB client accessor.
src/app/layout.tsx Adds Vercel Analytics to the app layout.
scripts/reset-local-db.mjs Implements destructive local-only schema reset + migration runner.
README.md Documents DB scripts, migration workflow, and env/security notes.
package.json Adds Drizzle/Neon/pg dependencies and DB-related scripts.
pnpm-lock.yaml Locks newly added dependencies.
drizzle/0000_gorgeous_mephisto.sql Initial SQL migration creating core DB objects.
drizzle/meta/_journal.json Tracks applied migrations.
drizzle/meta/0000_snapshot.json Snapshot of the schema state for Drizzle.
drizzle.config.ts Drizzle Kit config + dotenv loading for CLI usage.
.github/workflows/database.yml CI workflow for migration generation check and production migration apply.
.env.example Documents ENCRYPTION_KEY format (including rotation-friendly format).
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread drizzle/0000_gorgeous_mephisto.sql
Comment thread src/db/index.ts
Comment thread src/lib/encryption.ts
Comment thread src/lib/audit.ts
Comment thread src/lib/encryption.ts Outdated
Comment thread src/db/schema.ts
Comment thread package.json
Copy link
Copy Markdown

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/database.yml (1)

27-83: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Set explicit permissions following least-privilege principle.

The workflow uses default (broad) permissions. Restrict to only what each job requires.

🔐 Proposed fix to add explicit permissions

Add at workflow level (after concurrency block):

 concurrency:
   group: database-${{ github.ref }}
   cancel-in-progress: false
+
+permissions:
+  contents: read

For migration-check, if the job only reads code and verifies diffs:

 jobs:
   migration-check:
     if: github.event_name == 'pull_request'
     name: Migration Check
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     steps:

For migrate-production, same permissions suffice unless you need to write back to the repo:

   migrate-production:
     if: github.event_name == 'push'
     name: Migrate Production
     runs-on: ubuntu-latest
     environment: production
+    permissions:
+      contents: read
     steps:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/database.yml around lines 27 - 83, Add an explicit
permissions block to restrict GitHub Actions tokens to least-privilege for the
migration-check and migrate-production jobs: set permissions to only what’s
needed (e.g., contents: read) instead of the default broad permissions, either
as a top-level "permissions" block for the workflow or per-job for
"migration-check" and "migrate-production"; ensure no repo write permissions are
granted unless a job (e.g., git push) actually requires them.
🧹 Nitpick comments (1)
src/db/schema.ts (1)

69-76: ⚡ Quick win

updatedAt won't refresh on updates.

defaultNow() only applies on INSERT. Without $onUpdate (or a DB trigger), updated_at will never change on subsequent UPDATEs, making it indistinguishable from created_at.

♻️ Add $onUpdate to the shared helper
 const timestamps = {
   createdAt: timestamp("created_at", { withTimezone: true })
     .defaultNow()
     .notNull(),
   updatedAt: timestamp("updated_at", { withTimezone: true })
     .defaultNow()
+    .$onUpdate(() => new Date())
     .notNull(),
 };

Please confirm $onUpdate is supported and behaves as expected in drizzle-orm 0.45.2.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/db/schema.ts` around lines 69 - 76, The shared timestamps helper sets
updatedAt with defaultNow(), which only runs on INSERT so updated_at will never
change; update the helper (the timestamps const and the updatedAt field) to
include an $onUpdate that sets it to the current timestamp (e.g., using the SQL
now() expression or drizzle's sql helper) so updatedAt auto-updates on UPDATEs,
and confirm that $onUpdate is supported and behaves as expected in drizzle-orm
0.45.2 before merging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/database.yml:
- Around line 33-34: Update the Checkout steps that use actions/checkout@v4 in
the migration-check and migrate-production jobs to disable credential
persistence by adding persist-credentials: false to each checkout step so
credentials are not exposed to subsequent steps; locate the steps that call
actions/checkout@v4 and insert the persist-credentials: false property under
each of those steps.
- Line 34: Replace floating tags for GitHub Actions with pinned commit SHAs:
locate each "uses: actions/checkout@v4" and any "uses: pnpm/action-setup@..."
occurrences (the uses lines mentioned in the review) and change them to the
corresponding immutable commit SHA form (e.g., actions/checkout@<commit-sha>)
for every job in the workflow; ensure you look up the canonical commit SHAs in
the action repos and apply the same SHA-pinning pattern to all referenced
actions so both jobs use pinned SHAs instead of tags.

In `@src/db/schema.ts`:
- Around line 82-88: The wallet_address uniqueness is case-sensitive and
updated_at never changes on UPDATEs; change the schema to enforce
case-insensitive uniqueness and make updated_at auto-refresh: update the
app_users definition around walletAddress and the
uniqueIndex("app_users_wallet_address_unique") to either (a) store a canonical
form (lowercase) by normalizing inputs in your insert/update paths and keep the
index, or (b) change the index to a functional index on
lower(table.walletAddress) / use citext so mixed-case EIP-55 checksums collide
correctly (pick one approach and apply consistently in code), and for timestamps
replace the current default-only updatedAt (timestamps / updatedAt) with an
on-update mechanism—either use Drizzle's onUpdateNow/updatedAt helper or add a
DB trigger/function that sets updated_at = now() on UPDATE; apply the change to
the same schema symbols: walletAddress,
uniqueIndex("app_users_wallet_address_unique"), timestamps / updatedAt.

In `@src/lib/encryption.ts`:
- Around line 27-41: The key parsing currently uses positional IDs (key-${index
+ 1}) and does not trim entries, which breaks decryption after rotations; update
the parser that currently iterates rawKeyring.split(",") to first split and trim
each entry, split on the first ":" into id and key (trim both), require explicit
ids when more than one entry is present (throw an Error if an entry has no id
while ENCRYPTION_KEY contains >1 item), decode the maybeKey as base64 and
validate 32 bytes as before, and keep references to the same id strings so
encryptField/decryptField can reliably match keys by id.

---

Outside diff comments:
In @.github/workflows/database.yml:
- Around line 27-83: Add an explicit permissions block to restrict GitHub
Actions tokens to least-privilege for the migration-check and migrate-production
jobs: set permissions to only what’s needed (e.g., contents: read) instead of
the default broad permissions, either as a top-level "permissions" block for the
workflow or per-job for "migration-check" and "migrate-production"; ensure no
repo write permissions are granted unless a job (e.g., git push) actually
requires them.

---

Nitpick comments:
In `@src/db/schema.ts`:
- Around line 69-76: The shared timestamps helper sets updatedAt with
defaultNow(), which only runs on INSERT so updated_at will never change; update
the helper (the timestamps const and the updatedAt field) to include an
$onUpdate that sets it to the current timestamp (e.g., using the SQL now()
expression or drizzle's sql helper) so updatedAt auto-updates on UPDATEs, and
confirm that $onUpdate is supported and behaves as expected in drizzle-orm
0.45.2 before merging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e9869c4-bc6a-4e86-a890-2d322f957f08

📥 Commits

Reviewing files that changed from the base of the PR and between 5f37256 and f7b67a6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • .env.example
  • .github/workflows/database.yml
  • README.md
  • drizzle.config.ts
  • drizzle/0000_gorgeous_mephisto.sql
  • drizzle/meta/0000_snapshot.json
  • drizzle/meta/_journal.json
  • package.json
  • scripts/reset-local-db.mjs
  • src/app/layout.tsx
  • src/db/index.ts
  • src/db/schema.ts
  • src/lib/audit.ts
  • src/lib/encryption.ts

Comment thread .github/workflows/database.yml Outdated
Comment thread .github/workflows/database.yml Outdated
Comment thread src/db/schema.ts Outdated
Comment thread src/lib/encryption.ts Outdated
@ECWireless ECWireless merged commit 83ce562 into main May 29, 2026
5 checks passed
@ECWireless ECWireless deleted the codex/database-foundation branch May 29, 2026 17:35
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.

2 participants