First Boot & Beyond: Taming the nyxCore Beast (Session 2)
A deep dive into the thrilling — and sometimes frustrating — journey of getting the nyxCore dashboard to boot, fixing runtime errors, and verifying core user flows during our latest development sprint.
There's a special kind of thrill in the developer's world: the "first boot." It's that moment you finally hit npm run dev (or docker compose up), hold your breath, and watch the logs scroll by, hoping to see the glorious ready - started server on 0.0.0.0:3000 message. But as any seasoned dev knows, a "first boot" is rarely just one boot. It's a series of battles, fixes, and triumphant small victories.
This post chronicles our second major development session for nyxCore, our new dashboard application. The mission: get the app to boot, squash all runtime errors, and verify the essential user flows, specifically login (email and GitHub OAuth) and basic admin functionality.
The Mission: From Code to Live UI
Our goal was ambitious for a single session:
- Boot the nyxCore dashboard app for the first time successfully.
- Fix all runtime errors that inevitably emerge when components meet data and APIs.
- Verify login and admin flows end-to-end, ensuring users can sign in and manage API keys.
By the end of the session, we had a dashboard that was not just alive, but functional: app boots, email login works, GitHub OAuth works, and API key CRUD (Create, Read, Update, Delete) is operational. We were even deep into testing the discussion detail page, having just overcome a particularly tricky use(params) error.
The Road to Readiness: A List of Triumphs
Getting to this point involved a significant number of adjustments, configurations, and bug fixes. Here's a rundown of the key milestones achieved:
- Next.js 14 Configuration: Bid farewell to
next.config.tsand embracednext.config.mjs. Next.js 14 is opinionated about its config files, and.mjswith JSDoc is the way forward for ESM modules. - Dockerized Infrastructure: Spun up our essential services with
docker compose up -d, ensuring Postgres 16 on:5432and Redis 7 on:6379were ready for action. - Environment Setup: Created our
.envfrom the example, generating criticalAUTH_SECRET(base64) andENCRYPTION_KEY(hex) for security. - Database Schema & RLS:
- Pushed our Prisma schema with
npx prisma db push, creating all necessary tables. - Crucially, we fixed our
prisma/rls.sqlscript. Prisma generates camelCase column names ("tenantId","discussionId"), so our Row-Level Security policies needed double-quoted camelCase instead of snake_case (tenant_id) to prevent "column does not exist" errors. - Applied all 14 RLS policies and set up our full-text search index and trigger with
psql. - Seeded the database with
npx prisma db seed, giving us a default tenant and four personas to start with.
- Pushed our Prisma schema with
- Frontend Polish: Squashed a hydration error in
src/components/layout/theme-toggle.tsxby introducing amountedstate, deferring icon rendering until after the client-side hydration. - Robust Authentication & Authorization:
- Fixed
enforceRole403 errors on admin routes. New users now automatically get anownerrole on the default tenant via auto-assign logic insrc/server/auth.ts's JWT callback. - Created SQL to backfill existing users with the
ownermembership for a seamless experience. - Addressed a race condition by modifying the JWT callback to check for a missing
tenantIdon every token refresh, not just the initial sign-in.
- Fixed
- Crypto Enhancements: Improved
src/server/services/crypto.ts'sgetEncryptionKey(). It now intelligently accepts both hex (64 chars) and base64 (44 chars) encoded keys using regex detection. - User Feedback: Integrated error feedback display in
src/app/(dashboard)/dashboard/admin/page.tsx's Add Key dialog, showingcreateKeyMutation.error.messagefor a better UX. - Email & OAuth Configuration:
- Set
EMAIL_FROMtoonboarding@resend.devfor Resend sandbox testing. - Configured GitHub OAuth with our
GITHUB_CLIENT_IDandGITHUB_CLIENT_SECRET. - Fixed GitHub OAuth missing email issue by adding a
profile()callback to the GitHub provider, falling back to<id>+<login>@users.noreply.github.comfor users who keep their email private. - Made the
emailfield optional (String?) inprisma/schema.prisma'sUsermodel to accommodate GitHub users who might not expose their email even with the profile callback.
- Set
- Next.js 14 Dynamic Routing Fix: Resolved a critical
use(params)error in dynamic routes likediscussions/[id]/page.tsxandworkflows/[id]/page.tsx. Next.js 14 passesparamsas a plain object, not a Promise, so we switched fromuse(params)to direct destructuringconst { id } = params.
Lessons Learned: Navigating the Debugging Maze
No development session is complete without its share of head-scratching moments. These "pain points" are often where the most valuable lessons are learned.
-
Next.js Config Evolution:
- Challenge: Attempting to use
next.config.tswith Next.js 14. - The Problem: Next.js 14 explicitly stated:
Configuring Next.js via 'next.config.ts' is not supported. - The Solution: Renamed the config file to
next.config.mjsand used JSDoc for type annotations, aligning with Next.js's move towards ESM. - Insight: Always check framework release notes for breaking changes, especially around configuration and module systems.
- Challenge: Attempting to use
-
psqlvs. Prisma Connection Strings:- Challenge: Using a Prisma-generated
$DATABASE_URLdirectly withpsqlfor RLS script execution. - The Problem:
psqlcomplained:invalid URI query parameter: "schema". It doesn't understand the?schema=publicquery parameter that Prisma uses. - The Solution: Manually stripped
?schema=publicfrom the connection string when callingpsql. - Insight: Tools sometimes have subtle differences in how they interpret connection strings or environment variables. Don't assume universal compatibility.
- Challenge: Using a Prisma-generated
-
Prisma's CamelCase and SQL RLS:
- Challenge: Applying RLS policies using
tenant_id(snake_case) column names in SQL. - The Problem: PostgreSQL threw
column "tenant_id" does not exist. Prisma, by default, maps snake_case database columns to camelCase in its generated schema and queries. - The Solution: Changed all RLS SQL to use double-quoted camelCase for column names (e.g.,
"tenantId","discussionId"). Double quotes force PostgreSQL to treat the name literally, preserving the camelCase. - Insight: Be acutely aware of the naming conventions and transformations your ORM (Prisma) applies between your schema definition and the actual database.
- Challenge: Applying RLS policies using
-
ENCRYPTION_KEYDecoding Woes:- Challenge: Providing an
ENCRYPTION_KEYas a hex string when the decoding logic expected base64. - The Problem: The application complained:
ENCRYPTION_KEY must be 32 bytes (base64 encoded). A hex string decoded as base64 results in an incorrect length. - The Solution: Updated
getEncryptionKey()to auto-detect whether the key is hex or base64 using a regex (/^[0-9a-fA-F]{64}$/) and decode accordingly. - Insight: Robust input validation and flexible parsing are crucial for environment variables, especially for sensitive cryptographic keys.
- Challenge: Providing an
-
GitHub OAuth and Missing Emails:
- Challenge: Integrating GitHub OAuth with
PrismaAdapterwhen a user has a private email. - The Problem:
Argument 'email' is missingduring user creation, as GitHub doesn't always return an email, even with aprofile()callback configured. The NextAuth v5 adapter flow used raw user info separately. - The Solution: Implemented a
profile()callback in the GitHub provider to generate a fallback email (<id>+<login>@users.noreply.github.com) AND made theemailfield optional (String?) inprisma/schema.prisma'sUsermodel. - Insight: Third-party authentication can be surprisingly complex. Always account for users who guard their privacy, and ensure your database schema can handle optional data fields.
- Challenge: Integrating GitHub OAuth with
-
Next.js 14
use(params)Gotcha:- Challenge: Using
use(params)in dynamic route pages in Next.js 14. - The Problem:
An unsupported type was passed to use(): [object Object]. In Next.js 14,paramsis passed directly as a plain object, not a Promise that needs to beuse()d. - The Solution: Changed to direct destructuring:
const { id } = paramswith a type annotation{ id: string }. - Insight: Frameworks evolve, and even seemingly minor changes in API surface can lead to runtime errors. Keep an eye on migration guides!
- Challenge: Using
The Current State & The Path Ahead
With these critical fixes and configurations, nyxCore is humming. Our Docker containers (Postgres 16, Redis 7) are stable, and the dev server is running on :3000. We have two users in the database, both with owner roles, and our .env is fully configured.
Our immediate next steps are focused on feature verification and stability:
- Continue testing the discussion detail page: verify message loading and sending.
- Test workflow creation and the start/pause functionality.
- Test wardrobe CRUD operations and gap analysis.
- Test memory creation, search, and export features.
- Verify Server-Sent Events (SSE) streaming across the dashboard, discussions, and workflows.
- Implement a proper error page for
/api/auth/errorinstead of a generic 500. - Run
npm run buildto ensure all fixes translate to a successful production compilation. - Address minor issues like the missing
icon-192.pngin/public/icons/.
This session was a fantastic dive into the nitty-gritty of getting a complex application off the ground. Every solved bug and configured service brings us closer to a robust, production-ready nyxCore. Stay tuned for more updates as we continue to build!