Files
iios/docs/insignia-platform-live-state.md
maaz519 64c498a97a build: publish @insignia/iios-* SDKs to the Gitea package registry
- .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) <noreply@anthropic.com>
2026-07-10 17:13:55 +05:30

8.0 KiB

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 → PATPOST /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 BridgePOST /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 decisionPOST /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:80POST /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:80GET /v1/profile, GET /v1/stats. Backed by sqlite3 (/data/profiles.db, PVC insignia-profile-data, WAL, persistent, single-replica RWO).
  • CRM crm.insignia:80GET /v1/leads?view=list (applies obligations).
  • Policy Gateway policy-gateway.insignia:80POST /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.