Platform architecture
ZajLibrary is built to be forked into other libraries (verticals). A new library is not a rewrite — it is a fork-and-swap: copy the repo, change three things, deploy.
The three swap points
Section titled “The three swap points”| Swap | Where | What changes |
|---|---|---|
| Identity + features | app/src/tenant.config.mjs | Site name, tagline, domain, feature flags, visibility policy, MCP identity, admin allowlist. |
| Data | DATABASE_URL (env) | Point at a new database. Each library is its own deployment with its own DB — so there are no tenant-id columns and no per-row isolation to maintain. |
| Content | app/src/content/ | Replace the documents. |
Because each library is a separate deployment, “multi-tenancy” here means multi-instance:
the deployment boundary is the isolation boundary. New UI reads branding and flags from
tenant.config instead of hardcoding “ZajLibrary”, so a fork inherits the whole platform.
Feature flags
Section titled “Feature flags”tenant.config.features toggles feedback, chat, folderView, and sideSheet.
Half-built or library-specific features can ship turned off. The components check the
flag before rendering.
- Shelf view (default) — Learn / Build / Resources / Directory / System Docs.
- Folder view (
/browse) — Category › Subcategory › Topic, in reading order, with a type badge per document and a side-sheet “peek”. The header toggle remembers your choice (localStorage when anonymous, the database when signed in).
Visibility (public / private)
Section titled “Visibility (public / private)”- The default policy lives in
tenant.config.visibilityand can be overridden at runtime from the admin dashboard (thetenant_settingstable; a null override means “use the config default”). - Private content is a separate collection (
src/content/private/) rendered only through the gated, on-demand/private/[...slug]route. It is never built as static HTML (nothing to leak from the CDN), never enters the public search index, and a doc can also be flaggedvisibility: privatein the regular collection to drop it from anonymous MCP results. - Gating is fail-closed: any error resolving the session redirects to login.
Feedback
Section titled “Feedback”A Feedback control on every page files bugs / suggestions / missing-content / updates /
questions to one database review queue (triaged in the admin dashboard, promoted to
the Roadmap). Three input channels, one store: the UI form, the
MCP submit_feedback tool (for agents), and the raw API.
MCP (agent access)
Section titled “MCP (agent access)”The library is queryable over the Model Context Protocol at /api/mcp:
- Anonymous callers get public, read-only access (
search_library,get_document,list_contents) plussubmit_feedback. - Authenticated callers (
Authorization: Bearer <token>, or a logged-in browser session) can additionally read private content (read-privatescope). An admin token unlockslist_feedback. - Every response carries the build commit (
commitShaShort) so an agent can verify the index matches the live deploy in one call.
/admin (signed-in + allowlisted email) triages feedback, flips the visibility policy
and feature flags, and shows which integration keys are configured — status only,
never secret values. All secrets stay in environment variables.
Key files
Section titled “Key files”app/src/tenant.config.{mjs,ts}— the fork contract.app/src/lib/visibility.ts+app/src/middleware.ts— the gate.app/src/mcp/search.ts+app/src/pages/api/[transport].ts— the scoped MCP.app/src/lib/feedback.ts+app/src/lib/admin.ts— feedback + admin.