Oshun Platform · Features

Keep, Share, Shareability, Takedown, and Lineage

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

9sections11 minread4tables

On this page

This page documents what happens to a Living Scene after it is rendered: how a user keeps a session as a personal artifact, how an explicit second consent turns it into a share, the per-component shareability matrix that decides how far an artifact can reach, the public-viewer and embed surface, the seven-disposition takedown cascade, tenant share policy, and reshare / lineage / remix attribution. It serves the contemplative product (users keeping and sharing offerings), the public web (a per-artifact viewer), and tenant and operator administration. In the V1 stack it sits between the renderer and the customer/public surfaces; the domain logic lives in libs/yemaya/living-scenes-runtime/src/personal-artifacts/personal-artifacts.ts and …/shareability/matrix.ts and …/takedown/dispositions.ts, and the HTTP surface is the 1871-line BFF route at apps/oshun/bff/src/routes/living-scenes.ts. The safety/provenance companion to this page is Scene Safety, Determinism, Provenance, and Cue Privacy.

Keep is one action; Share is a second, consented action#

Keep is the default personal-artifact action: at any point during or after a session, the user saves the current Score + Render Envelope + Cue Plan as a kept Living Offering. createKeptLivingOffering validates the score and envelope, binds the artifact to the score author account (it throws ownership-mismatch if the score's authorUserId differs from the keeper), computes a canonicalEnvelopeHash, attaches a full accessibility-artifacts bundle (reduced-motion carousel, per-locale VTT captions, audio-description tracks, transcripts, accessible viewer chrome), and stamps a retention envelope. Score storage is small (kilobytes per artifact), so the persistence policy is lifetime, no quota-based eviction — the BFF keep route replies with bffPersistencePolicy: 'lifetime-no-quota-eviction'. Retention policy is one of LIVING_SCENE_RETENTION_POLICIES = '30-day' | '1-year' | 'indefinite' (default indefinite); quotaEvictionAllowed is hardcoded false and tombstoneRequiredOnDelete true. The keep endpoint is POST /v1/living-scenes/artifacts/keep; the owner's list is GET /v1/living-scenes/artifacts.

Share is an explicit, separately-consented action. createLivingSceneShare requires explicitConsent plus a non-empty consentReceiptId distinct from Keep — it throws consent-required otherwise — and then materializes a share record (LivingSceneShare) carrying a short-code, privacy level, rendition, a visible mark, a signed C2PA manifest, a Lilith pre-share check, and an attestation URL. The share endpoint is POST /v1/living-scenes/artifacts/:artifactId/share. The BFF makes both keep and share content-addressed and idempotent: shares are stored by shareId with a shareIdByShortCode index, so a re-submitted identical request resolves to the same record rather than duplicating.

The four share privacy levels (and the rendition choice)#

A share carries one of four privacy levels — LIVING_SCENE_SHARE_PRIVACY_LEVELS:

Level Meaning
private-link Default. Unguessable URL, no listing; expires only when revoked.
link-with-password Shared URL guarded by a user-set passphrase (createLivingSceneShare throws password-required if the level is set with no password).
tenant-internal Visible to members of the sharing tenant only; the public viewer returns tenant-auth-required to outsiders.
public Surfaceable via Open Graph and embed; opt-in per share.

A share also chooses a LIVING_SCENE_RENDITIONS value — full-motion or reduced-motion — so a user can deliberately share the still-image carousel instead of the moving render. Note the internal-consistency fix this page makes explicit: the privacy-level set has four members, and the shareability matrix (below) likewise uses four reach levels — earlier prose that described per-component grants as a three-level tag (shareable | tenant_internal_only | private_only) omitted link-with-password and was internally inconsistent with the same document's four-level privacy list. The code is the four-level authority.

Indexability, robots, and the attestation URL#

The default robots meta is the literal constant LIVING_SCENE_DEFAULT_ROBOTS_META = 'noindex, nofollow, noarchive'. Indexability is opt-in and only honored at the public level: indexable is input.indexable === true && privacyLevel === 'public', and only then does robotsMeta flip to LIVING_SCENE_INDEXABLE_ROBOTS_META = 'index, follow'. Even at the public level the artifact does not surface in any Oshun-curated discovery feed at V1. The attestation URL is built as `${origin}/scene/${encodeURIComponent(shortCode)}` from the visible mark's short code (a deterministic 6-character Crockford-style code), not from an artifact id and not against a hardcoded oshun.app host — the origin is injected. Embed (<iframe> + oEmbed) is offered only for private-link and public shares; it is denied for link-with-password and tenant-internal, and any tenant embedAllowed === false denies it too.

Pre-share Lilith re-check (honest about materialization)#

createLivingSceneShare runs evaluatePreShareLilithCheck on the materialized artifact before issuing the share, and throws lilith-blocked if the verdict is not pass. The check blocks for a deleted artifact, an invalid C2PA hash (mediaHashSha256.length !== 64), a missing 6-char visible mark, a requested privacy level that exceeds the resolved shareability reach (privacy-exceeds-shareability:<reach>), or any segment/cue that trips a blocked safety phrase. Crucially, a pending MP4 materialization is non-blocking and reported honestly: the deterministic render still serves a watermarked, C2PA-signed (PNG) artifact, so an unbaked MP4 is surfaced as a materialization-pending note, never silently claimed as baked-in and never a block. The download grant is equally candid: audioWatermarkIntact is 'not-applicable' for the silent Living Scene render rather than a fabricated true.

Revocation, tombstones, and abuse reports#

The originating user can revoke any share at any time; revokeLivingSceneShare sets state revoked, kills Open Graph / embed / indexability and resets robots to noindex. tombstoneShare does the same with state tombstoned. The public viewer (resolvePublicViewer) returns a tombstone resolution whose message is the constant PUBLIC_VIEWER_TOMBSTONE_COPY = 'This offering has been retired.' for a missing/revoked share or a deleted artifact. The standard revocation SLA is the constant LIVING_SCENE_REVOCATION_SLA_SECONDS = 60 (one minute). Each public page carries an independent abuse-report flow (recordAbuseReportPOST /v1/living-scenes/public/:shortCode/report) routing to the lilith-living-scene-public-reports queue; reports are stored idempotently by a stable reportId and triaged through open → resolved | dismissed. The BFF also implements detectMassSharePattern to gate one-user-to-many-channels spam vectors, and the public surface is rate-limited per IP and per artifact. The public viewer is contemplative by construction: autoplayWithSoundAllowed is hardcoded false, there is no recommended-feed sidebar, and accessibility artifacts ride along.

The shareability matrix — nine components, four reach levels, min-reach#

Every kept artifact carries a per-component shareability matrix, computed at save time and re-evaluated at share time, resolved by resolveShareability in shareability/matrix.ts. There are nine SHAREABILITY_COMPONENTS:

voice, likeness, persona, source, music, intent, tenant-policy, lilith-policy, crisis-state.

Each component carries a ComponentGrant whose reach is one of four ordered REACH_LEVELS:

Reach Order Meaning
private-only 0 Most restrictive.
link-with-password 1
tenant-internal 2
public 3 Least restrictive.

The resolution rule is minimum reach across all components: the artifact's reach is the most-restrictive component's reach (minReach), and the verdict returns the decidingComponents (every component sitting at the minimum) so the UI can explain why. Two hard rules:

  • All nine components must be present. resolveShareability throws ShareabilityError('missing-grants', …) if any of the nine is absent (and duplicate-component / invalid-grant for malformed input). There is no silent default — an artifact cannot be shareable by omission.
  • Crisis-state has hard precedence. If the supplied crisisLabel is non-null and not 'safe', the verdict is { kind: 'crisis-private-only', crisisLabel } before any component is even examined. An artifact whose render history touched a Lilith crisis frame is private-only regardless of every other grant, with a soft notice.
ts
type ResolutionVerdict =
  | {
      kind: 'resolved';
      resolvedReach: ReachLevel;
      decidingComponents: ShareabilityComponent[];
    }
  | { kind: 'crisis-private-only'; crisisLabel: string };

A per-component grant reflects the component's own consent record — a cloned voice's share-out grant, a teacher persona's contractual share rights, a Sophia source's redistribution license, a music workflow class's rights metadata, tenant and Lilith policy, and crisis state. When full-motion sharing is restricted the system offers reduced-shareability fallbacks (still-frame preview with audio muted, transcript-only, audio-with-still) and the user picks — the system never silently degrades; that fallback is observable on the scene feed as a scene.fallback-engaged event with trigger: 'shareability-restriction'.

Takedown cascade — seven dispositions, one structured impact#

When an upstream input is retired, retracted, or terminated, the takedown cascade extends into user-owned and user-shared Living Offerings. takedown/dispositions.ts defines seven DISPOSITION_KINDS, each producing a structured DispositionImpact the UI consumes — covering playback affordance, banner copy, re-render path, and shared-link state:

Disposition playback rerenderOption sharedLinkState
voice-profile-retired voice-muted-with-banner substitute-voice-or-text-only banner-only
sophia-source-retracted unchanged corrected-source banner-only
workflow-class-frozen unchanged (plays from cache; no re-render/reshare) none-workflow-frozen banner-only
lilith-policy-drift unchanged (with policy-version notice) reapply-current-policy banner-only
re-detection-sweep (PSE/strobe/luminance/motion) quarantined-until-rerender recompute-detectors invalidated
persona-contract-terminated voice-muted-with-banner substitute-persona banner-only
originating-user-account-deletion hard-deleted-tombstoned none-deleted tombstoned

Each apply… function returns a frozen impact with concrete banner copy — for example, the voice-retired banner is "The voice in this scene (profile …) has been retired. Audio narration is muted; re-render this scene with a substitute voice or as text-only narration." The re-detection-sweep disposition is the one that quarantines and invalidates the shared link until re-render under the updated detector completes — it is the takedown side of the PSE/strobe killer described in Scene Safety, Determinism, Provenance, and Cue Privacy. workflow-class-frozen disables resharing precisely because reshare requires a re-bake for channel-watermark robustness, which a frozen class cannot perform. Nightly cascade-reach audits report SLA compliance across the artifact corpus (BFF GET /v1/living-scenes/tenant/audit).

Tenant share policy — fail-closed by construction#

Tenant Console gains a Living Scenes governance surface, and the share flow consults tenant policy at every step. In the BFF, resolveTenantSharePolicy resolves the effective policy for the request: in non-production it can honor header-policy overrides gated behind the OSHUN_LIVING_SCENES_HEADER_POLICY_OVERRIDES flag, otherwise it reads the stored tenant policy. createLivingSceneShare enforces it: a tenant policy whose state is ambiguous (or not resolved, or not shareAllowed) throws tenant-policy-deniedambiguous policy resolves to deny (the route surfaces a denied_tenant_internal_scope-style disposition). A tenant can outright deny share, deny public share while allowing tenant-internal only (publicSharingAllowed === false, tenantInternalSharingAllowed), set retention defaults (30-day | 1-year | indefinite, with user preference within tenant policy), and apply a tenant-branded public viewer (logo, accent color, attribution copy) for tenant-internal shares. Every share action is recorded with full audit metadata, and the tenant operator can revoke any tenant-member share (POST /v1/living-scenes/tenant/shares/:shareId/revoke). Institution-tenant Metis Lesson Visualizer templates default student-generated share scope to tenant-internal with operator override. See Tenant, Institution, and Operator Toolkit.

Public share surface, embed, and the route map#

The complete public/owner/tenant route surface in apps/oshun/bff/src/routes/living-scenes.ts:

Route Purpose
POST /v1/living-scenes/artifacts/keep Keep a session as a lifetime artifact.
GET /v1/living-scenes/artifacts List the caller's kept offerings.
GET /v1/living-scenes/artifacts/:artifactId Fetch one artifact.
POST /v1/living-scenes/artifacts/:artifactId/share Create a consented share.
POST /v1/living-scenes/shares/:shareId/revoke Owner revoke.
POST /v1/living-scenes/tenant/shares/:shareId/revoke Tenant-operator revoke.
POST /v1/living-scenes/admin/artifacts/:artifactId/takedown Admin takedown disposition.
GET /v1/living-scenes/tenant/audit Tenant share-audit feed.
GET /v1/living-scenes/public/:shortCode Public viewer resolution.
GET /v1/living-scenes/public/:shortCode/media Watermarked media bytes.
POST /v1/living-scenes/public/:shortCode/unlock-token Exchange a verified password for an unlock token.
GET /v1/living-scenes/public/:shortCode/embed oEmbed / iframe.
POST /v1/living-scenes/public/:shortCode/download-grants Per-share download grant.
GET /v1/living-scenes/public/:shortCode/transcripts/:locale Accessible transcript.
POST /v1/living-scenes/public/:shortCode/report Independent abuse report.

resolvePublicViewer keys on the short code, returns one of playable | password-required | tenant-auth-required | tombstone, and generates Open Graph + Twitter Card previews from the first frame plus the public-redacted intent — previews never expose the private intent (publicTitle / publicDescription read only score.intent.publicRedactedText). The unlock-token route lets the BFF skip the password gate on revalidation calls without the raw password riding along (unlockTokenVerified). These flows are exercised by the e2e specs public-scene-abuse-report, scene-viewer-deepening, and living-scene-card (per WALKTHROUGH/results/v1-triage-2026-06-23.md).

Reshare, lineage, and remix attribution#

The Output Gallery lineage tree (see Output Gallery, Lineage, Branch, and Replay) extends to user-owned artifacts; lineage itself is owned by the gallery, not by a separate Living-Scenes module:

  • Reshare of a public link preserves attribution to the originating user by default; a tenant-internal reshare cannot escalate to public.
  • Replay-with-tweak from a public artifact produces a child artifact in the lineage tree with attribution-policy edges; the default strips identifying metadata of the original creator unless they opted into derivative attribution.
  • Branch from this moment forks a new score from a kept artifact at a chosen segment boundary; the new score's seeds and Cue Plan are user-owned; the original is unchanged.
  • Lineage display shows the user's own derivations and (with attribution consent) parent-creator attribution; the admin surface sees the full lineage including non-attributed parents.
  • Remix takedown of a parent propagates a retraction notice to children but does not delete child artifacts authored by other users — each child retains its own content and provenance.

Launch-locale note for shared/accessible artifacts#

Per-template locale parity is the constant V1_LAUNCH_LOCALES (ten regionalized locales: en-US, en-GB, es-ES, es-419, fr-FR, de-DE, pt-BR, ja-JP, ar-EG, he-IL, with RTL_LOCALES = { ar-EG, he-IL }) in libs/isis/workflow-classes/src/living-scene/locale-parity.ts. Note this differs from the platform's eight-locale OSHUN_LAUNCH_LOCALES set (libs/oshun/i18n/src/index.ts, bare ar / he) — the two lists disagree, and the divergence is called out more fully in Scene Safety, Determinism, Provenance, and Cue Privacy. For shared artifacts the accessibility bundle (captions, audio description, transcripts) is generated per the locales requested at keep time.