Lilith Metaverse · Features

Identity, Embodied Safety & Provenance Foundations

A focused page within the Lilith Metaverse Features documentation. The full map and every sibling page live in the Features hub.

6sections13 minread1diagram1table

On this page
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.

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.

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 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.

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: fullvisible / master-mix / included; observergeneric-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.