nyxcore-systems
7 min read

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.

Next.jsPrismaNextAuthPostgreSQLDockerDebuggingFrontendBackendTypeScript

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.ts and embraced next.config.mjs. Next.js 14 is opinionated about its config files, and .mjs with JSDoc is the way forward for ESM modules.
  • Dockerized Infrastructure: Spun up our essential services with docker compose up -d, ensuring Postgres 16 on :5432 and Redis 7 on :6379 were ready for action.
  • Environment Setup: Created our .env from the example, generating critical AUTH_SECRET (base64) and ENCRYPTION_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.sql script. 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.
  • Frontend Polish: Squashed a hydration error in src/components/layout/theme-toggle.tsx by introducing a mounted state, deferring icon rendering until after the client-side hydration.
  • Robust Authentication & Authorization:
    • Fixed enforceRole 403 errors on admin routes. New users now automatically get an owner role on the default tenant via auto-assign logic in src/server/auth.ts's JWT callback.
    • Created SQL to backfill existing users with the owner membership for a seamless experience.
    • Addressed a race condition by modifying the JWT callback to check for a missing tenantId on every token refresh, not just the initial sign-in.
  • Crypto Enhancements: Improved src/server/services/crypto.ts's getEncryptionKey(). 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, showing createKeyMutation.error.message for a better UX.
  • Email & OAuth Configuration:
    • Set EMAIL_FROM to onboarding@resend.dev for Resend sandbox testing.
    • Configured GitHub OAuth with our GITHUB_CLIENT_ID and GITHUB_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.com for users who keep their email private.
    • Made the email field optional (String?) in prisma/schema.prisma's User model to accommodate GitHub users who might not expose their email even with the profile callback.
  • Next.js 14 Dynamic Routing Fix: Resolved a critical use(params) error in dynamic routes like discussions/[id]/page.tsx and workflows/[id]/page.tsx. Next.js 14 passes params as a plain object, not a Promise, so we switched from use(params) to direct destructuring const { 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.

  1. Next.js Config Evolution:

    • Challenge: Attempting to use next.config.ts with 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.mjs and 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.
  2. psql vs. Prisma Connection Strings:

    • Challenge: Using a Prisma-generated $DATABASE_URL directly with psql for RLS script execution.
    • The Problem: psql complained: invalid URI query parameter: "schema". It doesn't understand the ?schema=public query parameter that Prisma uses.
    • The Solution: Manually stripped ?schema=public from the connection string when calling psql.
    • Insight: Tools sometimes have subtle differences in how they interpret connection strings or environment variables. Don't assume universal compatibility.
  3. 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.
  4. ENCRYPTION_KEY Decoding Woes:

    • Challenge: Providing an ENCRYPTION_KEY as 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.
  5. GitHub OAuth and Missing Emails:

    • Challenge: Integrating GitHub OAuth with PrismaAdapter when a user has a private email.
    • The Problem: Argument 'email' is missing during user creation, as GitHub doesn't always return an email, even with a profile() 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 the email field optional (String?) in prisma/schema.prisma's User model.
    • 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.
  6. 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, params is passed directly as a plain object, not a Promise that needs to be use()d.
    • The Solution: Changed to direct destructuring: const { id } = params with 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!

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:

  1. Continue testing the discussion detail page: verify message loading and sending.
  2. Test workflow creation and the start/pause functionality.
  3. Test wardrobe CRUD operations and gap analysis.
  4. Test memory creation, search, and export features.
  5. Verify Server-Sent Events (SSE) streaming across the dashboard, discussions, and workflows.
  6. Implement a proper error page for /api/auth/error instead of a generic 500.
  7. Run npm run build to ensure all fixes translate to a successful production compilation.
  8. Address minor issues like the missing icon-192.png in /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!