Skip to content

Faction System

Overview

The Faction System models organizations, groups, and power structures within a collaborative world. Factions represent any collective entity that characters can belong to -- vampire clans, noble houses, criminal syndicates, military orders, religious cults, or corporate conglomerates. They provide the organizational layer that sits above individual characters and below the world itself.

Factions support nested hierarchies (a clan contains sub-factions, a kingdom contains duchies), character memberships with ranks, and inter-faction relationships that capture alliances, rivalries, and political dynamics. A dedicated graph endpoint assembles faction nodes and relationship edges into a visualization-ready structure, giving Storytellers and players a political map of their world.

The Faction System is tightly coupled with the Character System. Characters join factions through memberships, and the faction graph can surface member counts and character groupings. Together, the two systems create a complete social and organizational model for any creative project.

Goals

  • Allow Storytellers and Co-Creators to define factions with types, descriptions, and emblems
  • Support nested faction hierarchies (parent-child) for modelling organizational structure
  • Enable character-to-faction membership with optional rank/role
  • Model inter-faction relationships with types, strength, and secrecy
  • Expose a permission-filtered faction relationship graph endpoint
  • Track how faction relationships evolve over time
  • Integrate cleanly with the Character System for cross-feature queries

User Stories

Game Master / Storyteller

  • As a Storyteller, I want to create factions representing the major power groups in my world so players understand the political landscape.
  • As a Storyteller, I want to nest factions (e.g., "Camarilla" > "Ventrue Clan" > "Chicago Ventrue") so I can model organizational hierarchies.
  • As a Storyteller, I want to define secret alliances between factions that only I and the Owner can see, so I can prepare political intrigue.
  • As a Storyteller, I want to assign characters to factions with specific ranks (e.g., "Prince", "Sheriff", "Neonate") so the power structure is clear.
  • As a Storyteller, I want to view the full faction relationship graph -- including secret relationships -- so I can plan storylines around political dynamics.

Writer / Co-Creator

  • As a Co-Creator, I want to create factions and link characters to them so I can contribute to the world's organizational fabric.
  • As a Co-Creator, I want to upload faction emblems/symbols so each faction has a visual identity.
  • As a Co-Creator, I want to define relationships between factions (allied, enemy, vassal) so the political web is documented.

Player

  • As a Player, I want to see which factions exist and who belongs to them so I understand the world's power structure.
  • As a Player, I want to see the public faction relationship graph so I know which groups are allied or opposed.
  • As a Player, I want to see my character's faction membership and rank so I understand my place in the world.

Functional Description

Faction CRUD

Factions are aggregate roots within the KnowledgeBase.Factions feature module. Each faction belongs to a single project and receives a project-scoped sequence number on creation (e.g., VNO-15).

Creation rules by role:

RoleCan Create?Can Edit?Can Delete?
OwnerYesAny factionAny faction
StorytellerYesAny factionAny faction
Co-CreatorYesOwn factionsOwn factions
PlayerNoNoNo
ViewerNoNoNo
  • Only Owner, Storyteller, and Co-Creator roles can create factions. Players and Viewers have read-only access.
  • Co-Creators may only edit or delete factions they created.
  • Deleting a faction cascades: all memberships, child faction references (parentFactionId set to null or children deleted -- see Edge Cases), and faction relationships involving the deleted faction are removed.

Faction Types

The type field categorizes factions at a high level:

TypeDescriptionExamples
politicalGoverning bodies, ruling councils, noble houses"The Camarilla", "House Stark", "Senate of Waterdeep"
militaryArmed forces, mercenary companies, defence orders"The Night's Watch", "Silver Fang Pack", "City Guard"
religiousChurches, cults, spiritual orders"Church of the Unconquered Sun", "Cult of Ecstasy"
criminalUnderworld organizations, gangs, smuggling rings"Giovanni Crime Family", "Thieves' Guild", "The Syndicate"
otherAny faction that does not fit the above categories"The Mages' Academy", "Merchant Consortium"

Faction Properties

PropertyTypeDescription
namestringDisplay name of the faction
descriptionstringNarrative description, purpose, history
typeenumpolitical, military, religious, criminal, other
symbolUrlstring?URL to the faction's emblem or symbol image
parentFactionIdGuid?Reference to parent faction for hierarchy
sequenceNumberintProject-scoped public ID

Nested Factions (Hierarchy)

Factions support a parent-child hierarchy through the self-referencing parentFactionId field. This models real organizational structures where groups contain sub-groups.

Example hierarchy:

Hierarchy rules:

  • A faction with parentFactionId = null is a top-level faction.
  • A faction with a valid parentFactionId is a child of that parent.
  • Maximum nesting depth: 5 levels. The system validates on creation and update that adding a parent would not exceed this limit. This prevents runaway recursion and keeps traversal queries performant.
  • A faction cannot be its own parent (self-referencing loop detection).
  • A faction cannot be set as a child of one of its own descendants (cycle detection).
  • Moving a faction to a new parent is supported via the update endpoint by changing parentFactionId.

Hierarchy traversal:

  • The faction list endpoint returns all factions with their parentFactionId populated, allowing the frontend to assemble the tree.
  • The faction detail endpoint includes a children array of immediate child factions for convenience.
  • Full ancestor/descendant traversal uses recursive CTE queries when needed (e.g., "show all factions under the Camarilla").

Faction Memberships

Memberships link characters to factions. A character can belong to multiple factions simultaneously, and each membership can carry an optional rank or role title.

Membership Properties

PropertyTypeDescription
factionIdGuidFK to the faction
characterIdGuidFK to the character
rankstring?Freeform rank/role title within the faction

Membership Examples

CharacterFactionRank
Marcus VitelVentrue ClanPrince
Elena VasquezToreador ClanHarpy
The BaronCamarillaJusticar
The BaronChicago VentruePatron

In this example, "The Baron" belongs to two factions with different ranks in each.

Membership CRUD

EndpointDescription
GET /api/projects/{key}/factions/{seq}/membershipsList all members of a faction
POST /api/projects/{key}/factions/{seq}/membershipsAdd a character to the faction
PUT /api/projects/{key}/factions/{seq}/memberships/{id}Update rank/role
DELETE /api/projects/{key}/factions/{seq}/memberships/{id}Remove character from faction

Permission rules for memberships:

RoleCan Add Members?Can Update Rank?Can Remove Members?
OwnerYesYesYes
StorytellerYesYesYes
Co-CreatorTo factions they createdOn factions they createdFrom factions they created
PlayerNoNoNo
ViewerNoNoNo
  • A character can only be added to a faction once (unique constraint on factionId + characterId).
  • The character must be visible to the user performing the operation. Private characters cannot be added to factions by users who cannot see them.

Faction Relationships

Faction relationships model the political and social dynamics between organizations. They follow a similar pattern to character relationships but with faction-specific semantics.

Relationship Types

TypeDescriptionExample
alliedFormal or informal allianceVentrue and Toreador clans cooperating
enemyOpen hostility or oppositionCamarilla vs. Sabbat
neutralNo strong relationshipTwo clans with no interaction
tradeEconomic or resource exchangeMerchant guild supplying the military
vassalSubordination or fealty relationshipA barony sworn to a kingdom
customUser-defined relationship typeAny specific dynamic not covered above

Relationship Properties

PropertyTypeDescription
sourceFactionIdGuidFK to the source faction
targetFactionIdGuidFK to the target faction
typeenumallied, enemy, neutral, trade, vassal, custom
strengthint (1-5)Intensity of the relationship
isSecretboolIf true, visible only to Storyteller and Owner
descriptionstring?Narrative context for the relationship

Secret Relationships

The isSecret flag provides a simpler visibility model than character relationships (which use a full public/private visibility enum). When isSecret = true:

  • The relationship is visible only to users with the Storyteller or Owner role.
  • Co-Creators, Players, and Viewers cannot see the relationship in list endpoints or in the faction graph.
  • This enables Storytellers to document hidden alliances, secret enmities, or behind-the-scenes political manoeuvring that players should not know about.

Faction Relationship Permission Rules

RoleCan Create?Can View?
OwnerYesAll (including secret)
StorytellerYesAll (including secret)
Co-CreatorYesNon-secret only
PlayerNoNon-secret only
ViewerNoNon-secret only

Historical Relationship Changes

Faction relationships evolve over time. The system supports this through two mechanisms:

  1. Direct updates: A faction relationship's type, strength, or description can be updated via the PUT endpoint. The version history system (see Version History & Changelog) records each change, creating an auditable timeline of how the relationship evolved.

  2. End and replace: For dramatic shifts (e.g., allies becoming enemies), the existing relationship can be deleted and a new one created. Both actions are captured in the activity feed and version history.

The version history integration means that even though the faction relationship entity itself stores only the current state, the full evolution is recoverable from the audit trail.

Faction Relationship Graph Endpoint

GET /api/projects/{key}/graphs/factions

This endpoint returns a pre-assembled graph of factions (nodes) and their inter-faction relationships (edges), optimized for frontend visualization.

Graph Response Structure

Node properties:

FieldDescription
idFaction sequence number
nameFaction name
typeFaction type (political, military, etc.)
symbolUrlFaction emblem URL (nullable)
parentIdParent faction sequence number (nullable)
memberCountNumber of characters in this faction
membersArray of member summaries (character name, rank)

Edge properties:

FieldDescription
idFaction relationship internal ID
sourceSource faction sequence number
targetTarget faction sequence number
typeRelationship type (allied, enemy, etc.)
strength1-5 weight for edge thickness
isSecretWhether this is a secret relationship (included only for authorized users)

Graph Data Flow

Faction-Character Integration

The Faction System and Character System are closely related. Key integration points:

  • Membership links: FactionMembership references both a Faction (from this module) and a Character (from the Characters module). The Factions module depends on the Characters module for character lookups.
  • Cascade on character deletion: When a character is deleted, all their faction memberships are removed. This is handled via a domain event from the Character System or a direct cascade rule.
  • Cascade on faction deletion: When a faction is deleted, all memberships within that faction are removed, and any child factions have their parentFactionId set to null (orphaned to top-level) or are recursively deleted (configurable -- see Edge Cases).
  • Visibility alignment: The faction graph filters out memberships for private characters that the requesting user cannot see. The member list for a faction only includes characters the user has permission to view.
  • Cross-feature queries: A character's detail response can include a summary of their faction memberships. A faction's detail response includes its member list. These cross-feature queries use read-only access patterns.

Data Flow

Faction Creation Flow

Membership Management Flow

Diagrams

Faction Membership Data Model

Cross-System Interaction: Factions and Characters

Key Components

FactionsController

Handles faction CRUD endpoints. Routes follow the pattern /api/projects/{key}/factions/{seq}. Supports listing with optional parent filtering to retrieve subtrees.

MembershipsController

Handles faction membership CRUD endpoints. Routes are nested under the faction: /api/projects/{key}/factions/{seq}/memberships/{id}.

FactionRelationshipsController

Handles inter-faction relationship CRUD. Routes follow /api/projects/{key}/factions/relationships/{id}.

FactionGraphController

Handles the GET /api/projects/{key}/graphs/factions endpoint. Assembles the node-and-edge graph with member counts, member summaries, and permission-filtered edges.

FactionService

Core business logic for faction lifecycle: creation (with hierarchy validation and sequence number allocation), updates (with cycle detection for parent changes), and deletion (with cascade handling for memberships, child factions, and relationships).

MembershipService

Business logic for managing character-faction memberships: adding members (with duplicate detection and character visibility checks), updating ranks, and removing members.

FactionRelationshipService

Business logic for inter-faction relationships: creation, updates, deletion, and secrecy filtering.

IPermissionService (shared)

Consulted by all handlers. For the Faction System specifically, it determines whether a user can see secret faction relationships and whether a Co-Creator has ownership of a particular faction.

Feature Interactions

FeatureInteraction
Character SystemFaction memberships reference characters. Character deletion cascades to remove memberships. The faction graph includes character member data. See Character System.
Project ManagementFactions belong to a project. Project membership and roles drive permission checks. Faction creation increments the project's shared sequence counter.
Permission & VisibilityAll faction operations go through IPermissionService. Secret faction relationships are filtered for non-Storyteller/Owner roles. Membership lists filter out private characters. See Permission & Visibility.
Real-time & CollaborationFaction creation, membership changes, and relationship changes raise domain events for the activity feed and SignalR notifications. See Real-time & Collaboration.
Version HistoryFaction edits, membership changes, and relationship changes are tracked for audit and rollback. Historical relationship evolution is recoverable from this trail. See Version History & Changelog.
Search & NavigationFactions are indexed for project-wide search by name, type, and description. See Search & Navigation.

Use Cases

Vampire: The Masquerade -- Clan Politics

A Storyteller running a Vampire campaign creates the major sects as top-level factions (Camarilla, Sabbat, Anarchs), then nests clans within each sect, and further nests city-level coteries within clans. Characters are assigned to their clan with ranks like "Prince", "Primogen", or "Neonate". Inter-faction relationships map alliances, enmities, and secret deals between clans. The faction graph reveals the political web that drives the chronicle.

Fantasy Noble Houses

A writer building a fantasy world creates noble houses as factions (House Stark, House Lannister) of type political. Vassal relationships model fealty. Allied and enemy relationships capture shifting alliances. Characters are assigned with ranks like "Lord", "Heir", "Knight". Nested factions represent cadet branches or sworn bannermen.

Urban Gang Territories

A Co-Creator modelling a modern urban setting creates gangs as criminal factions. Trade relationships represent drug supply chains. Enemy relationships mark turf wars. Characters hold ranks like "Boss", "Lieutenant", "Soldier". Secret alliances between supposedly rival gangs add layers of intrigue visible only to the Storyteller.

Corporate Factions

A writer modelling a cyberpunk setting creates megacorporations as factions of type other or political. Nested factions represent divisions and subsidiaries. Trade and vassal relationships model corporate hierarchies and partnerships. Characters hold ranks like "CEO", "Division Head", "Field Agent".

Edge Cases & Error Handling

ScenarioHandling
Creating a faction with a non-existent parentFactionIdReturn 404 Not Found for the parent faction.
Nesting exceeds 5 levels deepReturn 422 Unprocessable Entity with a message about the depth limit.
Setting a faction as its own parentReturn 422 Unprocessable Entity. Validate on create and update.
Creating a cycle in the hierarchy (A > B > C > A)Detect cycles by traversing ancestors before updating parentFactionId. Return 422 if a cycle would result.
Deleting a faction that has child factionsSet children's parentFactionId to null, promoting them to top-level factions. Log a warning in the response that child factions were orphaned.
Deleting a faction with existing membershipsCascade delete all memberships. The characters themselves are unaffected.
Deleting a faction with existing relationshipsCascade delete all faction relationships where this faction is source or target.
Adding a character to a faction they already belong toReturn 409 Conflict. Unique constraint on (factionId, characterId).
Adding a private character to a faction (user cannot see the character)Return 404 Not Found for the character. Do not reveal the character exists.
Faction graph with secret relationships for a PlayerSecret relationship edges are omitted from the graph. The graph appears as if those relationships do not exist.
Faction membership references a character that is later made privateThe membership still exists in the database, but the member is omitted from membership lists and graph member counts for users who cannot see the character.
Co-Creator tries to edit a faction they did not createReturn 403 Forbidden. Co-Creators can only modify their own factions.
Concurrent sequence number allocationHandled by PostgreSQL atomic UPDATE ... RETURNING on the project's counter.

Phase & Priority

AspectValue
PhasePhase 2: Relationship Mapping
PriorityHigh -- factions are a core part of Phase 2 alongside characters
DependenciesRequires Phase 1 (Project Management, Auth) and the Character System (for memberships)
DependentsCollaboration features (comments on factions), Search (faction indexing)