Skip to content

Posts Module

Location: apps/api/src/modules/posts/

Aggregate roots: Post, PostReply, PostReaction, PostBookmark, PostInterest, PostCoAuthor, PostSeries, PostBoost


Post Formats

FormatDescription
noteShort-form text (Twitter-style), default
essayLong-form article with title
photoImage with optional caption
videoVideo with optional caption
quoteQuote from another source with commentary
threadMulti-part connected note
repostBare share of another post (empty body)

Quotes and reposts reference another post via Post.quotedPostId.


Content Depth

Authors self-rate their post's technical depth on a 0–5 scale:

LevelLabelMeaning
0SurfaceCasual, conversational
1IntroIntroductory coverage
2DeepSubstantive exploration
3ExpertExpert-level analysis
4ResearchResearch-grade content
5ThesisOriginal thesis / academic

Readers can filter feed by minimum depth.


Visibility

ValueWho Can See
publicEveryone (including unauthenticated in future)
circlesUsers who are in at least one of the author's circles
innerUsers in the author's inner circle specifically

Multi-Interest Tagging

Posts have one primary interest (Post.interestId) plus up to 4 additional interests via PostInterest junction table.

  • PostInterest.isPrimary = true mirrors the primary tag
  • Feed and trending queries use Post.interestId for performance
  • Cross-interest discovery uses PostInterest

#Journalism Tag

Posts can be tagged as journalism (original reporting, factual news, investigative work).

Gate: Author must have reputation ≥ 20 in any single interest (canPostJournalism capability from AccessMatrixService). Admin override available.

Effects:

  • Post.isJournalism = true
  • Journalism badge shown on post card in mobile and admin
  • Filterable via GET /feed?journalism=true

Co-Authorship

A post's primary author can invite collaborators. The PostCoAuthor table manages the invitation lifecycle.

Flow

mermaid
sequenceDiagram
    Author->>API: POST /posts/:id/co-authors {userId, splitShare}
    API-->>Collaborator: notification (co-author.invited)
    Collaborator->>API: POST /posts/:id/co-authors/:inviteId/respond {accept: true}
    Note over API: Validate total splitShare ≤ 90
    API-->>Author: notification (co-author.accepted)

Split Rules

  • splitShare is 0–100 (percentage)
  • Server enforces: sum of all accepted splitShare values ≤ 90
  • Primary author always receives at least 10 % of each EmojiPay payment
  • Only accepted rows participate in mana splits

Replies

Each reply has a kind that enables richer threading:

KindPurpose
replyPlain reply (default)
reactionShort emotional response
criticismCounter-argument (surfaced separately in UI)
contributionSubstantive addition (accrues more reputation for replier)

Series

Authors can group posts into named series (e.g. "Introduction to Stoicism — 5 parts").

  • PostSeries: title, description, authored by one user
  • PostSeriesItem: join table with explicit position (1-based)
  • SeriesFollow: users subscribe to series and get notified on new parts
  • Max one position per series (unique constraint (series_id, position))

Scheduled Publish

Set publishAt to a future timestamp. The scheduled-publish worker checks every minute and:

  1. Emits the post to the feed
  2. Sets scheduledEmittedAt to prevent double-emission on worker restart

Counters

Materialised counters on Post are updated synchronously on each action:

CounterIncremented By
reactionsPostReaction with kind = 'heart'
repliesPostReply create
sharesformat = 'repost' create
bookmarksPostBookmark create
viewsFire-and-forget view increment (non-author opens)

Moderation

  • Post.hiddenAt (non-null) = moderator hide. Post is invisible in feed but kept for audit.
  • Post.deletedAt (non-null) = author soft-delete. Also hidden from feed.
  • Both are independent — a moderated-hidden post can still be author-deleted.

API (key endpoints)

MethodPathDescription
GET/feedHome feed
GET/feed?tab=followingFollowing tab
GET/feed?tab=trendingTrending tab
GET/feed?tab=circlesCircles-only tab
GET/feed?journalism=trueJournalism filter
POST/postsCreate post
GET/posts/:idPost detail with replies + emojiBreakdown
PATCH/posts/:idEdit (author only)
DELETE/posts/:idSoft-delete
POST/posts/:id/repliesAdd reply
GET/posts/:id/repliesPaginated replies
POST/posts/:id/reactReact
POST/posts/:id/bookmarkBookmark
POST/posts/:id/repostRepost or quote
POST/posts/:id/pinPin to profile (max 3)
POST/posts/:id/boostBoost post
GET/posts/by/:usernameUser's profile posts
GET/posts/seriesList my series
POST/posts/seriesCreate series
POST/posts/series/:id/itemsAdd post to series
GET/posts/bookmarksMy bookmarked posts

Regulus — invite-only social-knowledge platform