Skip to main content

Architecture

What this page covers

This page describes the full system, including parts that are not published. Lorediv is in beta and still being aligned.

PartPublished
packages/shared/ — formulations, schema, validators✅ Yes
apps/frontend/src/server/ — routers, ranking, community, tags✅ Yes
apps/frontend/src/lib/ — auth, permissions, rate limiting✅ Yes
apps/backend/ — Drizzle config and migrations✅ Yes
apps/docs/ — this documentation✅ Yes
apps/frontend/src/components/ — the interface❌ No
apps/frontend/src/app/ — the pages❌ No
apps/landing/ — the landing site❌ No

What is public is the rule layer: the formulations that decide rank, energy, votes, reputation, trending, community and tags, plus the contracts that feed them. Reading it tells you how the tree decides what to trust. It does not give you a runnable product.

Stack​

LayerTechnology
FrameworkNext.js 16 (App Router)
UIReact 19, CSS Modules
APItRPC v11
DatabasePostgreSQL via Drizzle ORM
CacheRedis (ioredis)
AuthAuth.js v5 (JWT)
ValidationZod v4
Monorepopnpm workspaces + Turborepo

Data flow​

flowchart TD
Browser["Browser"] -->|"tRPC query"| Next["Next.js server"]
Browser -->|"tRPC mutation"| Next
Next -->|"publicProcedure"| RateLimit["Rate limiter"]
Next -->|"Auth.js JWT"| Auth["Session check"]
Auth -->|"protectedProcedure"| RateLimit
RateLimit --> Router["tRPC Router"]
Router -->|"Drizzle ORM"| DB[(PostgreSQL)]
Router -->|"ioredis"| Redis[(Redis)]
Redis -->|"cache hit"| Router
DB -->|"query result"| Router
Router -->|"response"| Browser

Public reads (feed, topics, profiles, community, trending) use publicProcedure — no session required. Writes (create, vote, bookmark, report) use protectedProcedure — session and rate limits enforced.

Key directories​

packages/shared/src/ # PUBLIC
├── schema/ # Drizzle table definitions
├── types/ # Shared TypeScript types
├── validators/ # Zod schemas
└── lib/ # reputation-rank.ts, voting-power.ts, slugify

apps/frontend/src/
├── server/ # PUBLIC
│ ├── routers/ # tRPC routers (topics, votes, branches, tags)
│ └── lib/ # community.ts, capture-trending.ts, tag-counts.ts
├── lib/ # PUBLIC — auth, permissions, rate-limit
├── app/ # NOT PUBLISHED — Next.js App Router pages
├── components/ # NOT PUBLISHED — React components
└── hooks/ # NOT PUBLISHED — React hooks

apps/backend/ # PUBLIC — Drizzle config and migrations

Request lifecycle​

  1. Browser sends a tRPC query or mutation
  2. Next.js server checks the Auth.js JWT session
  3. Rate limiter checks the per-IP and per-user budgets
  4. tRPC router processes the request — reads from cache (Redis) or database (PostgreSQL)
  5. Response flows back through tRPC to the browser

Rendering model​

Every (app)/ route produces dual output:

  • Crawler-visible HTML (SeoDocument) with structured semantic markup, JSON-LD, and meta tags
  • Client-side SPA (AppFrame) hydrated via AppSeed props for interactive use

This means search engines and agents see full HTML, while users get an interactive single-page app.

Caching​

Redis caches: topics, feeds, tags, community, user profiles, hover cards, voting power, reputation, trending. Cache invalidation happens on every write operation. HTTP caching on document twins uses s-maxage=60, stale-while-revalidate=300.

Rate limiting​

Every write operation is rate-limited per IP and per user. Public reads are capped at 60 requests per minute per IP. Login attempts are capped at 5 per minute. The rate-limiting contract is the single source of truth for all access limits.