Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ docs/drizzle-libsql-reference.md
config/
.worktrees/
*.bak
test-results/
1 change: 1 addition & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# In dev mode, config.json is proxied to Vite along with all other frontend requests

(dev-frontend) {
header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' ws://vite:5173 ws://localhost:5173; font-src 'self'; frame-ancestors 'none'"
reverse_proxy vite:5173
}

Expand Down
3 changes: 2 additions & 1 deletion Dockerfile.runtime
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ RUN if [ -f runtime/src/mastra/index.ts ]; then \
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/output/ ./
COPY runtime/init-db.mjs ./init-db.mjs
EXPOSE 4111
ENV PORT=4111 NODE_ENV=production
USER node
CMD ["node", "index.mjs"]
CMD ["sh", "-c", "node init-db.mjs && node index.mjs"]
6 changes: 3 additions & 3 deletions PROJECT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
|----|------|-------|---------|-------------|
| SPEC-20260407-001 | infra-docker-k8s-init | CODE | 2026-04-07 | Infrastructure: Docker Compose (9 containers), K8s scaffolding, documentation templates |
| SPEC-20260408-001 | docker-compose-architecture-alignment | CODE | 2026-04-08 | Docker Compose update: two networks, dev/prod mode, Caddyfile env switching, config.json |
| SPEC-20260409-001 | better-auth-drizzle-libsql-auth | QA | 2026-04-09 | Better Auth + Drizzle/LibSQL: schema, Better Auth instance, drizzle-kit push, Zod schemas, route mounting in Mastra |
| SPEC-20260409-001 | db-schema-drizzle-integration | SPEC | 2026-04-09 | Drizzle ORM + Better Auth + wiki vectors + local tickets DB layer |
| SPEC-20260408-002 | linear-resend-integration-tools | CODE | 2026-04-08 | Linear ticketing tools, Resend email tools, shared schemas, and runtime integration tests |
| SPEC-20260408-003 | langfuse-observability-integration | CODE | 2026-04-08 | OpenRouter Broadcast + Langfuse SDK fallback, Cloudflare tunnel, trace verification |
| SPEC-20260409-001 | db-schema-drizzle-integration | SPEC | 2026-04-09 | Drizzle ORM + Better Auth + wiki vectors + local tickets DB layer |
| SPEC-20260409-002 | fe-auth-pages-login-register | COMPLETE | 2026-04-09 | Auth pages (login/register), Better Auth client SDK, auth guard, logout, neumorphic UX — 33 E2E tests passing |

## Integration Test Status

Expand All @@ -31,4 +31,4 @@ _No completed specs._

## Archived Specs

_No archived specs._
_No archived specs._
16 changes: 12 additions & 4 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,33 @@ services:
vite:
image: node:22-alpine
working_dir: /app
command: npx vite --host 0.0.0.0
command: sh -c "npm install && npx vite --host 0.0.0.0"
ports:
- "127.0.0.1:5173:5173"
volumes:
- ./frontend:/app
- /app/node_modules
- vite_node_modules:/app/node_modules
networks:
- app

frontend:
environment:
FRONTEND_MODE: dev
FRONTEND_MODE: static

runtime:
build:
context: .
dockerfile: Dockerfile.runtime
target: builder
working_dir: /app/runtime
command: npx mastra dev --port 4111
command: sh -c "sed -i 's|LIBSQL_URL=.*|LIBSQL_URL=http://libsql:8080|' /app/runtime/.env && node /app/runtime/init-db.mjs && npx mastra dev"
environment:
- PORT=4111
- LIBSQL_URL=http://libsql:8080
- NODE_ENV=development
volumes:
- ./runtime/src:/app/runtime/src
- ./runtime/init-db.mjs:/app/runtime/init-db.mjs

volumes:
vite_node_modules:
116 changes: 116 additions & 0 deletions docs/FE-03-auth-pages-handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# FE-03 — Auth Pages (Login/Register) — Implementation Handoff

**Branch:** `feature/fe-auth-pages`
**Spec:** SPEC-20260409-002
**Ticket:** FE-03 — Auth Pages (Login/Register)
**Epic:** Frontend | **Tier:** 1 (Must Ship) | **Estimate:** 2h
**Date:** 2026-04-09

---

## Ticket Acceptance Criteria — Status

| # | Criterion | Status | Evidence |
|---|-----------|--------|----------|
| 1 | User can register with email/password | ✅ Done | `/register` page → `signUp.email()` → auto-login → redirect |
| 2 | User can login and be redirected to chat | ✅ Done | `/login` page → `signIn.email()` → redirect to `/chat` |
| 3 | Unauthenticated access redirects to login | ✅ Done | Auth guard in `__root.tsx` — all routes protected |

## Ticket Requirements — Status

| Requirement | Status | Details |
|------------|--------|---------|
| Email/password form | ✅ Done | Both login and register pages with validated forms |
| Better Auth client SDK integration | ✅ Done | `auth-client.ts` with `createAuthClient`, basePath `/auth` |
| Redirect to chat after login | ✅ Done | `navigate({ to: "/chat" })` on success |
| Protected routes | ✅ Done | Root layout auth guard with `<Navigate to="/login" />` |
| Clean, minimal design with shadcn/ui | ✅ Done | Neumorphic cards, navy bg, orange CTA, Space Grotesk headings |
| Dependency: INFRA-02 (Better Auth backend) | ✅ Met | Backend auth at `/auth/*` operational with LibSQL persistence |

## What Was Built

### Frontend (6 files)

| File | What It Does |
|------|-------------|
| `frontend/src/lib/auth-client.ts` | Better Auth client SDK singleton. baseURL from `window.location.origin`, basePath `/auth`. Exports `signIn`, `signUp`, `signOut`. |
| `frontend/src/hooks/use-auth.ts` | `useAuth()` hook wrapping Better Auth's `useSession()`. Returns `{ user, isLoading, isAuthenticated }`. Re-exports `signIn`, `signUp`, `signOut`. |
| `frontend/src/routes/login.tsx` | Login page at `/login`. Email+password form, error handling (API errors, network errors, fallback messages), loading state, redirect if authenticated. |
| `frontend/src/routes/register.tsx` | Register page at `/register`. Name+email+password form, duplicate email detection, password error passthrough, loading state, redirect if authenticated. |
| `frontend/src/routes/__root.tsx` | Root layout with auth guard. Loading spinner → unauthenticated redirect → auth page bypass → authenticated layout with sidebar (nav links, user info, theme toggle, logout button). |
| `frontend/vite.config.ts` | Dev proxy: `/auth` → backend:4111, `/chat` POST-only → backend:4111 (GET serves SPA). |

### Backend (1 file modified)

| File | Change |
|------|--------|
| `runtime/src/auth/index.ts` | Added `localhost:3002` to dev trusted origins for SSH port-forwarded testing. |

### Infrastructure (1 file modified)

| File | Change |
|------|--------|
| `docker-compose.override.yml` | Fixed `mastra dev --port 4111` → `PORT=4111` env var (mastra CLI dropped `--port` flag). |

### Tests (E2E with Playwright)

| File | Tests | What It Covers |
|------|-------|----------------|
| `tests/fe-auth-pages/auth-e2e.e2e.ts` | 33 | Full E2E against running stack: health checks, auth guard redirects, login/register rendering, real sign-up/sign-in flows, error handling, sidebar verification, authenticated redirects, proxy integration, page navigation |
| `playwright.config.ts` | — | Playwright config: chromium headless, baseURL localhost:3001 |

**All 33 E2E tests pass against the real stack (Vite + Mastra + Better Auth + LibSQL). Zero mocks.**

## Auth Flow

```
Unauthenticated user → any route → auth guard → redirect /login
↓
/login ← fill form → signIn.email()
↓
success → redirect /chat → sidebar with user info
error → show alert (invalid creds / network error)

New user → /register → fill form → signUp.email()
↓
success → auto-login → redirect /chat
error → "already exists" / password error / network error

Authenticated user → /login or /register → redirect to /chat
Authenticated user → click logout → signOut() → redirect /login
```

## How to Run

```bash
# Start the stack
cd runtime && PORT=4111 npx mastra dev &
cd frontend && npx vite --port 3001 --host &

# Run E2E tests
npm run test:e2e:auth

# Or run all E2E tests
npm run test:e2e
```

Requires LibSQL running on `:8080` (via docker-compose or direct).

## Commits on This Branch

| Hash | Message |
|------|---------|
| `5218fc1` | feat(auth): auth client SDK, useAuth hook, vite proxy |
| `2ba6bee` | feat(auth): register page + auth guard activation |
| `f9facf0` | feat(auth): login page with neumorphic UX |
| `0bc2e3a` | test(auth): auth guard tests |
| `8d58853` | qa(auth): QA report GO — 53/53 tests, 6/6 REQs |
| `eda6eca` | test(auth): rewrite tests as E2E with Playwright — 33/33 |
| `621dc7c` | fix(auth): logout button, /chat proxy bypass, dev trusted origin |

## Known Limitations

- No password confirmation field on register (acceptable for hackathon)
- No forgot password flow (out of scope per spec)
- No OAuth/social login (out of scope per spec)
- Spec says `max-w-sm` for cards, implementation uses `max-w-md` (cosmetic)
110 changes: 110 additions & 0 deletions docs/FE-03-docker-infra-fixes-handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# FE-03 — Docker Infrastructure Fixes — Handoff

**Branch:** `feature/fe-auth-pages`
**Date:** 2026-04-09
**Context:** After merging dev (`5ac0512`), the Docker environment was broken — runtime crashed on startup, frontend served stale builds, auth tables didn't exist. These fixes make `docker compose up --build` work out of the box.

---

## Summary of Issues & Fixes

### 1. Runtime crash: LibSQL ECONNREFUSED (localhost:8080)

**Root cause:** `mastra dev` uses dotenv to load `runtime/.env`, which contains `LIBSQL_URL=http://localhost:8080` (for local dev without Docker). This overrode the Docker-injected env var `http://libsql:8080`.

**Fix (docker-compose.override.yml):** `sed` rewrites `LIBSQL_URL` in the baked-in `.env` before `mastra dev` starts. Also sets `LIBSQL_URL=http://libsql:8080` explicitly in the environment block.

### 2. Database tables missing: "no such table: auth_user"

**Root cause:** Fresh LibSQL container has no tables. `drizzle-kit push` requires interactive TTY (unsuitable for Docker).

**Fix:** Created `runtime/init-db.mjs` — runs `CREATE TABLE IF NOT EXISTS` for all 7 tables (auth_user, auth_session, auth_account, auth_verification, wiki_documents, wiki_chunks, local_tickets). Executes before the server starts in both dev and production Docker.

### 3. Vite container crash: ERR_MODULE_NOT_FOUND

**Root cause:** Anonymous volume `/app/node_modules` was empty with no `npm install` step.

**Fix (docker-compose.override.yml):** Changed Vite command to `sh -c "npm install && npx vite --host 0.0.0.0"` and switched to a named volume `vite_node_modules`.

### 4. CSP blocks Vite inline scripts in dev mode

**Root cause:** Caddy's `Content-Security-Policy` header had `script-src 'self'`, blocking Vite's HMR preamble injection (`@vitejs/plugin-react can't detect preamble`).

**Fix (Caddyfile):** Dev-frontend snippet overrides CSP to allow `'unsafe-inline'` and `'unsafe-eval'` for scripts, plus `ws://` for Vite HMR WebSocket.

### 5. Duplicate import in mastra/index.ts

**Root cause:** Merge conflict left duplicate `import { registerApiRoute }` on lines 2 and 5, causing `mastra build` to fail.

**Fix:** Removed the duplicate import line.

### 6. Auth 403 "invalid origin" via SSH tunnel

**Root cause:** Better Auth `trustedOrigins` only included `localhost:3001`. SSH port-forward testing uses `localhost:3002`. Also, `NODE_ENV=production` was set by Dockerfile even in dev override (via `target: builder` inheriting base image state).

**Fix:** Added `http://localhost:3002` to dev trustedOrigins in `runtime/src/lib/auth.ts`. Added `NODE_ENV=development` to docker-compose.override.yml environment.

---

## Files Changed

| File | Change |
|------|--------|
| `runtime/init-db.mjs` | **New.** Creates all 7 DB tables idempotently on startup. |
| `Dockerfile.runtime` | Copies `init-db.mjs` into production image. CMD runs `node init-db.mjs && node index.mjs`. |
| `docker-compose.override.yml` | Vite: `npm install` + named volume. Runtime: `sed` fix for .env, `LIBSQL_URL` + `NODE_ENV=development` env vars, init-db mount. Frontend: `FRONTEND_MODE: static`. |
| `Caddyfile` | Dev-frontend snippet: relaxed CSP for Vite HMR (unsafe-inline, unsafe-eval, ws://). |
| `runtime/src/lib/auth.ts` | Added `localhost:3002` to dev trustedOrigins for SSH tunnel testing. |
| `runtime/src/mastra/index.ts` | Removed duplicate `registerApiRoute` import (merge artifact). |

## Architecture Notes

### Dev mode (docker-compose.override.yml auto-loaded)

```
Browser → Caddy:3001 (static files from /srv/) → runtime:4111 (/api/*, /auth/*)
↕
LibSQL:8080

Runtime runs: sed .env → init-db.mjs → mastra dev (hot reload)
```

### Production mode (`docker compose -f docker-compose.yml up --build`)

```
Browser → Caddy:3001 (static files from /srv/) → runtime:4111 (/api/*, /auth/*)
↕
LibSQL:8080

Runtime runs: node init-db.mjs && node index.mjs
```

### SSH tunnel testing

```
Local browser:3002 → SSH tunnel → Server:3001 → Caddy → runtime:4111
```

Requires `http://localhost:3002` in Better Auth trustedOrigins (dev only).

## How to Run

```bash
# Dev mode (default — auto-loads override)
docker compose up --build

# Production mode (no override, no Vite, no hot reload)
docker compose -f docker-compose.yml up --build

# SSH tunnel from local machine
ssh -L 3002:localhost:3001 agent@<server-ip>
# Then open http://localhost:3002
```

## Verified Auth Flow (E2E in Docker)

1. Navigate to `/` → auth guard redirects to `/login`
2. Click "Register" → fill form → Create Account → redirects to `/chat` with user info in sidebar
3. Click "Sign out" → redirects to `/login`
4. Fill login form → Log In → redirects to `/chat`
5. All tested via Playwright (server-side) and manual SSH tunnel (client-side)
Loading
Loading