Files
git.stella-ops.org/docs/technical/architecture/multi-tenant-flow-sequences.md

4.2 KiB

Multi-Tenant Same-Key End-to-End Flow Sequences

Date: 2026-02-22
Source sprint: SPRINT_20260222_053_DOCS_multi_tenant_same_api_key_contract_baseline.md
Related ADR: docs/architecture/decisions/ADR-002-multi-tenant-same-api-key-selection.md

1) Sign-in to Tenant Mapping

sequenceDiagram
    autonumber
    participant User
    participant Web as Web Console
    participant Auth as Authority
    participant Gw as Router/Gateway
    participant Svc as Tenant-scoped API

    User->>Web: Sign in
    Web->>Auth: /connect/authorize + PKCE
    Auth-->>Web: auth code
    Web->>Auth: /connect/token (client credentials or password grant, tenant=<optional>)
    Auth->>Auth: Resolve selected tenant from tenant + tenants metadata
    Auth-->>Web: Access token (stellaops:tenant + optional stellaops:allowed_tenants)
    Web->>Auth: /console/tenants
    Auth-->>Web: { tenants[], selectedTenant }
    Web->>Web: Hydrate ConsoleSessionStore + AuthSessionStore + PlatformContext
    Web->>Gw: API request + canonical tenant header
    Gw->>Svc: Forward resolved tenant context
    Svc-->>Web: Tenant-scoped response

Deterministic selection rule:

  • If tenant parameter is present at token request time, it must be in assigned tenant set.
  • If no parameter and only one assignment/default exists, use that selected tenant.
  • If ambiguous (multi-assigned and no default/request), reject.

2) Header Selector Tenant Switch

sequenceDiagram
    autonumber
    participant User
    participant Topbar as Header Tenant Selector
    participant Session as ConsoleSessionService
    participant Auth as Authority
    participant Stores as Session/Context Stores
    participant APIs as Platform/Scanner/Graph APIs

    User->>Topbar: Select tenant "tenant-bravo"
    Topbar->>Session: switchTenant("tenant-bravo")
    Session->>Stores: Optimistic selectedTenant update
    Session->>Auth: /console/tenants (tenant header=tenant-bravo)
    Auth-->>Session: allowed tenants + selectedTenant
    Session->>Auth: /console/profile + /console/token/introspect
    Auth-->>Session: profile/token introspection for selected tenant
    Session->>Stores: Commit tenant to Console/Auth/Platform/TenantActivation stores
    Session->>APIs: Trigger context reload for tenant-scoped data
    APIs-->>Topbar: Refreshed tenant-scoped responses

Error recovery path:

  • On switch failure (403, tenant_conflict, session expiry), restore previous tenant in all stores.
  • Attempt context reload for previous tenant.
  • Surface deterministic error in tenant panel with retry action.

3) API Request Propagation Through Gateway

sequenceDiagram
    autonumber
    participant UI as Web API Client
    participant I as Tenant/Context Interceptors
    participant Gw as Router/Gateway
    participant Backend as Platform/Scanner/Graph

    UI->>I: Outgoing request
    I->>I: Resolve active tenant from canonical runtime state
    I-->>UI: Add canonical header X-StellaOps-Tenant (+ compat aliases)
    UI->>Gw: Request with tenant headers + token
    Gw->>Gw: Strip caller-supplied identity headers, derive tenant from validated claims, rewrite canonical headers
    Gw->>Backend: Forward tenant-scoped request
    Backend->>Backend: Resolve tenant context + enforce tenant ownership
    Backend-->>UI: Deterministic success/failure payload

Cache/store invalidation points after tenant switch:

  • Console session context cache.
  • Tenant-scoped page stores (Platform/Scanner/Graph read models).
  • URL context synchronization where tenant is persisted as global context.

4) Failure Sequences

Missing tenant context

  • Expected result: deterministic 400/401/403 based on service policy and auth stage.
  • UI behavior: keep prior selection if available; show recoverable error panel.

Tenant mismatch

  • Trigger: claim tenant != header/request tenant.
  • Expected result: reject with deterministic conflict error (for example tenant_conflict or tenant_forbidden).
  • Audit/telemetry: record attempted tenant override + resolved tenant.

Insufficient scope

  • Trigger: token lacks required policy scope for requested endpoint.
  • Expected result: deterministic 403 with scope policy failure context.
  • UI behavior: no tenant mutation; show access-denied state.