Architecture
This page describes the full system, including parts that are not published. Lorediv is in beta and still being aligned.
| Part | Published |
|---|---|
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
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, CSS Modules |
| API | tRPC v11 |
| Database | PostgreSQL via Drizzle ORM |
| Cache | Redis (ioredis) |
| Auth | Auth.js v5 (JWT) |
| Validation | Zod v4 |
| Monorepo | pnpm 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
- Browser sends a tRPC query or mutation
- Next.js server checks the Auth.js JWT session
- Rate limiter checks the per-IP and per-user budgets
- tRPC router processes the request — reads from cache (Redis) or database (PostgreSQL)
- 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 viaAppSeedprops 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.