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.
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 insrc/index.tsplusper-region-rules.tsandrecording-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 bysrc/__tests__/index.spec.tsandrecording-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-lilithand@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(theJwtService,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 underlibs/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.
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 — 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. 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), and the BFF/contracts/auth/audit integration points are in V1 Integration & the Identity Bridge (architecture). 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 — the avatar pipeline, costume slots, swap cooldown, and per-body Isis provenance bundle these policies enforce on
- Governance, Safety, Recording & Consent — the operator console, reporting/appeals, and end-to-end recording-consent flow
- Persona Policy, Provenance & Rights (architecture) — the C++/TS internals: rolling window, crisis gate, C2PA Ed25519 signing, watermarking, and Themis adjudication
- V1 Integration & the Identity Bridge (architecture) — the BFF, contracts, auth, and audit integration points around the bridge
- The section hub: ../V3_features.md