Context
0197 keeps HeroUI Pro in Stackra’s own apps until HeroUI answers the OEM request (STA-41). It does not say how the apps use Pro. Our apps need Pro components, and each one should take the same props as the matching free component in@stackra/ui (0302). The Pro
components therefore need a thin wrapper. That wrapper cannot live in @stackra/ui. The HeroUI Pro licence (heroui.pro/license,
quoted in 0113) forbids putting Pro components in a UI library, and @stackra/ui is the public
framework contract. The wrapper needs a home that is never published.
Pro also installs in an unusual way. The public npm packages are shells. Their postinstall downloads the licensed build from
HeroUI’s CDN, so CI needs a token for that download. The owner decided the token names on 2026-10-23.
Decision
-
A private package.
@stackra/ui-prolives atpackages/design-system/ui-pro. It sets"private": trueand has nopublishConfig, so it is never published. - Only our own apps import it. The consumers are the console, the docs, the playground, the mobile app and Figentra OS.
-
Never in a free package.
@stackra/uiand every free or published package never import@stackra/ui-pro,@heroui-pro/reactorheroui-native-pro. This restates 0165 decision 5 and 0197 decision 2. Pro stays an end-product dependency of our apps. -
The same vocabulary as
@stackra/ui. Each Pro wrapper is thin. It takes the props of its@stackra/uicomponent (the vocabulary of ADR 0303, decision 7:size,tone,isDisabled,isLoading,labelandonPress). Its contract type is<Name>ContractProps. Unknown props pass through to HeroUI Pro.- The contracts live in this package’s
src/contracts. They reuse the contract types of@stackra/uithrough its./contractsentry, and never import itssrc/.
- The contracts live in this package’s
-
Names follow the grammar. HeroUI Pro’s
Commandis wrapped asActionPalette, becauseCommandmeans a CLI command only (0294 decision 23). -
The first components.
- Web (
./react): DataGrid, ActionPalette, Sidebar and AppLayout, Kanban, KPI and TrendChip. Each one gets a playground route under a “Pro” navigation group, with Playwright and axe end-to-end tests (0302). - Native (
./native): ProgressButton, SlideButton, SwipeableRow, Calendar and DatePicker, NumberField. Each one gets unit tests against recording stand-ins and a screen in the mobile app’s design-system review area (stackra://design-system), the native twin of the playground. The owner asked for it on 2026-10-23; it amends the waves decision O33 (no native review surface yet). The free@stackra/uinative components, merged with waves 1 to 3, join that area in a follow-up (STA-416). The area is a development surface: release builds (__DEV__false) leave out its Settings row, its screens and its deep links.
- Web (
-
Pins and install path. Checked against the HeroUI Pro docs and the published packages on 2026-10-23.
- Web:
@heroui-pro/react1.0.0-beta.10 incatalog:frontend, withrecharts3.10.1 for KPI. - Native:
heroui-native-pro1.0.0-beta.11 incatalog:mobile, with@internationalized/date3.12.4 and@internationalized/number3.6.9. motion12.43.0 andreact-resizable-panels4.14.5 joincatalog:frontend: they are required peers of@heroui-pro/react(Sheet, Sidebar, Kanban, AppLayout).- The npm shells declare no peers, because the real
package.jsonarrives with the download.packageExtensionsinpnpm-workspace.yamlcopies the downloaded peers onto both packages, so pnpm links one React (and one React Aria) for Pro and our code. The copy is updated with each Pro bump. - Both come from the public npm registry. Both are listed in
allowBuildsinpnpm-workspace.yaml, so theirpostinstallruns. There is no private registry and no.npmrcauth. - Locally, the postinstall uses the developer’s
heroui-pro loginsession, which lasts 180 days. - In CI, it uses
HEROUI_AUTH_TOKEN, HeroUI’s CI/CD token. A step of the Quality Gate reads that one name from Infisical/ci(environmentprod) over GitHub OIDC. The step exports onlyHEROUI_AUTH_TOKEN, and it runs beforepnpm install.
- Web:
-
The token names.
HEROUI_PERSONAL_TOKENstays in Infisical/agents, and in this repository only the HeroUI MCP servers use it. Our apps install with theheroui-pro loginsession locally and withHEROUI_AUTH_TOKENin CI. CI uses the CI/CD token because HeroUI’s postinstall reads only that variable, and HeroUI tells pipelines to use the CI/CD token. This amends 0197 decision 4, which said that our apps and CI keepHEROUI_PERSONAL_TOKEN. One exception sits outside this repository: the public stackra-template console uses HeroUI Pro directly (@heroui-pro/react, andheroui-native-prowhere its native app uses it, not@stackra/ui-pro) with Figentra’sHEROUI_PERSONAL_TOKEN, which the template’s CI reads from Infisical over GitHub OIDC (ADR 0197 as amended). -
The CI permission (change risk R3, 0099). The Quality Gate job gains
id-token: write, and only the Infisical step uses it. The Infisical machine identity for that step is named by the repository variableINFISICAL_GATE_IDENTITY_ID, which is not a secret. The identity is allowed to read onlyHEROUI_AUTH_TOKENandUNIWIND_AUTH_TOKEN(decision 11). Until the owner sets both, the gate fails at a named step that says what is missing. Without the Pro build, the Pro package cannot typecheck or build. The identity trusts three GitHub OIDC subjects, one per event the gate runs on:repo:figentra/stackra:pull_request,repo:figentra/stackra:ref:refs/heads/main(pushes to main) andrepo:figentra/stackra:ref:refs/heads/gh-readonly-queue/main/*(the merge queue, which main’s ruleset requires; without it no pull request can merge). A fork never gets the tokens. The exported tokens stay in the job’s environment, so every later gate step can read them, and a branch of this repository that editsci.ymlcould print them. Accepted: they only download the licensed packages, and anyone who can push a branch is already trusted with the code. The release, SBOM and static deploy jobs never read them: they install without the three members that pull in Pro. -
The download budget. Each uncached CI install counts against HeroUI’s daily CDN download limit for the CI/CD token. The limit
is 1,000 on an individual licence. Caching
node_modulesand the pnpm store, keyed by the lockfile, avoids repeat downloads. That cache is a follow-up. -
Uniwind Pro in our own apps (owner, 2026-10-23). Uniwind is chosen per app, not per component: Uniwind Pro is
API-identical and installs under the
uniwindname ("uniwind": "catalog:uniwind-pro", the catalog holdingnpm:uniwind-pro@1.9.0), so everyuniwindimport in the app runs on the Pro engine, the workspace packages’ included.apps/mobileruns Uniwind Pro 1.9.0 (React Native 0.83 to 0.88, Expo SDK 55 to 58), with its native peerreact-native-nitro-modules0.37.1. Figentra OS may follow.- Free packages (
@stackra/ui) and@stackra/ui-prokeep a peer on freeuniwindand use only the shared API, so a framework user runs them on free, MIT Uniwind. - The install path is HeroUI Pro’s: a shell whose postinstall downloads the licensed build,
uniwindanduniwind-proinallowBuilds, auniwind-prologin locally,UNIWIND_AUTH_TOKEN(the CI/CD licence) in CI from Infisical/ciand as an EAS secret. The CI/CD licence allows 1,000 downloads a month.
Consequences
- Pro is in beta. The pins are exact, and each bump is reviewed like any catalog change.
- A fork, or a contributor without a HeroUI licence, cannot build
@stackra/ui-proor the playground. - Every CI install depends on HeroUI’s CDN and on the
/citoken. A CDN outage or an expired token blocks the install, so the gate cannot run. - A developer runs
heroui-pro loginonce, and renews it every 180 days. That is a chore for each developer. - The owner adds
HEROUI_AUTH_TOKENandUNIWIND_AUTH_TOKENto Infisical/ci, and sets theINFISICAL_GATE_IDENTITY_IDrepository variable. - Lifting the private rule needs a new ADR, after the OEM answer (STA-41). The open question in 0197 stays open until then.
Enforcement
"private": trueinpackages/design-system/ui-pro/package.json, with nopublishConfig.checkintools/scripts/validate-repository.mjs(PRO_DEPENDENCIES, run bypnpm validate) fails any member other than an app or@stackra/ui-prothat depends on@heroui-pro/reactorheroui-native-pro, any non-app member that depends on@stackra/ui-pro, and@stackra/ui-proitself without"private": trueor with apublishConfig. It also fails any non-app member that namescatalog:uniwind-pro(decision 11).- gitleaks (the pre-commit hook and the CI scan) keeps the token out of git, as 0173 requires.
- The Quality Gate (
.github/workflows/ci.yml) stops at itsLicensed UI prerequisitesstep whenINFISICAL_GATE_IDENTITY_IDis unset, and itsHeroUI Pro token (Infisical OIDC)andUniwind Pro token (Infisical OIDC)steps export onlyHEROUI_AUTH_TOKENandUNIWIND_AUTH_TOKEN(decisions 9 and 11). The release workflow builds only the packages, never@stackra/ui-pro(.github/workflows/release.yml,Build), and its installs and the static deploy’s skip@figentra/mobile,@figentra/playgroundand@stackra/ui-pro, whose postinstalls fail without the tokens. - Decision 10 (the download budget) has no check. It is a comment, and it stays one until the cache lands.
Alternatives
- Pro wrappers inside
@stackra/ui. Rejected: the licence forbids it, and every framework user would receive Pro code (decision 3). - The mirror bundle and the bring-your-own-key path of 0113. Already superseded by 0197. This ADR does not reopen them.
Supersedes and sources
Supersedes: none. Amends: 0197 (decision 4). Sources: owner decisions in chat, 2026-10-23, recorded in Linear STA-403. The HeroUI Pro docs and the published@heroui-pro/react and heroui-native-pro packages,
read on 2026-10-23.