Lilith Metaverse · Architecture

Persona Policy, Provenance & Rights

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

7sections12 minread1diagram1table

On this page

V3 ("Lilith") is an embodied, real-money, AI-generative metaverse where the same body can teach a yoga class, headline a stadium concert, or wander a commons — and where music, motion, costumes, and scenes are generated, not shipped. That combination makes two questions cert-blocking rather than nice-to-have: who is allowed to act through this body, and in what tone (persona policy at the avatar), and who owns, authored, and can revoke the thing this body just made (provenance and rights). V3 answers both by composing V1's safety, audit, and provenance machinery through thin, server-authoritative V3 surfaces — the same "don't reinvent trust" stance the platform takes everywhere. The avatar runs a client-side preview; every binding decision is made on a server. Every generated artifact is born with a provenance bundle and (on release) a signed content credential, and a rights withdrawal cascades on a bounded clock. This page is the architecture-side companion for the "Trust, Rights, Commerce, and Operations" set; the section hub is ../V3_ARCHITECTURE.md.

What ships, honestly#

The policy engine and the provenance/rights data plane are real TypeScript, each backed by automation tests. libs/v3/lilith-body-policy (≈1,980 lines, src/index.ts) is a fully-implemented, server-authoritative adapter over V1's @oshun/persona-policy-lilith and @oshun/avatar-pipeline: rolling voice-transcript tone scoring, a crisis-language 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. On the provenance side, libs/shared/content-security implements C2PA content-credential plans, Google SynthID watermark plans, forensic invisible watermarks, a provenance-chain DAG with cycle detection and an integrity digest, and a trust-scored authenticity verifier; libs/v3/isis-music's track-provenance-bundle.ts writes the V3 Saraswati track bundle; and libs/v3/saraswati-stage's track-c2pa-manifests.ts signs released tracks with a genuine Ed25519 signature (Node crypto). Rights adjudication and the 24-hour enforcement bound are real code in themis-rights-adjudication.ts, and consent ledgers (TTS-voice, class/concert recording) are real in tara-studio and lilith-identity-bridge.

Three honest qualifications. First, @oshun/content-security is a plan/verify layer: it emits the embedding command and the verification policy, defines per-modality detection thresholds, and verifies externally-supplied signature/detection evidence — the actual watermark DSP and the C2PA cryptographic signing run in external tooling, and its stableHash is an FNV-1a content-address for stable IDs, not a cryptographic digest (the V3 track signer, by contrast, is real Ed25519). Second, "Lilith-Rights" is an operator-scope and role name (e.g. the operator id user:lilith-rights:tara-tts-consent), not a single package — the machinery is the composition of content-security, isis-music, saraswati-stage, tara-studio, and the V1 @oshun/audit-platform described below. Third, the full multi-surface takedown fan-out (archived performances flagged historical, in-world replays paused, off-platform reshare notices) is a documented cascade contract driven over the V1 event bus; what is coded and gated is the Themis adjudication, the catalog-license pause, the royalty-waterfall pause, and the 24-hour enforcement deadline. The sections below say where each claim is backed.

Persona policy at the body#

The defining property is server authority. A UE client may run a costume-rule preview before a wardrobe change commits, but @oshun/lilith-body-policy makes every binding decision on the world server or the relevant tenant service. The package's descriptor (v3LilithBodyPolicyDescriptor) declares its upstreams exactly — ['@oshun/persona-policy-lilith', '@oshun/avatar-pipeline'] — and six capabilities, each with an operational metric the tests assert (v3LilithBodyPolicyReadinessScore returns matched/6, so a single live capability scores 0.167). The five invocation points map to five evaluators:

Trigger Evaluator Action on violation
Voice transcript (rolling 5 s) evaluateV3VoiceTranscriptTonePolicy First miss prompts the speaker; the second routes voice-quality-violation to the V1 voice-quality-qa-queue.
Crisis language (rolling 5 s) evaluateV3VoiceTranscriptCrisisClassifierevaluateV3EmbodiedCrisisRouting In-world soft prompt + V1 crisis-response-queue 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 cosplay zone routes to persona-registration-policy-mismatch review.
AI-persona interaction evaluateV3AIPersonaEnforcement Out-of-scope asks graceful-no-op; medical/legal/financial/voice-copy/political hand off to a Lilith operator.

The rolling window — context that decays#

Voice policy is windowed, not per-utterance, which matters for both tone and crisis. buildV3VoiceTranscriptSlidingWindow assembles a 5,000 ms window (V3_VOICE_TRANSCRIPT_WINDOW_MS), keeping only segments that match the session and speaker and overlap [now − 5 s, now); another speaker's chatter and an utterance that aged out are excluded by construction (a property the tests assert: an "outside the active window" line never reaches the verdict). 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 to the operator queue 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, buildLilithCrisisResponsePlan produces the resource set and evaluateV3EmbodiedCrisisRouting assembles three things: a non-dismissible in-world soft prompt (safety-overlay, blocksPracticeResume: true, an assertive ARIA live region and a 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: the classifier is validated against a labelled set of 22 positive cases (one per LilithCrisisType — suicide-ideation, self-harm, acute-panic, dissociation, trauma-resurfacing, substance, interpersonal/other-directed violence, abuse, eating-disorder, child-safety, psychosis-adjacent, acute-grief) and ~60 benign negatives, and evaluateV3CrisisClassifierValidationSet only reports releaseGateGreen when recall ≥ 0.95 and false-positive-rate ≤ 0.02 (V3_CRISIS_CLASSIFIER_MIN_RECALL / ..._MAX_FALSE_POSITIVE_RATE). A separate 13-case evaluateV3EmbodiedCrisisRoutingDrill requires recall = 1.0 — every drill prompt must produce the soft prompt, the handoff, and the page.

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

Each of the six GA Saraswati AI artists gets 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 role-swap and system-prompt-disclosure attempts (matched against literal phrase sets like 'ignore previous instructions', 'reveal your system prompt', 'act as my private assistant'); and operator-handoff for the unsafe classes — medical/legal/financial advice (via V1 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 — a single persona that breaks character, leaks a prompt, or gives medical advice fails the suite. Critically, policyBreached treats a stay-in-persona outcome on a prompt that should have been refused as a breach, so the gate cannot be satisfied by a permissive classifier.

Provenance — every artifact is born documented#

V3 attaches provenance at two layers: a domain bundle that travels with the artifact in-platform, and a C2PA content credential that makes an export verifiable off-platform.

The V3 track provenance bundle#

writeSaraswatiTrackProvenanceBundle (isis-music/track-provenance-bundle.ts) emits a saraswati.music.track-provenance-bundle.v1 record carrying the seven required sections the inspector validates: the model card (provider, model, version, training disclosure, license — e.g. "no living-artist voice clone"), the prompt seed (deterministic seed + fingerprint, so a generation is reproducible), the reference set (each a sha256-hashed source: a Sophia citation, a rights clearance, a persona-policy tag), the generating user, the persona (with its voiceConsentLedgerId), the lineage-credit distribution (which inspectSaraswatiTrackProvenanceBundle rejects unless it sums to exactly 100%), and the artifact refs (master, review preview, inspector URI). The store-level evaluateSaraswatiTrackProvenanceBundleWrite enforces everyGeneratedTrackHasBundle — no track ships without one. The same bundle pattern is what avatar-pipeline uses for avatars (createImportedAvatarProvenanceBundle(), both variants of a premium persona sharing one bundle id), described in Avatar, Animation & Spatial Audio.

C2PA on export — a real signature for tracks, a plan for the rest#

Two C2PA paths exist, and the honesty line runs between them. saraswati-stage/track-c2pa-manifests.ts is the real-signature path: a released track is signed with an Ed25519 key via Node crypto (createDefaultSaraswatiTrackC2paSigner() uses a deterministic dev seed; the production key is KMS-bound, with the Adobe-CAI trust root tracked as [~]), and verifySaraswatiReleasedTrackC2paManifest performs genuine tamper detection against the required assertions (c2pa.actions.v2, org.oshun.v3.track-release, ...generated-artifact-provenance, ...rights-waterfall, ...pro-registration, ...adobe-cai-verification). The shared content-security/provenance.ts is the plan/verify path: createC2PAContentCredentialPlan builds the assertion store, signer binding (ecdsa-p256-sha256), embedding command, and verificationPolicy (requireSignature, requireManifestStore); createSynthIDWatermarkPlan emits a Google-SynthID plan per modality with calibrated detection thresholds (text 0.78, image 0.82, audio 0.80, video 0.84); and verifyContentAuthenticity computes a trust score (C2PA 0.40 + chain 0.25 + AI declaration 0.20 + SynthID 0.15) and a verdict (verified-humanverified-ai-generated / mixed-provenance / insufficient-evidence / tampered), surfacing typed findings for each missing or mismatched signal. The createProvenanceChain DAG proves lineage integrity: it topologically orders nodes, rejects cycles, and records an integrity digest that validateProvenanceChainIntegrity recomputes to catch tampering. This layer plans and verifies; the cryptographic embedding runs downstream.

Forensic watermarking — invisible, per-recipient, robustness-tested#

content-security/watermarking.ts plans the invisible marks that make leaks traceable: per-session video (luma-DCT midband, frame-cadenced tiles, robust to transcode/compression/screen-capture), transcode-robust audio (psychoacoustic spread-spectrum, 1.2–4.2 kHz band, time-scale invariant), resilient image (DCT-wavelet hybrid), and per-recipient document watermarks (recipient code + canary phrases + layout perturbation). identifyWatermarkSource matches an extracted observation back to a registered WatermarkRecord above a confidence floor, and createWatermarkRobustnessTestSuite

  • evaluateWatermarkRobustness define and score the transformation gauntlet each mark must survive. The provenance bundle records the watermark hash; this is how an off-platform reshare ties back to a session and a recipient.
flowchart TB GEN["Generation (Isis / Saraswati / Tara)"] --> BUNDLE["Provenance bundle<br/>writeSaraswatiTrackProvenanceBundle<br/><sub>model card · seed · refs · lineage 100%</sub>"] GEN --> WM["Forensic watermark<br/>content-security/watermarking"] BUNDLE --> REL{Release} WM --> REL REL -->|export| C2PA["C2PA manifest (Ed25519)<br/>track-c2pa-manifests"] REL --> AUDIT["@oshun/audit-platform<br/>hash-chain ledger"] TD["Provenance withdrawal /<br/>rights claim"] --> THEMIS["Themis adjudication<br/>themis-rights-adjudication"] THEMIS -->|≤ 24 h| ENF["Enforce: catalog-license-paused<br/>+ royalty-waterfall-paused"] ENF --> BUS["V1 event bus fan-out<br/>replays paused · editions historical · reshare notices"] ENF --> AUDIT

Rights and takedown#

Themis adjudication on a 24-hour clock#

Ownership and unlicensed-use disputes route through saraswati-stage/themis-rights-adjudication.ts, a three-step state machine: fileSaraswatiThemisRightsDisputeissueSaraswatiThemisRightsDecisionenforceSaraswatiThemisRightsDecision. Dispute kinds include remix-priority-claim, ownership-challenge, sample-use-claim, and independent-rights-holder-takedown; evidence kinds include released-track-c2pa-manifest and consent-ledger (so the C2PA manifest and the consent ledger are first-class adjudication evidence). A decision resolves to credit-claimant, uphold-original-rights, or pause-release, and enforcement emits the corresponding actions: rights-record-updated, royalty-waterfall-updated, catalog-license-paused, claim-denial-audited. The 24-hour SLA is a coded invariant, not a doc promise: SARASWATI_THEMIS_RIGHTS_MAX_ENFORCEMENT_MS = 86_400_000, and enforceSaraswatiThemisRightsDecision throws if enforcement lands more than that after the decision (or before it). evaluateSaraswatiIndependentRightsHolderTakedownFlow ties the knot for the takedown case: it passes only when the outcome is pause-release, the catalog-license-paused action fires with a real catalogHoldRef, and enforcementLatencyMs ≤ 24 h. Upstream, fingerprint-pre-release-check.ts (Audible Magic / Pex) catches collisions before release, returning editorial-review-required when a master matches a major-label or independent reference — so most rights conflicts are stopped at the gate, not litigated after.

Consent is stored as withdrawable ledgers, not one-time checkboxes. tara-studio/tts-voice-consent.ts models the instructor voice-signature grant (tara-tts-voice-signature-consent:v1, purposes aja-cues / localized-dubs, status active/withdrawn, a 30-day withdrawal window, every change written as a rights-log entry under the user:lilith-rights:... operator scope), with sibling contract, scope-lock, and royalty-distribution modules. lilith-identity-bridge/recording-consent.ts governs class and concert capture: a participant chooses full / observer / decline-and-leave, which resolves to a concrete avatarRecordingTreatment (visible / generic-silhouette / not-recorded), voiceRecordingTreatment (master-mix / excluded-from-master-mix), and chatRecordingTreatment, with minor-attendee guardian confirmation required and a mid-session opt-out path. Every consent decision and rights enforcement publishes a canonical event through V1's @oshun/audit-platform — a hash-chained, tamper-evident ledger (hash-chain.ts) with investigation export, compliance attestation, content-license review, and source-asset lineage. V3 produces the evidence; Oshun retains and exports the investigation-ready trail, and a DSAR over a V3 user joins these records to the UE/Pixel-Streaming session data the identity bridge holds.

Edge cases and failure modes#

  • Context decays, deterministically. The 5 s window excludes other speakers and aged-out utterances structurally, so a crisis line that scrolled past the window can't re-fire and a neighbour's words can't be scored against you.
  • The crisis gate is two-sided. A permissive classifier can't pass — recall ≥ 0.95 catches misses and FPR ≤ 0.02 catches over-triggering; the drill demands recall = 1.0 for the full embodied response (prompt + handoff + page).
  • Lineage must total 100%. A provenance bundle whose credit shares don't sum to exactly 100 is marked incomplete, and the store refuses to pass — royalty routing can't be ambiguous.
  • Takedown is bounded, not eventual. Enforcement outside the 24-hour window throws; the takedown flow asserts the catalog hold and the latency bound together, so "paused, eventually" is not a valid state.
  • Real signature vs. content-address. The track C2PA path detects tampering with a genuine Ed25519 signature; the shared stableHash is an FNV-1a identifier, and authenticity verification consumes externally-supplied signature evidence rather than minting it — read it as a verifier, not a signer.
  • "Lilith-Rights" is a role, not a service. Looking for a lilith-rights package will mislead; the capability is the composition named above, gated by the operator scope that appears in the consent ledgers.

How it connects#

Persona policy reaches up into the embodiment stack and down into identity and commerce. The costume and avatar-swap checks operate on the realms, costume slots, and lilithSafetyCostumeRuleCheck defined in Avatar, Animation & Spatial Audio, and the account ↔ avatar binding every decision is made against is owned by the bridge described in V1 Integration & the Identity Bridge. The rights side is the trust spine of the economy: the lineage-credit distribution in the provenance bundle is exactly the royalty waterfall that Commerce & Royalties settles on-chain via Aje, and a Themis pause-release is what stops a payout mid-flight. The @oshun/* packages this page composes — persona-policy-lilith, avatar-pipeline, content-security, audit-platform — sit in the platform domain map at ../../platform/oshun-domain-libraries.html.