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 (recordAbuseReport →
POST /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.
resolveShareabilitythrowsShareabilityError('missing-grants', …)if any of the nine is absent (andduplicate-component/invalid-grantfor malformed input). There is no silent default — an artifact cannot be shareable by omission. - Crisis-state has hard precedence. If the supplied
crisisLabelis 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 isprivate-onlyregardless of every other grant, with a soft notice.
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-denied — ambiguous 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.
Related#
- Scene Safety, Determinism, Provenance, and Cue Privacy — the PSE killer, crisis frame, determinism gate, and the provenance signals a share carries.
- Living Scenes — Concept and Customer Promise — the customer framing of kept Living Offerings.
- Output Gallery, Lineage, Branch, and Replay — the lineage tree reshare and branch extend.
- Tenant, Institution, and Operator Toolkit — tenant share policy, branding, and audit.
- Review, Compliance, and Trust & Safety — the queues abuse reports route into.
- Privacy, Consent, Data Portability, and User Controls — the consent and deletion model keep/share inherit.
- The features hub: ../features.md.