Skip to main content

Onboards an external, consuming repo onto the @idhub identity resolution stack (identity-core, identity-adapter-nextjs, identity-adapter-angular, identity-provider-segment, identity-provider-launchdarkly, identity-provider-fullstory) without ever touching this monorepo's own packages. Detects the target's framework from real config-file evidence (next.config.*, angular.json, or neither → framework-agnostic core-only path), asks — never infers — which vendor providers to wire via one multiSelect AskUserQuestion call with no preselection and an explicit none-yet option, installs with the target's OWN package manager (pnpm/npm/yarn matched to its existing lockfile, never introducing a second one), and asks the cookie domain scoping question (host-only default vs. an explicit shared-subdomain value) rather than guessing either narrow (silently fails to share identity across subdomains) or broad (leaks the anonymous id to sibling subdomains). Scaffolds framework wiring write-if-absent from this skill's own self-contained references (never a repo-relative path back into this monorepo, per skill-channel-rollout's reference-normalization invariant), writes only vendor env var PLACEHOLDER names to .env.example-style files (never a real key, never touching a live .env/.env.local), and surfaces — but never auto-edits — the target's actual login/logout call sites where identify()/reset() must be placed by a human, since auth flow is high-blast-radius and a multi-step login can have more than one plausible 'now logged in' point.

identity-onboarding

Overview

Onboards another repo (a consuming app, not this monorepo) onto the @idhub identity resolution stack: one canonical anonymous id generated upstream of every vendor SDK, and a single identify() call that fans out to every registered provider. This skill is the rollout side — it never changes anything in the @idhub packages themselves, only in the target repo.

target repo path


1. framework detection — next.config.*(js/ts/mjs)? angular.json? neither?
│ ambiguous or unrecognized → ask, never guess

2. AskUserQuestion — which vendor providers (segment / launchdarkly /
fullstory / none yet), multiSelect, no preselection

3. AskUserQuestion — cookie domain scoping (host-only default, or a shared
subdomain value via "Other")

4. install with the TARGET's own package manager (pnpm-lock.yaml → pnpm add,
package-lock.json → npm install, yarn.lock → yarn add) — never introduce a
second lockfile

5. scaffold wiring, write-if-absent, from this skill's own references/
(never a repo-relative path back into this monorepo)

6. write vendor env var PLACEHOLDERS only (.env.example or equivalent) —
never a real key

7. report: files written/skipped, env vars the human must fill in, and the
exact login/logout call sites found — do NOT auto-edit those

STOP

The invariants

Create one todo per invariant. Each is independently verifiable.

  1. Framework detection is explicit file evidence, never an inferred guess. Check for next.config.js/.ts/.mjs (→ Next.js adapter, references/nextjs-wiring.md) or angular.json (→ Angular adapter, references/angular-wiring.md). Neither present → framework-agnostic path, references/vanilla-wiring.md (raw identity-core only, no adapter package). Both present (a monorepo with more than one app) → stop and ask which app directory is the actual target rather than picking one. Verify: a plain Express/vanilla-JS repo gets identity-core only, never an adapter package it can't use.

  2. Provider selection is asked, never inferred. Which vendors (Segment, LaunchDarkly, FullStory, or none yet) a team has actually licensed cannot be derived from the target repo's code — a fresh app may want zero, one, two, or all three. One AskUserQuestion call, multiSelect: true, no option preselected, an explicit "none yet" choice included. Never install all three providers "just in case" or skip the ask because the repo already has one vendor's raw SDK installed (that SDK may predate this rollout and be slated for removal, not reuse). Verify: running this skill against a repo with zero identity-related dependencies still produces exactly the providers the user selected — not more, not fewer.

  3. Installation uses the target repo's own package manager — this skill never introduces a second lockfile. Detect from pnpm-lock.yaml/package-lock.json/yarn.lock (or a packageManager field) and install with the matching command (pnpm add/npm install/yarn add). A target with no lockfile at all is a stop-and-ask case (same posture as skill-channel-rollout invariant 1) — never silently pick one on the target's behalf. Verify: onboarding into an npm-managed repo never creates a pnpm-lock.yaml alongside the existing package-lock.json.

  4. identify()/reset() call sites are surfaced, never auto-wired. Grep the target for likely login/logout handlers (signIn, login, signOut, logout, auth callback routes). Show the exact file:line and the snippet to add from the chosen wiring reference — but a human places it. Auth flow is high-blast-radius: a multi-step login (OAuth redirect, MFA step-up, impersonation) can have more than one plausible "the user is now logged in" point, and firing identify() at the wrong one either double-fires or fires before the real userId is known. The same applies to reset() at logout — firing it before session teardown completes can race the provider SDKs' own cleanup. Verify: after a run, the target's actual login/logout handler files are byte-identical to before — only wiring/config files from step 5 changed.

  5. Vendor credentials are never invented, guessed, or hardcoded — only placeholder env var names are written. Each provider's real config shape is already fixed by its package API: Segment needs a writeKey (+ a caller-supplied analytics/analytics-node client instance, which this skill does not create), LaunchDarkly needs a clientSideId, FullStory needs an orgId. Write the placeholder names (see references/provider-env-vars.md) into .env.example (or the target's equivalent) with an empty or obviously-fake value — never a value that looks like it could be real, and never write to a real .env file that might already hold live secrets. Verify: git grep for the literal placeholder strings in references/provider-env-vars.md finds only .env.example-style files this skill wrote, never a .env/.env.local file.

  6. Cookie domain scoping is asked, never guessed. Host-only (the identity-core default — unset domain) is correct for a single-domain app; a shared value (.example.com-style) is required only when the app spans subdomains that must share the same anonymous id. Guessing wrong in either direction is a real defect, not a style choice: too narrow silently fails to share identity across subdomains that need it; too broad leaks the anonymous id to sibling subdomains that should never see it. Ask via AskUserQuestion with an explicit "single domain, no sharing needed" option plus an "Other" free-text path for the shared-domain value. Verify: the scaffolded client/module config's cookie.domain is either absent or exactly the value the user typed — never a placeholder guess like .example.com left in verbatim.

  7. Scaffolding is write-if-absent — an existing wiring file is reported as a conflict, never overwritten. middleware.ts, app/providers.tsx, app.module.ts (or equivalent) may already exist with unrelated logic. Check existence before every write from step 5; if present, print the snippet that needs to be merged in by hand and move on rather than clobbering the file. Matches the same posture as skill-channel-rollout invariant 4. Verify: running this skill twice against the same target produces identical output on the second run — no file is silently duplicated or rewritten.

Red flags

  • Assuming a framework from package.json dependencies (e.g. next listed as a devDependency for tooling only) instead of the real config-file evidence → invariant 1.
  • Installing all three vendor providers by default, or because "the repo will probably want them eventually" → invariant 2; ask, don't guess.
  • Running pnpm add in a repo whose own lockfile is package-lock.json → invariant 3.
  • Editing a signIn/onLogin handler directly to insert the identify() call → invariant 4; surface it, let a human place it.
  • Writing anything into .env or .env.local (as opposed to .env.example) → invariant 5, and a real risk of committing a path that later leaks a live key.
  • Defaulting cookie.domain to .example.com or leaving it unset without asking → invariant 6.
  • Overwriting an existing middleware.ts/app.module.ts/providers.tsx found in the target repo → invariant 7.

Publishing new @idhub package versions, or changes inside this monorepo's own packages/* is out of scope — this skill only touches the target (consuming) repo. See this repo's own README.md and each package's README.md for the packages' own API and release flow.

Choosing which vendor SDKs a team should adopt (Segment vs. a competitor, LaunchDarkly vs. a competitor) is a product/business decision outside this skill's scope — this skill only wires up the providers already decided on in step 2.

Angular Universal's companion Express middleware for SSR apps is covered in references/angular-wiring.md step 4 (mirroring the adapter's own quick-start caveat) — it is a real, separate wiring step for SSR Angular targets, not an edge case to skip.

References