# Governance, Safety & Recording-Consent

```mermaid
stateDiagram-v2
  [*] --> Private
  Private --> ConsentRequested: recording or broadcast proposed
  ConsentRequested --> Recording: every required participant grants current scope
  ConsentRequested --> Declined: any participant refuses or cannot consent
  Recording --> Paused: consent changes participant joins or policy signal
  Paused --> Recording: consent set revalidated
  Recording --> Stopped: host end rights safety or incident
  Stopped --> Processing: governed retention redaction and provenance
  Processing --> Published: audience rights and safety review pass
  Processing --> Withheld: consent rights safety or minor policy fails
  Published --> TakenDown: withdrawal claim or policy decision
```

Consent is scoped, revocable, participant-aware, and versioned. A room-level
toggle cannot silently authorize a newly joined member, a different audience,
voice cloning, or later reuse.

V3 ("Lilith") is the first Oshun product where a member has a _body_ in a room
full of strangers — and three of the platform's oldest promises have to be
re-answered at body resolution and in real time. **Children must be protected**
when a 13-year-old and an adult can share a yoga commons. **Harassment must have
an instant, self-serve answer and an accountable operator trail**, because a
shove or a slur in an embodied room is gone the moment it happens unless someone
captures it. And **no one is recorded without a real choice**, even when the
"room" is a 4,096-seat stadium concert whose master capture is the artist's
catalog. V3's stance is the same one it takes everywhere: it does **not** fork
the trust spine. Embodiment is a thin, server-authoritative layer of _computed
decisions_ over V1's audit ledger, persona policy, and DSAR pipeline — the
library decides, the world server and tenant services persist and enforce. This
page covers the operator-and-policy side of that promise: **minor-protection
mode**, **harassment and abuse response**, and the **recording-consent flow**,
end to end. The companion that owns the at-the-body evaluators (tone, crisis,
costume, persona) is
[Identity, Embodied Safety & Provenance Foundations](./identity-safety-provenance-foundations.md);
the section hub is [../V3_features.md](../V3_features.md).

## What ships, honestly

This layer scores high on the implemented-vs-spec axis because its hard parts
are pure decision logic, not art or engine plugins. Two real TypeScript modules
carry it, each backed by an automation suite.

- **The in-world governance engine is real, computed code.** The
  minor-protection defaults, the point-at-avatar reporting pipeline with its
  60-second replay evidence buffer, the operator-review SLA dashboard, the
  progressive-sanction state machine, and the minor-cohort background-check gate
  are all implemented as pure functions in `@oshun/tenant-lilith-commons`
  (`libs/v3/lilith-commons/src/index.ts`). Each capability is declared in the
  package descriptor with an operational metric (`in-world-reporting`,
  `operator-review-sla`, `progressive-sanctions`, `minor-protection-defaults`,
  `minor-operator-alerts`, `minor-instructor-background-checks`) and exercised
  by `src/__tests__/index.spec.ts`.
- **The recording-consent policy is real, computed code.**
  `libs/v3/lilith-identity-bridge/src/recording-consent.ts` resolves every class
  and concert capture decision — full/observer/decline, the free privacy seat,
  the front-row visibility exception, the publication gate, DSAR coverage, and
  post-publication withdrawal — as discriminated-union functions, tested by
  `src/__tests__/recording-consent.spec.ts`.
- **The evidence trail is the genuine V1 audit platform.** Every decision below
  publishes a canonical event (`v3.lilith.in_world_report.submitted`,
  `v3.lilith.progressive_sanction.transitioned`, `recording_consent.*`, …) into
  V1's `@oshun/audit-platform` — the same hash-chained, tamper-evident ledger V1
  ships, with investigation export and retention tooling. V3 produces evidence;
  Oshun retains it.

Three honest qualifications. **First**, these libraries are _pure decision
kernels_: no database, no socket. The report-submission function returns a typed
operator-queue entry and an audit event; the world server is what actually files
the report and the tenant service is what runs the 30-minute mute clock.
**Second**, two spec-level product behaviors in V3*features.md are **not** in
this code and are labeled as such below: the one-tap "step back" (personal-space
expansion + per-avatar self-mute) and the automatic "report-and-remove → quiet
cell" routing are documented UX flows; what is \_coded* is the report → evidence
→ operator-queue pipeline and the operator-applied `mute` sanction level. (The
solo-quiet-cell venue itself is a real Commons capability —
`privacyMode: 'solo-quiet-cell'` — it just isn't auto-triggered from a report
here.) **Third**, the platform-level trust-safety domains `@themis` and
`@kuanyin` are **not imported** by V3's in-world governance: those operate at
the V1/V2 layer (e.g. `@v2/themis-privacy-dsr-routing` joins `@themis/privacy`
for data-subject requests, per
[V2 Security, Compliance & Sister-Monorepo Integration](../../V2/architecture/security-compliance-and-sister-monorepo-integration.md)).
V3 reuses V1's spine instead. The one real Themis touchpoint for V3 is _rights
adjudication_ in `saraswati-stage/themis-rights-adjudication.ts`, which belongs
to [Commerce, Rights & Takedown](./commerce-rights-takedown.md), not to in-world
moderation.

## Minor-protection mode

V3 admits minors aged 13+, with realm-level age gates (Tara and Commons admit
13+; Saraswati Stage is 16+ at the realm boundary). The protection that travels
with a minor account is enforced as **computed defaults that cannot be toggled
off**, plus two structural gates around them.

### Defaults that fail closed — and prove they can't be bypassed

`evaluateV3MinorProtectionDefaults` treats any account under
`V3_MINOR_PROTECTION_ADULT_AGE_YEARS` (18) as protected and resolves four locked
surfaces (`V3_MINOR_PROTECTION_LOCKED_SURFACES`): **voice-chat → `off`**,
**presence-outside-cohort → `hidden`**, **signed-edition-purchase → `blocked`**,
and **remix-licensing → `blocked`**. Presence is the nuanced one: a minor's
"online in Lilith" presence is visible only to themselves, an operator, or a
viewer who _shares a cohort_ (`hasCohortOverlap`) — a non-cohort stranger sees
nothing. The function does not merely set these states; it derives a
`bypassProofPassed` flag that is true only when every requested bypass was
**denied**. `runV3MinorProtectionThirteenYearOldBypassFixture` drives exactly
the adversarial case — a 13-year-old
(`V3_MINOR_PROTECTION_TEST_ACCOUNT_AGE_YEARS`) who requests voice chat on,
presence exposed, purchases unlocked, and remix licensing enabled, observed by a
non-cohort viewer — and asserts all four locks hold. The capability's
operational metric is literally `minor-protection:13yo-locked-no-bypass`. This
is a fail-closed default a child cannot click their way out of, not a settings
page with a recommended toggle.

### Any flagged minor interaction pages an operator within 30 seconds

`createV3MinorFlaggedInteractionOperatorAlert` is deliberately one-sided: it
returns `null` for an adult-only flag, but for any interaction with at least one
participant under 18 it emits a **`P0-minor-safety`** alert to the
`v3-lilith-minor-safety-alerts` queue, console-visible, with the minor avatar/
account ids attached and an audit event
`v3.lilith.minor_flagged_interaction.operator_alerted`. The delivery SLA is a
hard 30 seconds (`V3_MINOR_INTERACTION_OPERATOR_ALERT_SLA_MS = 30_000`), and
`deliveredWithinSla` is computed against it.
`runV3MinorFlaggedInteractionOperatorAlertFixture` asserts that adult-only flags
are ignored, every minor flag produces a console-visible alert, and every alert
lands inside the 30-second window — so a flagged minor interaction can never
sink quietly into the normal queue.

### The background-check gate runs at scheduling time, not at the door

The strongest minor protection is the one a child never sees, because it fires
before the room exists. `evaluateV3MinorCohortClassScheduling` blocks an adult
instructor from being scheduled against a minor cohort unless their background
check is `cleared` **and** valid through at least
`V3_MINOR_COHORT_BACKGROUND_CHECK_MIN_VALIDITY_MS` (24 h) past the scheduled
start. Every other status — `missing`, `pending`, `failed`, `expired` — produces
a `blocked` decision with a precise reason
(`minor-cohort-background-check-missing`, `…-not-cleared`, `…-expired`) and an
audit event. `runV3MinorCohortBackgroundCheckEnforcementFixture` proves all four
failure modes block, a cleared instructor is allowed, and an adult-only cohort
does not require the check (`background-check-not-required`) — so a non-cleared
adult never reaches a room with minors in the first place. Minors in recordings
are handled by the recording-consent flow below: observer-by-default for
classes, an auto-assigned, un-disable-able privacy seat for concerts.

## Harassment and abuse response

Harassment in an embodied room is ephemeral — the durable artifact is whatever
the platform chooses to keep. V3's answer pairs an immediate report with a
**60-second replay buffer** so the operator sees what happened, routes it
through a measured SLA, and escalates repeat offenders through an audited,
appealable sanction ladder.

### Point-at-avatar reporting, built as a short interaction

The report is a few seconds of pointing, not a form you leave the world to fill
out. `createV3InWorldReportDraft` → `pointV3InWorldReportAtAvatar` →
`selectV3InWorldReportCategory` advance a draft through
`target → category → comment → submitted`, and `renderV3InWorldReportUiModel`
drives the in-world panel (category options unlock only once an avatar is
targeted). The six categories (`V3_IN_WORLD_REPORT_CATEGORIES`) are
`harassment`, `impersonation`, `unsafe-conduct`, `voice-abuse`, `minor-safety`,
and `other` — and `minor-safety` is special: `submitV3InWorldReport` stamps it
`P0-immediate` on the operator queue while everything else is `P1-same-day`.

### The 60-second evidence buffer, and why voice transcripts stay ephemeral

A report is only as good as its evidence. `validateV3InWorldReportReplayBuffer`
**requires** a full 60-second window (`V3_IN_WORLD_REPORT_REPLAY_WINDOW_MS`) of
both voice and _target-avatar_ motion coverage, and `submitV3InWorldReport`
**throws** if the buffer is incomplete — the report cannot be filed on a partial
record. The submission's `evidenceKinds` are
`['voice-replay-buffer', 'avatar-motion-replay-buffer']`, and every object is
written under a tenant-resident partition
(`tenant:<tenantId>:<REGION>:safety-reports:<reportId>`) so evidence never
leaves the user's residency zone.

The privacy design here is the interesting part. Voice transcript is normally
**ephemeral**: `createV3VoiceTranscriptRollingBuffer` keeps only the trailing 60
seconds of the _current_ session and sets `durablePersistence: 'none'`.
Transcript becomes durable **only** when `attachV3VoiceTranscriptBufferToReport`
binds it to a specific report (`persistenceScope: 'attached-report-only'`).
`auditV3Voice TranscriptRetention` then proves the invariant, flagging
`unattached-voice-content-persisted`, `voice-content-beyond-60s-buffer`, or
window drift, and `passed` is true only with zero violations. In plain terms:
the platform keeps a rolling buffer to make reports possible, but it cannot
retain your voice beyond 60 seconds _unless you filed a report about it_ — the
evidence trail and the privacy ceiling are the same mechanism.

### The operator review SLA is a release gate, not an aspiration

Reports land on `v3-lilith-safety-report-review`, and
`evaluateV3OperatorReviewSlaDashboard` scores every review against a
**30-minute** SLA during programmed hours and a **2-hour** SLA off-hours
(`V3_OPERATOR_REVIEW_PROGRAMMED_HOURS_SLA_MS` / `..._OFF_HOURS_SLA_MS`). It
buckets events into daily windows and exposes `releaseGateGreen` — true only
when the most recent **7 consecutive UTC days** each clear **95% compliance**
(`V3_OPERATOR_REVIEW_SLA_MIN_COMPLIANCE = 0.95`, `..._CONSECUTIVE_DAYS = 7`).
The gate is strict on both axes: it checks the days are genuinely consecutive
(`areConsecutiveUtcDays`) and the fixture refuses fewer than 20 reviews/day, so
a green light means a real, sustained queue, not one quiet afternoon. This is
the follow-the-sun staffing promise made measurable.

### Progressive sanctions, every step audited and appealable

Repeat offenders escalate through a four-step ladder
(`V3_PROGRESSIVE_SANCTION_LEVELS`): `warning → mute → realm-ban → account-ban`.
`advanceV3ProgressiveSanctionState` is the transition function — a `mute` stamps
`muteUntilMs` 30 minutes out (`V3_PROGRESSIVE_SANCTION_MUTE_DURATION_MS`), a
`realm-ban`/`account-ban` flips the corresponding flag, and **every** transition
carries both an appeal route to `v3-lilith-appeals-review` (7-day window) and an
audit event `v3.lilith.progressive_sanction.transitioned`, appended to the
state's `transitions` and `auditLog`. `runV3ProgressiveSanctionsIntegrationTest`
drives the full sequence and passes only when the final level is `account-ban`,
the exercised order equals the canonical ladder, **all** transitions are
audit-logged, and **all** appeals are routed — so "recorded and appealable" is a
coded invariant, not a policy paragraph. The operator-applied `mute` here is
distinct from the self-serve, in-the-moment controls below.

### Self-serve "step back" and the quiet cell — honestly labeled

V3*features.md describes two immediate, self-serve controls: a one-tap **"step
back"** that expands the target's personal-space radius and mutes the chosen
avatar with no operator in the loop, and a one-tap **"report and remove"** that
files the report \_and* routes the reporter out into a quiet cell. These are
real product commitments, but in the libraries on this page they are
**spec-level**: `lilith-commons` implements the report draft → evidence → submit
→ operator-queue pipeline and the operator-side sanction ladder, while the
personal-space expansion, the per-avatar self-mute, and the automatic
reporter-to-quiet-cell hop are world-server behaviors not present in this
module. The destination is real — the solo-quiet-cell venue is a coded Commons
capability (`privacyMode: 'solo-quiet-cell'`, reflection-scope-bound) — but the
report-triggered routing into it is documented, not yet computed here. Crisis
routing (a non-dismissible in-world soft prompt, a V1 crisis-pipeline handoff,
and an on-call page, gated at recall ≥ 0.95 / FPR ≤ 0.02) lives in
`@oshun/lilith-body-policy` and is detailed on
[Identity, Embodied Safety & Provenance Foundations](./identity-safety-provenance-foundations.md).

## The recording-consent flow

Class and concert capture have different scales and customs, so they use two
different consent models — but both resolve to a concrete avatar/voice/chat
treatment, both audit into `@oshun/audit-platform`, and both route DSAR and
withdrawal through the same V1 pipeline. The whole flow is real code in
`recording-consent.ts`.

### Class recording — per-participant consent at the gate

`resolveV3ClassRecordingConsent` maps a participant's choice to a treatment.
**Consent in full** → `visible` / `master-mix` / `included`. **Observer mode** →
`generic-silhouette` / `excluded-from-master-mix` / `redacted`, stamped
`redactionStatus: 'observer-redaction'`. **Decline and leave** → routed to the
lobby (`route: 'lobby'`) with the ticket retained and nothing recorded. The
load-bearing branch is the minor one: a **minor** choosing `full` triggers
`guardianConfirmationRequired`, and absent a `guardianConfirmedAtIso` the
decision **downgrades to observer automatically** rather than recording the
child in the master mix — fail-closed, by computation, not by reviewer
diligence. A mid-class change of heart is honored by
`resolveV3ClassMidSessionOptOut`, which downgrades to observer and sets
`retroactiveFromMs` to the opt-out instant, so the recording is redacted from
that point forward.

### Concert recording — blanket terms with a free, un-removable privacy seat

Per-attendee dialogs at 4,096 seats are impractical, so concerts use blanket
ticket-terms consent _plus_ a non-recorded option.
`resolveV3ConcertTicketRecording` **throws** if `ticketTermsAffirmed` is false —
you cannot hold a concert ticket without acknowledging the recording. From there
it branches by tier: a **minor** is auto-assigned a `privacy-seat` with
`privacySeatLocked: true` (the toggle cannot be turned off); a **crowd-band
LL-HLS** spectator is `spectator-aggregate-only` (not in the master recording,
chat counted only in aggregate); a **front-256** named seat throws unless the
buyer both affirms full visibility _and_ was offered an alternate tier, and
throws again if they try to request a privacy seat (front-row is inherently
visible); any other attendee may opt into a privacy seat. Crucially,
`privacySeatCostDeltaCents` is the literal type `0` — privacy is free by
construction. Two server-enforced guards back this: `validateV3AudienceCamCue`
refuses to publish any cue whose `targetSection` is `privacy` (the audience
camera can never sweep the privacy section), and
`resolveV3ConcertMidShowPrivacySwitch` lets an attendee switch mid-show,
excluding them from audience-cam sweeps thereafter and forcing
`retroactiveRedaction` with an operator review gate.

### Publication gate, DSAR, and post-publication withdrawal

Consent is enforced again at the moment of release.
`evaluateV3RecordingPublicationGate` sets `publishAllowed` only when the consent
ledger is valid, the privacy-seat ledger is valid, **and** redaction has
actually been applied — otherwise it returns precise `blockedReasons`
(`consent-ledger-invalid`, `privacy-seat-ledger-invalid`,
`redaction-not-applied`). DSAR coverage is a gate, not best-effort:
`buildV3RecordingDsarCoverage` **throws** if the export is missing the consent
ledger, the privacy-seat decisions, or the list of recordings the user appears
in — the three `exportSections` are all required. And consent is revocable after
the fact: `resolveV3PostPublicationWithdrawal` returns `rerenderRequired`,
`redactionReapplied`, and `v1TakedownCascade`, all `true`, with a 48-hour
deadline — a withdrawal forces a re-render, re-redaction, and a hand-off into
the V1 takedown pipeline. "Published" is never the end of the consent story.

## How it connects

This page is the operator-and-policy face of a trust foundation owned elsewhere.
The at-the-body evaluators it leans on — tone scoring, the measured crisis gate,
costume rules, avatar-swap impersonation blocking, and AI-persona enforcement —
are
[Identity, Embodied Safety & Provenance Foundations](./identity-safety-provenance-foundations.md),
and the rights side of a recording — the Isis provenance bundle, the C2PA
signature, and the Themis 24-hour adjudication a takedown rides on — is
[Commerce, Rights & Takedown](./commerce-rights-takedown.md). The internal
mechanics this page summarizes (the rolling-window math, the crisis thresholds,
the consent-ledger shape, and the audit hash-chain) are dissected in
[Persona Policy, Provenance & Rights (architecture)](../architecture/persona-policy-provenance-and-rights.md).
The honest line throughout: the minor-protection defaults, the reporting and
evidence pipeline, the SLA and sanction gates, and the recording-consent policy
are real, computed, and tested; the personal-space "step back," the
report-triggered quiet-cell routing, and the operator-console UI are the runtime
and surfaces these decisions drive.

## Related

- [Identity, Embodied Safety & Provenance Foundations](./identity-safety-provenance-foundations.md)
  — the identity bridge and the at-the-body safety evaluators (tone, crisis,
  costume, persona) this page escalates from
- [Commerce, Rights & Takedown](./commerce-rights-takedown.md) — the provenance
  bundle, C2PA signature, and Themis rights adjudication a recording withdrawal
  cascades into
- [Persona Policy, Provenance & Rights (architecture)](../architecture/persona-policy-provenance-and-rights.md)
  — the internals: rolling window, crisis gate, consent ledgers, and the audit
  hash-chain
- [V2 Security, Compliance & Sister-Monorepo Integration](../../V2/architecture/security-compliance-and-sister-monorepo-integration.md)
  — where the platform's `@themis`/consent/DSR trust-safety domains actually sit
- The section hub: [../V3_features.md](../V3_features.md)
