Skip to content

WhiskerPlatform Architecture

The system diagram, runtime flow, and integration paths behind WhiskerPlatform. For a conceptual introduction and the full document index, see Overview.

If a module team wants to add a navigation item, a widget, a page, an avatar menu entry, or a header action, they declare it in their own src/moduleManifest.ts and merge to their own repo. The host picks it up at runtime. No host PR. No host release.

Capability Where it lives
Runtime shell that aggregates modules whisker-host — auth, navigation, dashboard, iframe sandbox, settings persistence
Scaffolding + AI context + audits CLI @nike/whisker-cli (binary: thundercats)
Module + service scaffold library @nike/create-whisker
Day-to-day dev CLI for modules @nike/whisker-module-kit (whisker binary)
Day-to-day dev CLI for services @nike/whisker-service-kit (whisker-service binary)
Shared retail components @nike/whisker-component-library (WCL) — the only package published to Artifactory today
Host federation contract artifact whisker-host/public/whisker-contract.json, regenerated on every host commit
Automated module registration whisker-manifest-publisher Lambda + DynamoDB — modules self-register by publishing to S3, no host PR
Build-time federation handshake whisker-host/.github/workflows/sync-deployed-registry.yml — auto-PR to develop, manual promotion to main
Pre-flight checks whisker doctor — Node/host/ports/BFF reachability checks with remediation hints
Host + module nightly E2E whisker-host/tests/e2e/ + Jenkinsfile.nightly, registry-driven
AI agent context for IDEs @nike/whisker-cli (Thundercats) — a global @thundercats Cursor skill (orchestrator + three specialists) plus a per-repo .cursor/ always-on rule

The whisker-platform stack above is feature-complete and positioned for its first Artifactory pilot publish, but has not published yet — see Risks & improvements.

Three layers from the user’s perspective: the shell they interact with, the modules the shell composes, and the platform-owned backing services + registry. Scaffolders, kits, and CI are build-time support that produce the artifacts shown here.

flowchart TB
  user(["End user<br/>in-store athlete"])

  subgraph shell["whisker-host shell — port 3001"]
    host["auth · nav · dashboard<br/>iframe sandbox · WCL providers"]
  end

  svc["whisker-service<br/>serves /manifests/v1"]
  ddb[("DynamoDB<br/>module registry")]
  s3[("S3<br/>whisker-modules-env")]

  subgraph fed_lane["Federation modules"]
    direction LR
    inv["inventory-management-ui"]
    rfid["rfid-reporting-ui"]
    fmore["…"]
  end

  subgraph iframe_lane["Iframe consumers"]
    direction LR
    web["sim-web-ui"]
    cash["nike-cash"]
    rcf["rcf-reporting-ui"]
  end

  cicd(["CI publishes manifest"])

  user --> host
  host -->|GET /manifests/v1| svc
  host -->|loadRemote| s3
  host -->|renders iframe| iframe_lane

  svc --> ddb
  s3 -. served by .-> fed_lane

  fed_lane -.-> cicd
  iframe_lane -.-> cicd
  cicd -.-> ddb

  classDef highlight fill:transparent,stroke:#ff5a26,stroke-width:2px;
  class host,svc highlight;

Concrete sequence using inventory-management-ui (federation) and sim-web-ui (iframe consumer):

sequenceDiagram
  autonumber
  actor U as Athlete
  participant H as whisker-host<br/>(browser shell)
  participant B as sim-bff (today)<br/>→ whisker-bff (planned)
  participant DB as DynamoDB<br/>module registry
  participant S3 as S3<br/>whisker-modules-{env}
  participant SI as inventory-management-ui<br/>remoteEntry.js
  participant SW as sim-web-ui<br/>(iframe app)

  U->>H: open /dashboard
  H->>B: GET /manifests/v1 (no auth)
  B->>DB: query per-env registry
  DB-->>B: [inventory-management-ui, sim-web-ui, rfid-reporting-ui, …]
  B-->>H: manifest list
  H->>H: render nav + dashboard widgets from manifest

  U->>H: enter BERM credentials
  H->>B: POST /legacy-berm/v1
  B-->>H: BERM access + refresh tokens

  Note over H,SI: Athlete navigates to /inventory (federation module)
  H->>S3: GET inventory-management-ui/remoteEntry.js
  S3-->>H: bundle (cached on CDN)
  H->>SI: loadRemoteModule("inventory-management-ui", "./pages/Inventory")
  SI-->>H: React component
  Note over SI: imports useAuth, authService from host<br/>via remoteSafeSelectors — no BFF call yet

  Note over H,SW: Athlete navigates to /SIMWeb/dashboard (iframe consumer)
  H->>SW: render iframe (src = iframeIntegration.urls.prod)
  SW->>H: postMessage { name: "requestAccessToken" }
  H-->>SW: postMessage { name: "accessToken", token, authType }
  Note over SW: existing app uses the BERM token<br/>to call its own backend

What this makes explicit:

  • Manifest fetch is unauthenticated. The shell renders its nav + widget shells before any login happens, and CI/GitHub Actions can call the same endpoint from outside Nike’s network.
  • Federation modules import from the host directly — no BFF round-trip to get a token at module load. The remoteSafeSelectors surface (useAuth, authService, useInEditMode, useIsNavOpen) is the entire host contract.
  • Iframe consumers ask the host for the token via postMessage on demand. The host stays the single auth authority; the iframe app keeps its own backend.
  • Both module types share one registry and one auth flow — the only difference is the rendering primitive.
Federation module Iframe consumer
Use when Greenfield, or rewrite You already have a working app and just want it inside the host
Built with Vite + Module Federation Whatever you already use (React, Angular, anything)
Talks to host via Direct ESM imports from remoteSafeSelectors.ts postMessage (T.R.U.E-compatible protocol)
Routing Inside host’s BrowserRouteruseParams, useNavigate, Link all work natively Owns its own router; host mounts a catch-all route
Auth token acquired useAuth().accessToken requestAccessToken message → host responds with accessToken
Best for new code ⚠ Last resort; convert when you can

The host implements both paths simultaneously. A single deployment can host a mix of federation modules and iframe consumers, and a module team can move between them by changing one field in their manifest.

whisker-host calls sim-bff for three platform concerns that don’t belong there long-term:

  • POST /legacy-berm/v1 — legacy BERM token exchange (login)
  • POST /legacy-berm/v1/refresh — token refresh
  • GET /manifests/v1 — the runtime module registry
  • whisker-bff — a platform-owned service extracted from sim-bff that will own the three endpoints above plus future component-provider endpoints (store/athlete data WCL needs at runtime). sim-bff keeps serving inventory routes unchanged until the migration is intentional. GET /manifests/v1 will stay unauthenticated on the new service too — see Manifest pipeline & deployment.
  • Devkit panel (port 3009) — a live, non-blocking compliance status surface. Design is settled; no code exists yet. See Automation, devkit & E2E.

Module teams own a module-manifest.json file, ship their code on their own cadence, and the platform’s runtime + pipeline glue them into a coherent shell without any coordinated release with the host.

The rest of these documents explain how each part of that sentence is made true and where it is not yet.