Oshun Platform · Features

Generation Audience Tiers and Surface Boundaries

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

7sections10 minread3tables

On this page

The same provider stack — Civitai intake, ComfyUI on RunPod, hosted image/voice/music/video providers, and the 3D pipeline — serves four canonical generation audience tiers with non-overlapping exposure profiles. This page specifies the tier taxonomy as it actually exists in code, the canonical vocabulary of 28 named surfaces, the deterministic resolver that maps an entitlement bundle to a tier and a surface allowlist, and the deny-by-default boundary enforcement that keeps raw generation machinery off the contemplative product. It serves product, platform, and trust-and-safety readers who need to know who can see what and why the wrong customer cannot reach a surface even by typing its URL. It sits on top of the Isis Generation Control substrate and is the front door for the External Model Intelligence and Execution Providers page.

Where this sits, and what is real#

The tier system is real, in-repo, and unit-tested today. The resolver, the frozen tier list, the 28-surface vocabulary, the per-tier allowlists, and the deny-by-default surface guard all live in libs/isis/entitlements/src/generation-tier.ts (module @isis/entitlements/generation-tier). The product-shell boundary that turns a denied surface into either a 404 or a Yemaya upgrade CTA is wired into the Oshun-web proxy at apps/oshun/web/src/proxy.ts. What is gated is everything downstream of the resolver: a tier may grant access to a surface, but the live generation that surface invokes still fails closed unless deploy-time provider credentials are present (see External Model Intelligence and Execution Providers). The resolver itself never touches a provider; it is pure policy.

The canonical tier taxonomy#

The implemented tier union is the single source of truth. In code (generation-tier.ts:23-28) it is:

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

These four values are frozen into the exported GENERATION_TIERS array. The prose names used across the rest of the V1 docs map onto the code identifiers as follows — and the mapping matters, because two of the "verbatim" doc names do not match the code:

Doc / product name Code identifier (GenerationTier) Product shell
Customer (contemplative product) contemplative Oshun contemplative apps
Curated-Creator curated-creator Oshun Studio, apps/oshun/web/src/app/studio/generation
AAA-Creator aaa-creator Yemaya Studio, apps/yemaya/studio-web + studio-desktop
Operator operator-admin Oshun Admin, apps/oshun/admin/

Naming note (accuracy correction): earlier doc text claimed the four tier names Customer, Curated-Creator, AAA-Creator, and Operator were "used verbatim" as the code taxonomy. In the implementation, the Customer tier is contemplative and the Operator tier is operator-admin. The remaining two (curated-creator, aaa-creator) line up. Treat the code identifiers above as authoritative for anything that touches the resolver, allowlists, or boundary guards.

Free-versus-paid is a subscription distinction within the contemplative (Customer) tier, not a separate tier. platform-emitted generation — operational notifications and system artifacts — is a non-user generation source governed by the operator-admin tier.

What each tier exposes#

  • contemplative (Customer / contemplative product). Curated, entitlement-gated, customer-facing generation only: Living Offerings (Arete), Contemplative Arcs (Tara), Grounded Explainers (Veritas), Lesson Visualizers (Metis), Sky Briefings (Nyx). Tone-gated by Lilith, provenance always on. No raw graph editor and no model picker. In allowlist terms, this tier sees exactly three surfaces: curated-image, curated-living-scene, curated-voice-clip.
  • curated-creator (Oshun Studio). A small set of curated cards — illustration, narration, ambient audio, explainer, caption/dub, accessibility-pass — bound to approved workflow classes, approved voice profiles, and approved persona/policy bindings. It adds two surfaces on top of the contemplative set: curated-workflow-pick and curated-asset-search. No raw model picker, no LoRA hash selector, no scheduler choice, no graph editor.
  • aaa-creator (Yemaya Studio). Power-user surfaces: full graph editor, full Civitai browser, LoRA training, model merging, music generation, voice cloning, the full 3D pipeline, plus GPU/region orchestration. It inherits every curated surface and adds the entire creative power-user block (see the allowlist table below). It does not receive the operator-only admin surfaces.
  • operator-admin (Oshun Admin). Everything aaa-creator has plus the admin-only surfaces: the RunPod endpoint dashboard, intake review queues, the LoRA training queue, the output-gallery operator view, the ComfyUI node registry, and the audit trail. In code, operator-admin's allowlist is simply the frozen GENERATION_SURFACES array — all 28 surfaces.

The 28-surface vocabulary#

The tier system does not describe surfaces in prose only; it pins a canonical, frozen enum of 28 GenerationSurface values (GENERATION_SURFACES, generation-tier.ts:87-126). This vocabulary is the high-value contract every boundary guard, route, and component checks against. The surfaces, grouped by the tier band that first introduces them:

Band GenerationSurface values
Contemplative (curated finished products) curated-image, curated-living-scene, curated-voice-clip
Curated-creator curated-workflow-pick, curated-asset-search
AAA-creator (creative power-user) 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

Each surface name is meaningful and stable: topaz and rife are the upscaling and frame-interpolation surfaces, animatediff is the motion surface, gaussian-splatting and auto-rigging are 3D-pipeline surfaces, and civitai-lora-hash-picker is the operator/AAA hash-precise model selector. The curated tier deliberately exposes none of these directly; it only offers curated-workflow-pick (choose an approved workflow class) and curated-asset-search (search post-intake approved assets).

Per-tier allowlists#

TIER_ALLOWLISTS (generation-tier.ts) is a frozen Record<GenerationTier, readonly GenerationSurface[]>. The allowlists are strictly nested — each higher tier is a superset of the one below — which is what makes "the same stack, different exposure" structurally true:

Surface contemplative curated-creator aaa-creator operator-admin
curated-image
curated-living-scene
curated-voice-clip
curated-workflow-pick
curated-asset-search
graph-editor3d-generation (17 AAA surfaces)
runpod-dashboardaudit-trail (6 admin surfaces)

The contemplative tier therefore resolves to exactly three surfaces — finished, curated products and nothing else. There is no path from a contemplative entitlement to the graph editor, a model picker, a LoRA hash selector, or the RunPod dashboard, because none of those surfaces are on its allowlist.

The deterministic resolver#

resolveGenerationTier(entitlement) is the single source of truth that BFF, Admin, and Studio middleware all consult before rendering any generation surface (generation-tier.ts:128-184). Its inputs and outputs:

ts
interface GenerationEntitlement {
  readonly id: string;
  readonly tenantId: string;
  readonly userId: string;
  readonly persona: string;
  readonly tags: readonly string[];
}

interface ResolvedTier {
  readonly entitlement: GenerationEntitlement;
  readonly tier: GenerationTier;
  readonly surfaceAllowlist: readonly GenerationSurface[];
}

The resolution rule is a deterministic, ordered tag check (resolveTierFromTags): if the bundle's tags include operator-admin, the tier is operator-admin; else if they include aaa-creator, that; else if curated-creator, that; otherwise the tier defaults to contemplative. Tags are normalized first (normalizeEntitlementTags): non-string entries are dropped, each tag is trimmed, and empty strings are discarded, so a malformed or empty tag list resolves cleanly to the most restrictive tier (contemplative) rather than throwing. The resolver returns a frozen surfaceAllowlist copied from TIER_ALLOWLISTS[tier], so callers cannot mutate the canonical lists.

This design is deny-by-default with the safe default being the most restrictive tier: the absence of an entitlement tag, a corrupt tag array, or an unexpected tag value all collapse to contemplative (curated products only). Privilege is only ever granted by an explicit, recognized tag.

Surface access check#

checkSurfaceAccess({ entitlement, surface }) returns a typed SurfaceAccessVerdict (generation-tier.ts):

ts
type SurfaceAccessVerdict =
  | { readonly verdict: 'allow'; readonly tier: GenerationTier }
  | {
      readonly verdict: 'deny';
      readonly tier: GenerationTier;
      readonly action:
        | { readonly kind: '404-with-aaa-cta' }
        | { readonly kind: '404-hard-block' };
    };

The function resolves the tier, and if the requested surface is on that tier's allowlist it returns allow; otherwise it returns deny with a 404-hard-block action. Importantly, the surface check itself is deny-by-default and never emits a CTA — its deny outcome is always 404-hard-block. The CTA branch (404-with-aaa-cta) is reserved for the product-shell boundary guard, which is the only layer that can prove the caller is already entitled to a surface but reached it from the wrong host app. The type carries both action shapes so the boundary guard and the raw surface check share one verdict vocabulary.

Boundary enforcement: component, route, and proxy#

Deny-by-default is enforced at three layers, and the proxy layer is where the two distinct deny behaviors are decided.

  1. Component layer. Studio components consult the resolved allowlist before rendering a surface; a surface not on the allowlist is never mounted.

  2. Route layer (BFF). Generation routes re-check the verdict server-side, so a forged client cannot reach an executor for a surface it lacks.

  3. Product-shell proxy. apps/oshun/web/src/proxy.ts applies the §24.11 Studio boundary for the legacy contemplative studio/isis/* tree. applyStudioBoundary(pathname, entitlement) checks for the STUDIO_ISIS_PREFIX (/studio/isis/), resolves the route segment, and routes AAA-only segments through resolveStudioBoundary. The outcome is one of:

    • render-as-is — the caller is entitled and in the right shell;
    • render-yemaya-cta — the caller is AAA-entitled but reached the surface from the contemplative shell, so the proxy issues a 307 redirect to the Yemaya upgrade URL with X-Studio-Boundary: aaa-cta;
    • hard-block-404 — the caller has no upgrade path (e.g., a contemplative customer), so the proxy rewrites to Next's internal /_not-found route returning a real 404 with X-Studio-Boundary: hard-block.

    The 404 is rendered through the root document shell deliberately, so it ships with <html lang="en"> and a <title> — a WCAG document-title / html-has-lang fix, since a bare Not Found body failed axe on every AAA-only route.

Staleness note: earlier text said "AAA-tier routes return disclosure + Yemaya signup gate for entitled users, hard-block for the rest." That is partly accurate. The proxy preserves both behaviors — render-yemaya-cta (a redirect to upgrade) for AAA-entitled callers in the contemplative shell, and a real hard 404 for everyone else. The legacy studio/isis/* segments are 404-hard-blocked when the caller is not entitled, not merely shown a CTA. The live contemplative-Studio generation surfaces today are studio/generation and studio/generation-gallery; the older studio/isis tree is gated by this boundary.

Why it works this way#

The "raw machinery never appears on the contemplative product" guarantee is not a UI convention — it is a structural property of the resolver plus the nesting of allowlists. Because each tier's allowlist is a frozen superset of the tier below, and because the resolver's default is the most restrictive tier, there is no combination of inputs that grants a contemplative customer a power-user surface. A direct URL to graph-editor, an attempt to import an AAA component, or a forged BFF request all hit the same checkSurfaceAccess deny path, and the proxy decides whether the right answer is "upgrade here" (proven-entitled, wrong shell) or "this does not exist for you" (a hard 404). The surface enforcement tests assert exactly this: a contemplative entitlement cannot resolve to an AAA route or component even via direct URL or import.

Cross-tier execution: where the surfaces lead#

A surface being visible is necessary but not sufficient for generation to run. The AAA execution surfaces (gpu-worker, multi-gpu-orchestration, runpod-region-selector) are fronted by the render-farm scheduler in @oshun/render-farm (libs/oshun/render-farm, imported by apps/yemaya/studio-web), which provides the priority queues, worker-node capability matching, GPU-requirement matching, preemption, checkpoint/resume, cloud-burst, and cost estimation that the AAA tier's scheduling surfaces expose. The curated and contemplative surfaces, by contrast, route through the BFF generation executors and the autonomous @oshun/creative-orchestrator. Both are detailed on the External Model Intelligence and Execution Providers and Creator Surfaces, Voice, Music, and 3D Generation pages.

Backlog references: the resolver and surfaces are §24.1; the RunPod operator surface §24.4; LoRA training §24.5; music generation §24.8; the unified output gallery §24.10; the Studio boundary §24.11. External-provider intake and adapters are tracked in deps§9.