Skip to content

Consumer Integration Guide

What a team outside the WhiskerPlatform core group does to onboard. Two paths exist: build a new federation module from scratch, or convert an existing app into an iframe consumer. Both land in the same runtime and the same pipeline — and neither ever requires a PR against whisker-host.

One-time setup — install the platform CLI

Section titled “One-time setup — install the platform CLI”
Terminal window
# .npmrc routing @nike scope to Artifactory — see whisker-platform/README.md
pnpm install -g @nike/whisker-cli
which thundercats
thundercats install-skill # once per machine: installs the global @thundercats Cursor skill

Run thundercats upgrade when you want a newer global CLI build (then re-run thundercats install-skill to refresh the global skill too). thundercats update refreshes a module/service repo’s .cursor/rules/ templates only — the skill itself lives outside any repo.

Terminal window
thundercats create module my-module
# back-compat alternatives that hit the same library code:
# pnpm create whisker
# npx @nike/create-whisker
#
# kind: module; name, description, team, slack channel, manager email
# accept default ports or set your own
#
# @nike/create-whisker writes AGENTS.md + .cursor/rules/thundercats-core.mdc
# itself as the last scaffold step — .cursor/ is already populated before
# you cd in, no separate `thundercats init` call happens under the hood.
# `whisker dev` (from @nike/whisker-module-kit) finds whisker-host on your
# machine the first time you run it — see Day 1 below — offering to clone
# it via SSH next to your module if it can't.

End state in the new repo:

my-module/
src/
moduleManifest.ts ← your platform surface
components/ ← your code
config/whisker.config.json ← orchestration (do not hand-edit)
module-federation.config.ts
package.json ← whisker-module-kit + WCL pinned
Jenkinsfile ← branch-aware, ready
.husky/ ← pre-commit + post-commit hooks wired
.github/workflows/notify-host.yml
.cursor/rules/thundercats-core.mdc ← written by create-whisker itself
AGENTS.md ← written by create-whisker itself

@thundercats itself doesn’t live in the repo — it’s the global Cursor skill installed once per machine via thundercats install-skill, and it already works here the moment the repo exists.

Terminal window
pnpm exec whisker doctor # pre-flight checks
pnpm exec whisker dev # full stack against the test BFF

whisker dev generates and validates the manifest, locates (or auto-clones) whisker-host, builds your module with VITE_IS_LOCAL=true, rebuilds the host’s remote loader, and starts everything with browser-sync proxying the host with reload on remoteEntry.js changes. You open the browser-sync URL and see the entire registered platform plus your local module. SIGINT tears everything down cleanly.

import { useAuth, useInEditMode } from 'whisker-host/remoteSafeSelectors';
import { DataTable, useModuleAnalytics, nikeTheme } from '@nike/whisker-component-library';
import { ThemeProvider } from '@mui/material';
import { useParams, Link } from 'react-router-dom';
export default function InventoryPage() {
const { accessToken, loggedIn } = useAuth();
const inEditMode = useInEditMode();
const { storeId } = useParams();
const { trackEvent, trackError } = useModuleAnalytics('my-module');
return <ThemeProvider theme={nikeTheme}>{/* your UI */}</ThemeProvider>;
}

You do not initialize analytics, mount your own router, set up auth, or configure MUI — the host and WCL already did.

Terminal window
git push # Jenkins CI: generate-manifest → validate-manifest → vite build
# → aws s3 sync to whisker-modules-test → upload manifest to whisker-manifest-test
# Lambda picks up the manifest, merges, writes to DynamoDB
# Module appears in test immediately

Merging to main follows the same flow against the prod bucket, but the Lambda preserves any admin-controlled enabled / allowedPersonas values — your first production publish lands behind an admin “turn it on” decision. .github/workflows/notify-host.yml fires a repository_dispatch so the host’s federation config picks up your remoteEntry.js on its next build.

Terminal window
cd existing-app
thundercats add iframe-consumer
# back-compat: npx @nike/create-whisker add-iframe-consumer
# thundercats init runs automatically here too

This detects your package manager and JS/TS from your lockfile, writes src/moduleManifest.(ts|js) with iframeIntegration and config/whisker.config.json (hostPort: 3001), patches package.json#scripts.dev to shell into whisker dev, and scans for *_ORIGINS arrays to patch idempotently. Your existing build, CI, framework, and tests are untouched.

Terminal window
pnpm dev # your existing command — now shells into `whisker dev`

This regenerates the manifest, starts your child app on its port, and starts whisker-host on 3001 with VITE_LOCAL_IFRAME_APPS pointing at your local app. Navigate to your module’s route and whisker-host renders IframeRenderer with your app inside it.

window.parent.postMessage({ name: 'requestAccessToken' }, '*');
window.addEventListener('message', (event) => {
if (event.data.name === 'accessToken') {
const { token, authType } = event.data;
// use it
}
});

The host handles refresh transparently. This is the same protocol rwe-core / T.R.U.E speaks — apps that worked there work here unchanged.

document.body.classList.add('whisker-embedded');
body.whisker-embedded {
scrollbar-width: none;
}
body.whisker-embedded::-webkit-scrollbar {
display: none;
}

The host already handles iframe sizing; suppressing internal scrollbars makes the embed look like part of the host.

Your CI publishes your manifest to S3 the same way federation modules do. Your app deploys wherever it already deployed — no federation bundle, no remoteEntry.js, no build-time GitHub Actions handshake needed.

Change Path
Add a navigation item / widget Edit navigation[] / widgets[] in src/moduleManifest.ts, commit, push. For federation nav, reference pages by pageId (not path). navigation.componentPath is optional and only needed for custom icon components. Husky regenerates + validates. Host picks it up on next page load.
Rename a component Treated as new — the old name is hard-deleted, the new one shows up with the manifest’s declared enabled.
Restrict a page to admins Edit pages[].allowedPersonas, ship — unless an admin previously overrode it via the (planned) config panel, in which case their override wins.
Update your team’s Slack channel Edit notifications.slack.channel — nightly + pipeline alerts route accordingly.
  • Open a PR against whisker-host. Never. For anything.
  • Coordinate a release with the platform team.
  • Wire your own analytics, auth, theming, routing, or store context.
  • Maintain a CORS/origin allow-list — the host derives it from the registry.
  • Keep your src/moduleManifest.ts honest.
  • Run whisker doctor if something feels off.
  • Periodically run thundercats audit (and update if drift is reported) to keep .cursor/ in sync; re-run thundercats install-skill after any thundercats upgrade to keep the global skill current.
  • Run pnpm update @nike/whisker-module-kit when update-check flags a newer version.
  • For federation modules: be careful renaming a componentPath — the manifest reference must match what your module actually exports.
  • For iframe consumers: maintain whatever build/deploy/test pipeline you already had; the platform only sits on top of it.
  • thundercats audit — read-only diagnostic of this repo’s .cursor/ layout.
  • pnpm exec whisker doctor — first stop for “it won’t run” questions.
  • @thundercats in your IDE — routes to module-author, bff-service-author, or manifest-validator as appropriate.
  • notifications.slack.channel in your manifest — used by the nightly runner.
  • This documentation tree — start at the WhiskerPlatform Architecture diagrams and the contract.