Core systems (frontend & backend)
PURPOSE
This documents the implemented scaffolding aligned with Practical-core-systems.md: centralized logging, config feature flags, a standard API error shape, and frontend data/prefetch helpers. It maps to observability (§1), feature flags (§8), error system (§15), and preparation for data sync / prefetch (§17, §19).
Frontend (frontend/lib/)
Logging — lib/logger.ts
- API:
logger.debug,logger.info,logger.warn,logger.error(message, context?) - Behavior:
debug/infoare no-ops in production;warn/erroralways run. Optionalcontextis JSON-serialized. - Intent: Replace ad-hoc
console.*so Sentry or another sink can be wired in one place later.
Feature flags — lib/flags.ts
- API:
getFlag(name),getFlags() - Flags:
newUI,aiStreaming,debugMode(typed asFeatureFlagName). - Env (build-time):
| Flag | Environment variable | Default |
|---|---|---|
newUI | NEXT_PUBLIC_FF_NEW_UI | false |
aiStreaming | NEXT_PUBLIC_FF_AI_STREAMING | false |
debugMode | NEXT_PUBLIC_FF_DEBUG_MODE | false |
- Truthy values:
1,true,yes,on(case-insensitive). Falsy:0,false,no,off, empty/unset. - Note: Dev-only toggles in the dev overlay are separate from these product flags.
Error system — lib/errors/
AppErrorBody:{ code: string; message: string; status: number; details?: unknown }— matches Nest error responses after the global filter.AppError: throwable client error with the same fields.isAppErrorBody(value): type guard for JSON.parseApiError(res)/parseApiErrorBody(res): parse a failedfetchResponse.throwIfNotOk(res): throwsAppErrorwhen!res.ok.
Example:
import { throwIfNotOk } from "@/lib/errors"
const res = await fetch(url, options)
await throwIfNotOk(res)
Data layer prep — lib/data/
query-keys.ts— stablequeryKeystree (auth,users,courses,institutions,documents) for future TanStack Query / SWR.routes.ts—appRoutescanonical paths (home, login, onboarding, dashboard, test).use-app-prefetch.ts— client hookuseAppPrefetch()→prefetchHref(href)using Next.jsrouter.prefetch.
Example:
import { appRoutes, useAppPrefetch } from "@/lib/data"
const { prefetchHref } = useAppPrefetch()
// e.g. onMouseEnter={() => prefetchHref(appRoutes.dashboard)}
Backend (backend/src/common/)
Logging — common/logger/app-logger.ts
- API:
appLogger.debug,appLogger.log,appLogger.warn,appLogger.error(message, trace?, context?) - Wraps Nest
Loggerwith optional JSONcontexton messages.
Error envelope — common/filters/app-exception.filter.ts
- Registered globally in
app.module.tsviaAPP_FILTERprovider. - All thrown errors are normalized to JSON:
{
"code": "UNAUTHORIZED",
"message": "Human-readable message",
"status": 401,
"requestId": "uuid-v4-trace-id",
"details": {}
}
requestId: A unique correlation ID attached to every request, used for cross-log tracing.detailsis omitted when undefined.
Request Tracing & Logging — common/interceptors/logging.interceptor.ts
- Global Interceptor: Captures every request/response cycle.
- X-Request-ID: Generates or propagates a correlation ID for tracing.
- Latency Tracking: Logs execution time for every endpoint.
- Sanitization: Automatically masks sensitive fields (
password,token,secret,key) in logs.
Security & Protection (backend/src/common/)
Tiered Rate Limiting
Configured via ThrottlerModule with three distinct tiers:
auth: 5 requests / 5 minutes (Strict protection for login/signup).ai: 20 requests / 1 hour (Cost control for LLM usage).default: 100 requests / 1 minute (General API protection).
Guards:
AuthThrottlerGuard: Tracks by IP or User ID for authentication routes.AiThrottlerGuard: Specific limits for AI inference to manage provider costs.
Abuse Prevention — common/services/abuse-prevention.service.ts
- Identity Blocking: Persistence-based blocking using the
blocked_identitiestable. - Automated "Smart Ban": Automatically blocks IPs/Users for 24 hours if they trigger 5+ suspicious events (e.g., rate limit hits, failed logins) within 10 minutes.
- Security Audit Log: Every suspicious event is logged to
security_eventsfor manual review. - Global Shield:
GlobalAbuseGuardenforces blocks across all routes before any logic executes.
AI Cost Controls — backend/src/ai/ai.service.ts
- Real-time Credit Guarding: Every AI request checks the user's
subscription_usages. - Atomic Enforcement: Prevents credit overruns via atomic database increments.
- Usage Cycles: Automatically tracks usage within monthly windows aligned with subscriptions.
Authentication (backend/src/auth/)
Secure cookie session flow
- Login/signup issue two HTTP-only cookies:
access_token(short-lived, default 15 minutes)refresh_token(long-lived, default 30 days)
- Refresh tokens are stored only as hashes in
auth_sessions. - Every refresh rotates the refresh token hash (replay-resistant rotation).
- Logout clears cookies and removes active session rows.
- Frontend auth UX is page-based (
/login,/signup) with redirect continuation vianext. - Unauthenticated protected access redirects to login; there is no in-page auth modal fallback.
Guard behavior
CookieAuthGuardvalidates access token + session row (auth_sessions) for protected routes.- If access token is expired and refresh token is valid, the guard auto-refreshes and reissues cookies.
- If session is missing/expired/revoked, request is rejected as unauthorized.
Structured Notes (backend/src/notes/)
FEAT-59 module behavior
- Module:
NotesModuleregistered inAppModule. - Guarding: all endpoints use
CookieAuthGuard; ownership derives fromrequest.user.user_id. - Endpoints:
GET /notes,POST /notesGET /notes/:id,PATCH /notes/:id,DELETE /notes/:idPOST /notes/:id/referencesPATCH /notes/:id/references/:referenceIdDELETE /notes/:id/references/:referenceIdPATCH /notes/:id/canvas
Related repo doc
Practical-core-systems.md(repo root) — full checklist; this page only describes what is implemented today.
Not implemented here (see Practical-core-systems.md)
- Sentry & Metrics (OpenTelemetry/Prometheus)
- Job Queues idempotency (Queues are implemented but idempotency logic is pending)
- Per-user / remote feature flags
- Zod (or similar) on every route
- TanStack Query / SWR (queryKeys and prefetch hook are ready for when you add them)
QA Coverage Expectations
| Test ID | Scenario | Expected Result |
|---|---|---|
| SEC-01 | Correlation ID Injection | All response headers contain X-Request-ID and logs show the same UUID. |
| SEC-02 | Tiered Rate Limiting | Auth routes block after 5 attempts; AI routes block after 20. |
| SEC-03 | Automated Smart Ban | 5 rate-limit hits in 10m results in a 24h entry in blocked_identities. |
| SEC-04 | Global Abuse Shield | Blocked IP/User receives 403 Forbidden on any endpoint. |
| SEC-05 | AI Cost Enforcement | User with 0 credits receives 402 Payment Required on AI endpoints. |
| SEC-06 | Data Sanitization | Logs for /auth/login do not contain plaintext passwords. |