Session Learnings & Discoveries

Session Date: August 28, 2026 Duration: ~3 hours Objective: Standalone rootless Podman deployment; k3s cleanup; Caddy reverse proxy; ACME rate limit workaround


Key Learnings

2026-08-28 — Container isolation on fogserv.cloud

22. Rootless Podman Deployment (2026-08-28)

Problem: k3s cluster (Traefik ingress) was running but not needed; ACME rate-limited; container needed SQLite persistence; CI needed Dockerfile .env/Prisma fixes. Solution: Disabled k3s (systemctl disable/mask k3s; rm -rf /etc/rancher /var/lib/rancher; killed Traefik PID 2975402; killed nginx). Built standalone Dockerfile + docker-compose.yml. Deployed via podman run -d --name fogserv-cloud -p 127.0.0.1:8080:8080 --restart=always .... Caddy reverse_proxy 127.0.0.1:8080; tls internal as ACME rate-limit temporary workaround. Auto-admin role added (email.endsWith("@fogserv.cloud") ? "ADMIN" : "AUTHOR"). Mailgun env vars documented (MAILGUN_API_KEY, MAILGUN_DOMAIN, MAILGUN_FROM_EMAIL, MAILGUN_WEBHOOK_SIGNING_KEY). Files changed: Dockerfile, .dockerignore, docker-compose.yml, vite.config.ts, src/server/api.ts, .github/workflows/deploy-site.yml, README.md, CHANGELOG.md. Status: Working. Rate limit clears ~13:32 UTC for real HTTPS cert.


21. Docker Build Requires .env Fallback and Prisma Generate

Problem: CI build failed with two errors: (1) dotenvx run -- vite build errors [MISSING_ENV_FILE] missing file (.env), and (2) @prisma/client/default module not found. Solution: In Dockerfile build stage, before bun run build:

RUN ln -sf .env.example .env || true   # provides .env for dotenvx
RUN bunx prisma generate               # generates .prisma/client/default
RUN bun run build

Duration: Multi-turn agentic session
Objective: CRM auth backend + frontend, newsletter UI, Ghost import script, KB audit


Key Learnings

16. Auth Without Third-Party Libraries (native crypto.scrypt)

Problem: Adding bcrypt introduces a native binary dependency that complicates Bun/edge deployments.
Solution: Node.js crypto.scrypt (promisified) is sufficient for production password hashing:

import { scrypt, randomBytes, timingSafeEqual } from 'node:crypto'
import { promisify } from 'node:util'
const scryptAsync = promisify(scrypt)

async function hashPassword(plain: string): Promise<string> {
  const salt = randomBytes(16).toString('hex')
  const key = (await scryptAsync(plain, salt, 64)) as Buffer
  return `${salt}:${key.toString('hex')}`
}

Lesson: Always use timingSafeEqual for both HMAC (webhooks) and password verification. If you import it twice under different names, alias one: import { timingSafeEqual as tse } from 'node:crypto'.


17. React Context for Auth in Single-File Apps

Problem: useAuth() called inside Navigation but AuthProvider wraps the whole app — works fine when wired correctly, but easy to forget the provider wraps <RouterProvider>.
Solution Pattern:

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <AuthProvider>
      <RouterProvider router={router} />
    </AuthProvider>
  </React.StrictMode>,
)

Lesson: Auth context must wrap the router, not sit inside a route component, or useAuth() will throw/return defaults on the initial render for protected pages.


18. Duplicate Symbol Insertion Pitfall in Long Single-File Projects

Problem: When conversation context summarizes, the agent may not know which blocks already exist in a file. Inserting auth helpers that were already written creates duplicate const/function declarations → TypeScript compile errors.
Detection: get_errors immediately shows Duplicate identifier / Cannot redeclare block-scoped variable.
Fix: Search the file for the symbol name before inserting; remove the duplicate block rather than rewriting both.
Lesson: For projects with a large single-file pattern (main.tsx > 1000 lines), run grep_search for key symbol names before any insertion to confirm they don't already exist.


19. Vite Config as Full-Stack API Server (Middleware Pattern)

Pattern: All backend API routes live in vite.config.ts as a Vite dev/preview middleware. This works in dev and preview mode; in production you'd need a separate server or an adapter.
Benefit: Zero extra processes — one bun run dev starts both the frontend and the API.
Limitation: Not production-grade for high traffic; plan a migration to a real server (e.g., Hono, Elysia, or Bun's native HTTP) before launch.
Session pattern:

server: {
  middlewareMode: false, // runs as normal dev server
},
plugins: [react(), {
  name: 'api-middleware',
  configureServer(server) {
    server.middlewares.use(async (req, res, next) => {
      if (req.url?.startsWith('/api/')) { /* handle */ return }
      next()
    })
  }
}]

20. Ghost JSON Export → Prisma Seed Script (Bun)

Key decisions:


11. Proactive WIP Guardrails

Problem: User wants to prevent accidental deployment of a work-in-progress site.
Discovery: Adding a persistent, aesthetic banner to the RootLayout ensures the development status is clear on every single page.
Solution:

Lesson: Always implement visual state indicators when working on live-adjacent environments to prevent user confusion or premature deployment.


Session Date: January 30, 2026
Duration: ~3 hours
Objective: Setup React/TanStack/Prisma stack for fogserv.cloud rebuild


Key Learnings

1. React 19 + TanStack Router Hydration

Problem: Cannot render <html> elements inside React root div
Discovery: React expects to mount inside an existing HTML structure, not replace it
Solution:

Lesson: When using SSR/SSG frameworks, understand where the boundary is between static HTML and React hydration.


2. Tailwind CSS v4 Architecture Change

Problem: tailwindcss package cannot be used directly as PostCSS plugin
Discovery: Tailwind v4 separated the PostCSS plugin into @tailwindcss/postcss
Solution:

bun add -d @tailwindcss/postcss

Update postcss.config.js:

export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
}

Update CSS import:

@import "tailwindcss";  /* Not @tailwind directives */

Lesson: Major version upgrades may restructure packages. Always check migration guides.


3. ES Modules in Node.js Ecosystem

Problem: PostCSS config threw "module is not defined" error
Discovery: package.json has "type": "module", making all .js files ES modules by default
Solution: Use export default instead of module.exports in config files

Lesson: When "type": "module" is set, all config files need ES module syntax or .cjs extension.


4. dotenvx Integration Pattern

Discovery: dotenvx provides a cleaner approach than traditional dotenv libraries
Benefits:

Implementation Pattern:

{
  "scripts": {
    "dev": "dotenvx run -- vite",
    "db:push": "dotenvx run -- prisma db push"
  }
}

Lesson: Environment injection at the script level is more maintainable than in-code loading.


5. Prisma Accelerate Connection Architecture

Discovery: Prisma offers two connection methods:

  1. PRISMA_ORM (Accelerate): Connection pooling + edge caching
  2. PRISMA_ANY (Direct): Standard PostgreSQL connection

Best Practice:

Lesson: Separate read/write optimizations from admin operations for better performance.


6. Current fogserv.cloud Content Strategy

Discovery: Production site is Ghost-based blog with rich philosophical content
Content Themes:

Strategic Insight: The mission "helping people help themselves" is already manifest in content—rebuild should preserve this.

Lesson: Don't reinvent what already works. Migrate valuable content; enhance infrastructure.


7. TanStack Router Simplification

Problem: Initially tried to use file-based routing with separate route files
Discovery: For MVP, inline route definitions are simpler and more maintainable
Solution:

const rootRoute = createRootRoute({ component: RootLayout })
const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: HomePage,
})
const routeTree = rootRoute.addChildren([indexRoute, aboutRoute, ...])

Lesson: Start simple. File-based routing can be added later with @tanstack/router-plugin.


8. Knowledge Base as Living Memory

Insight: The /kb/ directory isn't just documentation—it's the system's memory
Pattern Observed:

Implementation:

Lesson: Documentation-first development prevents context loss and enables autonomous agents.


9. GitOps + Ticketing + Documentation Trinity

Discovery: The three pillars aren't independent—they form a closed loop:

Issue Created → Code Written → Commit Referenced → Docs Updated → Issue Closed
     ↑                                                                    ↓
     └─────────────── KB Entry Created With Learnings ←─────────────────┘

Lesson: Every change must flow through all three systems to maintain auditability and memory.


10. Bun as Drop-in Node Replacement

Experience: Bun worked flawlessly as package manager and runtime
Benefits:

No Issues Encountered: Everything "just worked"

Lesson: Bun is production-ready for modern JavaScript stacks.


Anti-Patterns Avoided

❌ Hardcoding Secrets

Could have put database URLs directly in code. Instead, used dotenvx from day one.

❌ Skipping Documentation

Could have rushed to build features. Instead, documented architecture and decisions immediately.

❌ Monolithic Components

Could have put everything in one file. Instead, structured with separate pages and components.

❌ Ignoring TypeScript

Could have used plain JavaScript. Instead, set up TypeScript from the start for type safety.

❌ Manual Environment Management

Could have used process.env directly. Instead, centralized via dotenvx for consistency.


Tools That Exceeded Expectations

  1. TanStack Router - More flexible than expected, great DX
  2. Prisma Accelerate - Connection pooling + edge caching built-in
  3. Tailwind CSS v4 - Faster, better DX with new @import syntax
  4. dotenvx - Superior to traditional dotenv libraries
  5. Bun - Zero friction switching from Node/npm

Areas for Future Exploration

  1. TanStack Query Integration - For server state management
  2. TanStack Router Plugin - For file-based routing at scale
  3. Prisma Pulse - For real-time database subscriptions
  4. Vite SSR/SSG - For better SEO and performance
  5. Forgejo Actions - For CI/CD automation

Mistakes Made (and Fixed)

Mistake #1: HTML in React Component

Rendered <html> inside React root component, causing hydration errors.
Fix: Moved to index.html where it belongs.

Mistake #2: Wrong Tailwind Plugin

Used old tailwindcss directly instead of @tailwindcss/postcss.
Fix: Installed correct plugin and updated config.

Mistake #3: CommonJS in ES Module Project

Used module.exports in postcss.config.js.
Fix: Changed to export default.


Session Metrics


Quotes to Remember

"The system treats infrastructure not as a static resource, but as a living entity that learns and evolves alongside its operator."

"Documentation is Memory. Without it, each agent starts from scratch."

"GitOps + Ticketing + Documentation = Auditability + Reversibility + Institutional Memory"


Next Session Preparation

What Worked:

What to Improve:

Carry Forward:


Last Updated: January 30, 2026 03:15 AM
Related Files:

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse