aaa-upgrade.mdstudio-isis-gpu-worker.mdJourney flow#
Generated from the authored steps below — click a node to jump to that section.
A signed-in customer hits an AAA-only generation surface on the contemplative
product (/studio/isis/<aaa>). proxy.ts runs resolveStudioBoundary; if the
user has aaa-creator or operator-admin, they are redirected with status 307
to /aaa-upgrade?from=<orig>; if not, they get a hard 404. This is the
cross-surface handoff from the contemplative route boundary into the Yemaya
Studio surface.
Personas#
- Contemplative customer with AAA entitlement — primary path; sees the disclosure and chooses Yemaya Studio or returns to the gallery
- Contemplative customer without AAA — hard-blocked at the proxy; 404 on any direct nav to an AAA-only segment
- Operator-admin — also passes the boundary (
operator-adminis the second allowed tier inresolveStudioBoundary) - Non-AAA customer on a hub page — the proxy can't see inside an embed;
AaaGateplugs the leak per-embed inside hubs likeStudioIsisJobEnvelopeWorkspace
Pre-conditions#
- User signed-in (
OSHUN_ACCESS_TOKEN_COOKIE_NAME = 'oshun-access'set) readCustomerStudioEntitlement()can decode the access token (3-segment prod JWT or 2-segment dev token); malformed falls back toanon- Target segment is in
AAA_ONLY_STUDIO_ROUTES(frozen list inlibs/isis/entitlements/src/studio-boundary.ts— includesgpu-worker,lora-training,comfyui-nodes,model-merging,animatediff-lightning,rife-interpolation, etc.) resolveGenerationTier(entitlement)returns one ofcontemplative,curated-creator,aaa-creator,operator-admin
Steps#
1. Customer attempts a direct nav to an AAA-only surface#
- Customer follows a deep link or types
/studio/isis/gpu-worker - Proxy receives the request;
pathname.startsWith(STUDIO_ISIS_PREFIX)is true; segment extracted viapathname.slice(STUDIO_ISIS_PREFIX.length).split('/')[0] -
AAA_ONLY_STUDIO_ROUTES.includes(segment)is true - Proxy calls
resolveStudioBoundary({ routeSegment, entitlement })
2. Branch A — AAA tier present, render-yemaya-cta#
- Tier is
'aaa-creator'or'operator-admin' -
resolveStudioBoundaryreturns{ action: 'render-yemaya-cta', disclosureCopy, upgradeUrl: '/aaa-upgrade' } -
applyStudioBoundaryreturns{ kind: 'render-yemaya-cta', upgradeUrl } -
studioBoundaryResponsebuildsNextResponse.redirect(url, 307)withurl.pathname = '/aaa-upgrade'andurl.searchParams.set('from', request.nextUrl.pathname) - Headers:
X-Request-Id: <id>,X-Studio-Boundary: aaa-cta - Browser follows the 307 to
/aaa-upgrade?from=/studio/isis/gpu-worker
3. Branch B — no AAA, hard-block-404#
- Tier is
'contemplative'or'curated-creator' -
resolveStudioBoundaryreturns{ action: 'hard-block-404' } -
studioBoundaryResponserewrites to/_not-found(NextResponse.rewrite(notFoundUrl, { status: 404, headers: { 'X-Request-Id', 'X-Studio-Boundary': 'hard-block' } })) so the 404 ships with the root document shell (<html lang="en">+<title>) for WCAG document-title / html-has-lang — not a baretext/plain"Not Found" body - No leakage of
/aaa-upgradeexistence for this segment - Verify:
studio/domain-bridges/isis/studio-isis-gpu-worker.mdgated state (hard-block branch)
4. /aaa-upgrade renders the handoff (Branch A continuation)#
apps/oshun/web/src/app/aaa-upgrade/page.tsx is a server async component that
reads searchParams.from, sanitises it to string | null, and renders
inline-styled chrome (no ShellLayout).
- H1 "This surface lives in Yemaya AAA Studio"
- Body "Oshun keeps the contemplative product curated — raw graph editors, LoRA training, model merging, and the rest of the AAA-tier generation surfaces live in Yemaya AAA Studio. Your entitlement includes access."
- "You arrived from
/studio/isis/gpu-worker" conditional line (only whenfromis a string) - Primary Link
data-action="open-yemaya"→/studio; secondarydata-action="back-to-gallery"→/studio/generation-gallery -
data-testid="aaa-upgrade-page"on<main> - Verify:
customer/13-system/aaa-upgrade.mddefault +?fromstates; CTA labels; touch targets
5. Customer chooses one CTA#
- "Continue in Yemaya Studio" →
/studio(cross-surface handoff; the contemplative product never embeds the AAA generation surface itself) - OR "Back to gallery" →
/studio/generation-gallery(returns to curated surface)
6. Embedded-leak case — fail-closed legacy route plus AaaGate defence-in-depth#
The legacy /studio/isis/* tree now fail-closes at the route boundary before a
non-AAA user can reach a hub that embeds an AAA workspace. AaaGate remains the
component-level defence for any future reachable parent surface where the proxy
cannot see inside the embed.
- Route-level boundary:
/studio/isis/job-envelopereturns the samehard-block404 for contemplative users, preserves the URL, and does not render an in-place blocked card - Component tree:
StudioIsisWorkflowRegistryWorkspacemounts<AaaGate routeSegment="gpu-worker"><StudioIsisGpuWorkerWorkspace /></AaaGate>(same pattern in Job Envelope, Web, Output Registry, AI Video, etc.) -
AaaGateis server-side; callsreadCustomerStudioEntitlement()from cookies, runsresolveGenerationTier(entitlement).tier - For
aaa-creator/operator-admin: renders children - For
contemplative/curated-creator: rendersAaaBlockedCard(data-aaa-gate-blocked,data-aaa-gate-route-segment="gpu-worker") with eyebrow "AAA tier required", route-segment label, and a Link to/aaa-upgrade("Open the upgrade page") - Unknown segments (not in
AAA_ONLY_STUDIO_ROUTES) render children (defensive default) - BFF independently enforces entitlement — the gate is UI-only
- Verify:
studio/domain-bridges/isis/studio-isis-gpu-worker.md"AAA-gated parent embedding non-AAA-gated Generation API child" open question — captures the inverse case to this one
Post-conditions#
- AAA-tier user: at
/studio(or back at/studio/generation-galleryafter declining) - Non-AAA user: at a 404 for direct nav and the legacy hub; any future reachable
hub embed should show an in-place
AaaBlockedCard; the user never sees the AAA surface or leaked state - Telemetry: the
/aaa-upgradeclient emitsaaa_upgrade_viewed,aaa_upgrade_open_yemaya_clicked, andaaa_upgrade_back_to_gallery_clicked; the proxy boundary remains observable throughX-Studio-Boundary
Failure modes#
- Cookie missing / malformed —
decodeEntitlementfalls back toanon; user sees the 404 (correct posture, not the disclosure) - Unknown segment wrapped in
AaaGate— defensive default renders children - Segment removed from
AAA_ONLY_STUDIO_ROUTES— proxy +AaaGateboth fall through to render-as-is; verify the bridge change is intentional - Open redirect via
?from=—fromrenders as plain<code>content, no<a href>; Next escapes; CTAs go to fixed destinations only - Cross-domain leak — entitlement decoder is
import 'server-only'; never reaches client bundles - BFF skew — the gate is UI-only; if BFF rules drift from
AAA_ONLY_STUDIO_ROUTES, a non-AAA user could reach data through the API — covered by entitlement-equivalence tests
E2E coverage#
apps/oshun/web/e2e/aaa-upgrade-entitlement-bounce.spec.ts— steps 1–6: asserts the 307render-yemaya-cta(withX-Studio-Boundary: aaa-ctaandfrom=), the contemplative hard-block 404 (URL preserved, WCAG doc shell, no/aaa-upgradeleak), the disclosure page + both CTA destinations,aaa_upgrade_viewed, both CTA click telemetry events, the fail-closed legacy/studio/isis/job-envelopehub denial, and the?from=open-redirect failure mode (echoed as inert<code>, never an<a>), plus mobile standalone rendering with safe source propagation/no overflow/≥44px CTAs and real service-worker offline document replay of the warmed/aaa-upgradepage.apps/oshun/web/e2e/entitlements-aaa-bff.spec.ts— the BFF entitlement resolver behind the proxy bounce (the journey's Open question #4) over real HTTP:GET /v1/entitlements/aaareturns entitled foraaa-creator, bouncescontemplativetoward/aaa-upgrade, and401s without a bearer token — so a drift between theproxy.tsboundary and the BFF's own enforcement is now detectable.- Coverage depth: deep — see
coverage.md. - Uncovered: proxy-side structured access-log telemetry for redirect / 404
decisions remains a backend observability gap; the browser
aaa_upgrade_*telemetry is covered. Theoperator-adminsecond-tier path is asserted via tier-equivalence in the boundary unit tests rather than re-driven here (samerender-yemaya-ctaoutcome asaaa-creator).
Per-view files touched#
Cross-references#
V1/features.md§ Crypto Payments — Non-Custodial Entitlement Settlement (upstream path that grants the AAA tag in the first place)V1/ARCHITECTURE.md§ Security, Privacy, and Compliance (entitlements interlock withDomainAuthPolicy.scopes)apps/oshun/web/src/proxy.ts—applyStudioBoundary,studioBoundaryResponseapps/oshun/web/src/lib/server/studio-entitlement.ts—readCustomerStudioEntitlement(server-only token decoder)libs/isis/entitlements/src/studio-boundary.ts—AAA_ONLY_STUDIO_ROUTES,isAaaOnlyRoute,resolveStudioBoundary,StudioBoundaryOutcomeapps/oshun/web/src/components/studio/AaaGate.tsx— defence-in-depth embed gateapps/oshun/web/src/app/aaa-upgrade/page.tsx— disclosure page source- Sibling journeys:
sign-up-and-pay-crypto.md(how the user got the AAA tag),nyx-to-tara-bridge.md(sibling cross-domain handoff pattern)
Open questions / known gaps#
-
/aaa-upgradeemitsaaa_upgrade_viewed,aaa_upgrade_open_yemaya_clicked, andaaa_upgrade_back_to_gallery_clicked; the Playwright journey captures all three events and verifies their safe source/destination payloads - Proxy has no telemetry on the redirect / 404 path itself; response headers
(
X-Studio-Boundary) are the only signal; consider a structured access-log event - Page chrome uses raw inline styles, not Lilith design-system primitives; keyboard focus outlines, ≥44px CTA targets, reduced-motion static state, mobile standalone containment, and offline replay are now covered by the route-level Playwright assertions. The design-system primitive migration remains a visual-design gap, not an unverified interaction.
- Document the BFF-side entitlement check that mirrors
AAA_ONLY_STUDIO_ROUTES, so a drift between proxy + BFF is detectable (the comment inAaaGate.tsxsays "The BFF still independently enforces entitlement on every request" —entitlements-aaa-bff.spec.tscaptures the real HTTP resolver) - Per-view file flags "AAA-gated parent embedding non-AAA-gated Generation
API child — confirm intended UX"; the embedded-leak
AaaGatepattern is the inverse (non-AAA parent embedding AAA child). The journey now documents the fail-closed legacy hub behavior and the real spec asserts no embedded blocked card leaks after route-level denial.