# Live Service & DLC

V5 is an open-world narrative universe split into cells — an Urban heist city, a
three-era Period crime saga, a Frontier outlaw trail, a Hunter's
monster-contract world, a Sci-Fi galactic theatre, and the cross-cutting Mind
Palace deduction layer — and a universe that big is a service, not a box. It
ships five launch cells and then keeps growing for years: a 90-day season every
quarter, a 100-tier battle pass, weekly cold-case packs, persistent world
events, a whole Steampunk-Detective DLC cell, and a Year-3 roadmap that adds
four sub-cells and a new console SKU. Two pieces of machinery make that
post-launch life real rather than promised. The first is the **live-service
calendar service** — a NestJS package under `apps/v5` that publishes seasonal
rotations, world events, and hot-fix windows over JWT-gated, ETag-cacheable
feeds, mirrored by a family of `UV5_Online_*` Blueprint builders in the UE
client. The second is the **DLC boundary**: every cell and mode is a GameFeature
plugin discovered, classified, and build-target-gated by `V5Modes`, so a new
cell drops in as a content-bearing plugin without forking the engine. This page
explains how both are wired, what is real TypeScript and C++ on disk versus
authored-but-uncooked content, and where the shipped code is honestly thinner
than the architecture prose. The section hub is
[../V5_ARCHITECTURE.md](../V5_ARCHITECTURE.md).

The design choice underneath everything is the same one the rest of V5 follows,
and it is the same competitive line V2 and V4 drew: **nothing sold or seasonal
ever touches gameplay power**. Every calendar payload asserts
`exclusiveGameplayRewards: false`, the battle pass marks gameplay-affecting
rewards `never-paid-only`, store bundles are cosmetic-only, and the workshop
marketplace sells data-only mods. Live service grows the world; it never sells
an advantage in it.

## What ships, honestly

The split is clean, and stating it up front keeps the rest of the page honest.

The **calendar service is real**. `apps/v5/live-service-calendar/` is a NestJS +
Fastify package (`src/main.ts`, port 4223) whose controller
(`src/controller.ts`) exposes five GET routes — `/feed`, `/year1`,
`/world-events/year1`, `/pro-stadium-tour`, `/year3-pipeline` — each
`requiresJwt: true` and `regional: true` in `contract.json`, each delegating to
`handleRealRequest` in the shared `@v5/service-shared` runtime. A 172-line
`contract.test.ts` pins the endpoint set, the 21 declared capabilities, the
port, and the exact shape of every feed payload, plus a
success/outage/unauthorized contract-case sweep and a Docker/k8s/failover
artifact check.

The **UE client tier is real C++**. `V5/ue/Source/V5OnlineServices` (a 1950-line
`V5OnlineServicesSystems.cpp` over a 1215-line types header, with an 809-line
test) ships `UV5_Online_LiveServiceCalendarClient`,
`UV5_Online_CrossCellPersistentWorld`, `UV5_Online_FactionRepClient`, and the AI
highlight-reel builders as `UBlueprintFunctionLibrary` composers that build
season rotations, world-event scripts, the Year-3 pipeline, and the request
envelopes the HTTP client sends.

The **DLC machinery is real C++**. `V5/ue/Source/V5Modes` (`V5ModeSystems.cpp`,
639 lines) classifies, discovers, and build-target-gates GameFeature plugins;
**29 `V5Mode_*.uplugin` descriptors** sit under `V5/ue/Plugins/`, and **all 29
declare an enabled `GameFeatures` dependency**.

The **economy domains are real**. `@v5/service-shared`'s `WorkshopDomain`
(`domain/workshop.ts`) is a Postgres-backed UGC marketplace with a moderation
state machine, an ML pre-screen, and an exact-to-the-cent 70/20/5/3/2 revenue
split; `FactionRepDomain` (`domain/faction-rep.ts`) is a real exponential-decay
retention model. The **store** is a transparent Next.js storefront under
`apps/v5/web/src/app/store/`.

Five honest caveats matter. **First, the shipped feed payloads are object
literals.** `handleRealRequest` resolves to `evaluateServiceSpecificRule`
(`runtime.ts:1818`), which returns hand-authored constants —
`seasonalThemeRotations: 4`, `scriptCount: 12`, `stopCount: 6` — not rows read
from a database or parsed from the manifests. **Second, a real Postgres calendar
domain exists but is not on that path.** `LiveServiceCalendarDomain`
(`domain/live-service-calendar.ts`) implements `ensureSchema`/`upsertEvent`/
`feedEvents`/`activeEvents` and a SHA-256 content-derived `etag`, but it is
imported only by `realtime.ts` and a test — the deployed `/year1` feed does not
call it. **Third, there is no `V5/store` directory**; the only store surface is
the web storefront, which is presentation, not a transactional IAP backend.
**Fourth, the content is uncooked**: a scan of all 29 plugins finds **zero
binary `.uasset` and 62 JSON stand-ins** — V5, like V4, represents assets and
levels as JSON descriptors. **Fifth, V5's GameFeature activation is thinner than
V4's.** `V5Modes` ships discovery, classification, a static roster, and
per-target gating; it does not ship V4's `UGameFeatureAction` subclass that
drives `UAssetManager` async streaming on activation. The DLC _boundary_ is
real; the runtime asset-streaming seam is a recorded step, not a call.

## Live-service architecture: the calendar spine

Post-launch content reaches players through one service and three
representations of the same numbers, kept consistent by tests rather than by a
single source of truth. The service is the calendar; the representations are the
authored JSON manifests, the NestJS literals, and the UE C++ builders.

```mermaid
flowchart TD
  subgraph Authored["Design source of truth"]
    JSON[V5/live-service/*.json<br/>season-1 · year-1 · year-3 manifests]
  end
  subgraph Service["apps/v5/live-service-calendar (NestJS + Fastify :4223)"]
    Ctl[controller.ts<br/>5 JWT-gated GET routes] --> HRR[handleRealRequest →<br/>evaluateServiceSpecificRule<br/>authored literal payloads]
    Dom[(LiveServiceCalendarDomain<br/>Postgres + SHA-256 ETag<br/>real, not on the feed path)]
  end
  subgraph Client["V5/ue/Source/V5OnlineServices (C++)"]
    Build[UV5_Online_LiveServiceCalendarClient<br/>BuildYear1Calendar · Year3 · WorldEvents]
    Http[UV5_Online HTTP client]
  end
  JSON -. authors .-> HRR
  JSON -. authors .-> Build
  Build --> Http
  Http -->|Bearer JWT| Ctl
  HRR -->|ETag feed| Http
  HRR -->|/api/public/season-calendar| Web[apps/v5/web public calendar + store]
  subgraph DLC["V5/ue/Source/V5Modes (C++)"]
    Plug[29× V5Mode_*.uplugin<br/>GameFeatures dependency] --> Disc[DiscoverGameFeaturePlugins]
    Disc --> Gate[GateKnownMode<br/>per build target]
  end
```

`V5LiveServiceCalendarController` is a thin router: each method stamps a
`requestType`/`feed` pair onto the query and calls
`V5LiveServiceCalendarService.handle`, which builds a `ServiceRequest` and runs
`handleRealRequest(slug, request, now)`. `resolveEndpointPath`
(`runtime.ts:1233`) maps `year1-calendar` → `/v5/live-service-calendar/year1`,
`year1-world-events` → `/world-events/year1`, and so on, so the response carries
the canonical path back. Every route is JWT-gated and regional, every payload
carries `clientCacheEtagRequired: true` and `orderedWindows: true`, and the
`failover` block declares `regional-active-active` across `iad/fra/sin`
primaries with `pdx/dublin/syd` fallbacks. This is a genuine deployable service
with a genuine contract; what it is not, yet, is a database query —
`evaluateServiceSpecificRule` returns the authored constants directly, and the
contract test asserts exactly those constants
(`calendarId: 'calendar.year1.live-service'`, `seasonalThemeRotations: 4`). The
Postgres `LiveServiceCalendarDomain` is the shape that path is specified to grow
into; today it is real code one wiring step removed from the feed.

### Seasons, battle pass & the persistent calendar

Season One is authored in `V5/live-service/season-1-live-service-manifest.json`:
_Fracture Signals_, a 90-day window (2027-03-01 → 2027-05-30) opened by a 30-day
_Continuum Signal_ day-one event. Its battle pass is 100 tiers at a 6–8
hours-per-week target, with an explicit `tierSkipPolicy` — skips are purchasable
(`purchaseEnabled: true`, `maxSkipsPerSeason: 20`) but
`gameplayAffectingRewards: "never-paid-only"`, the monetization line drawn in
the data itself. Reward families are per-cell cosmetics, battle-pass XP, creator
spotlights, and Mind Palace case stamps — never frame data or stats. The
manifest also names its `sourceServices` (the calendar, faction-rep,
balance-ledger, and workshop contracts), so the design doc points back at the
code that serves it.

The UE side builds the same data in C++. `UV5_Online_LiveServiceCalendarClient`
(`V5OnlineServicesSystems.h:260`) exposes `BuildYear1Calendar`,
`BuildYear1SeasonalRotations`, `BuildYear1SpecialEvents`, and the predicate
`EvaluateCalendarEventState` over constants `Year1SeasonDays = 90` and
`Year1SeasonCount = 4` (`V5OnlineServicesSystems.cpp:20`). `SeasonalRotation`
(`:106`) computes each season's start as
`Year1CalendarStartUtc() + (n-1)·90 days` and attaches per-cell themes from
`BuildCellThemesForSeason` — real date arithmetic, not a flag. The `/year1` feed
literal mirrors it: four seasonal-theme rotations plus the Bureau Founding Day
anniversary, the Shelter Signal charity event, and the Platform Circuit console
tie-in, all with `gameplayProgressionGated: false`.

**Persistent world events** are their own feed.
`BuildYear1PersistentWorldEvents` and `BuildYear1WorldEventScripts`
(`V5OnlineServicesSystems.h:311`) compose the twelve one-week scripts authored
in `year-1-persistent-world-events.json` — Gang War Week first — and the
`/world-events/year1` literal pins `scriptCount: 12`,
`gangWarWeek: 'world-event.year1.01.gang-war-week'`,
`persistentWorldState: true`, `settlementSnapshots: 12`, and crucially
`exclusiveGameplayRewards: false`: a player who misses Gang War Week loses no
power, only a moment. The **Year-3 pipeline** (`BuildYear3LiveServicePipeline`,
`BuildYear3SubCellPlans` with a `DlcGate` per sub-cell,
`BuildYear3CrossCellCoopMissionPlans`, `BuildYear3ConsoleSkuPlan`) plans four
sub-cells, eight cross-cell co-op missions
(`Year3CrossCellCoopMissionCount = 8`, `…Minutes = 240`), and one
`sku.year3.console.switch2-native-complete` SKU with a first-party cert track
and reduced-install budgets — a roadmap feed, honestly labelled
`contentLock: year-3-planning`. Each builder also returns a `Get…ManifestPath()`
string so the companion app and the `/api/public/season-calendar` web surface
can resolve the same authored JSON the C++ encodes.

### Faction-rep decay, highlight reels & retention

Retention has real domain logic behind it. `FactionRepDomain`
(`domain/faction-rep.ts`) relaxes reputation toward a neutral floor while a
player is inactive: `decayedRep(rep0, daysInactive)` returns
`REP_FLOOR + (rep0 − REP_FLOOR)·exp(−ln2/halfLife · effectiveDays)` with
`REP_FLOOR = 0`, `REP_CEILING = 1000`, `DEFAULT_HALF_LIFE_DAYS = 30`, and a
`DEFAULT_GRACE_DAYS = 14` window below which nothing decays. The surplus over
the floor halves every 30 idle days; a weekly `runDecay(nowUnix)` journals every
decrement into an append-only ledger. The file's own header records that this
_"replaces the prior `simulatedDecay: true`"_ handler — a real de-fabrication,
not a flag pretending to be a model. The
`UV5_Online_FactionRepClient::BuildYear1DecayPolicy` builder surfaces the same
policy to the client, and `year-1-faction-reputation-decay.json` authors the
five restoration mission arcs that let a lapsed player climb back. The **AI
highlight reel** (`BuildYear1AIHighlightReel`, `BuildTop10HighlightClips`,
`HighlightSignal`) ranks replay clips into an optional commentator track; its
manifest id `v5.year1.ai-generated-highlight-reel` and the
`/v5/replays/commentary/tts/year1` route both require synthetic-content
indicators and source-replay verification.

## DLC delivery: the GameFeature boundary

A new cell, a new mode, a new DLC expansion — in V5 these are all the same
thing: a `V5Mode_*` GameFeature plugin. The boundary is enforced in `V5Modes`,
and it is real engine-integration C++. `IsV5GameFeaturePlugin`
(`V5ModeSystems.cpp:86`) accepts a plugin only if its name starts with `V5Mode_`
**and** its descriptor references the engine `GameFeatures` plugin with
`bEnabled` — a two-part predicate, not a name match.
`DiscoverGameFeaturePlugins` (`:102`) walks `IPluginManager`'s discovered or
enabled plugin set, filters through that predicate, de-duplicates with
`AddUnique`, and sorts by `FNameLexicalLess` so discovery is deterministic
across machines. The static roster `MakeMode` (`:41`) binds each plugin name to
an `EV5Cell`, an `EV5ModeNetworkModel`, a HUD surface, a max-player count, and
dedicated/listen-server flags — the 29 modes spanning every launch cell plus the
Steampunk DLC cell, the editors, and the online/co-op variants.

The DLC cell itself is `V5Mode_Steampunk_Detective.uplugin`: its descriptor
reads `"EnabledByDefault": false`, `"CanContainContent": true`,
`"BuiltInInitialFeatureState": "Installed"`, an enabled `GameFeatures`
dependency, and the description _"Content-bearing GameFeature root for the Brave
New World Steampunk Detective DLC cell."_ `EV5Cell::SteampunkDetective`
(`V5ModeTypes.h:41`) is a first-class cell, and
`MakeMode("V5Mode_Steampunk_Detective", …, EV5Cell::Steampunk, …)` registers it
alongside the launch roster — so the DLC cell is structurally identical to a
shipped cell, gated off by default until entitled.

What turns the roster into a per-build catalogue is `GateKnownMode` (`:129`), a
real build-target predicate over `EV5ModeBuildTarget`. A `DedicatedServer` build
enables only `bSupportsDedicatedServer` rulesets; a `LANOffline` build refuses
any mode with `bRequiresOnlineServices` or a `CompetitivePvP`/`SocialLobby` net
model; `ArcadeCabinet` cooks only the curated local co-op roster
(`IsArcadeCabinetMode`); `Editor` previews everything non-exclusive. This is the
fail-closed seam between content and platform: a DLC cell that needs online
services will not enable itself in an offline cabinet build. The honest limit,
stated above, is that V5 stops at _discovery + gating + a roster_. It does not
ship V4's `UV4GameFeatureAction_ActivateModeAssets` that issues a real
`RequestAsyncLoad` on activation — and with zero cooked `.uasset` on disk, the
soft content paths resolve to nothing at runtime. The plugin scaffolding,
dependency wiring, classification, and build-target logic are real and
test-pinned; the streaming activation is a documented gap, not a claimed
feature.

The DLC _catalogue_ the architecture enumerates rides this boundary: 20 weekly
Cold Cases Mind Palace packs (`V5Mode_Detective_ColdCases`), Frontier bounty and
naturalist drops (`V5Mode_Frontier_OutlawTrail`), and the full-vision expansion
cells — Polynesian and Pirate Frontier, Egyptian and Cyberpunk-2087 sub-cells,
the Cosmic-Horror Hunter, and the Steampunk Detective cell — each a
plugin-shaped, JSON-manifested content drop owned by its mode, never a binary
client patch.

### The store & the workshop marketplace

The retail store is a transparent web surface, not a dark-pattern funnel.
`apps/v5/web/src/app/store/page.tsx` and `store/[slug]/page.tsx` render
`storeEntries` from `lib/publicContent.ts`: exactly two entries — the
**Continuum Edition** base game ($69.99, "all five launch cells and
cross-progression", a nine-platform entitlement map) and the **Founders Field
Kit** ($19.99, "cosmetic-only … profile frames, camp props, and ship decals",
tagged "No power"). The store copy's stated job is to _"separate gameplay access
from cosmetics … without pressure tactics"_ and keep "balance-affecting rewards
out of paid bundles" — the same player-protective constraint expressed as
content rather than as a checkout flow.

The transactional economy with real backend logic is the **workshop
marketplace**. `WorkshopDomain` (`domain/workshop.ts`) persists UGC items
through a moderation state machine: a publish runs
`prescreenRisk(reportCount, flaggedTerms)` and `moderationFromRisk` to set an
initial state, a human `moderate` call overrides it, and `revenueShareSplit`
computes an exact-to-the-cent payout from `REVENUE_SHARE` basis points — **7000
creator (70%)**, 2000 platform, 500 creator fund, 300 chargeback reserve, 200
tax — with a 48-hour `REFUND_WINDOW_HOURS`. The marketplace endpoints
(`/v5/workshop/marketplace/{listings,purchase,revenue-share,payouts}`,
`runtime.ts:1220`) are the paid data-only mod marketplace the architecture flags
as `[P3]`, and they are the closest thing in V5 to a real store ledger — a
creator economy, not a loot box.

## The honest line: no pay-to-win, no binary hot-patch

Two invariants run through every system on this page. **No gameplay power is
sold or seasonal-gated.** It is asserted in the calendar literals
(`exclusiveGameplayRewards: false`, `gameplayProgressionGated: false`), in the
battle-pass manifest (`gameplayAffectingRewards: "never-paid-only"`), in the
store copy ("No power"), and in the cosmetic-only reward families — the same
hard competitive-integrity line V2's player-protective store and V4's
sim-isolated cosmetics draw. **And hot-fixes are data, not binaries.** The
architecture's "balance + UI strings via CDN-served data files; no binary patch"
is expressed as the calendar's `hotfix-window` capability and `hotfix-windows`
feed, paired with the append-only `balance-ledger` service whose every entry is
telemetry-evidence-linked. A balance change ships as a calendar-scheduled data
window and a ledgered tuning record, never as a forced client rebuild — which is
also why a DLC cell can be `EnabledByDefault: false` and simply flip on when
entitled.

## Where this connects

- **The backbone underneath:**
  [Persistence, Online Services & Cross-Play](./persistence-online-and-crossplay.md)
  — the 16-service `V5OnlineServices` client catalog, the `@v5/service-shared`
  NestJS tier, and the Bureau account/XP/cosmetic ledgers that the seasonal
  calendar, world events, workshop payouts, and faction-rep decay all read from
  and write to.
- **The guardrails around it:**
  [Accessibility, Security & Compliance](./accessibility-security-and-compliance.md)
  — the JWT auth, spend-limit and store-compliance posture, synthetic-content
  indicators on AI highlight reels, and the moderation/DSAR path behind the
  workshop marketplace.
- **Platform foundations:**
  [Oshun Domain Libraries](../../platform/oshun-domain-libraries.html) — the
  shared economy, governance, and content substrates the V5 store, workshop, and
  live-ops services are specified to compose rather than reinvent.
- **The catalogue index** for the whole V5 architecture set is
  [../V5_ARCHITECTURE.md](../V5_ARCHITECTURE.md).
