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.
What’s built today
Section titled “What’s built today”| 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.
System diagram
Section titled “System diagram”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;
Runtime flow
Section titled “Runtime flow”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
remoteSafeSelectorssurface (useAuth,authService,useInEditMode,useIsNavOpen) is the entire host contract. - Iframe consumers ask the host for the token via
postMessageon 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.
Two integration paths — one platform
Section titled “Two integration paths — one platform”| 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 BrowserRouter — useParams, 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.
The host BFF today
Section titled “The host BFF today”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 refreshGET /manifests/v1— the runtime module registry
Planned / proposed
Section titled “Planned / proposed”whisker-bff— a platform-owned service extracted fromsim-bffthat will own the three endpoints above plus future component-provider endpoints (store/athlete data WCL needs at runtime).sim-bffkeeps serving inventory routes unchanged until the migration is intentional.GET /manifests/v1will 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.
Operating model in one sentence
Section titled “Operating model in one sentence”Module teams own a
module-manifest.jsonfile, 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.
