Skip to content

Canonical data model

web-tanstack/prisma/schema.prisma is the canonical schema. Objects Prisma cannot express live in web-tanstack/prisma/manual-indexes.sql.

flowchart LR
  Source["Source"] -->|"nullable provenance"| Resource["Resource"]
  Resource --> Translation["ResourceTranslation"]
  User["User"] --> Library["LibraryItem"] --> Resource
  Workspace["Workspace"] -->|"pins"| Link["ResourceLink"] --> Resource
  Workspace -->|"pins"| Link --> Collection["Collection"]
  Collection -->|"pins"| Link
  Workspace -->|"owns through workspace_id"| Document["UserDocument"]
  User -->|"pins shared document"| Link --> Document
Prisma model Database table Responsibility
Resource resources canonical URL/blob identity, type, scope, lifecycle, dates, source provenance, platform payload
ResourceTranslation resource_translations localized title, summary, content, keywords
Source sources monitored RSS, Twitter, or YouTube configuration and monitoring policy
ResourceEntity resource_entities normalized resource-to-entity edge

Resource.type is the only content-kind discriminator. Resource.sourceId is a nullable foreign key to the monitored source that produced the resource; it is not an array of resources on Source. Deleting a source sets provenance to null and does not delete content.

platform_metadata stores provider payloads and display snapshots. Its sourceName value is not relational identity, and it must not contain another content-kind field.

Use two date meanings:

  • published_at: nullable source publication time;
  • effective_at: published_date ?? scraped_date ?? created_at, used for discovery order and recency filters.

library is the per-user edge to a canonical resource. It owns acquisition origin, visibility, saved time, note, and state. A resource may be public corpus content, viewer-private content, or both public and saved by a user without duplicating the resource row.

Public corpus documents are ranked in Cloudflare AI Search. Viewer-private library documents stay in PostgreSQL and are merged with public ranks in the Web Worker. Private content never enters the public index.

resource_links is a user-owned pin graph with these valid pairs:

  • workspace → resource;
  • collection → resource;
  • workspace → collection;
  • user → document.

Workspace-owned documents use user_documents.workspace_id, not a resource link. Pinning a collection to a workspace does not copy every collection member into workspace links; reads expand the collection one hop.

AI and document citations are inline [n](URL) markers in generated prose. They are not resource_links rows and do not have a citation CRUD API.

  • Web owns product authorization, library/pin writes, feeds, collections, resource HTTP/MCP DTOs, private search, and PostgreSQL hydration.
  • Core owns monitored acquisition, enrichment, translations, entities, and the public AI Search corpus.
  • Chat owns chat state and tools. It calls Web/Core through service bindings and does not query product resource tables directly.

See resource-access-inventory.md for the live read/write touchpoint inventory and worker-boundaries.md for cross-Worker rules.