From 64c498a97a428558acd57f0dfb107f9fa1e78ea3 Mon Sep 17 00:00:00 2001 From: maaz519 Date: Fri, 10 Jul 2026 17:13:55 +0530 Subject: [PATCH] build: publish @insignia/iios-* SDKs to the Gitea package registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .npmrc routes the @insignia scope to https://git.lynkedup.cloud/api/packages/insignia/npm/ (auth via ${GITEA_TOKEN} env — no secret committed). - The 9 frontend SDK packages (contracts, kernel-client, adapter-sdk, *-web) are now publishable: private dropped, version 0.1.0, publishConfig pinned to Gitea. iios-service and iios-testkit stay private (pnpm publish skips them). - Root `release` / `release:dry` scripts; a Gitea Actions workflow publishes on a v* tag. - PUBLISHING.md documents publish + consumer (.npmrc) setup. Verified: dry-run packs cleanly and workspace:* deps resolve to 0.1.0 in the tarball. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/publish-sdks.yml | 27 ++++++ .npmrc | 6 ++ PUBLISHING.md | 53 +++++++++++ docs/insignia-platform-live-state.md | 113 +++++++++++++++++++++++ package.json | 4 +- packages/iios-adapter-sdk/package.json | 10 +- packages/iios-ai-web/package.json | 17 +++- packages/iios-community-web/package.json | 17 +++- packages/iios-contracts/package.json | 10 +- packages/iios-inbox-web/package.json | 17 +++- packages/iios-kernel-client/package.json | 17 +++- packages/iios-meeting-web/package.json | 17 +++- packages/iios-message-web/package.json | 17 +++- packages/iios-support-web/package.json | 17 +++- 14 files changed, 307 insertions(+), 35 deletions(-) create mode 100644 .gitea/workflows/publish-sdks.yml create mode 100644 .npmrc create mode 100644 PUBLISHING.md create mode 100644 docs/insignia-platform-live-state.md diff --git a/.gitea/workflows/publish-sdks.yml b/.gitea/workflows/publish-sdks.yml new file mode 100644 index 0000000..8956bd4 --- /dev/null +++ b/.gitea/workflows/publish-sdks.yml @@ -0,0 +1,27 @@ +name: publish-sdks + +# Publish the @insignia/iios-* SDK packages to the Gitea npm registry on a version tag. +# Requires: Gitea Actions enabled + a runner, and a repo secret GITEA_PUBLISH_TOKEN +# (a token with `write:package` scope for the `insignia` org). +on: + push: + tags: + - 'v*' + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: pnpm install --frozen-lockfile + - run: pnpm -r build + - run: pnpm -r publish --no-git-checks + env: + # maps to ${GITEA_TOKEN} in .npmrc; private packages (service, testkit) are skipped + GITEA_TOKEN: ${{ secrets.GITEA_PUBLISH_TOKEN }} diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..83c8b52 --- /dev/null +++ b/.npmrc @@ -0,0 +1,6 @@ +# @insignia SDK packages publish to / install from the Gitea package registry. +# Auth comes from the GITEA_TOKEN env var (never commit the token itself). +# publish → token needs the `write:package` scope +# install → token needs `read:package` +@insignia:registry=https://git.lynkedup.cloud/api/packages/insignia/npm/ +//git.lynkedup.cloud/api/packages/insignia/npm/:_authToken=${GITEA_TOKEN} diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 0000000..17ab5a1 --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,53 @@ +# Publishing the IIOS SDKs (Gitea package registry) + +The `@insignia/iios-*` **frontend SDKs** publish to our self-hosted Gitea npm registry: + +``` +https://git.lynkedup.cloud/api/packages/insignia/npm/ +``` + +**Published (public in the registry):** `iios-contracts`, `iios-kernel-client`, `iios-adapter-sdk`, +and the React hook packages `iios-message-web`, `iios-inbox-web`, `iios-support-web`, +`iios-ai-web`, `iios-community-web`, `iios-meeting-web`. +**Kept private (never published):** `iios-service` (the deployed backend) and `iios-testkit` (dev fakes). + +> be-crm does **not** consume these — it talks to IIOS over REST. The SDKs are a **frontend** concern +> (chat-web, the CRM support UI, mobile). + +## One-time: get a token +Gitea → **Settings → Applications → Generate Token**: +- to **publish**: scope `write:package` +- to **install** (private packages): scope `read:package` + +Export it (never commit it): +```bash +export GITEA_TOKEN= +``` +The repo `.npmrc` already routes the `@insignia` scope to Gitea and reads `${GITEA_TOKEN}`. + +## Publish +```bash +pnpm release:dry # build all + pack (no upload) — verify the 9 packages pack cleanly +pnpm release # build all + publish the non-private packages to Gitea +``` +`pnpm -r publish` automatically **skips** `private` packages, so only the 9 SDKs go out. +Versions are **immutable** — bump before re-publishing (edit `version`, or adopt `changesets`). + +Or push a tag and let CI do it (see `.gitea/workflows/publish-sdks.yml`; needs Gitea Actions + +a runner + the `GITEA_PUBLISH_TOKEN` secret): +```bash +git tag v0.1.0 && git push origin v0.1.0 +``` + +## Consume (in chat-web / the CRM front-end) +Add an `.npmrc` to the consuming repo: +```ini +@insignia:registry=https://git.lynkedup.cloud/api/packages/insignia/npm/ +//git.lynkedup.cloud/api/packages/insignia/npm/:_authToken=${GITEA_TOKEN} +``` +Then: +```bash +export GITEA_TOKEN= +pnpm add @insignia/iios-kernel-client @insignia/iios-contracts +``` +This replaces the **vendored** client that chat-web copies today — one source of truth for all frontends. diff --git a/docs/insignia-platform-live-state.md b/docs/insignia-platform-live-state.md new file mode 100644 index 0000000..32f0295 --- /dev/null +++ b/docs/insignia-platform-live-state.md @@ -0,0 +1,113 @@ +# Insignia Platform — Live State (from the mesh-verify probe, 2026-07-10) + +> Distilled from the authenticated `mesh-verify.lynkedup.cloud` dashboard (`/api/results` + +> `/api/journey`). This is the **real** platform IIOS is meant to plug into — service inventory, +> the identity/session/governance flow, and the exact contracts to wire IIOS's platform ports. +> Tokens redacted (the raw JSON dumps contain live SAT/PAT/refresh tokens — do not commit them). + +## Cluster / mesh +- **Cluster:** `lynkedup-tech` (NYC2 / DigitalOcean). **Istio** (istio-envoy) + **SPIFFE/SPIRE**, + trust domain **`spiffe://insignia.tech`** (SVIDs like `spiffe://insignia.tech/ns/sre/sa/default`, + `.../sa/realmdm-sas`). mTLS **PERMISSIVE**. Reached by ClusterIP DNS. +- **Namespaces:** `sre` (MDM, OPA, misc), `cmp` (consent platform), `insignia` (identity/session/ + app-facing services), `istio-system`. +- Public edges: `*.lynkedup.cloud` (behind oauth2-proxy → Keycloak). + +## The identity/session/governance flow (7 steps — the doctrine) +> **Separate authorities:** Session Broker *authenticates*, OPA *authorizes*, CMP decides *purpose*, +> RealMDM *resolves identity*. Purpose-proof ≠ authorization. The PAT carries the external `sub` +> only as a **SHA-256 hash**, never raw; `canonical_person_id` is null until MDM VERIFIES (MDM never +> blocks login). + +1. **Anonymous consent (CMP Edge)** — browser CMP SDK → `POST /edge/v1/cache-policy` → EdDSA-signed + cache-category manifest; a ConsentReceipt goes to CMP over gRPC. Purpose proof only. +2. **External login (Supabase)** → **SAT** (ES256 JWT). Claims: `iss` (project `/auth/v1`), opaque + UUID `sub`, `aud=authenticated`, `email`, `user_metadata` (full_name, avatar_url), `aal`, `amr`. + Proves the *session*, not the person/permission. Verified via Supabase **JWKS** (kid-selected). +3. **Session Broker exchange SAT → PAT** — `POST /v1/sessions/exchange` (Bearer SAT + + `X-Client-Authorization` = BFF Keycloak client-creds, aud=session-broker). Broker verifies the + SAT, calls the MDM bridge, resolves scope from **memberships**, mints the **PAT** (~5 min). +3b. **Workload identity (SPIFFE/mTLS)** — each meshed pod gets an X.509-SVID; OPA receives **both** + the user (`principal`) and the caller (`caller.spiffe_id`) — different layers, never merged. +4. **MDM Auth-Subject Bridge** — `POST /v1/auth-subjects/resolve {issuer, subject}` → `{platform_ + principal_id, canonical_person_id (null until VERIFIED), link_state (PENDING/VERIFIED), + link_version, match_method}`. Keyed on **iss+sub** (never email). Idempotent. +5. **OPA decision** — `POST /v1/decisions` → `{decision_id, allow, reason_codes, obligations, + policy_version}`. Obligations = masks / row-filters / denied fields / audit level / ttl. A + decision, not a 50-line entitlement JWT — the PEP MUST enforce every obligation. +6. **AppShell assembles the ACE** — combines PAT + OPA obligations + CMP consent into an App + Context Envelope (HttpOnly cookie): `capabilities[]` (policy-derived), `ui_obligations` + (hide/mask/step_up), `consent`. **No tokens, no raw PII in the browser.** +7. **CRM renders** — applies OPA `row_filter` (SQL WHERE) + `mask_fields` + `deny_fields` + server-side; rows reference `canonical_person_id`, not email. + +## Service inventory + real endpoints +**Identity / session (`insignia` ns):** +- **Session Broker** `session-broker.insignia:80` — `POST /v1/sessions/exchange` (SAT→PAT). +- **Memberships** `memberships.insignia` (public `insignia-memberships.lynkedup.cloud`) — + `POST /v1/internal/resolve`, `GET /v1/memberships` (Bearer PAT) → scope tuple + `allowed[]`. +- **AppShell BFF** `appshell-bff.insignia:80` (public `insignia-appshell.lynkedup.cloud`) — + `GET /apps/crm-web/bootstrap` (Bearer PAT) → ACE. +- **Profile** `profile.insignia:80` — `GET /v1/profile`, `GET /v1/stats`. Backed by **sqlite3** + (`/data/profiles.db`, PVC `insignia-profile-data`, WAL, persistent, single-replica RWO). +- **CRM** `crm.insignia:80` — `GET /v1/leads?view=list` (applies obligations). +- **Policy Gateway** `policy-gateway.insignia:80` — `POST /v1/decisions`, `POST /v1/decisions/batch`. + +**MDM (`sre` ns):** `mdm-kernel.sre:80` (`/v1/auth-subjects/resolve`, `/healthz`, `/readyz`; auth = +Keycloak service token **aud=realmdm**) · `mdm-ai.sre:80` · `mdm-sas.sre:9090` (**gRPC** — tokenization/SAS). + +**OPA / policy (`sre` ns):** `opa.sre:8181` (`/health`, `GET /v1/data` = live policy tree; +default-deny) · `realmdm-opa.sre:8181` · `opal-server.sre:7002` (OPAL policy distribution). + +**CMP (`cmp` ns), backed by Postgres + NATS + Redis:** `cmp-core:8080` (real `CheckConsent` gRPC; +service token aud=cmp) · `cmp-admin:8086` · `cmp-evidence:8081` · `cmp-sync:8085` · +`cmp-tollgate:8082` (NATS JetStream gating) · `cmp-media:8083` · `cmp-worker:8084`. Plus +`insignia-consent-edge.insignia` (`POST /edge/v1/cache-policy`) and `insignia-consent-adapter.insignia` +(`POST /v1/consent/evaluate {purpose}` → `{permitted, legal_basis, state, consent_epoch}`). + +**Other (`sre` ns):** `poi-api:5000` · `roof:8002` (YOLO segmentation) · `commit:8081` · +`presign:8080` · `egs:8090` · `artifact-retrieval:8082` (was 503 on probe day) · `cmp-docs`. + +## The two contracts IIOS must match + +### PAT (Platform Access Token) — what IIOS should verify +`iss = https://identity.insignia.internal` · **ES256** (EC P-256), verify via the broker **JWKS** +(`.../.well-known/jwks.json`, kid-selected — **no shared secret**) · `aud` includes the app (e.g. +`crm-web`) · `lifetime ~300s`. Claims: +``` +sub = platform_principal_id app_id tenant_id org_id bu_id +region environment role aal amr auth_source +external_subject_hash = sha256:… (NOT the raw external sub) +canonical_person_id (null until VERIFIED) +session_epoch · policy_epoch · consent_epoch (stale-detection) +sid (platform_session_id) · typ = platform-access+jwt +``` + +### OPA decision — `POST http://policy-gateway.insignia.svc.cluster.local/v1/decisions` +Request `{ input: { principal{platform_principal_id, canonical_person_id, auth_source, aal, roles, +memberships[]}, caller{spiffe_id}, resource{type, id, tenant_id, classification[]}, action, +context{purpose, device_trust, consent_receipt_ids[], network_zone, time} } }` +Response `{ decision_id, allow, reason_codes[], obligations{ row_filter, allow_fields[], mask_fields{}, +deny_fields[], audit, decision_ttl_seconds }, policy_version }`. + +## What this means for wiring IIOS's ports +1. **Auth — verify the PAT, not the Supabase SAT.** IIOS today verifies the Supabase SAT directly + (a dev shortcut). In the real platform the **Session Broker** does SAT→PAT; a platform workload + verifies the **PAT**. The PAT already carries the full scope tuple + `platform_principal_id`, so + `MessagePrincipal` maps ~1:1: `userId = platform_principal_id` (→ `canonical_person_id` once + VERIFIED), `appId = app_id`, `orgId = org_id`, `tenantId = tenant_id`, `+ buId`. Wire it by adding + the broker as an `AUTH_ISSUERS` entry (iss `https://identity.insignia.internal`, ES256, its JWKS). +2. **OPA — point `OpaPort` at the Policy Gateway** (`POST /v1/decisions`). Build `{principal, caller. + spiffe_id, resource, action, context}` from the PAT + the op; `decideOrThrow` maps `allow` → proceed + and **must enforce the obligations** (masks/row-filter/deny). The gateway's input is richer than + IIOS's current `{action,…}` — that's the adapter's job to assemble. +3. **MDM — usually don't call it.** The PAT already carries `platform_principal_id`/`canonical_person_id` + (the broker resolved at login). Only call `/v1/auth-subjects/resolve` if IIOS is the identity-exchange + edge (it isn't — the Broker is). +4. **CMP — consent gate** via `insignia-consent-adapter /v1/consent/evaluate {purpose}` when processing + content for AI/analytics/marketing. +5. **SAS — tokenization/masking** is `mdm-sas` (gRPC) — the "tokenize sensitive parts before storage" + requirement. + +*Source: two API payloads captured 2026-07-10 (`result.json` = probes, `journey.json` = the CRM +first-vertical-slice journey). Re-capture from the authenticated dashboard to refresh.* diff --git a/package.json b/package.json index 0ae9bfd..cc492e1 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "build": "pnpm -r build", "typecheck": "pnpm -r typecheck", "test": "vitest run", - "boundary": "node scripts/check-import-boundary.mjs" + "boundary": "node scripts/check-import-boundary.mjs", + "release:dry": "pnpm -r build && pnpm -r publish --dry-run --no-git-checks", + "release": "pnpm -r build && pnpm -r publish --no-git-checks" }, "devDependencies": { "@types/node": "^26.0.1", diff --git a/packages/iios-adapter-sdk/package.json b/packages/iios-adapter-sdk/package.json index ba4ff3e..760da27 100644 --- a/packages/iios-adapter-sdk/package.json +++ b/packages/iios-adapter-sdk/package.json @@ -1,15 +1,19 @@ { "name": "@insignia/iios-adapter-sdk", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "main": "dist/index.js", "types": "dist/index.d.ts", - "files": ["dist"], + "files": [ + "dist" + ], "scripts": { "build": "tsc -p tsconfig.json", "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { "@insignia/iios-contracts": "workspace:*" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-ai-web/package.json b/packages/iios-ai-web/package.json index 5f03714..5f1af7a 100644 --- a/packages/iios-ai-web/package.json +++ b/packages/iios-ai-web/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-ai-web", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -23,5 +29,8 @@ "react": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-community-web/package.json b/packages/iios-community-web/package.json index d336921..e20903d 100644 --- a/packages/iios-community-web/package.json +++ b/packages/iios-community-web/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-community-web", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -23,5 +29,8 @@ "react": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-contracts/package.json b/packages/iios-contracts/package.json index 2286f9f..fba7c80 100644 --- a/packages/iios-contracts/package.json +++ b/packages/iios-contracts/package.json @@ -1,12 +1,16 @@ { "name": "@insignia/iios-contracts", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "main": "dist/index.js", "types": "dist/index.d.ts", - "files": ["dist"], + "files": [ + "dist" + ], "scripts": { "build": "tsc -p tsconfig.json", "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-inbox-web/package.json b/packages/iios-inbox-web/package.json index 81d1c57..92aac0e 100644 --- a/packages/iios-inbox-web/package.json +++ b/packages/iios-inbox-web/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-inbox-web", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -23,5 +29,8 @@ "react": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-kernel-client/package.json b/packages/iios-kernel-client/package.json index c2504da..28ec6d9 100644 --- a/packages/iios-kernel-client/package.json +++ b/packages/iios-kernel-client/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-kernel-client", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -19,5 +25,8 @@ "devDependencies": { "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-meeting-web/package.json b/packages/iios-meeting-web/package.json index 368c160..202630f 100644 --- a/packages/iios-meeting-web/package.json +++ b/packages/iios-meeting-web/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-meeting-web", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -23,5 +29,8 @@ "react": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-message-web/package.json b/packages/iios-message-web/package.json index a8da0a8..371d92a 100644 --- a/packages/iios-message-web/package.json +++ b/packages/iios-message-web/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-message-web", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -23,5 +29,8 @@ "react": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } } diff --git a/packages/iios-support-web/package.json b/packages/iios-support-web/package.json index d54cadd..1a70b0e 100644 --- a/packages/iios-support-web/package.json +++ b/packages/iios-support-web/package.json @@ -1,13 +1,19 @@ { "name": "@insignia/iios-support-web", - "version": "0.0.0", - "private": true, + "version": "0.1.0", "type": "module", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, - "files": ["dist"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], "scripts": { "build": "tsup", "typecheck": "tsc --noEmit" @@ -24,5 +30,8 @@ "react": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.3" + }, + "publishConfig": { + "registry": "https://git.lynkedup.cloud/api/packages/insignia/npm/" } }