# Identity, Embodied Safety & Provenance Foundations

```mermaid
erDiagram
  ACCOUNT ||--o{ IDENTITY_LINK : binds
  ACCOUNT ||--o{ TENANT_MEMBERSHIP : holds
  ACCOUNT ||--o{ AVATAR_VERSION : controls
  ACCOUNT ||--o{ BLOCK_RELATION : enforces
  ACCOUNT ||--o{ CONSENT_RECORD : decides
  ROOM ||--o{ PRESENCE_SESSION : hosts
  ACCOUNT ||--o{ PRESENCE_SESSION : joins
  CONTENT_RELEASE ||--o{ ASSET : contains
  ASSET ||--o{ PROVENANCE_RECORD : records
  ASSET ||--o{ RIGHTS_GRANT : requires
  PRESENCE_SESSION ||--o{ SAFETY_EVENT : produces
  SAFETY_EVENT ||--o{ REVIEW_CASE : opens
  REVIEW_CASE ||--o{ AUDIT_EVENT : records
```

Identity links, tenant membership, avatar versions, presence, consent, block,
content provenance, rights, safety review, and audit are independently durable.
That separation makes unlink, takedown, DSAR, appeal, and incident response
possible without rewriting world history.

V3 ("Lilith") is the first time an Oshun member gets a _body_. They walk into a
yoga class, headline or attend a stadium concert, wander a commons — and the
moment they do, three questions that V1 answered as flat-screen policy have to
be answered again at body resolution. **Who are you** when you wear an avatar in
a room full of strangers — and is that still the same trusted V1 account, with
the same privacy and deletion guarantees? **What are you allowed to do, and in
what tone**, when "you" might be a verified instructor, a supervised AI persona,
or someone trying to impersonate one? And **what travels with the things you
make or appear in** — the recorded class, the concert you were filmed at, the
generated track — so that consent, ownership, and origin are never lost? V3's
answer is the same one it gives everywhere: it does **not** fork the platform's
trust spine. A V3 user is a V1 user; embodiment is a thin, server-authoritative
translation layer over identity, safety, audit, and provenance machinery V1
already operates. This page covers that foundation from a product perspective —
the **identity and account bridge**, **embodied safety and persona policy at the
avatar**, and the **provenance and consent foundations** the rest of the
metaverse is built on. The section hub is
[../V3_features.md](../V3_features.md).

## What ships, honestly

This layer is unusually high on the implemented-vs-spec spectrum, because the
hard parts are pure decision logic, not art or engine plugins. Two real,
substantial TypeScript libraries carry it, each backed by an automation suite.

- **The identity bridge is real, server-authoritative code.**
  `@oshun/lilith-identity-bridge` (`libs/v3/lilith-identity-bridge`, ~1,170
  lines in `src/index.ts` plus `per-region-rules.ts` and `recording-consent.ts`)
  implements OAuth account-avatar binding, per-realm display-name overrides, a
  real reputation-scoring model with graded bands, five jurisdiction profiles,
  the recording-consent decision policy, and an eight-category DSAR export — all
  as pure discriminated-union functions tested by `src/__tests__/index.spec.ts`
  and `recording-consent.spec.ts`.
- **The embodied-safety engine is real, computed policy.**
  `@oshun/lilith-body-policy` (`libs/v3/lilith-body-policy`, ~1,980 lines) is a
  fully-implemented adapter over V1's `@oshun/persona-policy-lilith` and
  `@oshun/avatar-pipeline`: rolling voice-transcript tone scoring, a crisis
  classifier with a measured recall/false-positive gate, costume-rule
  enforcement, avatar-swap impersonation blocking, and a six-persona adversarial
  prompt suite are all _computed_, not stubbed.
- **The shared foundations are genuine V1 packages.** `@oshun/identity`,
  `@oshun/auth-primitives` (the `JwtService`, `OAuthClientRegistry`), and
  `@oshun/audit-platform` (the hash-chained, tamper-evident event ledger) are
  the same packages V1 ships, each with its own test suites. The V3 contracts
  are real Zod under `libs/contracts/src/v3/` (`AvatarBindingSchema`,
  `EmbodiedConsentSchema`).

Three honest qualifications, carried over from the architecture companions.
**First**, the identity bridge is a _pure decision kernel_: it has no database,
opens no socket, and does not itself verify a JWT signature — JWT verification
lives upstream in `@oshun/auth-primitives`, and persistence + audit writes
happen at the call sites (the world server and tenant services). The library
decides; the platform executes. **Second**, the 24-hour avatar-swap cooldown is
a _contract field_ (`AvatarBindingSchema.swapCooldownUntil`) plus a server-side
enforcement obligation — the bridge stamps the binding but does not run the
cooldown clock. **Third**, the heavyweight provenance machinery (C2PA Ed25519
signing, forensic watermarking, Themis rights adjudication) lives in the
persona/rights layer (`saraswati-stage`, `isis-music`, `content-security`), not
in these two libraries; what these libraries own is the _consent and audit
foundation_ everything else hangs from. Each section below says which is which.

## The identity and account bridge

`@oshun/lilith-identity-bridge` is the single translation layer between a V1
account and an embodied V3 session. It resolves who you are, which avatar you
wear, what name and reputation you carry into a realm, and what jurisdiction
rules apply — and it returns _typed decisions_ that the world server persists,
rather than mutating anything itself.

### Account resolution and the headset OAuth bridge

On session start a client presents a V1 JWT, verified upstream by the platform's
`JwtService` (`@oshun/auth-primitives`). Native console / headset sign-in
arrives through the platform OAuth bridge, and the bridge pins exactly three
platforms: `V3_SUPPORTED_OAUTH_BRIDGE_PLATFORMS` is
`['quest', 'vision-pro', 'psvr2']`, and `v3OAuthBridgeIssuerForPlatform()` binds
each to its canonical issuer — `https://auth.meta.com/quest`,
`https://appleid.apple.com/vision-pro`, `https://ca.account.sony.com/psvr2`.
`bindV3AvatarToV1Account()` is the entry point: it normalizes the external
account, derives an `externalAccountKey` of the form
`${platform}:${externalAccountId}`, and **rejects any account whose issuer does
not match the bridge platform** with `oauth-account-not-verified`. A headset
account spoofing its issuer never binds. A successful bind upserts the linked
account and the avatar binding into the profile and emits a
`v3.account_avatar.linked` audit event; one V1 profile can link Quest _and_
Vision Pro _and_ PSVR 2 without losing its active avatar. Notably this is the
_third_ context bound onto the one account root, following the established
`v2-account-binding` module in `@oshun/identity` — a pattern reused, not
invented.

### Avatar binding and the active avatar

There is exactly one active avatar per session. `bindV3AvatarToV1Account()`
stamps the binding with `profileField: 'v3.activeAvatarId'` and
`persistedToV1Profile: true`, and `v3AvatarIdVisibleInV1Profile()` enforces
read-back consistency — an avatar counts as "visible" only when it is the
profile's `activeAvatarId` _and_ appears as a persisted binding. Because the
active avatar id lives on the immutable V1 profile (not a V3-private store), a
DSAR or a deletion reaches it through the existing V1 pipeline. The
anti-harassment 24-hour swap cooldown is the `swapCooldownUntil` contract field;
the world server runs the clock, the bridge only records it. The richer avatar
mechanics — VRM import, costume slots, the Isis provenance bundle on every body
— belong to the avatar pipeline; see
[Avatars, Nameplates & Spatial Audio](./avatars-nameplates-spatial-audio.md).

### Display name, reputation, and presence privacy

Three identity-adjacent decisions round out the bridge. **Display name**
defaults to the V1 Oshun display name, but a per-realm override is allowed as an
anti-stalking measure: `applyV3RealmDisplayNameOverride()` sanitizes the input
(collapses whitespace, strips control characters with
`code > 31 && code !== 127`, caps at 32 characters), writes a
`v3.realm_display_name.override` audit entry, and emits a
`V3OperatorDisplayNameOverrideRow` for the Lilith Operator Console;
re-overriding one realm replaces that alias while other realms' aliases survive.
**Reputation** is never exposed as a number. `deriveV3ReputationSummary()`
computes a real weighted score — a base of 24, an instructor-rating term
(`(avg − 3) × 12` quality plus a `log2`-bounded volume term), an
audience-sentiment term (`positiveRatio × 24 − negativeRatio × 20` plus volume),
completed-review and overturned-report bonuses, and penalties of **−18 per
confirmed policy violation** — then collapses it into graded bands: `new` (<
45), `known` (≥ 45), `trusted` (≥ 75). A confirmed harassment report subtracts a
further 28 and forces a hard `flagged` band with `operatorEscalationRequired`,
regardless of an otherwise high score; `buildV3OperatorReputationRows()` sorts
the moderation queue flagged-first. **Presence privacy** carries the user's
"online in Lilith" visibility band into the digest the world server broadcasts.

### Per-region claims at bind time

Jurisdiction is decided once, at the bridge, not improvised per feature.
`evaluateV3PerRegionRules()` takes a profile, tenant, age, requested
content-rating, fan-token request, and storage/compute zones and returns an
`allow` / `adapt` / `block` decision. The five wave-1 profiles encode real
policy: **US-CA** (`ESRB/IARC`) permits a `native-token` fan economy and pins
storage to `us-west`; **DE** (`USK`, 16+), **GB** (`BBFC/IARC`), **IN**
(`IARC India`, content capped at `teen`), and **BR** (`ClassInd`) fall back to a
`v1-account-benefit` delivery (an _adaptation_, not a block) and pin to `eu-de`
/ `uk` / `in` / `br`. Age floors take the max of the account minimum and the
per-tenant floor — Saraswati Stage's 16+ overrides a 13+ account.
`buildV3PerRegionRulesJurisdictionReport()` runs five fixtures and asserts every
profile is covered with every dimension applied; underage access, over-rating
content, and residency drift each produce a `block` with precise
`blockedReasons`, while a fan-token request in a non-native market becomes an
adaptation rather than a failure. The residency _legality_ of moving data across
zones is decided upstream by `@oshun/data-residency`; the bridge decides which
profile applies and emits the audit tags.

### DSAR continuity

A V3 user's data is part of the one V1 subject record.
`buildV3SessionDsarExport()` assembles **eight categories** — avatar history,
voice transcripts, attendance, signed-edition holdings, recordings, Pixel
Streaming sessions, consent decisions, and the ticket/refund/credit ledger —
into a manifest, a V1 review action (`generate_export_package`), and a
`v3.session_dsar.export_packaged` audit event. It **fails loud**: a missing
category returns `missing-v3-session-data` (coverage is a gate, not a
best-effort smaller bundle), and an encrypted package without a key reference
returns `missing-encryption-key`. `buildV3SessionDsarV1PipelineCoverageReport()`
derives a `fullCoverage` flag proving the export flowed into the V1
`admin-dsar-review` pipeline with every category present. Crucially, the
recording entries carry `provenanceBundleUri`, `c2paManifestUri`, and a
`redactionStatus` — so a DSAR joins identity data to the provenance and consent
trail described below.

## Embodied safety and persona policy at the body

The defining property of `@oshun/lilith-body-policy` is **server authority**: a
UE client may run a costume-rule _preview_ before a wardrobe change commits, but
every binding decision is made on the world server or a tenant service. The
descriptor declares its upstreams exactly —
`['@oshun/persona-policy-lilith', '@oshun/avatar-pipeline']` — and six
capabilities mapping to five evaluators that turn V1 policy into embodied
consequences.

| Trigger                        | Evaluator                                 | Action on violation                                                                           |
| ------------------------------ | ----------------------------------------- | --------------------------------------------------------------------------------------------- |
| Voice transcript (rolling 5 s) | `evaluateV3VoiceTranscriptTonePolicy`     | First miss prompts the speaker; the second routes `voice-quality-violation` to a V1 queue.    |
| Crisis language (rolling 5 s)  | `evaluateV3EmbodiedCrisisRouting`         | In-world soft prompt + V1 crisis-pipeline handoff + on-call operator page.                    |
| Costume change                 | `evaluateV3CostumeRulePolicy`             | Costume blocked; `v3.persona_policy.costume.checked` audit event.                             |
| Avatar swap                    | `evaluateV3AvatarSwapImpersonationPolicy` | Protected-persona swap outside a sanctioned zone routes to a persona-policy-mismatch review.  |
| AI-persona interaction         | `evaluateV3AIPersonaEnforcement`          | Out-of-scope asks graceful-no-op; medical/legal/financial/voice-copy hand off to an operator. |

### The rolling window — context that decays

Voice policy is windowed, not per-utterance.
`buildV3VoiceTranscriptSlidingWindow` assembles a 5,000 ms window
(`V3_VOICE_TRANSCRIPT_WINDOW_MS`) keeping only segments that match the same
session **and** speaker and overlap `[now − 5 s, now)`. By construction another
speaker's chatter and an utterance that aged out are excluded — so a crisis line
that scrolled past the window can't re-fire, and a neighbour's words can't be
scored against you. Tone scoring delegates to V1's
`evaluateLilithContemplativeToneQuality`; a failure short of the repeat
threshold (`V3_TONE_OPERATOR_ROUTE_REPEAT_THRESHOLD = 2`) yields
`prompt-speaker`, and the second failure escalates via
`routeLilithModerationEvent` with the failing tone axes in the payload.

### Crisis routing — measured, then embodied

The same window feeds a crisis classifier built on V1's `detectLilithCrises`.
When a signal fires, `evaluateV3EmbodiedCrisisRouting` assembles three things: a
**non-dismissible in-world soft prompt** (`safety-overlay`,
`dismissible: false`, `blocksPracticeResume: true`, an `assertive` ARIA live
region and a single soft haptic pulse), a handoff to the
`v1-lilith-crisis-routing` pipeline, and an operator notification that pages
on-call and auto-suspends the session. This is not best-effort.
`evaluateV3CrisisClassifierValidationSet` runs a labelled set — **23 positive
crisis cases** spanning the `LilithCrisisType` space (suicide ideation, active
self-harm, acute panic, dissociation, trauma resurfacing, and more) and **15
benign negatives** — and reports `releaseGateGreen` only when **recall ≥ 0.95
and false-positive-rate ≤ 0.02** (`V3_CRISIS_CLASSIFIER_MIN_RECALL` /
`..._MAX_FALSE_POSITIVE_RATE`). The gate is two-sided: a permissive classifier
fails on FPR, a missing one fails on recall. A separate **13-case** drill
(`createV3EmbodiedCrisisRoutingDrillCases`) demands **recall = 1.0** — every
drill prompt must produce the prompt, the handoff, and the page.

### Costume rules and avatar-swap impersonation

`evaluateV3CostumeRulePolicy` runs a wardrobe change through V1's
`lilithSafetyCostumeRuleCheck` (realm-scoped: a costume fine on a concert stage
isn't automatically allowed in a beginners' class) and the V1 surface-policy
binding, returning `allow` / `block` plus a `v3.persona_policy.costume.checked`
audit event. `evaluateV3AvatarSwapImpersonationPolicy` guards persona
impersonation: it matches the requested identity against a registry of protected
personas and **blocks** when there is a match, the requester is not an
authorized avatar, and the swap is not inside a sanctioned cosplay zone —
routing the case to a `persona-registration-policy-mismatch` review with V1
voice-abuse signals attached. Cosplay in a sanctioned zone is explicitly
allowed; impersonation outside one is not.

### AI-persona enforcement — staying in persona is a state machine

The six GA Saraswati AI artists each get a policy profile from
`createV3SaraswatiGAPersonaPolicyProfiles()`, one per genre cell (`electronic`,
`indie-folk`, `hip-hop`, `classical-crossover`, `ambient`, `devotional`).
`evaluateV3AIPersonaEnforcement` resolves one of three actions:
`stay-in-persona` for in-scope asks; `graceful-noop` for jailbreak and
system-prompt-disclosure attempts (matched against literal phrase sets like
`'ignore previous instructions'` and `'reveal your system prompt'`); and
`operator-handoff` for the unsafe classes — medical/legal/financial advice (via
V1's `classifyLilithUnsafeClaimClasses`), living-public-figure voice-copy (via
`detectLilithVoiceAbuseSignals` against a restricted-identity list), and
political persuasion. The release gate is
`evaluateV3SaraswatiAIPersonaAdversarialPromptSuite`: **6 personas × 6
adversarial prompts = 36 cases**, and `passed` is true only when
`policyBreachCount === 0`. Critically, a `stay-in-persona` outcome on a prompt
that _should_ have been refused is itself counted as a breach — so the gate
**cannot** be satisfied by a permissive classifier that simply never refuses.

## Provenance and consent foundations

Provenance in V3 is layered. The cryptographic edge — C2PA Ed25519 signing,
forensic per-recipient watermarking, the Saraswati track provenance bundle, and
Themis rights adjudication on a 24-hour clock — lives in the persona/rights
layer and is documented in the architecture companion and the governance feature
page. What the two libraries on _this_ page own is the **consent and audit
foundation** those mechanisms hang from — the part that decides what is
recorded, how a person appears, and what evidence is retained.

### The recording-consent decision policy

`recording-consent.ts` governs how class and concert capture treats each
participant. For a class, `resolveV3ClassRecordingConsent` maps a choice of
`full` / `observer` / `decline-and-leave` to a concrete avatar/voice/chat
treatment: `full` → `visible` / `master-mix` / `included`; `observer` →
`generic-silhouette` / `excluded-from-master-mix` / `redacted`; decline retains
the ticket in a lobby and records nothing. A **minor** choosing `full` requires
guardian confirmation, and absent it the decision **downgrades to observer**
automatically rather than recording the minor in the master mix. A mid-session
opt-out (`resolveV3ClassMidSessionOptOut`) downgrades retroactively. For
concerts, `resolveV3ConcertTicketRecording` auto-assigns a **privacy seat to
every minor**, makes the privacy seat free (`privacySeatCostDeltaCents: 0`), and
_throws_ if a front-row ticket hasn't affirmed full visibility. A mid-show
privacy switch triggers retroactive redaction and an operator review gate, and
`validateV3AudienceCamCue` forbids an audience camera from ever targeting the
privacy section. Publication is gated: `evaluateV3RecordingPublicationGate`
blocks unless the consent ledger and privacy-seat ledger are valid _and_
redaction has been applied, and a post-publication withdrawal forces a
re-render, re-redaction, and a V1 takedown cascade.

### Audit as the retained evidence trail

Every binding decision, consent change, override, and DSAR packaging publishes a
canonical event into V1's `@oshun/audit-platform` — a **hash-chained,
tamper-evident ledger** (`hash-chain.ts`: `hashEvent`, `chainHashStep`, the
`HashChainedAuditEventStore`) with investigation export and retention tooling.
The bridge emits exactly these event types (`v3.account_avatar.linked`,
`v3.realm_display_name.override`, `v3.session_dsar.export_packaged`); the body
policy emits `v3.persona_policy.costume.checked` and routes crisis/tone/swap
events through the V1 moderation queue. Because V3 reuses the V1 audit platform,
it inherits V1's audit-query, DSAR, and retention behavior instead of
duplicating it — V3 produces the evidence; Oshun retains and exports the
investigation-ready trail. This is the foundation the heavier provenance
bundles, watermark hashes, and C2PA manifests are anchored to.

## How it connects

This page is the trust foundation the rest of the metaverse reads. The avatar
body, its costume slots, and the Isis provenance bundle that the swap-cooldown
and persona-policy checks operate on are owned by
[Avatars, Nameplates & Spatial Audio](./avatars-nameplates-spatial-audio.md) —
the 24-hour cooldown and the locked AI-persona sponsor label cross the two
pages. The operator-facing side of safety — reporting, review, appeals,
minor-protection, and the recording-consent flow end to end — is
[Governance, Safety, Recording & Consent](./governance-safety-recording-consent.md).
The architecture-side internals this page summarizes — the rolling-window math,
the crisis-gate thresholds, the C2PA/Ed25519 signing, watermarking, and Themis
adjudication — are dissected in
[Persona Policy, Provenance & Rights (architecture)](../architecture/persona-policy-provenance-and-rights.md),
and the BFF/contracts/auth/audit integration points are in
[V1 Integration & the Identity Bridge (architecture)](../architecture/v1-integration-and-identity-bridge.md).
The honest line throughout: the identity decisions, the safety evaluators, and
the consent policy are real, computed, and tested; the cryptographic provenance
DSP and the avatar art they govern are the runtime and content they gate.

## Related

- [Avatars, Nameplates & Spatial Audio](./avatars-nameplates-spatial-audio.md) —
  the avatar pipeline, costume slots, swap cooldown, and per-body Isis
  provenance bundle these policies enforce on
- [Governance, Safety, Recording & Consent](./governance-safety-recording-consent.md)
  — the operator console, reporting/appeals, and end-to-end recording-consent
  flow
- [Persona Policy, Provenance & Rights (architecture)](../architecture/persona-policy-provenance-and-rights.md)
  — the C++/TS internals: rolling window, crisis gate, C2PA Ed25519 signing,
  watermarking, and Themis adjudication
- [V1 Integration & the Identity Bridge (architecture)](../architecture/v1-integration-and-identity-bridge.md)
  — the BFF, contracts, auth, and audit integration points around the bridge
- The section hub: [../V3_features.md](../V3_features.md)
