Skip to content

Social Module

Location: apps/api/src/modules/social/

Aggregate roots: Circle, CircleMember, CircleInvitation, UserFollow, ConnectionIntent

The Social context handles the personal network layer: circles for private audiences, bidirectional consent-based membership, and follow graph.


Prisma Models

ModelTableNotes
CirclecirclesOwner-managed group with kind + name
CircleMembercircle_membersAccepted membership with bondLevel
CircleInvitationcircle_invitationsPending invite before membership
UserFollowuser_followsUnidirectional follow graph
UserMuteuser_mutesSilence another user
UserBlockuser_blocksBilateral silence
ConnectionIntentconnection_intentsDiscovery intent (1 per user)

Circle Fields

FieldTypeNotes
idUUID
ownerIdUUID
kindStringgeneral | family | work | inner | custom
nameString
accentString?Color token

Constraints: Max 10 members per circle enforced at service level. One invite per (circle, invitee, status).


Key Methods

MethodDescription
createCircle(userId, dto)Create a circle (max 3 custom per user in Alpha)
addMember(circleId, memberId, ownerId)Directly add accepted member
sendInvitation(circleId, inviterId, inviteeId, bondLevel)Two-step consent flow
respondToInvitation(invitationId, userId, accept)Accept or decline
revokeInvitation(invitationId, userId)Owner cancels pending invite
removeMember(circleId, memberId, ownerId)Remove a member
getCircles(userId)List all circles (owned + member of)
follow(followerId, followingId)Unidirectional follow
unfollow(followerId, followingId)Remove follow
upsertIntent(userId, dto)Set/update discovery intent
getMyIntent(userId)Fetch current intent

HTTP Endpoints

All routes under /api/v1/social/. Require JwtAuthGuard.

MethodPathDescription
GET/circlesList my circles
POST/circlesCreate a circle
PATCH/circles/:idUpdate circle name/accent
DELETE/circles/:idDelete (owner only)
GET/circles/:id/membersList members
POST/circles/:id/membersDirectly add member
DELETE/circles/:id/members/:memberIdRemove member
POST/circles/:id/invitationsSend invitation
GET/circles/invitations/meMy pending invitations
POST/circles/invitations/:id/respondAccept or decline
DELETE/circles/invitations/:idRevoke (inviter only)
POST/users/:id/followFollow a user
DELETE/users/:id/followUnfollow
GET/me/followingUsers I follow
GET/me/followersUsers following me
PUT/intentUpsert connection intent
GET/intentGet my intent

Events Emitted

EventPayloadConsumed by
social.member.added{ circleId, memberId }Notifications
social.member.removed{ circleId, memberId }Notifications
social.invitation.sent{ invitationId, circleId, inviterId, inviteeId }Notifications
social.invitation.accepted{ invitationId, circleId, memberId }Notifications
social.user.followed{ followerId, followingId }Notifications

Circle Invitation Flow

Owner → POST /circles/:id/invitations
      → CircleInvitation row status='pending'
      → Event: social.invitation.sent
      → Invitee receives notification

Invitee → POST /circles/invitations/:id/respond { accept: true }
        → CircleMember row created
        → Invitation status → 'accepted'
        → Event: social.invitation.accepted
        → Owner receives notification

Invitee → respond { accept: false }
        → Invitation status → 'declined'
        → No membership created

Privacy rule for DMs: Two users can only open a DM if they share at least one circle on either side or have an existing conversation.

Regulus — invite-only social-knowledge platform