Oshun Platform · Architecture

Generation Audience Tiers

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

11sections15 minread1diagram5tables

On this page

Every AI generation in V1 — a curated devotional image, a narrated contemplative arc, an instrumental ambient bed, a creator's raw ComfyUI graph, an operator's RunPod dashboard — is gated by a single, deterministic tier resolver before a pixel or sample is produced. This page documents the canonical tier taxonomy, the 28 named generation surfaces and their per-tier allowlists, the enforcement spine a request crosses (entitlements → Lilith → Isis → provider → provenance → audit), and the real, provider-gated execution substrate behind it. It serves product, platform, and operator readers who need to know exactly what a given audience can do and where the fail-closed boundaries sit. It lives in the generation-pipeline area alongside Agentic AI Studio and Living Scenes, and rests on the Isis — Generation Control Substrate.

Product scope: V1/features.md § Generation Audience Tiers and Surface Boundaries.

The canonical tier taxonomy#

V1 has four generation audience tiers. The single source of truth is the GenerationTier union in libs/isis/entitlements/src/generation-tier.ts (lines 23–28), frozen as the GENERATION_TIERS array:

ts
type GenerationTier =
  | 'operator-admin'
  | 'aaa-creator'
  | 'curated-creator'
  | 'contemplative';

These four code-level identifiers are the canonical taxonomy. Earlier prose (V1/features.md–3230) named the tiers Customer, Curated-Creator, AAA-Creator, and Operator and claimed all four were "used verbatim" — but two of those do not match the implementation. The mapping below reconciles the product-facing labels with the enforced code identifiers:

Product-facing label Enforced GenerationTier Who it is
Customer contemplative The default consumer of finished, curated products — no raw generation surfaces.
Curated-Creator curated-creator A creator working only inside curated workflow classes — no raw model picker, no Civitai.
AAA-Creator aaa-creator A power creator with the full graph editor, Civitai browser, LoRA training, model merging, 3D.
Operator operator-admin Platform staff: full surface set plus admin-only review queues, RunPod, audit, node registry.

Free-versus-paid is a subscription distinction within the contemplative (Customer) tier, not a fifth tier. The resolver knows nothing about billing; it reads entitlement tags only.

How a tier is resolved#

resolveGenerationTier(entitlement) (line 134) is the deterministic resolver that BFF, Admin, and Studio middleware all consult before rendering any generation surface. It takes a GenerationEntitlement{ id, tenantId, userId, persona, tags } — and returns a ResolvedTier of { entitlement, tier, surfaceAllowlist }. Resolution is a strict, ordered precedence over the normalized tag set (resolveTierFromTags, line 143):

  1. tag operator-admin present → operator-admin;
  2. else tag aaa-creatoraaa-creator;
  3. else tag curated-creatorcurated-creator;
  4. else → contemplative (the deny-by-default floor).

Tags are trimmed, empties dropped, and a non-array input degrades safely to an empty set (normalizeEntitlementTags), so a malformed entitlement falls to contemplative rather than silently escalating. This is the why behind the ordering: the most privileged tier wins only on an explicit positive tag, and the absence of any recognized tag always lands a user on the smallest surface set.

The 28 generation surfaces#

The tier vocabulary is not abstract — it resolves to a frozen catalog of 28 named GenerationSurface values (GENERATION_SURFACE_VALUES, lines 35–68, exported frozen as GENERATION_SURFACES). Each surface is a concrete UI or capability that the resolver either grants or denies. The canonical list, grouped by the tier band that introduces it:

Band Surfaces
Contemplative (curated) curated-image, curated-living-scene, curated-voice-clip
Curated-creator curated-workflow-pick, curated-asset-search
AAA-creator graph-editor, civitai-search, civitai-lora-hash-picker, lora-trainer, model-merger, model-comparison, runpod-region-selector, gpu-worker, multi-gpu-orchestration, gaussian-splatting, auto-rigging, topaz, rife, animatediff, voice-cloning-tool, music-generation, 3d-generation
Operator-admin (admin-only) runpod-dashboard, intake-review-queue, lora-training-queue, output-gallery-admin, comfyui-node-registry, audit-trail

Per-tier allowlists#

TIER_ALLOWLISTS (lines 87–126) is a frozen Record<GenerationTier, GenerationSurface[]> that binds each tier to exactly the surfaces it may reach. The bands are cumulative: aaa-creator inherits the contemplative and curated-creator surfaces and adds the heavy creative tooling; operator-admin is the full GENERATION_SURFACES set (so it includes every admin-only review and infrastructure surface). The exact grants:

Tier Surface count Surfaces granted
contemplative 3 curated-image, curated-living-scene, curated-voice-clip
curated-creator 5 the 3 contemplative surfaces + curated-workflow-pick, curated-asset-search
aaa-creator 22 the 5 curated surfaces + all AAA-creator surfaces (graph editor through 3d-generation)
operator-admin 28 all of GENERATION_SURFACES (everything, including the 6 admin-only surfaces)

The contemplative floor is deliberately narrow: a Customer can only ever touch finished, curated products — never a raw model picker, a graph editor, or Civitai.

Surface access is deny-by-default#

checkSurfaceAccess({ entitlement, surface }) (line 180) is the guard every surface lookup calls. It resolves the tier, then returns a SurfaceAccessVerdict:

  • { verdict: 'allow', tier } when the surface is on the resolved allowlist;
  • { verdict: 'deny', tier, action: { kind: '404-hard-block' } } otherwise.

The verdict type also allows a softer { kind: '404-with-aaa-cta' } action, but checkSurfaceAccess itself never issues it — CTA handling is reserved for product-shell boundary guards that can prove the caller is already entitled to the surface but reached it from the wrong host app. Surface access in isolation is strictly deny-by-default: an unknown or unentitled surface yields a plain 404, not an upsell.

Where each tier actually lives#

Tier Product shell Capability cap
contemplative Living Offerings (Arete), Contemplative Arcs (Tara), Grounded Explainers (Veritas), Sky Briefings (Nyx), Lesson Visualizers (Metis) Tone-gated; no raw graph editor; provenance always on.
curated-creator apps/oshun/web/src/app/studio/generation + studio/generation-gallery Curated workflow classes only; no raw model picker, no Civitai access.
aaa-creator apps/yemaya/studio-web + apps/yemaya/studio-desktop (Yemaya Studio) Full graph editor, Civitai browser, LoRA training, model merging, 3D.
operator-admin apps/oshun/admin/ with @oshun/generation-control-isis Workflow graph editor, intake review queues, RunPod power-user surfaces.

The contemplative ↔ Yemaya AAA Studio boundary#

The legacy customer-facing tree under apps/oshun/web/src/app/studio/isis/* is now fail-closed. libs/isis/entitlements/src/studio-boundary.ts exports STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS as an empty frozen array — so isAaaOnlyRoute() treats every non-empty legacy isis segment as AAA-only. The approved customer-facing generation surfaces live under /studio/generation/* and /studio/generation-gallery, not under the legacy provider-machinery tree.

This corrects a stale claim: earlier docs said AAA-tier studio/isis/* routes return "disclosure + Yemaya signup gate." In reality, because the allowlist is empty, those legacy segments hard-block to 404 for a contemplative user. resolveStudioBoundary({ routeSegment, entitlement }) only emits the render-yemaya-cta outcome (disclosure copy + /aaa-upgrade link) when the caller is already an aaa-creator or operator-admin who landed on the wrong shell; everyone else gets hard-block-404. The AAA_ONLY_STUDIO_ROUTES catalog (e.g. 3d-generation, lora-training, model-merging, music-generation, multi-gpu-orchestration, comfyui-nodes, text-to-speech) is retained for documentation, tests, and operator diagnostics — not as a live allowlist.

A Yemaya output can flow back into the contemplative product through the editorial pipeline. YemayaPromotionRequest (studio-boundary.ts:101) posts to /api/isis/output-gallery/promote with a proposedCardKind restricted to curated-image | curated-living-scene | curated-voice-clip | curated-workflow-pick; validatePromotionRequest rejects missing output ids, tenants, authors, notes, or a non-curated card kind before forwarding to the §16 editorial lifecycle.

Generation request flow#

A generation request crosses the same enforcement spine regardless of tier: entitlements gate the surface, Lilith gates the tone and crisis frame, Isis dispatches against a versioned workflow template, the provider executes sandboxed, and a ProvenanceBundle + watermark + audit record are produced before the response returns. The provider node is intentionally drawn with the hosted adapters that the live customer-facing executors actually call (Stability, ElevenLabs, Suno, fal.ai), not the operator substrate (RunPod/ComfyUI) — see Why the provider node is hosted, not ComfyUI.

sequenceDiagram autonumber actor U as User / Creator participant BFF as BFF participant E as Entitlements participant L as Lilith<br/>(tone · crisis) participant I as Isis Control Plane participant P as Provider<br/>Stability · ElevenLabs · Suno · fal.ai (LTX) participant Pr as Provenance participant A as Audit U->>BFF: Generate request (prompt, surface, tier) BFF->>E: resolveGenerationTier + checkSurfaceAccess E-->>BFF: allow / deny (404-hard-block) BFF->>L: Tone band + crisis-frame check L-->>BFF: directive (allow / reduced / suppress) BFF->>I: Dispatch with WorkflowTemplate + ModelVersion I->>P: Execute (sandboxed, env-gated provider) P-->>I: Output (audio / image / video) I->>Pr: Build ProvenanceBundle<br/>consent · prompt · model · watermark · timestamp Pr-->>I: bundleRef I->>A: Audit generation (bundleId, actor) I-->>BFF: result + bundleRef + releaseMeasurement BFF-->>U: result (watermarked · C2PA manifest) or fail-closed block

The BFF generation executors#

The concrete request handlers live in apps/oshun/bff/src/generation/. Each is a real executor paired with a *-provider-env.ts resolver and a test:

  • image (image-executor.ts) — text-to-image;
  • narration (narration-executor.ts) — TTS;
  • music + music-enqueue (music-executor.ts, music-enqueue-executor.ts) — batch ambient/instrumental music;
  • video (video-executor.ts) — text-to-video and image-to-video;
  • caption-dub (caption-dub-executor.ts) — captioning + dubbing;
  • accessibility-pass (accessibility-pass-executor.ts);
  • explainer (explainer-executor.ts);
  • sky-briefing + sky-briefing-enqueue (sky-briefing-executor.ts);
  • curated-generation (curated-generation-executor.ts);
  • realtime-music (realtime-music-route.ts) — the live streaming path.

Every executor routes its output through a release measurement (provider-measurement.tsbuildReleaseMeasurement) that evaluateGenerationRelease (release-gate.ts:116) grades into one of three GenerationReleaseStatus values — complete, needs_review, or blocked. The gate is fail-closed: a result with no measurement, a malformed measurement, or a missing governance signal returns blocked with a specific reason (e.g. governance_measurement_absent, governance_measurement_invalid). Two output classifiers gate content before release: image-safety-classifier.ts scans image URLs and text-safety-classifier.ts scans spoken/explainer text. When a scanner is absent or its scan throws, the safety score is null and the gate blocks — an unscanned output is never released ungoverned.

The live providers (env-gated, fail-closed by design)#

Every live provider call is environment-gated and fail-closed. With no credential the resolver returns null, the executor never receives a generate function, and the job fails closed with provider_not_configured — never a fabricated result. Per their own source comments, the HTTP paths to each hosted provider are "exercised at deploy time with a live key," not in default CI; the env→provider wiring, request/result adaptation, and measurement emission are unit-tested.

Output kind Live provider Adapter symbol & path Required env
Image Stability SD3.5 (hosted) StabilityProvider@isis/ai-providers/providers/image-generation OSHUN_STABILITY_API_KEY (or STABILITY_API_KEY)
Voice / TTS ElevenLabs createElevenLabsProvider@psyche/voice-synthesis OSHUN_ELEVENLABS_API_KEY and OSHUN_ELEVENLABS_VOICE_ID
Batch music Suno SunoProvider@isis/audio-generation/generation OSHUN_SUNO_API_KEY
Realtime music Google Magenta RealTime (MRT2) MagentaRtProvider over @euterpe/providers / @euterpe/realtime-gen OSHUN_MRT2_ENABLED=true + on-device runtime loads
Video fal.ai-hosted LTX-Video LTXProvider@isis/ai-providers/providers/video-generation fal.ai key

The real env-var contract#

These are the deploy-time toggles that flip a surface from fail-closed to live:

  • ImageOSHUN_STABILITY_API_KEY is required; OSHUN_STABILITY_MODEL overrides the default sd3.5-large. Accepted aspect ratios are 1:1, 16:9, 9:16, 4:3, 3:4, 21:9, 9:21, 3:2, 2:3; anything else is coerced to 1:1 (image-provider-env.ts).
  • Voiceboth OSHUN_ELEVENLABS_API_KEY and OSHUN_ELEVENLABS_VOICE_ID are required; either missing returns null. Narration uses a fixed, deploy-configured, platform-licensed voice — not a per-request clone — so the cloned-voice-used human-review trigger does not apply (narration-provider-env.ts).
  • Batch musicOSHUN_SUNO_API_KEY is required; OSHUN_SUNO_MODEL_VERSION selects v3 | v3.5 | v4 | v5 (default model from the provider); OSHUN_SUNO_BASE_URL is an optional override. Curated music is instrumental (no lyrics/vocals), and there is no in-repo audio-content scanner, so its release measurement carries safetyScanScore: null and blocks at the audio safety floor until a deploy-bound audio scanner, watermarker, and C2PA signer are wired (music-provider-env.ts).
  • Realtime music — gated by OSHUN_MRT2_ENABLED=true and a successful runtime.load(). The runtime is the official-engine Python sidecar when OSHUN_MRT2_SIDECAR is set (OSHUN_MRT2_PYTHON, OSHUN_MRT2_TIER of 230m | 2.4b), otherwise the native (Rust) on-device engine. If no inference backend or weights are present, load() reports not_configured, the resolver returns null, and the realtime route serves a 503 — never a fake stream (realtime-music-provider-env.ts).

Realtime music is a distinct generation mode#

The batch pipeline is admit → enqueue → worker → { id, url? }. Realtime music (MRT2) is fundamentally different: a long-lived, frame-level interactive session where control flows in and audio frames flow out for the life of a socket (realtime-music-route.ts). buildSource drives a GenerationSession one block at a time — 48 kHz stereo PCM, an ~8-token-per-frame budget, a ~2.667 ms frame budget — pushing each frame to an audioSink that the route forwards on the WS generation-audio channel. Each tick layers the current live conditioning (text/MIDI/audio-ref updates from the control channel) over the request's initial prompt. The loop ends the moment the client disconnects (isActive() returns false) or the engine stops producing audio — terminating the stream. This streaming mode is omitted from the prose music section in features.md.

Why the provider node is hosted, not ComfyUI#

ComfyUI on RunPod is the operator substrate — the heavy GPU execution environment AAA creators and operators drive through graph-editor, gpu-worker, multi-gpu-orchestration, and runpod-dashboard. But the live, customer-facing executors call hosted HTTP providers directly: Stability for image, ElevenLabs for voice, Suno for batch music, fal.ai's LTX-Video for video. A mermaid label of "ComfyUI · ElevenLabs · Suno" is therefore incomplete for the customer path — image goes to StabilityProvider, video to LTXProvider, and ComfyUI/RunPod sit behind the AAA-creator surfaces, not the default executor flow.

The provider adapter library#

The full adapter catalog lives in libs/isis/ai-providers/src/providers/, one subdirectory per capability: animation, civitai, comfy, comfy-cloud, controlnet, conversational-ai, florence, image-generation, instantid, ip-adapter, live-preview-streaming, llm, model-registry, multi-gpu-orchestration, music-generation, three-d, tts, usage, video-generation, video-processing, and workflow-versioning. The audio provider set lives separately in libs/isis/audio-generation/src/generation/: suno-provider.ts, udio-provider.ts, self-hosted.ts, sfx-provider.ts, music-generator.ts, plus audio-analysis.ts.

Two accuracy notes on the music provider list. First, earlier docs described the music abstraction as "MusicGen, Suno, Udio, Stable-Audio, custom-on-Comfy"; the real files are suno-provider.ts, udio-provider.ts, self-hosted.ts, sfx-provider.ts, and music-generator.ts. There is no provider file named for MusicGen or Stable-Audio, and the realtime Magenta RT path is omitted from that list. Second, although deps§9 lists both Suno and Udio as V1-used, only Suno is wired into the BFF batch music executor (SunoProvider, OSHUN_SUNO_API_KEY, OSHUN_SUNO_MODEL_VERSION); udio-provider.ts exists as an adapter but is not customer-path-wired. The docs imply a Suno/Udio parity that the BFF wiring does not have.

The @isis/music-generation library ("provider abstraction, workflow classes, watermark + provenance" — §24.8) enforces per-class MIME guardrails in guardrails.ts: each workflow class declares an allowedMimeTypes list drawn from audio/wav, audio/mpeg, audio/ogg, and audio/flac, and a request whose mimeType is not in its class list is rejected before generation.

@isis/output-gallery ("filters, lineage, branch/replay, compare grid, bulk actions" — §24.10) is the unified review surface for generated outputs. Its real typed contracts (libs/isis/output-gallery/src/) are richer than the prose describes:

  • Compare grid (compare-grid.ts) — buildCompareGrid produces a typed CompareGridLayout of 2-up | 4-up | n-up (capped at 16 slots, MAX_N_UP_SLOTS). Each diff pair carries a DiffMetric typed per asset class: pixel-delta (mean absolute delta) for image, frame-delta for video, audio-rms-delta (RMS delta in dB) and waveform-delta (correlation coefficient) for audio, and mesh-vertex-delta (Hausdorff distance) for 3D. Mixing incompatible kinds throws CompareGridError with code mixed-kinds.
  • Branch / replay-with-tweak (branch-replay.ts) — branching is not free-form. validateBranchDelta checks every override in a BranchDelta against the workflow class's ParameterAllowedRange[]: an unknown parameter throws unknown-parameter, a number outside [min, max] or an enum value not in enumOptions throws out-of-range, and a type mismatch throws wrong-type. planBranch then assembles a BranchRunRequest (parent output id, validated inputs, workflow class, estimated cost) — so a derived run can only ever stay inside the parent class's bounded parameter space.
  • Bulk actions (bulk-actions.ts) — BulkAction / BulkActionInvocation drive multi-output operations.

The AAA-tier execution scheduler: @oshun/render-farm#

The AAA-creator tier's heavy GPU work is scheduled by @oshun/render-farm (libs/oshun/render-farm), a real lib imported by apps/yemaya/studio-web (its vite.config.ts, vitest.config.ts, and tsconfig.json all alias @oshun/render-farm to the lib source, and package.json declares the workspace:* dependency). It provides the render-farm scheduling primitives the tier docs gesture at but never name: priority queues, worker-node capability and GPU-requirement matching, dependency execution, preemption, checkpoint/resume, cloud-burst, cost estimation, and dashboard snapshots. Its public surface (from src/index.ts) includes RenderJob, RenderJobSubmission, RenderAssignment, RenderCheckpoint, RenderCloudBurstPlan/RenderCloudBurstDecision/ RenderCloudBurstProvider, RenderCostEstimate, RenderGpuCapability/ RenderGpuRequirement, PreemptionDecision, RenderQuotaBreach/ RenderQuotaEvaluation, RenderDashboardSnapshot, and the RenderFarmScheduler (via createRenderFarmScheduler).

The autonomous creative orchestrator: @oshun/creative-orchestrator#

The contemplative/curated "send-to-editorial" and autonomous generation pipelines depend on @oshun/creative-orchestrator (libs/oshun/creative-orchestrator), a real, BFF-wired lib (consumed by apps/oshun/bff/src/agentic/ and libs/yemaya/orchestration + agents). It turns a brief into produced content through three real stages, all built on the shared @oshun/ai/agent-loop primitives (runStructuredOutput, runReflexion) and fail-loud throughout — never fabricating a plan or artifact when no provider is configured:

  1. PlandecomposeBrief runs a real LLM planner that emits a schema-validated, acyclic CreativePlan DAG (validated against CREATIVE_PLAN_SCHEMA; cycle detection via detectCycle / validateDagStructure).
  2. Route + governroutePlan / CreativeOrchestrator dispatch plan nodes to a GeneratorRegistry of DomainGenerators, gated by a GovernanceGate (BudgetGovernanceGate, with ALLOW_ALL_GATE for tests).
  3. Critique → revisereviseArtifact runs a bounded generate → critique → revise (Reflexion) loop around each artifact, scored by an ArtifactCritic (createContentEvalCritic, createMetricCritic, or createLlmJudgeCritic).

It also exposes createYemayaAgentGenerator (wiring Yemaya's specialized agents in) and createMetisNarrator. This is the connective tissue behind the agentic studio; see Agentic AI Studio for the full picture.

What is real vs. provider-gated vs. aspirational#

To match the docs' candor:

  • Real, in-repo: the deterministic tier resolver (resolveGenerationTier, TIER_ALLOWLISTS, checkSurfaceAccess) with 4 tiers and 28 surfaces; the studio boundary fail-closed allowlist; output-gallery lineage, branch-replay, compare-grid, and bulk-actions contracts; the music-generation guardrails; the BFF executors, release gate, and safety classifiers; the provider adapter library; @oshun/render-farm; @oshun/creative-orchestrator.
  • Provider-gated (real wiring, live call exercised only at deploy): every hosted provider call — image (Stability), voice (ElevenLabs), batch music (Suno), video (fal.ai LTX), realtime music (MRT2). With no key the resolver returns null and the job fails closed (provider_not_configured).
  • Spec-only / unverified at runtime here: LoRA training, model merging, gaussian-splatting, and 3D pipelines exist as data models and surfaces (@isis/lora-training-surface — "LoRA training queue + model merging + tuning rehearsal + quality scoring + lineage," §24.5; @isis/runpod-surface — "endpoint registry, dashboard, cost/quota, queue inspector," §24.4), but live training execution was not verified. The AAA Yemaya Studio graph editor exists as apps/yemaya/studio-{web,desktop} (studio-web importing @oshun/render-farm), but a rendered AAA graph editor was not confirmed here. Udio is library-present but not customer-path-wired.