This page documents the governance spine of Living Scenes: the
photosensitive-epilepsy (PSE) / strobe killer, the continuous tone-and-motion
caps, the mid-stream crisis frame, the deterministic-render release gate, the
four redundant provenance channels, and the two-layer (private /
public-redacted) cue-privacy model. It serves the contemplative product, the
AAA-Creator and operator authoring consoles, and trust-and-safety review; in the
V1 stack it sits on top of the Yemaya generation substrate and the Lilith
policy substrate. It is the most genuinely implemented part of the Living Scenes
story because governance is decision logic, not media rendering. Most of what
follows is real, tested code in libs/yemaya/living-scenes-runtime/src/ and
libs/oshun/trust-safety/src/; where a piece is a fail-loud seam awaiting an
upstream model or a real transcoder, this page says so plainly. See
Living Scenes — Concept and Customer Promise for
the customer framing and Lilith Persona Policy for
the policy substrate this composes on.
The unit of safety: hard gates the user cannot turn off#
A Living Scene is a continuous AI-generated video that listens — the user shapes it live by voice, tap, or text. That responsiveness is exactly why the safety story is built as a set of hard, non-overridable gates rather than preferences. Two of those gates — the PSE/strobe killer and the crisis frame — cannot be relaxed by the user, the persona, the admin copilot, or any entitlement tier. The rest of the page works outward from those two.
Photosensitive-Epilepsy (PSE) / Strobe Detector — the hard kill#
detectPseStrobeHazard in
libs/yemaya/living-scenes-runtime/src/safety/pse-strobe-detector.ts is a real
flash-rate analysis after WCAG 2.3.1 / the Harding criteria (not, as an
earlier draft of the features doc claimed, ITU-R BT.1702-2; that standard is
not referenced in the code and was an imprecise citation). It runs on the live
frame-luminance sequence and returns a PseStrobeResult:
interface PseStrobeResult {
safe: boolean; // false ⇒ caller MUST block (non-overridable)
maxGeneralFlashesPerSecond: number;
maxRedFlashesPerSecond: number;
hazards: readonly string[]; // e.g. "general-flash 5/s exceeds 3/s"
}
The algorithm is concrete and load-bearing, so its constants are the safety contract:
| Constant | Value | Meaning |
|---|---|---|
LUMINANCE_FLASH_DELTA |
0.1 |
A general flash needs an opposing-luminance swing ≥ 10% of max. |
DARK_THRESHOLD |
0.8 |
The darker side of the opposing pair must be below 0.8 (a dark-to-bright reversal). |
RED_FLASH_DELTA |
0.2 |
A red flash is the same test on the saturated-red fraction, swing ≥ 0.2. |
FLASH_LIMIT_PER_SECOND |
3 |
More than three general flashes or three red flashes in any one-second window is a hazard. |
The detector walks the luminance series, counts a "flash" at each direction
reversal whose swing from the previous significant extremum clears the delta
(and, for general flashes, whose darker side is below DARK_THRESHOLD), then
takes the maximum number of such flashes inside any sliding window of fps
frames (one second). If either the general or red count exceeds three, safe is
false.
Why a hard kill. The module header is explicit: when safe === false the
caller must block the frame sequence — "no tier, flag, or operator setting may
override it." This is the detector the V1 audit found missing entirely (a Living
Scene could emit a flashing sequence with nothing to detect or block it), so the
implementation is the genuine fix. In the live runtime the kill manifests as an
immediate fade-to-still plus a plain operator-voice notice; the same detector is
re-run in the nightly re-detection sweep (see the takedown disposition below).
Actual pixel-level luminance/motion extraction from rendered frames is the one
upstream piece — the detector consumes a PseFrame[] of relative luminance and
red fraction; the renderer is responsible for producing those numbers.
Continuous tone, luminance, contrast, and motion caps#
Beyond the strobe killer, the runtime enforces per-persona-class budgets on
luminance, contrast, and motion density, plus forbidden-frequency flicker bands
and per-domain narrative-cadence bands. A Tara meditation budget is dramatically
lower than a Veritas explainer budget; templates declare the cap, and the
runtime enforces it. When the runtime intervenes, it emits a
scene.policy-intervention event (see Scene Events below) tagging the
interventionKind — pse-strobe-cap, luminance-cap, motion-cap,
tone-band-shift, or persona-cap — with the pre- and post-clamp values, so
the intervention is observable rather than silent.
Live-cue policy gate and the prompt-injection defense#
Every voice or text cue is policy-checked before it is allowed to queue. The
defense begins in the cue parser
(libs/yemaya/living-scenes-runtime/src/cues/cue-parser.ts): raw customer text
never reaches the segment generator. Instead, sanitizeForCueParse strips
quotes, control characters (U+0000–U+001F and U+007F), code-fence markers, and
HTML tags, collapses whitespace, and truncates to 240 characters; the
sanitized head token is then matched against the small, deliberate
CUE_VERBS_VOCABULARY (linger, advance, morph, recall, shift-style,
shape-by, save-moment, abandon, kill). Unrecognized input returns an
unrecognised verdict (it falls through to a Lilith pre-screen, never to the
generator). For the voice channel, an ASR confidence below
LOW_CONFIDENCE_THRESHOLD = 0.6 yields a low-confidence verdict so a misheard
cue is not acted on. Tap cues are policy-free by construction. The constrained
verb mapping — not a content filter — is the structural prompt-injection
defense: there is no path by which arbitrary text becomes a generation prompt.
Mid-stream crisis frame and cross-surface cascade#
A crisis signal — detected in conversation, in a live cue, or in a kept-artifact
replay viewer — supersedes persona, lineage, pedagogy, grounding, and any
user/admin override. The decision shape lives in
libs/yemaya/living-scenes-runtime/src/cues/crisis-classifier.ts:
LILITH_CRISIS_LABELS=self-harm-imminent,self-harm-ideation,harm-to-others,severe-dissociation,panic-acute,overwhelm,distress,safe.decideCrisisActionmaps the top label to a severity: the imminent set (self-harm-imminent,harm-to-others) →imminent; the severe set (self-harm-ideation,severe-dissociation,panic-acute) →severe;overwhelm/distress→moderate;safe→continue.checkLocaleParityenforces that the imminent-class false-negative rate stays ≤ 0.02 (alongside a macro-F1 floor) per locale — a miss on the most dangerous class is the failure the parity gate exists to catch.
The module owns the taxonomy and the decision shape; the classifier model itself is downstream (the header is candid: "the actual classifier model is downstream; this module owns the decision shape + taxonomy mapping"), so any compatible classifier output can be plugged in.
When a frame is activated, the propagation is real and cross-surface. In
libs/oshun/trust-safety/src/crisis/crisis-frame-cascade.ts, a single event
constant LILITH_CRISIS_FRAME_ACTIVATED_EVENT = 'lilith.crisis_frame.activated'
fans out to five surfaces — CRISIS_FRAME_SURFACES = psyche,
lilith-video, tara, iris, assistant. The published
LilithCrisisFrameActivatedEvent carries two non-overridable directives,
haltSynthesis: true and suspendMemoryWrites: true, so a per-surface
projector cannot soften them. The domain stays free of the event bus (it talks
to a CrisisFramePublishPort); crisis-frame-worker.ts binds the real Redis
bus. consumeCrisisFrameActivation attempts each projector independently and
records every outcome (projected | skipped | failed) so a recovery sweep can
re-project failures — one surface failing to enter its frame never blocks the
others. This is exactly the doc's "fade-to-still + plain operator voice +
suspend Iris memory writes + open a safety-incident record," now with the named
event and the five-surface fan-out (Iris memory-write suspend, Tara scheduling,
video policy).
The crisis frame also reaches the scene event feed as a scene.crisis-frame
event (see below), and any kept artifact whose render history touched a crisis
frame is forced private-only by the shareability matrix.
Determinism, the Render Envelope, and re-render-from-score#
Determinism is a V1 release gate: the same Render Envelope must produce a pixel-equivalent video within tolerance, every time, on any approved GPU node. Score storage — Score + Render Envelope + Cue Plan — is the source of truth for a kept artifact, not the materialized MP4; the MP4 is materialized on first share (when channel-watermark bake-in matters) and re-rendered bit-equivalently on demand.
The gate is checkDeterminism in
libs/yemaya/living-scenes-runtime/src/determinism/determinism-harness.ts, a
pure decision function over per-(node, frame) SHA-256 digests:
interface NodeFrameDigest {
nodeId: string;
frameNumber: number;
sha256: string;
}
interface DeterminismReport {
nodeIds: readonly string[];
frameCount: number;
mismatchedFrames: readonly number[]; // frames where node hashes disagree
perNodeMismatchCount: ReadonlyMap<string, number>;
pass: boolean; // every node within mismatchToleranceFrames
}
It groups digests by frame, flags any frame where the set of node hashes has
more than one unique value, attributes the mismatch to each diverging node
against the reference node, and fails if any node's mismatch count exceeds
mismatchToleranceFrames. A run with fewer than two nodes auto-passes
(there is nothing to cross-check). The actual rendering happens upstream; the
harness is the release-gate decision. When any envelope component is later
deprecated (model upgraded, blend kernel revised, locale assets updated), kept
artifacts gain a "v2 available" notice with a one-tap re-bake; the original
remains playable.
Provenance across channels — four redundant signals#
Provenance is carried in redundant channels so no single transcoding pipeline
can erase it. The runtime does not export a single ProvenanceBundle type
(a symbol by that name exists only in the unrelated
libs/shared/audit-platform); provenance is split into separate, independently
recoverable modules under libs/yemaya/living-scenes-runtime/src/provenance/.
Visible mark and the out-of-band attestation page#
provenance/visible/visible-mark.ts builds a VisibleMarkSpec — a minimal,
contemplative-aesthetic corner glyph (bottom-right at normalized 0.92, 0.94,
oshun-mono, opacity 0.45) carrying a 6-character short code drawn from a
Crockford-style base32 alphabet with confusable characters (0/O/1/I/L)
removed. The attestation URL is buildAttestationUrl({ origin, shortCode }),
which constructs ${origin}/scene/${encodeURIComponent(shortCode)}. Note: the
resolver key is the short code, not an artifact id, and the origin is
injected — there is no hardcoded oshun.app in the builder. Prose that wrote
the attestation page as the literal oshun.app/scene/<id> is therefore stale on
both counts. The page re-asserts provenance even if every embedded mark has been
stripped.
Inaudible audio watermark — a real keyed DSP#
provenance/audio-watermark/dsp.ts is a genuine spread-spectrum embed/extract,
not just a payload encoder. Each ±1 chip is spread across a block of audio
samples by a deterministic ±1 pseudo-noise (PN) sequence and added at low gain;
extraction correlates each block against the same PN sequence:
pnSequence(seed, length)generates the PN sequence via xorshift32 — the seed is the watermark key; embed and extract must share it.DEFAULT_CHIP_LENGTH = 128samples per chip;DEFAULT_GAIN = 0.08.embedWatermarkrequireschipBits.length * chipLengthsamples of headroom and throws otherwise;extractWatermarkrecovers0 | 1 | -1per chip (-1= erasure below an optional correlation threshold).
The key guarantee: the payload is recoverable from the watermarked signal and
robust to moderate additive noise, while a wrong PN key recovers nothing —
correlation ≈ 0. That keyed-correlation property is what makes the audio mark
hard to forge or strip blindly. (Note that the default deterministic Living
Scene render is silent; where there is no audio track, the download grant
honestly reports audioWatermarkIntact: 'not-applicable' rather than claiming a
mark that is not there.)
C2PA Content Credentials — real Ed25519, tamper-evident#
provenance/c2pa/ed25519-signer.ts provides the genuine cryptographic signer
the audit found faked. createEd25519C2paSigner({ privateKey, signerKeyId })
takes a 32-byte Ed25519 secret key (it throws if the length is wrong), keeps
the private key inside the closure, and exposes signerKeyId, publicKeyHex,
sign(bytes) → hex, and verify(bytes, hex) → boolean, all over the
@noble/curves Ed25519 (EdDSA) primitive.
createEd25519C2paVerifier(publicKeyHex) gives consumers a standalone verifier
from the published public key. The signer backs buildC2paManifest in the
sibling c2pa/sidecar.ts, which canonicalizes the media hash
(mediaHashSha256, 64 hex chars), sorted assertions, and claim generator, then
attaches a real signatureHex. The module header is explicit that this
de-fabricates a prior faked signature; the result is a tamper-evident sidecar.
Living Scene shares always carry two assertions: oshun.living_scene.artifact
(artifact id, envelope hash, privacy level, rendition) and a
c2pa.training-mining do-not-train preference. Real C2PA-into-MP4 /
HLS-manifest muxing at materialization time is the upstream piece; the manifest
itself is real and signed.
Channel-robustness eval — the release gate#
provenance/channel-robustness/channel-eval.ts measures per-channel survival
across SHARE_CHANNELS = telegram, whatsapp, twitter-x, instagram,
discord, email, generic-web (seven channels). For each channel,
evaluateChannel records, per fixture, whether at least one of
{ visibleMark, audioWatermark, c2paSidecar } was recovered; a channel passes
iff every fixture surfaced ≥ 1 signal. evaluateAllChannels rolls this up
into perChannel, missingChannels, and allChannelsPass — which is true only
when every channel is present and every channel passes. Below 100% recovery on
a supported channel blocks release of the share path. Real channel transcoding
(running fixtures through the actual platforms' recompression) is upstream; the
harness is the gate decision over recorded recovery results.
Cue privacy, redaction, and sensitive-intention handling#
Every Score's intent slot carries two layers: a private layer and a
public_redacted layer. cue-privacy/intent-redaction.ts owns the redaction
pass over the concrete Lilith taxonomy of eight categories, each a real regex +
replacement token:
LILITH_REDACTION_CATEGORIES |
Example pattern | Replacement |
|---|---|---|
named-person |
Dr Jane Smith, Mrs Smith (title + capitalized name) |
[NAMED-PERSON] |
deceased |
my late mother |
[DECEASED] |
medical-condition |
diagnosed with … |
[MEDICAL-CONDITION] |
abuse-disclosure |
I was abused … |
[ABUSE-DISCLOSURE] |
location-of-safety |
safe house at … |
[LOCATION-OF-SAFETY] |
minor-identity |
my 7-year-old … |
[MINOR-IDENTITY] |
financial-account |
account number … |
[FINANCIAL-ACCOUNT] |
legal-status |
undocumented, asylum-seeker, on probation |
[LEGAL-STATUS] |
redactIntent returns the publicRedactedText plus the set of categoriesHit;
buildIntentLayers wraps that into an IntentLayer and computes a
privateIntentHash by SHA-256 over `${intentId}\0${privateText}`
using @noble/hashes (chosen over node:crypto so the module bundles for the
browser 'use client' surfaces). evaluateCorpus computes per-category and
macro precision/recall against a fixture corpus, so the taxonomy's quality is
measurable rather than assumed.
Honest scope. The classifier header is candid: "Real-world deployment will swap in a model-backed classifier with the same signature; tests pin the categorical taxonomy." The shipped regexes are deterministic pattern-matchers — robust for the test taxonomy, but not an ML classifier. The seam is the safe kind: the signature is fixed and the corpus evaluator is real, so an upstream model can drop in without changing callers.
Default-deny and operator inspection. Sharing exports the public layer only; opting to share with the private layer requires per-share confirmation and is recorded in the artifact's audit log. The private intent layer and cue history are gated to the same authority class as Iris memory inspection (Iris Memory and Identity) — consented or elevated-authorization — while standard support and trust-and-safety reviewers see only the public-redacted layer (Review, Compliance, and Trust & Safety).
Backend resolver — and the contemplative hard guard#
Which generation backend serves a session is decided by resolveBackend in
backend/backend-resolver.ts. There are two backends
(BACKEND_IDS = chained-clip, phase-176-nous) and four entitlement tiers
(ENTITLEMENT_TIERS = contemplative, curated-creator, aaa-creator,
operator-admin). The selector:
- Hard guard: the
contemplativetier never resolves tophase-176-nous, even if the workflow class declares it; it must supportchained-clipand that backend must be able to serve under SLO, or the resolver throws. - Higher tiers sort the class's supported backends by
BACKEND_QUALITY_RANK(chained-clip0,phase-176-nous1) and pick the highest-rank backend that (a) is notphase-176-nous-while-the-phase176FeatureFlag-is-off and (b) passes the per-backend SLO check (canServeNow, finite non-negative headroom). If none can serve, it throwsno-backend-can-serve.
The contemplative guard is the reason a meditation session can never be silently routed onto an experimental world-model backend.
Scene events — the observable governance feed#
Governance actions surface on the Psyche envelope as scene events
(libs/oshun/embodiment-psyche/src/events/scene-events.ts,
SCENE_EVENT_KINDS). Two are governance-specific:
scene.policy-intervention—interventionKind∈pse-strobe-cap,luminance-cap,motion-cap,tone-band-shift,persona-cap, withpreValue/postValueandpolicyVersion.scene.fallback-engaged—fallbackKind∈reduced-motion-render,still-image-only,transcript-only,audio-with-still,pre-rendered-substitute;trigger∈provider-degraded,policy-rejection,eval-gate-fail,shareability-restriction.
Plus scene.crisis-frame itself. verifyEventStream enforces a gap-free,
monotonic sequence and per-session trace-id propagation, so the governance feed
is auditable end to end. See
Psyche Real-Time Runtime for the envelope these
ride on.
Launch locales — a known divergence to reconcile#
"V1 launch locales" is referenced widely but is two different sets in code, and the cue-policy / crisis "locale parity across V1 launch locales" requirement is therefore ambiguous about which set it must hold over:
| Set | Source | Members |
|---|---|---|
OSHUN_LAUNCH_LOCALES (8) |
libs/oshun/i18n/src/index.ts |
en-US, es-US, fr-FR, de-DE, ar, he, ja-JP, pt-BR |
V1_LAUNCH_LOCALES (10) |
libs/isis/workflow-classes/src/living-scene/locale-parity.ts |
en-US, en-GB, es-ES, es-419, fr-FR, de-DE, pt-BR, ja-JP, ar-EG, he-IL |
The Living-Scenes locale-parity module uses the 10-locale set with
regionalized tags and RTL_LOCALES = { ar-EG, he-IL }; the platform i18n module
uses the 8-locale set with bare ar / he. The crisis-classifier locale-parity
gate (imminent FNR ≤ 0.02) must hold over whichever set the deploy targets;
until the two lists are unified, treat the 10-locale LS set as authoritative
for Living-Scenes parity and flag the platform-vs-LS divergence in any
cross-locale launch checklist. See §25.5 (locale parity) and §25.2 (crisis
classifier) for the backlog framing.
Related#
- Living Scenes — Concept and Customer Promise — the customer promise these gates protect.
- Keep, Share, Shareability, Takedown, and Lineage — the share-time companion: the shareability matrix, takedown dispositions, and personal-artifact lifecycle.
- Lilith Persona Policy — the policy substrate the cue gate and crisis frame compose on.
- Psyche Real-Time Runtime — the envelope scene events ride on.
- Review, Compliance, and Trust & Safety — the review queues crisis and abuse signals route into.
- Scene Latency, Accessibility, Eval Sets, and Tests — the fixture eval-gates and quality bars.
- The features hub: ../features.md.