The single canonical architecture: a shared horizontal platform — services, agents, contracts, design system, native SDK ecosystem (HDK), and the AIM (Application & Identity Management) foundation — consumed by every vertical under projex_verticals/. Builds once. Multi-tenant. Multi-vertical. Multi-app. Multi-surface (web · mobile · kiosk). Petabyte-scale via pool-based horizontal scaling (no sharding). Identity expressed as a six-layer stack — Master Person · App Identity · Tenant Membership · Persona · Encounter · Relationship — encrypted end-to-end and governed by a three-evaluator access mesh.
../Architecture.html v1, ../Architecture-v2.html v2, ../v3/Architecture-v3.html v3) are retained as historical references; this file supersedes them in scope. New material introduced in v3 is marked v3; carried-forward v2 material is marked v2 where the distinction matters.
gaps/Analyze1.md) and its architectural critique (gaps/Analyze2.md), via the capability mapping in ../insignia/ProjexCloud-vs-Insignia-Reality-Report-v2.html. It adds §11A · Platform Control Planes & Obligation-Based Authorization and principles P16–P18: the eight-control-plane mapping, obligation-bearing authorization (decisions return mask/filter/audit/TTL, not just allow/deny), the minted Platform Principal Token (one signed internal identity, never trust forwarded headers), consent as a gating decision input, and a fail-closed + break-glass doctrine. It also records, explicitly, what is deferred by design (SPIFFE/mTLS service mesh, NATS/JetStream/Temporal, probabilistic MDM) and why. §11A.10 records the provisional Healthcare (multi-source) resolution that promotes EMPI / consent-gating / obligations to Required, and §11A.11 is the coverage map tying every analysis gap to its status and Projexlight implementation backlog (P10 · E1–E9, 53 tasks). Status note: v3.2 closes these gaps at the design level and schedules them as work — "designed & backlogged" is not "shipped." New v3.2 material is marked v3.2.
Every vertical we ship — Shantam (Seva), Kiana (Realty), Music/Video, BidWork, LeadPulse, FieldOps, OneEstate, future Wellness and Healthcare — reuses 80–90% of the same plumbing: identity, payments, notifications, content, search, analytics, audit, AI gateway, geospatial, dispatch, evidence capture, and a dozen platform agents. Re-implementing this per vertical is the single largest source of waste and the single largest source of drift.
This document describes that shared layer in its v3.1 (complete) form: what lives in projex_common/, the contracts that make it consumable, the HDK native SDKs that field-grade apps depend on, the AIM pool model that lets the platform grow from 1k to 1M+ tenants and petabyte-scale data, the six-layer identity stack that handles every cross-app / cross-tenant scenario the team has identified, and the discipline that keeps it shared rather than copy-pasted.
@projexlight/contracts, themes the shared design system, and ships. Pool allocation is automatic; encryption and access mesh are inherited.v3.1 = v2 (complete platform) ∪ v3 (AIM hardening). The table below shows where each capability area lives in this document.
| Capability area | Origin | Where in this doc |
|---|---|---|
| Three build-time disciplines (one workspace · one contracts · Rule of Three) | v2 | §3 Disciplines |
| Architecture principles P1–P10 (offline-first, evidence integrity, …) | v2 | §4 Principles |
| Principles P11–P15 (no sharding, layered identity, encryption-by-default) | v3 | §4 Principles |
| Client strategy (React Native + React Web) | v2 | §5 Client Strategy |
| HDK — ten native modules + install order | v2 | §6 HDK |
| Identity foundation — three pillars (MDM · ABAC · Consent), scope hierarchy, canonical IDs, MDM patterns | v2 | §7 Identity Foundation |
| Identity layers (Master Person · App Identity · Tenant Membership · Persona · Encounter · Relationship) | v3 | §7 Identity Foundation |
| AIM pools (no sharding · admin pool · app pool · evidence pool · pool indexing · capacity targets) | v3 | §8 AIM Pools |
| Pool routing & registry | v3 | §9 Pool Routing |
| Multi-level encryption (root · pool · tenant · person · device · org · encounter) | v2 + v3 | §10 Encryption |
| Access control mesh (ABAC + ReBAC + Encounter Grants) | v2 + v3 | §11 Access Mesh |
| Tenancy & verticals (Tier S/P/G isolation) | v2 | §12 Tenancy |
| Workspace layout | v2 | §13 Workspace |
| Layered view (channels · HDK · gateway · services · agents · data plane · geo · ingestion · capture) | v2 | §14 Layered View |
| Worked scenarios (healthcare · eCommerce · OneEstate · cross-domain) | v3 | §15 Scenarios |
| Services catalogue (29 horizontal services) | v2 | §16 Services |
| Permissions (three-layer device · role · consent) | v2 | §17 Permissions |
| Common AI agents (12) | v2 | §18 Agents |
| Contracts & event envelope | v2 | §19 Contracts |
| What stays vertical (do not lift) | v2 | §20 Vertical-Specific |
| Adoption playbook (new vertical) | v2 | §21 Playbook |
| Pool lifecycle & tenant migration | v3 | §22 Pool Lifecycle |
| Governance & ownership | v2 + v3 | §23 Governance |
| Cross-vertical roadmap | v2 + v3 | §24 Roadmap |
Reuse only holds if these three rules are enforced from day one. Every architectural decision in this document derives from one of them.
All shared services, agents, packages, and native SDKs live in projex_common/ as a single pnpm + turbo workspace. Verticals import packages and call services from here; they never copy code into their own tree. If a vertical needs to "tweak" a shared service, the tweak goes upstream as a config or contract change — not as a fork.
@projexlight/contractsA single source of truth for the event envelope, every event type, every shared API shape, every shared enum, every HDK module interface, the six identity-layer types, and the pool-routing types. Both verticals and SDK modules depend on it. Schemas evolve via semver; breaking changes require a new vN topic and a dual-write window. No service emits an event the contracts package hasn't registered.
A capability is lifted from a vertical to projex_common/ only after a third vertical needs an equivalent — and only with a generalized contract. This avoids two failure modes: premature lifting (building "common" abstractions from a single example) and accidental fragmentation (letting two verticals each grow their own version, drifting until reconciliation costs more than rebuilding).
projex_common/ does my code depend on?" in one sentence, you're not following discipline #1. If you're emitting an event whose schema isn't in @projexlight/contracts, you're breaking #2. If you're lifting code into common with only one consumer in mind, you're breaking #3.
The biggest risk to a platform this size is not technology — it is unbounded flexibility. When everything is configurable, nothing is optimizable; developers route around the platform; the rule-of-three governance gets overwhelmed by edge cases. v3.1 commits to a small set of opinionated, lint-enforced constraints that say "this is the way" — and CI rejects anything else.
../../Analyze.txt · ../../Analyze2.txt) converged on the same warning: "Platforms become too abstract, too configurable, too difficult to understand. Then developers bypass the platform." The cure is not more flexibility — it's fewer allowed patterns, enforced from week 1.
| # | Constraint | Enforced by | Cost of violation |
|---|---|---|---|
| OC-1 | Every SDK method that is billable carries @meter(sku, unit, tier) | CI lint over SDK exports | Unmetered traffic = revenue leakage; retrofit is 6+ weeks across 30+ SDKs. |
| OC-2 | Every event type is registered in the EventTypeRegistry (contracts) before any producer emits | Producer-side schema validator + CI registry diff | Kafka cardinality explodes; consumers break silently; observability dies (Analyze2 #4). |
| OC-3 | Every data-bearing SDK uses withTenant(...) from sdk-pool-router; no raw DSN reads | Lint blocks raw Postgres clients in package code | Cross-pool reads slip in; isolation guarantee breaks. |
| OC-4 | Every layer-attribute read goes through resolveIdentityContext() from sdk-identity-resolver | Lint blocks direct sdk-identity / sdk-persona imports in non-resolver SDKs | Six-layer manual traversal becomes the hot-path bottleneck (Analyze2 #1). |
| OC-5 | Every cross-pool read uses one of four sanctioned cases (resolver two-pool fetch · DSAR fan-out · analytics warehouse · lineage projection); marked with @cross_pool_sanctioned(reason) | Lint blocks unmarked cross-pool clients | Pool isolation degrades; ops nightmare at >200 pools. |
| OC-6 | Every agent action uses a capability token; no direct tool invocation | Tool runtime refuses without token; meter validates token at gate | Agent leaks Tenant A into Tenant B (Analyze2 #5). |
| OC-7 | Every offline-write HDK module routes through hdk-sync; no direct write-queue implementations | Lint blocks queue clients outside hdk-sync | Conflict-resolution policy fragments; field-ops conflicts unresolvable (Analyze2 #8). |
| OC-8 | Every encryption op uses sdk-vault; no direct KMS calls | Lint blocks AWS-KMS / GCP-KMS clients outside vault package | Key tier discipline breaks; cryptographic-shred becomes unreliable. |
| OC-9 | Every state-changing operation writes to sdk-audit | Lint requires audit emit on mutation handlers in code review | Pre-audit operations non-attestable; SOC2/HIPAA exposure. |
| OC-10 | No SDK ships a "v0 stub" of an upstream SDK to unblock its own development; upstream must be at v1.0 | CI fails if package depends on workspace v0 of another package | Stub-driven divergence; integration day 1 is a refactor day. |
user table.@meter from PR #1 or they don't ship.Complement to §3A. Where §3A says "this is the only allowed pattern," §3B says "this complexity belongs HERE and only here." The rule: global standards, local behaviors. Anything global has to be agreed by Working Groups; anything local can be decided by the vertical owning it.
| Concept | Global (one standard, platform-wide) | Local (vertical / tenant / app decides) |
|---|---|---|
| Identity | Six-layer model · canonical IDs · JWT claim set · projection schema | Persona-extension fields per app · role-template names · BU tree shape |
| Events | Event envelope · EventTypeRegistry namespaces · retention classes | Service-specific events within registered namespaces · per-event payload shape |
| SDK contracts | Public types in @projexlight/contracts · semver discipline · breaking-change protocol | SDK-internal types · vertical-specific contract extensions (until promoted) |
| Authorization | ABAC engine · ReBAC engine · IQL grammar · policy decision log format | Per-tenant policies · per-app role-template overrides · per-encounter grants |
| Pool placement | Pool families · placement matrix (§8A) · cross-pool sanction rules | Per-tenant Tier (S/P/G) · sub-tenant opt-out · per-app pool capacity policy |
| Pricing | SKU schema in contracts · pricing modes · catalog versioning | Per-tenant rate overrides · committed-use discounts · reseller commission rules |
| Semantic | SemanticObject/Relation/CapabilityGraph type system · core cross-vertical concepts (Person · Address · Money · Document) | Per-vertical ontology bundles · Intent definitions per vertical · per-tenant SemanticObject extensions |
| Agents | Agent identity types · Agent Isolation Runtime · capability-token format | Per-tenant agent definitions · per-agent scope · per-agent kill-switch |
| HDK | Native bridge ABI · permissions model · sync protocol | Per-app feature toggles · per-device profile · per-vertical sensor configurations |
| Notification | Channel abstraction · template engine · consent pre-flight | Per-tenant templates · per-persona quiet hours · per-vertical channel mix |
| Workflow | Temporal facade · envelope propagation · compensable-step protocol | Per-vertical workflow definitions · per-tenant SLA overrides |
P1–P10 carry forward from v2 unchanged. P11–P15 are introduced by v3 and now form the AIM-hardening core.
@projexlight/contracts. Sync APIs only where strict consistency is required.tenant_id and vertical_id. The platform never assumes a single tenant or single vertical.@projexlight/contracts with a versioned schema.@projexlight/branding; they do not fork the design system.person_id. Cross-tenant reads require an explicit Relationship + Consent receipt; the encryption layer enforces this.allow/deny plus obligations — fields to mask, rows to filter, audit level, decision TTL. Enforcement is server-side; UI visibility is advisory only. A caller may never be the sole thing standing between a hidden field and the wire (see §11A.3).IdentityContext. Downstream services trust that token — not raw user-supplied headers or claims. Closes the confused-deputy class of attacks (see §11A.4).One mobile framework. One web framework. One native SDK layer. One TypeScript contract spanning all three. The decision is locked; the table below records the trade-off so it stays auditable.
| Surface | Stack | Why |
|---|---|---|
| Web (browser) | React + TS · @projexlight/design-system | Map page is business-critical and lives on the web first. React Web shares business logic with RN via TS packages. |
| Mobile (iOS · Android) | React Native + TS | Single language across stack. Direct consumption of @projexlight/contracts and SDK packages. |
| Kiosk (sales devices) | React Native, Android in COSU / device-owner mode | Same RN APK as mobile, locked down via Android COSU. Login tracking, single-app pinning, MDM-managed. |
| HDK (native SDKs) | Kotlin (Android) · Swift (iOS) · TS facade | Camera, AR, scanner, biometric, Mapbox, watermark, diagnostic — all need native APIs. The TS facade is the contract. |
@projexlight/contracts is TypeScript. Flutter would need a parallel Dart contracts package — breaks discipline #2.react-native-web shares business logic with React Web. Flutter Web is not credible at production for this workload.The Hexa Development Kit (HDK) is the native counterpart to the JS/TS SDK packages. It owns the device-side capabilities that the platform needs to ship evidence-grade, offline-capable, sensor-driven experiences. Every HDK module is built once in Kotlin (Android) and Swift (iOS), bridged into React Native, and surfaced behind a TypeScript facade whose contract is registered in @projexlight/contracts.
flowchart TB classDef rn fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5; classDef bridge fill:#1a2234,stroke:#c9a86a,color:#e6ecf5; classDef native fill:#241622,stroke:#ff6b6b,color:#e6ecf5; classDef ctr fill:#16241c,stroke:#5dd39e,color:#e6ecf5; RN["React Native app
(SevaApp · RealtyApp · FieldOpsApp · HealthcareApp · KioskApp)"]:::rn TS["TypeScript facade
@projexlight/hdk-* packages"]:::bridge CTR["@projexlight/contracts
HDK module interfaces · identity layers"]:::ctr AND["Android Native
Kotlin · AndroidX · CameraX"]:::native IOS["iOS Native
Swift · AVFoundation · CoreMotion"]:::native RN --> TS --> AND TS --> IOS TS -. types .-> CTR AND -. emits envelope events with encounter_id .-> RN IOS -. emits envelope events with encounter_id .-> RN
encounter_id when in scope).| # | Module | Capability | Native deps | Owner |
|---|---|---|---|---|
| 1 | hdk-map | Provider-abstracted maps (Mapbox · Google · OSM), property bounding boxes, storm overlays, clustering, custom markers, Uber-style anchors/routes/ETA | Mapbox SDK · Google Maps SDK | Satyam |
| 2 | hdk-camera | Evidence-grade capture. Embeds GPS, IMU, timestamp, device ID, consent reference, encounter reference into media at capture time. | CameraX · AVFoundation · CoreMotion | Kunal |
| 3 | hdk-idp | Offline biometric + PIN. App authenticates locally, then resolves identity (incl. App Identity binding) via cloud IDP when online. | BiometricPrompt · LocalAuthentication | Shoaib · Krunal |
| 4 | hdk-scanner | QR · Barcode · AprilTag detection with context dispatch. | MLKit · Vision · custom AprilTag | TBD |
| 5 | hdk-image-editor | Shapes, arrows, text annotations, crops, filters, AI auto-correct, pinch straightening. Raw + edited both retained. | Skia · GPUImage · MLKit | Shoheb |
| 6 | hdk-video-editor | Trim, segment cut-in, speed, overlays, music, volume, branding. Local saving + URI outputs. | MediaCodec · AVFoundation | Shoheb |
| 7 | hdk-measure | Double-arrow measurement annotations (m · in · ft), AR length using start/end point recognition. | ARCore · ARKit | TBD |
| 8 | hdk-watermark | Logo placement, metadata overlays, brand-asset presets, font dropdowns. | Skia · CoreGraphics | TBD |
| 9 | hdk-diagnostic | Permissions, Wi-Fi, internet speed, device memory, sensors, temperature. Pre-crash snapshots tied to device ID. | Sentry-native · custom telemetry | Kunal |
| 10 | hdk-permissions | Device-level permission gates + role/consent overlay; pairs with cloud Policy + ReBAC. | System permissions APIs | Mayur |
1. @projexlight/contracts (TS shapes, no native deps) 2. @projexlight/sdk-identity (TS facade) 3. @projexlight/sdk-pool-router (TS — required by every data-touching module) 4. @projexlight/hdk-idp (native: biometric, PIN) 5. @projexlight/hdk-permissions (native: layered enforcement) 6. @projexlight/hdk-diagnostic (native: telemetry — required by all subsequent) 7. @projexlight/hdk-camera (native: depends on permissions + diagnostic) 8. @projexlight/hdk-map (native: depends on permissions) 9. @projexlight/hdk-scanner (native: optional, depends on camera) 10. @projexlight/hdk-image-editor (TS + native renderer) 11. @projexlight/hdk-video-editor (TS + native renderer) 12. @projexlight/hdk-measure (native: depends on camera + AR) 13. @projexlight/hdk-watermark (TS + native renderer)
Every HDK module that does offline-write (camera, map, scanner, image-editor, video-editor, measure, watermark) eventually faces the same problem: multiple devices, multiple users, concurrent edits, offline windows, then reconnect. Without a centralized model, every module reinvents conflict handling — and field-ops verticals hit unresolvable collisions in week 1 of production.
../../Analyze2.txt #8) flagged offline sync as one of the 9 hyperscale-killers: "bidirectional multi-device concurrent sync conflicts explode in field operations." The fix is not just hdk-sync (the SDK that executes resolution) but a documented per-event-type policy in @projexlight/contracts that every event author declares once. Then resolution is mechanical.
Every event type registered in the EventTypeRegistry (§8B + sdk-audit) declares one of these strategies as its conflict_policy. hdk-sync reads the policy and applies it on reconnect.
| Strategy | Use for | How it resolves | Example event types |
|---|---|---|---|
| CRDT (Conflict-free Replicated Data Type) | Collaborative editing of structured data; counters; sets | Mathematical merge — order doesn't matter; result is identical regardless of which device's edit arrives first | content.note.edit.v1 (collaborative notes); encounter.tag.add.v1 (tag sets); telemetry counters |
| LWW (Last-Write-Wins) | Ephemeral state; sensor readings; UI state | Highest timestamp wins; loser's write is recorded in audit but not applied | device.location.update.v1; device.battery.report.v1; diagnostic.heartbeat.v1 |
| Merge Policy (typed field-level merge) | Structured documents with non-overlapping field edits | Per-field merge rule (additive · max · last-write · veto); if rules conflict, escalate to human-review | property.listing.update.v1; contract.terms.amend.v1; tenant.config.update.v1 |
| Event Sourcing | Ledger-style data; financial entries; audit trail; immutable history | All conflicting events are appended in causal-order; state is the fold of all events; nothing is discarded | payment.charge.v1; audit.action.v1; donation.received.v1; encounter.note.append.v1 |
| Human Reconciliation | Sensitive data; PII edits; financial corrections; clinical notes | Conflicts queue to a Needs-Review surface; the responsible persona (or their delegate) picks the winner; sdk-approval governs delegation | profile.pii.update.v1; encounter.clinical-note.edit.v1; payment.refund.v1 (when amount disputed); property.title.amend.v1 |
sequenceDiagram participant D1 as Device 1 (offline) participant D2 as Device 2 (offline) participant SYNC as hdk-sync participant POL as ConflictPolicy
(from contracts) participant AUD as sdk-audit participant HR as Human-Review queue
(if needed) D1->>D1: Edit at t=10 (offline) D2->>D2: Edit at t=12 (offline) Note over D1,D2: Both reconnect D1->>SYNC: Replay queue: edit@t=10 D2->>SYNC: Replay queue: edit@t=12 SYNC->>POL: getPolicy(event_type) POL->>SYNC: strategy=CRDT|LWW|merge|event-sourcing|human-review alt CRDT SYNC->>SYNC: mathematical merge → single state SYNC->>AUD: record both inputs + merged output else LWW SYNC->>SYNC: t=12 wins (newer) SYNC->>AUD: record loser t=10 in audit else Merge Policy SYNC->>SYNC: field-level merge — if collision → escalate SYNC->>AUD: record per-field decisions else Event Sourcing SYNC->>SYNC: append both in causal order SYNC->>AUD: full event history preserved else Human Review SYNC->>HR: queue both versions with diff HR->>SYNC: human picks winner SYNC->>AUD: record human decision + reason end SYNC->>D1: ack with resolved state SYNC->>D2: ack with resolved state
// In @projexlight/contracts/conflict.ts
export type ConflictPolicy =
| { kind: 'crdt'; type: 'g-counter' | 'pn-counter' | 'lww-set' | 'or-set' | 'rga-text' }
| { kind: 'lww' }
| { kind: 'merge'; field_policies: Record<string, FieldPolicy> }
| { kind: 'event-sourcing' }
| { kind: 'human-review'; queue: string; delegation: ApprovalRoute };
export type FieldPolicy = 'additive' | 'max' | 'min' | 'last-write' | 'veto-conflict';
// Each event type in the EventTypeRegistry declares its policy:
export const EventTypeRegistry = {
'content.note.edit.v1': { policy: { kind: 'crdt', type: 'rga-text' }, ... },
'device.location.update.v1': { policy: { kind: 'lww' }, ... },
'payment.charge.v1': { policy: { kind: 'event-sourcing' }, ... },
'profile.pii.update.v1': { policy: { kind: 'human-review', queue: 'pii-review', delegation: 'tenant-admin' }, ... },
// ... every event type registered
} as const;
human-review consume human attention. CRDT / LWW / event-sourcing / merge handle 90%+ of conflicts without humans.The enterprise identity stack rests on three pillars (carried from v2) and is expressed through a six-layer entity stack (introduced in v3).
| Scope | Meaning | Example |
|---|---|---|
app_id | The application surface in use. Defines the data model and event namespace. | healthcare · seva.devotee-app · fieldops.contractor-kiosk |
bu_id | Business Unit within an app — a sub-division with its own approval chain, P&L, or geography. Optional. | verborix-east-region · shantam-mumbai-chapter |
tenant_id | The customer organization. Lives inside an app_id scope. | verborix-holdings · kiana-realty · hospital-a |
org_id | The org-admin scope — a meta-tenant. Used by re-sellers, MSPs, holding-company governance. | projexlight-india · kpmg-msp-northeast |
MDM holds these entities and their canonical IDs. Every other service and vertical stores its domain rows against these IDs — never duplicating personal, location, or device data.
person_idaddress_idaddress_id.device_uuidhdk-idp on first install). Joins captures, evidence chains, biometric assertions, and telemetry.org_id / tenant_idv2 left identity at "person_id + tenant_id". v3 expands to a six-layer entity stack. Each layer answers a question no other layer can; each carries its own audit trail, encryption key tier, and consent gates.
person_idapp_identity_id(person_id × app_id). Carries app-scoped profile, preferences, and notification routing.tenant_membership_id(app_identity_id × tenant_id). Carries tenant-scoped roles, status, and tenant-scoped policy attributes.persona_idencounter_idrelationship_id{
"person_id": "pers_01HXYZ...", // L1 master
"app_id": "healthcare", // app scope
"app_identity_id": "apid_01HXYZ...", // L2
"tenant_id": "ten_hosp_a", // tenant scope
"tenant_membership_id":"tmb_01HXYZ...", // L3
"persona_id": "pers_01HXYZ_patient", // L4
"persona_role": "patient",
"encounter_id": "enc_01HXYZ_visit_42", // L5 optional
"relationship_ids": ["rel_01HXYZ_dr_smith"], // L6 optional
"device_uuid": "dev_01HXYZ...",
"consent_refs": ["cnst_01HXYZ_phi_read"],
"session_id": "sess_01HXYZ..."
}
v2 introduced four bands of sensitivity hanging off the person. v3 re-homes them to their proper layer:
| Band | Layer in v3.1 | Examples | Encryption tier |
|---|---|---|---|
| Profile (display) | L2 App Identity | Name shown in this app, locale, avatar, role hints | Vault · Person key (per app envelope) |
| Preference | L2 App Identity | Channels, opt-ins, language, theme — per app | Vault · Person key |
| Notification routing | L2 App Identity | WhatsApp number, email, push tokens, quiet hours — per app | Vault · Person key |
| Secure Data | L1 Master Person | DL · PAN · Aadhaar (last-4 in clear, full in vault) · SSN · Passport · PCI tokens | Vault · Person key + per-field envelope · purpose-bound consent required |
The MDM layer is not a single style; it adapts per entity and per maturity. The four classic patterns live side-by-side:
| Pattern | Where used |
|---|---|
| Registry | person_id for regulated tenants (Tier G) — full PII stays in tenant-owned stores; MDM holds the alias graph and consent pointers only. |
| Consolidation | address_id across permits, property tax, drone feeds, and HDK GPS captures — golden record feeds Map/Geo and downstream OLAP. |
| Coexistence | device_uuid attributes (last-known location, OS version, telemetry health) — Diagnostic-Telemetry, HDK, and Identity all converge. |
| Centralization | The person_id alias graph; org_id and tenant_id registrations; consent receipts; the App Identity and Tenant Membership tables. |
The same identity foundation underpins every domain. Each domain adds extension schemas hanging off the canonical IDs and personas — it does not duplicate them.
| Domain | Personas (L4) | Encounter kinds (L5) | Common relationships (L6) |
|---|---|---|---|
| Construction | Worker, Contractor, Homeowner, Inspector | Site visit, Inspection, Punch-list | Homeowner-Contractor, Worker-Foreman |
| Healthcare | Patient, Doctor, Nurse, Caregiver | Visit, Admission, Surgery, ER episode | Patient-PCP, Care team |
| Education | Learner, Educator, Parent, Administrator | Enrollment term, Course session, Exam | Student-Teacher, Student-Counselor |
| Recruitment | Candidate, Recruiter, Hiring Manager | Interview, Offer round, Onboarding | Candidate-Recruiter |
| HR | Employee, Manager, HR partner | Performance cycle, Promotion review, Off-cycle | Employee-Manager |
| Real estate | Investor, Owner, Agent, Tenant | Site visit, Document execution, Cap call, Distribution | Investor-Advisor, Owner-Agent |
| Seva | Devotee, Acharya, Donor | Donation, Anushthan, Programme | Devotee-Acharya |
| FieldOps | Contractor, Rep, Homeowner, Roofer | Site visit, Estimate, Settlement, Job | Homeowner-Contractor, Rep-Territory |
| eCommerce | Buyer, Seller | Order, RMA, Cart session | Customer-Loyalty, Customer-Rep |
person_name column on a domain table, it fails review — the column is persona_id (or higher) and the name is fetched through the Persona / Profile SDKs, with the access mesh on every call.
The platform pivots from "Postgres per service" to "Postgres per service per pool" — and we add capacity by adding pools, not by sharding tables. This is the single largest scaling decision in v3.1.
(tenant_id, app_id) → pool_index in <5ms via Redis. Every domain query touches exactly one pool.
| Sharding (rejected) | Pooling (chosen) |
|---|---|
| Tables split across nodes by a sharding key. | Whole tenants on one node; many tenants share a node. |
| Cross-shard joins and transactions require coordinator nodes. | All a tenant's joins are local — single-node ACID transactions. |
| Rebalancing requires live row migration. | Rebalancing is offline tenant migration — staged dual-write, atomic cutover. |
| One bad query can blast every shard. | Blast radius confined to one pool — capped to ~5k tenants. |
| Schema changes ripple across N shards. | Schema changes roll pool-by-pool with the migration agent watching health. |
| Per-tenant encryption keys must be referenced cross-shard. | Per-tenant key cached locally in pool's HSM bridge. |
| Compliance auditing is shard-distributed. | Compliance auditing is pool-local — every PHI record on one pool, one region. |
admin_pool_indexapp_pool_index[app_id]evidence_pool_indexEach pool is identified by a stable, never-reused index: admin-001, app-onestate-014, app-healthcare-007, evidence-003.
// Tenant row in the Pool Registry
{
"tenant_id": "ten_01HXYZ...",
"org_id": "org_proj_in",
"isolation_tier": "S | P | G",
"region": "ap-south-1",
"admin_pool_index": "admin-014",
"app_pool_index": {
"healthcare": "app-healthcare-007",
"ecommerce": "app-ecommerce-022",
"onestate": "app-onestate-004",
"seva": "app-seva-002"
},
"evidence_pool_index":"evidence-003",
"status": "ACTIVE | PROVISIONING | MIGRATING | SUSPENDED | TOMBSTONE"
}
| Pool family | Tenants/pool | Size/pool | Hot rows |
|---|---|---|---|
| Admin (shared) | ~5,000 | ≤ 5 TB | ≤ 50M identity rows |
| App (shared · transactional) | ~2,000 | ≤ 30 TB | ≤ 1B domain rows |
| App (dedicated · Tier G) | 1 | ≤ 200 TB | ≤ 10B rows |
| Evidence | ~10,000 | ≤ 50 TB metadata | ≤ 500M blob refs |
| Stage | Tenants | Admin pools | App pools (avg per app) | Approx data |
|---|---|---|---|---|
| Pilot | 1 – 50 | 1 | 1 | < 100 GB |
| Early | 50 – 1,000 | 1 | 1 – 2 | ≤ 5 TB |
| Growth | 1k – 10k | 2 – 3 | 3 – 8 | ~ 50 – 200 TB |
| Scale | 10k – 100k | 20 – 25 | 30 – 60 | ~ 1 – 5 PB |
| Hyperscale | 100k – 1M+ | 200 – 500 | 300 – 1000+ | ≥ 10 PB |
flowchart TB
classDef router fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
classDef admin fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5;
classDef appp fill:#16241c,stroke:#5dd39e,color:#e6ecf5;
classDef ev fill:#241622,stroke:#ff6b6b,color:#e6ecf5;
classDef ten fill:#1d1830,stroke:#b88dff,color:#e6ecf5;
T1["Hospital A · tier S"]:::ten
T2["Hospital B · tier P"]:::ten
T3["Shantam · tier S"]:::ten
T4["ContractorCo · tier S"]:::ten
T5["KianaRealty · tier G"]:::ten
ROUTER["Pool Router"]:::router
AP1[("admin-001
~3000 tenants")]:::admin
AP2[("admin-002
~2500 tenants")]:::admin
HC1[("app-healthcare-001")]:::appp
HC2[("app-healthcare-002")]:::appp
EC1[("app-ecommerce-001")]:::appp
SV1[("app-seva-001")]:::appp
FO1[("app-fieldops-001")]:::appp
RE1[("app-realty-001 · dedicated")]:::appp
EV1[("evidence-001")]:::ev
EV2[("evidence-002")]:::ev
T1 --> ROUTER
T2 --> ROUTER
T3 --> ROUTER
T4 --> ROUTER
T5 --> ROUTER
ROUTER -. admin .-> AP1
ROUTER -. admin .-> AP2
T1 -. healthcare .-> HC1
T2 -. healthcare .-> HC2
T3 -. seva .-> SV1
T4 -. fieldops .-> FO1
T5 -. realty .-> RE1
T2 -. ecommerce .-> EC1
T1 -. evidence .-> EV1
T2 -. evidence .-> EV2
T4 -. evidence .-> EV1
Every SDK's data has to land in some pool. Without an explicit matrix, each SDK team makes its own placement decision and the petabyte-scale isolation guarantees from §8 erode. This section is the canonical authority — every new SDK declares its placement here before merging contracts.
@projexlight/contracts. Slow-changing larger artifacts get a Global Catalog read-replica pattern.| Family | What lives here | Sizing target | Index |
|---|---|---|---|
| Admin Pool | L1 Master Person · L2 App Identity · L3 Tenant Membership · L4 persona identity facet · tenant registry · reseller/sub-tenant rows · BU tree · role templates · fiscal periods · feature flags · billing rows · API keys · webhook registry · approval state · federation config · DSAR workflows · person-pool-residency registry | ≤ 5k tenants · ≤ 5TB | admin-NNN |
| App Pool | L4 persona extension data · L5 Encounter · L6 Relationship · domain entities (chart root, order, donation, listing) · agent memory namespaces · per-pool lineage subgraph | Varies by app (≤ 30TB Tier-S, ≤ 200TB Tier-G) | app-{vertical}-NNN |
| Evidence Pool | Provenance-stamped media blobs · raw + edited captures · imaging · signed contracts · chain-of-custody metadata | ≤ 10k tenants metadata · ≤ 50TB metadata + S3-backed blobs | ev-NNN |
| Global Catalog v3.1 | Semantic ontology bundles (when too large for contracts) · per-region static reference data (geographic-node tree, address normalization) · event-type registry · pricing-catalog schema. Read-replicated everywhere; writes via contracts releases. | One per region · ≤ 10GB | cat-{region} |
| Warehouse v3.1 | Aggregated cross-pool analytics · usage rollups for billing (ClickHouse partitioned by pool_index) · cross-pool lineage projections · per-tenant analytical extracts. No PII at the row level unless tenant has consented to warehouse export. | Petabyte-scale, region-pinned | wh-{region} |
| Vector store v3.1 | Per-tenant agent-memory namespaces · embeddings for sdk-knowledge-rag corpora. Hard physical partitions per tenant (not logical filters) to prevent prompt leakage. | Per-pool pgvector (default) · dedicated cluster (Tier-G) | vec-{pool_index} |
| SDK | Primary pool | Cross-pool reads? | Scaling notes |
|---|---|---|---|
sdk-vault | Per-pool KEK; key material in HSM/KMS | No | Per-pool key tier; cryptographic shred is scoped to pool |
sdk-audit | Per-pool append-only chain | No · regional rollup async | Event-type registry is global (contracts); retention enforcement per-pool |
sdk-pool-router | Pool Registry in Admin Pool of region · Redis cache per service | N/A — it is the routing layer | Federation manifest extends for 1000+ pool world |
sdk-secrets | KMS-backed; refs in any pool | No | — |
sdk-meter v3.1 | Events in Kafka (partitioned by tenant_id) · rollups in shared Warehouse ClickHouse partitioned by pool_index | No (rollups are pre-aggregated) | Shared ClickHouse is correct for OLAP. Pool-per-ClickHouse would be 1000+ clusters at scale — wrong tool. |
sdk-tenant | Admin Pool (reseller, tenant, sub-tenant, BU, geo, roles, fiscal) | No | Sub-tenant pool placement: hybrid — share parent by default, opt-out for Tier-P/G |
sdk-identity | Admin Pool (credentials, alias graph, federation config) | No | JIT-provisioning via SCIM writes Admin Pool rows |
sdk-consent | Admin Pool | Cross-tenant consent records may span 2 Admin Pools | Receipts keyed by person_id (always in person's home Admin) |
sdk-policy | Admin Pool (policies) · Redis (precomp cache) | No | IQL evaluator runs in-process per service |
sdk-rebac | App Pool for in-app relationships · Admin Pool for cross-tenant relationships | Bounded by traversal-depth cap | Edge indexes + async projection keep p99 ≤ 5ms at 10M edges |
sdk-api-keys v3.1 | Admin Pool (hashed keys + scope) | No | Each API key bound to a synthetic persona |
sdk-profile | L2/L3 bands in Admin Pool · Secure Data band in Admin Pool with per-field envelope | No | Profile/Preference/Routing now on App Identity (per-app) |
sdk-persona | Identity facet in Admin Pool · Extension in App Pool | Two-pool fetch (via resolver only) | Persona shred independent of person shred |
sdk-identity-resolver v3.1 | Stateless · reads Admin Pool + one App Pool per request | Sanctioned two-pool fetch | Redis cache; p99 ≤ 1ms warm; cold-path fallback returns L1+L2+L3 only |
sdk-data-rights v3.1 | Workflow state in Admin Pool · fan-out via person_pool_residency registry | DSAR fan-out across all pools holding person's data | Registry written on every first-touch; weekly reconciliation against actual presence |
sdk-geo | GeographicNode tree in Global Catalog · canonical addresses in Admin Pool | No | Static tree → cheap reads from any pool |
sdk-device | Device registry in Admin Pool | No | — |
sdk-feature-flags | Admin Pool (per-tenant) · Redis evaluator | No | — |
sdk-media | S3 with per-tenant prefix · keys per-pool | No | Encounter-keyed blobs sealed on encounter close |
sdk-notification | App Pool (delivery state) · provider APIs | No | — |
sdk-payment | Admin Pool (PCI tokenized refs) · provider APIs | No | — |
sdk-workflow | Temporal cluster · workflow state in App Pool | No | One Temporal namespace per pool family |
sdk-search | OpenSearch per-pool indexes · per-tenant aliases | No | Index-per-tenant within pool |
sdk-billing v3.1 | Admin Pool (invoices, line items) · reads Warehouse rollups | Reads Warehouse only | Invoices billed to reseller or end-tenant per contract |
sdk-webhook v3.1 | Admin Pool (endpoint registry) · per-pool outbox · per-pool delivery workers | No | HMAC keys vaulted; circuit breaker per endpoint |
sdk-approval v3.1 | App Pool (approval state for in-app actions) · Admin Pool (cross-app approvals) | No | — |
sdk-tenant-lifecycle v3.1 | Admin Pool (lifecycle state) · orchestrates pool allocations + shreds | By design | Sandbox sub-pool with masked PII |
sdk-engagement | Encounter + Relationship in App Pool · Encounter Grants short-lived | No | Encounter key shreds on seal |
sdk-crm · sdk-content · sdk-service-request · sdk-event · sdk-campaign · sdk-social | App Pool | No | — |
sdk-sequence · sdk-scheduling · sdk-deliverability · sdk-offer-catalog · sdk-handoff · sdk-incident P14·15 | App Pool | No | InboundCRM domain SDKs (Sprint3): multi-touch cadence, booking/no-show, suppression/bounce/reply, versioned offer truth, Sales→Delivery handoff, exception/incident evidence. Background workers opt-in per-SDK env flag; external I/O behind pluggable provider hooks (default no-op) |
connector-twilio-voice P14·15 | App Pool mirror (tracking numbers + call legs) · Twilio provider APIs | No | Telephony channel: number provisioning, recorded outbound calls, signed status/recording webhooks with AMD→voicemail. Recording gated on sdk-consent; call events bridge to sdk-crm timeline |
sdk-ai-gateway | Stateless gateway · provider APIs · Langfuse traces | No | Budget enforcement via sdk-meter |
sdk-taxonomy | Admin Pool (per-tenant overrides) · platform-global templates in Global Catalog | No | — |
sdk-knowledge-rag | Per-tenant corpora in App Pool · embeddings in Vector store partition | No | Hard physical partition prevents leakage |
sdk-parsing | Pipeline state in App Pool · documents in Media · extractions to App Pool | No | — |
sdk-agent-runtime | Agent definitions in Admin Pool · memory namespaces in Vector store partition per tenant | No (physical isolation enforced) | Cross-tenant prompt-leakage CI test on every change |
sdk-conversation · sdk-recommendation | App Pool | No | — |
sdk-analytics | Warehouse | By design | No live cross-pool joins |
sdk-lineage v3.1 | Per-pool lineage subgraph for in-pool edges · cross-pool projection in Warehouse | In-pool reads sync · cross-pool reads async via warehouse | Matches "no live cross-pool joins" rule |
sdk-semantic v3.1 | Ontology bundles in contracts (v1) or Global Catalog (when too large) · per-tenant SemanticObjects in App Pool | No | Tenant-defined extension types stay in App Pool; promoted types go to contracts via Rule of Three |
sdk-storm · sdk-dispatch · sdk-assignment · sdk-lead-scoring | App Pool | No | — |
sdk-evidence | Metadata in Evidence Pool · blobs in S3 · keyed per encounter | No | Sealed encounters block new evidence |
sdk-diagnostic-telemetry | App Pool · sample to Warehouse | No | — |
sdk-trace v3.1 | Stateless reader · pulls from sdk-telemetry (OTel) + sdk-audit + sdk-meter + sdk-lineage stores | Reads from every source (sanctioned: pure read, no mutation) | One trace_id propagates end-to-end; viewer renders unified timeline; trace export per request |
hdk-sync v3.1 | Local on-device offline queue · server-side reconciliation in App Pool · conflict-resolution decisions audited per pool | No (per-pool reconciliation; cross-pool conflicts escalate to human-review) | ConflictPolicy from contracts dictates resolution; replay-safe; idempotent |
Required by sdk-data-rights for DSAR fan-out. Lives in person's home Admin Pool. Schema:
person_pool_residency ( person_id uuid not null, pool_index text not null, -- which pool holds data data_class text not null, -- 'persona-ext' | 'encounter' | 'relationship' | 'evidence' | 'agent-memory' first_touched_at timestamptz not null, last_touched_at timestamptz not null, primary key (person_id, pool_index, data_class) )
Every data-bearing SDK writes a row on first-touch for a (person, pool, class) tuple. Weekly reconciliation job compares the registry against actual data presence — a discrepancy halts DSAR completion until investigated. Without this registry, "I erased this person's data" is unverifiable across hundreds of pools.
Required by sdk-lineage. In-pool lineage edges (record A in App Pool X derived from record B in same App Pool X) stay in the pool's lineage subgraph for sync queries. Cross-pool lineage edges (parsed doc in Evidence Pool E → derived record in App Pool A → AI score from sdk-ai-gateway) are projected asynchronously into a Warehouse table:
warehouse.cross_pool_lineage ( edge_id uuid not null, source_pool text not null, source_record text not null, target_pool text not null, target_record text not null, edge_type text not null, -- 'extracted_from' | 'derived_from' | 'merged_from' | ... tenant_id uuid not null, occurred_at timestamptz not null )
"Show me the full derivation chain for record X" first resolves in-pool, then queries the Warehouse projection for cross-pool hops. Hot path stays in-pool; cross-pool reasoning is async-acceptable.
When parent_tenant_id is set, the default policy is:
tenant_id, RLS isolates rows, audit and billing roll up through the parent chain via CTE traversal.@cross_pool_sanctioned(reason). Only sdk-identity-resolver, sdk-data-rights, sdk-analytics, and sdk-lineage's warehouse projection are sanctioned. Any other SDK attempting cross-pool reads fails the build.person_id without also upserting the residency registry.Postgres is the right choice — but not for everything. v3.1 commits to polyglot persistence: each workload routed to the storage engine designed for it. The Pool Placement Matrix in §8A is the per-SDK lookup; this section is the principle that explains why.
../../Analyze2.txt #7) flagged "Postgres doing everything" as one of the 9 hyperscale-killers: stuffing telemetry, vector indexes, graph traversals, PB-scale logs, and global analytics into Postgres pools will hit ceilings — at which point retrofitting polyglot persistence is a multi-month migration. Better to start polyglot.
| Workload | Storage engine | Why this and not Postgres |
|---|---|---|
| OLTP (transactional record-keeping; identity rows; encounters; payments) | PostgreSQL pools (per family · §8) | ACID, row-level security, well-understood operationally; Postgres is the right tool |
| Search (full-text queries; ABAC-filtered lookups; faceted search) | OpenSearch per-pool indexes; per-tenant aliases | Postgres FTS doesn't scale to 1M+ tenants with low latency; Lucene-based engines dominate |
| Telemetry & metrics (usage events; latency; counters; rollups) | ClickHouse partitioned by pool_index (shared warehouse, NOT per-pool ClickHouse) | OLAP columnar engine for high-cardinality time-series; Postgres tables would degrade by 100× |
| Blob storage (media; evidence; documents; signed artifacts) | S3 / object store per tenant prefix; encrypted under per-encounter or per-tenant keys | Postgres BLOBs are unworkable at scale; S3 is purpose-built |
| AI vectors (embeddings; agent memory; RAG corpora) | pgvector per-pool for v1 → dedicated vector DB (Pinecone / Weaviate / Qdrant) at hyperscale | pgvector works to ~10M vectors per pool; beyond that, dedicated stores have specialized ANN indexes |
| Graph projections (ReBAC traversals; lineage subgraphs; semantic CapabilityGraph) | Postgres projection tables with edge indexes (v1) → Neo4j or Dgraph at hyperscale | Recursive CTEs work to ~10M edges; beyond that, native graph engines amortize traversal cost |
| Lakehouse (PB-scale analytics; cross-pool aggregation; cold data; lineage cross-pool projections) | Iceberg / S3-tables (sdk-analytics in P7) | ClickHouse alone can't hold PB; lakehouse table format is the industry-standard answer |
| Event stream (domain events; usage events; HDK sync queue) | Kafka partitioned by tenant_id | Postgres LISTEN/NOTIFY does not scale; Kafka is the proven choice |
| Cache & coordination (routing cache; quota state; identity projection live store; rate limits) | Redis per region (with persistence for projection store) | Postgres reads would dominate latency budgets; Redis is purpose-built for sub-ms reads |
| Workflow state (durable workflows; saga state; long-running jobs) | Temporal (with its own Postgres or Cassandra backend) | Building durable workflows on raw Postgres is reinventing Temporal; use Temporal |
| Audit chain (append-only ledger; hash-chained; per-pool) | Postgres in per-pool audit chain (durability) + S3 for long-term archival | Postgres is fine for the append rate; immutability is enforced by the hash chain + archival |
| Keys & secrets (KMS-wrapped; HSM-backed) | AWS KMS / GCP KMS / HSM via sdk-secrets · sdk-vault | Never in Postgres; key material in dedicated KMS only |
The Pool Router resolves (tenant_id, app_id, pool_family) → connection string for every domain query. It is the most performance-critical lookup in the platform: p99 ≤ 5 ms.
TABLE pool ( pool_index text primary key, pool_family text, -- 'admin' | 'app' | 'evidence' app_id text, -- only when family='app' region text, status text, -- ACTIVE | DRAINING | MAINTENANCE | RETIRED | QUARANTINE capacity_tenants int, current_tenants int, capacity_bytes bigint, current_bytes bigint, primary_endpoint text, replica_endpoints text[], kek_arn text, isolation_class text -- 'shared' | 'dedicated' ); TABLE tenant_pool_map ( tenant_id text primary key, admin_pool_index text references pool, evidence_pool_index text references pool, app_pool_index jsonb, region text, status text, created_at timestamptz, migrated_at timestamptz );
sequenceDiagram
autonumber
participant C as Client
participant GW as API Gateway
participant ID as Identity
participant RT as Pool Router SDK
participant CACHE as Redis
participant REG as Pool Registry
participant DB as App Pool
C->>GW: POST /v1/encounters/E_001/notes
GW->>ID: validate JWT, extract identity tuple
ID-->>GW: { tenant_id, app_id=healthcare, person_id, ... }
GW->>RT: route(tenant_id, app_id)
RT->>CACHE: GET tenant:ten_001:pool:healthcare
CACHE-->>RT: app-healthcare-007 (cache hit)
RT-->>GW: dsn
GW->>DB: query routed to app-healthcare-007 only
import { withTenant } from "@projexlight/sdk-pool-router";
const result = await withTenant({ tenantId, appId: "healthcare" }, async (db) => {
return await db.encounters.fetch({ id: encounterId });
});
The SDK refuses to issue an untenanted query against any application pool. Cross-tenant queries go through the warehouse.
v3.1 Every typed SDK method declared with @meter(...) is auto-wrapped at build time by sdk-meter's codegen so a check() precedes the call and a report() emits the usage event. SDK authors do not write metering code by hand. See §10A.
| Entity | Pool family |
|---|---|
person (master) · MDM | Admin pool of home region |
app_identity · tenant_membership · persona identity facet | Tenant's admin pool |
| Persona extension data (donor history, patient chart root, investor stake) | App pool for that app |
encounter · relationship | App pool |
| Evidence blobs · imaging · contracts | Evidence pool |
| Audit ledger | Per-pool audit chain + regional roll-up |
v2 established the four-tier key hierarchy (root · app · tenant · person/device/org). v3.1 keeps all of it and adds two tiers: Pool KEK (per pool) and Encounter Key (per encounter).
flowchart TB classDef root fill:#1a2234,stroke:#c9a86a,color:#e6ecf5; classDef pool fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5; classDef ten fill:#16241c,stroke:#5dd39e,color:#e6ecf5; classDef per fill:#241622,stroke:#ff6b6b,color:#e6ecf5; classDef enc fill:#1d1830,stroke:#b88dff,color:#e6ecf5; ROOT["Regional Root CMK
HSM-backed (per region)"]:::root POOLK["Pool KEK (per pool) v3
wraps tenant keys held in pool"]:::pool TENK["Tenant Key (per tenant_id)
wraps memberships, personas, encounters"]:::ten PERK["Person Key (per person_id)
wraps master + secure-data + app identities"]:::per ENCK["Encounter Key (per encounter_id) v3
wraps medical/financial payloads"]:::enc DEVK["Device Key (per device_uuid)
wraps biometric templates"]:::per ORGK["Org Key (per org_id)
wraps cross-tenant rollups"]:::per ROOT --> POOLK POOLK --> TENK TENK --> ENCK ROOT --> PERK PERK --> DEVK ROOT --> ORGK
| Row / blob | Wrapped by | Pool family |
|---|---|---|
| Master person · alias graph | Person Key | Admin (home region) |
| Credentials & MFA secrets | Person Key (biometric → Device Key) | Admin |
| Secure Data band (DL · PAN · Aadhaar · SSN · Passport · PCI tokens) | Person Key + per-field envelope | Admin |
| App Identity (per-app profile/preference/notification) | Person Key (envelope, per-app salt) | Admin |
| Tenant Membership · Persona identity facet | Tenant Key | Tenant's admin pool |
| Persona extension (donor history, patient chart root) | Tenant Key | Tenant's app pool |
| Encounter payload | Encounter Key | Tenant's app pool |
| Evidence blobs | Encounter Key (when tied) · else Tenant Key | Evidence pool |
| Relationship metadata | Tenant Key (intra) · Cross-tenant Relationship Key (inter) | App pool |
| Audit ledger | Pool KEK (append-only, hash chain) | Per-pool |
| Shred | Effect |
|---|---|
| Person Key | Every layer below — every app identity, every tenant membership, every encounter — becomes undecryptable across every pool, every region. Global erasure. |
| Tenant Key | All that tenant's memberships, personas, encounters, relationships become undecryptable. Tenant offboarding. |
| Encounter Key | Only that one encounter's payload becomes undecryptable. Narrow legal-hold release or per-encounter purge. |
| Device Key | Biometric templates and on-device captures associated with that device become undecryptable. Used when a device is reported stolen. |
Pay-as-you-use is the platform's structural billing model. Every typed SDK call is gated, metered, and priced per-method per-tenant per-app. The implementation lives in two SDKs (sdk-meter in W1 and sdk-billing in W4) and is bolted into every other SDK via build-time codegen — SDK authors annotate methods with @meter(...) and never write metering code by hand.
usage.event.v1 to Kafka. Never blocks the request path on settlement.org · app · tenant · bu · persona · encounter) from the JWT. Customers split their bill by app, BU, persona-kind, or encounter — none of AWS/GCP/Azure offer this natively because their identity is flat.(tenant, day), links into Audit. Customer hits /billing/verify?day=... and re-derives the day's total. Hyperscalers do not expose raw events.flowchart LR classDef sdk fill:#1a2234,stroke:#c9a86a,color:#e6ecf5; classDef meter fill:#1d1830,stroke:#b88dff,color:#e6ecf5; classDef bill fill:#16241c,stroke:#5dd39e,color:#e6ecf5; classDef store fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5; CALL["Any SDK method
annotated @meter(sku,unit,tier)"]:::sdk CHK["sdk-meter · check()
≤ 2ms sync"]:::meter RDS[("Redis
quota state + live counter")]:::store EXEC["SDK body
(actual work)"]:::sdk REP["sdk-meter · report()
async, fire-and-forget"]:::meter KFK[("Kafka
usage.events.v1
partition by tenant_id")]:::store PROC["Per-pool stream processor"]:::meter CH[("ClickHouse
hourly · daily · monthly
rollups")]:::store CHN[("Hash-chained ledger
(tenant, day) → Audit")]:::store BIL["sdk-billing · monthly
apply pricing.catalog.vN"]:::bill INV["Invoice
(per SDK · per method ·
per app · per BU · per persona)"]:::bill PAY["sdk-payment → Stripe / Razorpay"]:::sdk LIVE["Customer /billing/live
≤ 60s lag"]:::bill CALL -->|"Phase 1"| CHK CHK -->|read| RDS CHK -->|ALLOW/WARN| EXEC CHK -->|DENY → throw QuotaExceeded| CALL EXEC -->|"Phase 2"| REP REP --> KFK KFK --> PROC PROC --> CH PROC --> CHN PROC --> RDS CH --> BIL RDS --> LIVE BIL --> INV INV --> PAY
// What an SDK author writes — once, at method definition:
@meter({ sku: 'identity.jwt.mint', unit: 'call', tier: 'core' })
async mintToken(req: MintRequest, ctx: Ctx): Promise<Token> { ... }
// What codegen produces at build time (never edited by hand):
async mintToken(req, ctx) {
const gate = await meter.check({ // Phase 1 — sync, ≤ 2ms p99
sku: 'identity.jwt.mint',
tenant_id: ctx.tenant_id, app_id: ctx.app_id, actor: ctx.actor,
});
if (gate.decision === 'DENY') throw new QuotaExceeded(gate.reason);
const result = await originalMintToken(req, ctx);
meter.report({ // Phase 2 — async, idempotent
event_id: ulid(),
sku: 'identity.jwt.mint',
units: 1,
dimensions: { // six-layer attribution from JWT
org_id, app_id, tenant_id, bu_id, persona_id, encounter_id, pool_index,
actor_kind, actor_id, region, latency_ms, bytes_in, bytes_out,
},
occurred_at: now(),
});
return result;
}
| Mode | Use case | Example SKU |
|---|---|---|
flat_per_call | Cheap idempotent ops | identity.jwt.verify @ $0.00005/call |
tiered_per_call | Most ops; volume reward | identity.jwt.mint — free ≤ 10k/day, $0.0001 to 100k, $0.00005 beyond |
passthrough_plus_margin | Provider-cost-driven | ai-gateway.complete — provider cost + 15% |
per_unit | Bytes, docs, tokens | parsing.extract @ $0.05/doc · media.put @ $0.02/GB-mo |
bundled_subscription + overage | Predictable bill | "Healthcare Tier" — 1M identity calls/mo + overage |
free_internal | Agent-issued calls on tenant's behalf | actor.kind = 'agent' → comped or rolled to "AI action" line item |
@projexlight/contracts as a typed const — every SKU, unit, and pricing mode is type-checked at compile time. Catalog schema changes require a contracts PR (high signal, CI catches orphan SKUs).pricing.catalog.v2 ships, existing tenants stay on v1 until renewal. Every invoice records the catalog_id it was generated against — bills never use "current rates" by accident.| Metric | Target | How we beat hyperscalers |
|---|---|---|
| Admission gate | p99 ≤ 2ms | In-process LRU over Redis; no hyperscaler exposes a sync gate < 5ms. |
| Event emission overhead | ≤ 0.5ms | Async enqueue to local ring buffer; flushed off the request path. |
| End-to-end (call → dashboard) | ≤ 60s | AWS/GCP bills lag 12–24h. We're real-time because Kafka → Redis live counter. |
| Invoice generation | T+24h after month close | AWS is T+72h. |
| Reprice dry-run | T+1h for any month | Possible because raw units are preserved; not a feature elsewhere. |
| Hash-chain verify | Nightly · zero tolerance for chain breaks | Customer-visible verification — unique. |
Rollups happen per-pool (the natural shard from §9). The Admin Pool consolidates per-tenant totals. Cross-pool aggregation goes through the warehouse only — never live cross-pool joins, matching the existing v3.1 isolation guarantee. Billing rows for a tenant live in that tenant's Admin Pool.
| Capability | AWS | GCP | Azure | Stripe Billing | Projexlight v3.1 |
|---|---|---|---|---|---|
| Two-phase gate (check + report) | — | ✓ Service Control | — | — (report only) | ✓ p99 ≤ 2ms |
| Per-method SKUs across the estate | Inconsistent per-service | Yes but drifted | Resource-level | Customer-defined | ✓ Typed in contracts |
| Per-persona / per-BU showback | Tags only | Tags only | Tags | Flat customer | ✓ Six-layer native |
| Per-encounter cost (e.g., one healthcare visit) | — | — | — | — | ✓ encounter_id in event |
| Real-time meter (< 60s) | 12–24h lag | ~24h lag | ~24h lag | Hours | ✓ < 60s |
| Customer-verifiable hash chain | — | — | — | — | ✓ /billing/verify backed by sdk-trace — customer hits the endpoint, gets back not just the hash-chain but the full timeline of identity + consent + routing + key + policy + meter behind each usage event |
| Reprice past months under new catalog | — | — | — | Limited | ✓ Dry-run + opt-in apply |
| Agent-vs-human cost split | — | — | — | — | ✓ actor.kind first-class |
| Method-level kill switch | Service-level | Service-level | Subscription | — | ✓ Per-SKU via meter |
| Built-in cost-optimization agent | Cost Explorer (read) | Recommender (read) | Advisor (read) | — | ✓ Cost & Safety Steward writes downgrades |
withTenant() with the full envelope. Metering becomes one typed middleware, not 44 implementations — and customers get real-time, verifiable, splittable bills no incumbent offers.
Authorization in v3.1 is the conjunction of three evaluators. Every read or write passes all three or fails.
person.kyc_status, tenant.tier, app.region, device.attested) and runs a Cedar / OPA policy. Anchors structural rules.relationship(Dr.Smith, Ravi, kind=primary-care, status=active) exists OR Dr.Smith is on the care team of an active Encounter with Ravi." Drives healthcare and longitudinal-customer access.
sequenceDiagram
autonumber
participant U as User · Dr.Smith
participant GW as API Gateway
participant POL as Policy SDK · ABAC
participant REB as ReBAC SDK
participant GRT as Encounter Grants
participant DB as App pool
participant AUD as Audit
U->>GW: GET /v1/patients/Ravi/chart
GW->>POL: ABAC(actor=Dr.Smith, target=Ravi.chart)
POL-->>GW: structural OK
GW->>REB: relationship(Dr.Smith ↔ Ravi)?
REB-->>GW: active primary-care relationship
GW->>GRT: time-bounded grant?
GRT-->>GW: not required
GW->>DB: read chart (decrypt via encounter key)
DB-->>GW: payload
GW->>AUD: log{actor, target, evaluators, decision, snapshot}
GW-->>U: chart
Default: zero cross-tenant access. A doctor at Hospital A cannot see a chart from Hospital B even for the same person. Exception: explicit cross-tenant Relationship + Consent receipt. Cross-tenant reads always go through a re-encryption proxy.
A platform-layer design discussion (gaps/Analyze1.md) and its architectural critique (gaps/Analyze2.md) proposed a platform of eight explicit control planes and warned against the failure mode of one flat "platform" carrying overlapping identity truth. v3.1 already realizes most of that model at the application layer; v3.2 makes the mapping explicit and commits to the handful of upgrades the critique correctly identifies as necessary. The full capability scoring lives in ../insignia/ProjexCloud-vs-Insignia-Reality-Report-v2.html.
| Control plane | Purpose | Owning packages / services | Status |
|---|---|---|---|
| Trust | Prove who/what is calling | sdk-identity, sdk-api-keys, sdk-identity-resolver (principal) · service mesh deferred | partial |
| Session / Edge | Authenticate, create session, map to principal | sdk-identity, api-gateway, sdk-projection, identity-projector | works |
| Policy | Decide what is allowed | sdk-policy (ABAC), sdk-rebac, Encounter Grants · obligations added in §11A.3 | works → +obligations |
| Consent | Decide whether the purpose is permitted | sdk-consent, sdk-data-rights · wired into decision in §11A.5 | works → +gating |
| MDM / Real-Identity | Resolve real-world entities | sdk-identity-resolver + six-layer tables (deterministic) | works (probabilistic deferred) |
| Event & Workflow | Make changes durable & replayable | kafka-runtime, clickhouse-runtime, sdk-meter, sdk-event, sdk-workflow | one Kafka log |
| Observability / Audit / Lineage | Know what happened, why, and what it touched | sdk-audit, sdk-trace, sdk-lineage, telemetry, sdk-diagnostic-telemetry | evidence portal partial |
| Developer Experience / Shell | Make app devs use the platform correctly by default | 3 Next.js apps (App Shell), api-gateway (Gateway), 70+ sdk-*/hdk-* + /build planner (SDK) | 3 of 4 (sidecar deferred) |
IdentityContext) ≠ master-data identity (Master Person · L1). Service identity (which workload is calling) is the one ProjexCloud does not cryptographically establish today — that is the Trust-Plane / service-mesh deferral in §11A.6. No plane is permitted to pretend to be another: Session authenticates, Policy authorizes, Consent permits-by-purpose, MDM resolves entities.
v3.1's access mesh returned a bare verdict. That leaves field masking and row filtering to every caller — the exact drift the critique flags (Scenario 7: "API allows, UI hides field, API still leaks it"). v3.2 extends the decision to carry obligations the gateway/service enforces server-side.
// @projexlight/sdk-policy — EvaluatePolicyResult, v3.2
export interface EvaluatePolicyResult {
decision: 'ALLOW' | 'DENY';
reason: string;
layers_used: string[];
projection_version: number;
cached: boolean;
obligations?: { // ← v3.2
mask_fields?: string[]; // e.g. ['ssn','profit_margin'] — redacted server-side
row_filter?: Record<string, unknown>; // e.g. { tenant_id, region: ['US','EU'] }
audit_level?: 'standard' | 'sensitive_access';
ttl_seconds?: number; // decision cache lifetime
};
}
obligations through the shared enforcement helper before serialization; every mutation honors audit_level. Lint-enforced: a handler that reads an obligation-bearing decision but serializes the raw row is a CI failure. UI visibility is advisory; the wire is shaped by obligations.
The resolved IdentityContext is already ProjexCloud's normalized subject identity. v3.2 has the gateway mint a signed, short-TTL, audience-bound token from it for east-west calls, so no downstream service ever trusts a forwarded header or a raw external claim.
// Minted by api-gateway after resolveIdentityContext(); verified by every service
{
"sub": "platform-principal:person:p_123",
"auth_assurance": "mfa",
"app_id": "healthcare", "tenant_id": "hospital-a", "bu_id": "...",
"root_tenant_id": "...", "reseller_id": "...",
"persona_ids": ["persona_doctor_..."],
"effective_scopes": ["chart:read"],
"aud": "platform-services", // audience-bound — not replayable elsewhere
"iss": "projexcloud-gateway",
"exp": 900 // short TTL; re-mint on refresh
}
Services verify iss/aud/exp and the signature, then read the principal — they never re-derive identity from user-supplied input. (When the service mesh of §11A.6 lands, this token rides on top of mTLS workload identity; until then it is the sole internal trust anchor.)
sdk-consent already owns purpose registry, grants, receipts, and cross-border checks, and active consents already surface in IdentityContext.active_consents. v3.2 wires that receipt into the access decision as a first-class, purpose-gated input: for a purpose-bound resource, a missing or revoked receipt is a DENY with reason consent_absent — it fails closed. Consent remains distinct from authorization: a doctor may be authorized to read a chart for treatment yet have no consent to use it for marketing.
sdk-approval (scoped, time-bounded, certificate-of-action), never a silent fallback. Every degraded decision is recorded with reason.| Deferred | Why it's optional today | Flips to necessary when… |
|---|---|---|
| SPIFFE/SPIRE + mTLS service mesh + gRPC | App-layer zero-trust (minted principal + JWT + RLS + Pool Router + lint boundaries) is a legitimate architecture; a mesh is an infra program, not a missing feature. | Topology becomes many polyglot services on untrusted paths, or a buyer requires literal workload-identity attestation. |
| NATS/JetStream command bus + Temporal | One Kafka "reality log" is the disciplined choice; adding three infra systems multiplies ops burden without a concrete need. | A specific long-running durable saga or internal command/control pattern Kafka+HTTP can't serve cleanly appears. |
| Probabilistic / AI-jury / steward-gated MDM | ProjexCloud's job is deterministic canonical IDs within tenants; fuzzy cross-system resolution + calibration is a large product bet. | Resolving the same real-world person across independent systems ("is this the same patient?") becomes a committed use case. |
| Literal OPA/Cedar runtime · OpenLineage/OCSF formats | The Cedar-shape evaluator and hash-chained audit/lineage already meet the control objective. | A due-diligence buyer contractually requires the named engine or wire format — then add a translation/export adapter, don't re-platform. |
The most grounded concern in gaps/Analyze1.md: infrastructure resources — droplets, clusters, databases, buckets, registries, Kafka topics — get created and deleted with no clear owner. Without ownership, platform security and cost control are impossible. v3.2 makes ownership a hard precondition for provisioning, reconciled through GitOps.
// platform.resource_registry — every provisioned resource has exactly one row
{
"resource_id": "rds-hospital-a-app-pool-03",
"type": "postgres" | "k8s_cluster" | "bucket" | "kafka_topic" | "registry" | "vm" | "droplet",
"environment": "prod" | "staging" | "dev",
"owner": "person_id_of_accountable_human",
"team": "platform-data",
"repo": "git@…/infra-pools",
"terraform_module": "modules/app-pool",
"cloud_account": "aws-prod-2",
"cost_center": "CC-4471",
"data_classification": "restricted" | "confidential" | "internal" | "public",
"network_zone": "corp_vpn" | "public_edge" | "isolated",
"created_by": "…", "approved_by": "…",
"created_at": "2026-06-14T…", "expires_at": "2026-12-31T…" | null
}
resource_registry row with a non-null owner and approved_by. The GitOps reconciler (Terraform/OpenTofu state diff) quarantines orphan resources — anything live but unregistered, or registered but past expires_at — and raises an ownership alert. Rule: no owner = no resource. Break-glass provisioning routes through sdk-approval and back-fills the row within the audit window.
This is an operations/GitOps program, not an application SDK — it sits beside the platform rather than inside it. A thin sdk-resource-registry read API can surface ownership to the admin app, but the source of truth is GitOps-managed infrastructure state.
The §11A.7 items are deferred not because they're hard but because they depend on business decisions not yet made. Recording them here makes each deferral a conscious "pending decision X," not an oversight. These are the decisions the Reality Report, gaps/Analyze3.md, and the gaps/Analyze4.md follow-up all leave open because engineering cannot answer them alone. D-1 and D-2 decide whether a deferred item becomes Required; D-3 decides whether it should be promoted to Planned ahead of that trigger.
| # | Decision (owner: product/leadership) | Options | What it unlocks / forces |
|---|---|---|---|
| D-1 | Which regulated verticals are committed for the next 2–3 quarters? | Healthcare · Insurance · FinServ · Real-estate · Internal-tooling-only | A committed regulated vertical makes P18 consent-gating mandatory and can make probabilistic MDM (§11A.7) a required capability rather than a deferred one. Internal-tooling-only keeps both optional. |
| D-2 | What is the final runtime topology? | A: Gateway → SDKs → DB (current). B: Gateway → 100+ microservices → service mesh. | Topology A keeps the minted Principal Token (§11A.4) as a sufficient internal trust anchor and leaves SPIFFE/mTLS mesh · gRPC · NATS · Temporal deferred. Topology B makes the Trust-Plane service mesh necessary, not optional. |
| D-3 | What is the expected 3-year topology & scale — independent of today's shape? | Small: 10–20 services · single cloud · single region. Large: 200+ services · multi-region · multi-cloud · partner-hosted. | The Small answer keeps all four §11A.7 items Deferred. The Large answer promotes service identity (SPIFFE/SPIRE) from Deferred → Planned now — it earns a roadmap slot and an abstraction seam, even though implementation still waits on D-2. Architecture roadmaps should anticipate likely evolution, not just today's need. |
| Capability | State for the healthcare path | Driver |
|---|---|---|
| Consent / purpose-of-use gating (P18) | Required — regulatory | HIPAA treatment/payment/operations model + 42 CFR Part 2 segmented consent for substance-use records. |
| Obligations / field-level masking (P16, OC-11) | Required — regulatory | HIPAA "minimum necessary" disclosure standard — server-side field masking + row filtering. |
| Probabilistic MDM / EMPI | Required (was Deferred) | Multi-source patient matching; duplicate/overlaid records are a patient-safety and billing-integrity hazard. |
| Minted Principal Token (P17) + audit attribution | Strongly recommended | HIPAA Security Rule audit controls — clean "who accessed which PHI, why." Audit already exists (sdk-audit); token sharpens service-hop attribution. |
| Durable workflow (Temporal) | Planned — pending scoping | Graduates from Deferred only if prior-authorization / referral workflows (multi-day, stateful, human-in-loop) are in scope. |
| Service mesh (SPIFFE/SPIRE) | Still Deferred | Healthcare is a regulatory trigger (D-1), not a topology one. Moves only on D-2/D-3, not on a single client. |
| Literal OPA/Cedar runtime | Still Deferred | Auditors require explainability/consistency, not the engine name. |
sdk-identity-resolver)The EMPI is the one large (L) build and must be its own workstream — in healthcare a false merge exposes the wrong patient's PHI and a false split fragments a record, so it is regulated-grade. It adds, behind the existing resolver interface: deterministic + probabilistic matching (name · DOB · address · phone · external IDs), confidence scores, a POSSIBLY_SAME candidate-link state (never a forced merge), a steward review queue (sdk-approval-governed adjudication), merge / unmerge as reversible compensating events (no destructive deletes), and match-quality calibration monitoring. Downstream systems continue to store the canonical ID — they never carry raw identity truth.
sdk-vault), DSAR / right-to-erasure + certificate (sdk-data-rights), consent receipts (sdk-consent), tamper-evident audit with retention classes (sdk-audit), per-field PHI encryption (sdk-profile + vault). The healthcare-specific net-new work is the three Required rows above (EMPI, consent-gating, obligations).
Every concern raised across gaps/Analyze1.md…Analyze4.md, mapped to its current disposition. Read honestly: Shipped = implemented in the 91-package build today; Designed + Backlogged = design committed here (v3.2) and scheduled as Projexlight work, not yet merged code; Deferred = consciously deferred against a named decision (§11A.9).
| Concern (source) | Status | Where / backlog |
|---|---|---|
| Five-identity separation (A2) | Shipped | Six-layer model §7 · §11A.2 |
| Multi-tenant · pools · reseller hierarchy (A1/A2) | Shipped | §8 · §12 |
| ABAC + ReBAC central PDP (A2) | Shipped | §11 access mesh |
| One event backbone — Kafka (A2) | Shipped | §11A.1 · P2 |
| Tamper-evident audit / trace / lineage (A2) | Shipped | sdk-audit/sdk-trace/sdk-lineage |
| Obligations: mask / filter / TTL (A2/A3) | Designed + Backlogged | §11A.3 + OC-11 · P10·E1 (TK-3499–3504) |
| Minted Platform Principal Token (A2/A3) | Designed + Backlogged | §11A.4 + P17 · P10·E2 (TK-3505–3510) |
| Consent-gated authorization (A2/A3) | Designed + Backlogged | §11A.5 + P18 · P10·E3 (TK-3511–3516) |
| Fail-closed PDP + break-glass (A2) | Designed + Backlogged | §11A.6 · P10·E4 (TK-3517–3520) |
| Resource ownership registry (A1/A3) | Designed + Backlogged | §11A.8 + OC-12 · P10·E5 (TK-3521–3524) |
| Probabilistic MDM / EMPI (A2/A3 + Healthcare) | Designed + Backlogged (Required) | §11A.10 · P10·E6 (TK-3525–3534) |
| Non-breaking integration & regression hardening | Designed + Backlogged | P10·E7 (TK-3535–3540) |
| Observability taxonomy + telemetry portal (A2 §3.11) | Designed + Backlogged | §11A.1 · P10·E8 (TK-3541–3548) |
| Context fields: device / network-zone / purpose (report §7.10) | Designed + Backlogged | P10·E9 (TK-3549–3551) |
| Service mesh · SPIFFE/SPIRE · gRPC (A1/A2) | Deferred | §11A.7/§11A.9 · gated on D-2/D-3 |
| NATS/JetStream command bus + Temporal (A2) | Deferred | §11A.7/§11A.9 · gated on D-2 |
| Literal OPA/Cedar runtime · OpenLineage/OCSF (A2) | Deferred | §11A.7 · optional, on customer requirement |
Two orthogonal axes. Tenant = a customer organization (Shantam, Kiana, Harmony Wellness, ContractorCo, Hospital A). Vertical = a productized domain bundle (Seva, Realty, Music, BidWork, LeadPulse, FieldOps, Healthcare, eCommerce, OneEstate). One tenant subscribes to one or more verticals; one vertical serves many tenants.
flowchart TB
classDef plat fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
classDef vert fill:#16241c,stroke:#5dd39e,color:#e6ecf5;
classDef ten fill:#241622,stroke:#ff6b6b,color:#e6ecf5;
subgraph COMMON["projex_common · horizontal capabilities + HDK + Pool Router"]
direction LR
IDS["Identity"]:::plat
POOL["Pool Router"]:::plat
PAY["Payment"]:::plat
GEO["Map/Geo"]:::plat
DSP["Dispatch"]:::plat
AGW["AI Gateway"]:::plat
ELSE["…29 services · 12 agents · 10 HDK modules"]:::plat
end
subgraph SEVA["Seva"]
DON["Donation"]:::vert
end
subgraph REALTY["Realty"]
PROP["Property · Visit"]:::vert
end
subgraph FIELDOPS["FieldOps"]
STM["Storm Estimation"]:::vert
CTR["Contractor Settlement"]:::vert
end
subgraph MUSIC["Music"]
CAT["Catalog · Rights"]:::vert
end
subgraph HC["Healthcare · new"]
CHART["Chart · Rx · Care plan"]:::vert
end
subgraph EC["eCommerce · new"]
ORD["Order · Cart · Catalog"]:::vert
end
T1["Shantam"]:::ten
T2["Kiana"]:::ten
T3["ContractorCo"]:::ten
T4["Hospital A"]:::ten
T5["BookStore"]:::ten
T1 --> SEVA
T2 --> REALTY
T3 --> FIELDOPS
T4 --> HC
T5 --> EC
SEVA --> COMMON
REALTY --> COMMON
FIELDOPS --> COMMON
HC --> COMMON
EC --> COMMON
MUSIC --> COMMON
tenant_id within pool.flowchart LR classDef edge fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5; classDef gw fill:#1a2234,stroke:#c9a86a,color:#e6ecf5; classDef svc fill:#16241c,stroke:#5dd39e,color:#e6ecf5; classDef store fill:#241622,stroke:#ff6b6b,color:#e6ecf5; USER(["Browser · RN · Kiosk"]):::edge CDN["CDN"]:::edge GW["API Gateway · Kong
resolves tenant_id + vertical_id"]:::gw TEN["Tenant Mgmt
plan · tier · region · modules · pool indices"]:::svc IDP["Identity
JWT carries six-layer tuple"]:::svc POOL["Pool Router
tenant_id + app_id → dsn"]:::svc SVC["Common service
routed query against one pool"]:::svc PG[("Postgres pool · RLS within")]:::store RC[("Redis · routing cache")]:::store USER --> CDN --> GW GW --> TEN GW -->|JWT| IDP GW --> POOL POOL --> RC POOL --> SVC SVC --> PG
A single pnpm + turbo workspace under projex_common/. Every service, agent, package, and HDK module is its own workspace member.
projex_common/
├── README.md
├── package.json
├── pnpm-workspace.yaml
├── turbo.json
├── tsconfig.base.json
│
├── docs/
│ ├── Architecture.html ← v1 (historical)
│ ├── Architecture-v2.html ← v2 (historical)
│ ├── v3/Architecture-v3.html ← v3 (AIM only)
│ ├── v3.1/Architecture-v3.1.html ← THIS · canonical
│ ├── v3.1/SDK-Build-Plan-v3.1.html
│ ├── v3.1/AIM-Identity-Model-v3.1.html
│ ├── Common-Services-and-Agents.md
│ ├── Data-Design.md
│ └── Data-Request-Manifests.md
│
├── packages/ ← TS/JS packages
│ ├── contracts/ @projexlight/contracts
│ ├── sdk-pool-router/ @projexlight/sdk-pool-router ← v3 NEW
│ ├── sdk-secrets/ @projexlight/sdk-secrets
│ ├── sdk-vault/ @projexlight/sdk-vault
│ ├── sdk-audit/ @projexlight/sdk-audit
│ ├── sdk-tenant/ @projexlight/sdk-tenant
│ ├── sdk-identity/ @projexlight/sdk-identity
│ ├── sdk-consent/ @projexlight/sdk-consent
│ ├── sdk-policy/ @projexlight/sdk-policy
│ ├── sdk-rebac/ @projexlight/sdk-rebac ← v3 NEW
│ ├── sdk-profile/ @projexlight/sdk-profile
│ ├── sdk-persona/ @projexlight/sdk-persona ← v3 NEW
│ ├── sdk-device/ @projexlight/sdk-device
│ ├── sdk-geo/ @projexlight/sdk-geo
│ ├── sdk-feature-flags/ @projexlight/sdk-feature-flags
│ ├── sdk-media/ @projexlight/sdk-media
│ ├── sdk-notification/ @projexlight/sdk-notification
│ ├── sdk-payment/ @projexlight/sdk-payment
│ ├── sdk-workflow/ @projexlight/sdk-workflow
│ ├── sdk-search/ @projexlight/sdk-search
│ ├── sdk-crm/ @projexlight/sdk-crm
│ ├── sdk-engagement/ @projexlight/sdk-engagement ← v3 NEW
│ ├── sdk-content/ @projexlight/sdk-content
│ ├── sdk-service-request/ @projexlight/sdk-service-request
│ ├── sdk-event/ @projexlight/sdk-event
│ ├── sdk-campaign/ @projexlight/sdk-campaign
│ ├── sdk-social/ @projexlight/sdk-social
│ ├── sdk-ai-gateway/ @projexlight/sdk-ai-gateway
│ ├── sdk-taxonomy/ @projexlight/sdk-taxonomy
│ ├── sdk-knowledge-rag/ @projexlight/sdk-knowledge-rag
│ ├── sdk-parsing/ @projexlight/sdk-parsing
│ ├── sdk-agent-runtime/ @projexlight/sdk-agent-runtime
│ ├── sdk-conversation/ @projexlight/sdk-conversation
│ ├── sdk-recommendation/ @projexlight/sdk-recommendation
│ ├── sdk-analytics/ @projexlight/sdk-analytics
│ ├── sdk-storm/ @projexlight/sdk-storm
│ ├── sdk-dispatch/ @projexlight/sdk-dispatch
│ ├── sdk-assignment/ @projexlight/sdk-assignment
│ ├── sdk-lead-scoring/ @projexlight/sdk-lead-scoring
│ ├── sdk-evidence/ @projexlight/sdk-evidence
│ ├── sdk-diagnostic-telemetry/ @projexlight/sdk-diagnostic-telemetry
│ ├── design-system/ @projexlight/design-system
│ ├── i18n/ @projexlight/i18n
│ ├── branding/ @projexlight/branding
│ ├── config/ @projexlight/config
│ └── telemetry/ @projexlight/telemetry
│
├── native/ ← HDK native modules
│ ├── hdk-map · hdk-camera · hdk-idp · hdk-permissions · hdk-scanner
│ ├── hdk-image-editor · hdk-video-editor · hdk-measure
│ ├── hdk-watermark · hdk-diagnostic
│
├── services/ ← horizontal microservices (29 + pool-router service)
│ ├── identity · tenant-management · user-profile · persona · engagement
│ ├── payment · notification · crm · service-request · content
│ ├── course · event · campaign · social-ingestion · analytics
│ ├── search · media · workflow · audit · ai-gateway
│ ├── knowledge-rag · conversation · recommendation · feature-flags
│ ├── map-geo · storm · dispatch · assignment · lead-scoring
│ ├── field-ops-evidence · diagnostic-telemetry · pool-registry
│
├── agents/ ← 12 horizontal agents (unchanged)
│
└── infra/
├── helm/ · terraform/ · k8s/
flowchart TB
classDef ch fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5;
classDef gw fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
classDef common fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
classDef vert fill:#16241c,stroke:#5dd39e,color:#e6ecf5;
classDef ag fill:#241622,stroke:#ff6b6b,color:#e6ecf5;
classDef hdk fill:#1d1830,stroke:#b88dff,color:#e6ecf5;
classDef aim fill:#241622,stroke:#b88dff,color:#e6ecf5;
subgraph L1["1 · Channels"]
SW["Seva · Realty · Music · FieldOps · Healthcare · eCommerce apps"]:::ch
end
subgraph LHDK["2 · HDK — device-side"]
HDK["10 native modules"]:::hdk
end
subgraph LGW["3 · Edge & Gateway"]
GW["Kong API Gateway · per-vertical BFFs"]:::gw
end
subgraph LAIM["4 · AIM Foundation · v3"]
AIM["Identity · Tenant · Pool Router · Persona · Engagement · ReBAC · Policy · Vault"]:::aim
end
subgraph LSVC["5 · Common Services"]
SVC["29 horizontal services across 6 clusters"]:::common
end
subgraph LVERT["6 · Vertical Services"]
VS["Seva · Realty · Music · FieldOps · Healthcare · eCommerce vertical services"]:::vert
end
subgraph LAG["7 · Agents"]
AGN["12 horizontal agents"]:::ag
end
L1 --> LHDK --> LGW --> LAIM --> LSVC --> LVERT
LAG -.calls.-> LAIM
LAG -.calls.-> LSVC
LAG -.calls.-> LVERT
LSVC -.events.-> LAG
LVERT -.events.-> LAG
flowchart LR
classDef cluster fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
classDef new fill:#1d1830,stroke:#b88dff,color:#e6ecf5;
subgraph IDT["Identity, Tenancy & AIM"]
A1["Identity"]:::cluster
A2["User Profile"]:::cluster
A3["Tenant Mgmt"]:::cluster
A4["Feature Flags"]:::cluster
A5["Pool Router · v3"]:::new
A6["Persona · v3"]:::new
A7["Engagement · v3"]:::new
A8["ReBAC · v3"]:::new
end
subgraph ENG["Engagement"]
B1["CRM"]:::cluster
B2["Service Request"]:::cluster
B3["Notification"]:::cluster
B4["Campaign"]:::cluster
B5["Social Ingestion"]:::cluster
end
subgraph CCM["Commerce · Content · Media"]
C1["Payment"]:::cluster
C2["Content"]:::cluster
C3["Course"]:::cluster
C4["Event"]:::cluster
C5["Media"]:::cluster
C6["Search"]:::cluster
end
subgraph OPS["Operations"]
D1["Workflow"]:::cluster
D2["Audit"]:::cluster
D3["Analytics"]:::cluster
D4["Diagnostic-Telemetry"]:::new
end
subgraph FLD["Geo · Dispatch · Evidence"]
F1["Map / Geo"]:::new
F2["Storm"]:::new
F3["Dispatch"]:::new
F4["Assignment"]:::new
F5["Lead Scoring"]:::new
F6["Field-Ops Evidence"]:::new
end
subgraph AIL["AI Layer"]
E1["AI Gateway"]:::cluster
E2["Knowledge / RAG"]:::cluster
E3["Conversation"]:::cluster
E4["Recommendation"]:::cluster
end
flowchart LR
classDef plat fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
classDef data fill:#0f1422,stroke:#5dd39e,color:#e6ecf5;
classDef use fill:#16241c,stroke:#5dd39e,color:#e6ecf5;
subgraph SPINE["Platform Spine"]
BUS["Kafka
envelope-wrapped events"]:::plat
TMP["Temporal
durable workflows"]:::plat
OBS["OTel · Grafana · Langfuse"]:::plat
SEC["Secrets · KMS · HSM"]:::plat
end
subgraph DATA["Data Plane · per pool"]
PG[("Postgres pool · per service per pool")]:::data
RD[("Redis
cache · sessions · pool routing")]:::data
OS[("OpenSearch
full-text")]:::data
CHC[("ClickHouse
OLAP · cross-pool warehouse")]:::data
S3[("S3
media · evidence")]:::data
VEC[("pgvector / Pinecone
embeddings")]:::data
POSTGIS[("PostGIS
bbox · routing")]:::data
end
CONS["Common & vertical services"]:::use
AGENTS["Agents"]:::use
HDK["HDK modules · device"]:::use
CONS -- emit/consume --> BUS
HDK -- emit --> BUS
CONS -- run --> TMP
CONS --> PG
CONS --> RD
CONS --> OS
CONS --> CHC
CONS --> S3
CONS --> VEC
CONS --> POSTGIS
CONS -.metrics/logs/traces.-> OBS
CONS -.fetch keys.-> SEC
AGENTS -- consume --> BUS
End-to-end flows showing how pools, identity layers, encryption, and the access mesh compose.
Ravi sees three doctors at Hospital A and one specialist at Hospital B, has two admissions at A and one outpatient visit at B.
person_id = pers_ravi encrypted under his person key.app_identity for Healthcare, in his home-region admin pool.tenant_membership rows (one per hospital), each encrypted under that hospital's tenant key.Patient personas — chart roots in app-healthcare-007 and app-healthcare-002.person: admin-india-007 (home) app_identity: admin-india-007 hospital_a: membership · persona (identity): admin-014 chart, encounters: app-healthcare-007 evidence (scans): evidence-002 hospital_b: membership · persona (identity): admin-021 chart, encounters: app-healthcare-002 evidence (scans): evidence-004
Anita registers at BookStore, ElectroMart, GroceryNow, FashionHub, GardenShed — five tenants in the eCommerce app.
person_id = pers_anita.BookStore cannot see ElectroMart purchases. Aggregating Anita's spend across stores requires her cross-tenant consent and goes through the warehouse — never live cross-pool reads.
Suresh invests in three funds run by KianaRealty.
person_id; L2 · OneEstate App Identity.Three personas in one membership keep per-fund reporting tidy without inventing fake sub-tenants.
Priya is a doctor, yoga student, donor, investor, shopper at four stores, homeowner, and HR employee at her own clinic.
29 horizontal services (v2 baseline) + 4 AIM services introduced by v3. Each owns its data, exposes a versioned HTTP API, emits envelope-wrapped events, and is consumed by every vertical that needs the capability.
| # | Service | Owns | Key APIs | Tier |
|---|---|---|---|---|
| 1 | Identity | identity_db · admin pool | OIDC · /authorize · /token | P1 |
| 2 | Tenant Mgmt | tenant_db · admin pool | POST /v1/tenants · /modules | P1 |
| 3 | Pool Router v3 | pool_registry | GET /v1/pool/route · /v1/pool/migrate | P1 |
| 4 | User Profile | profile_db · L2 bands | GET/PUT /v1/profiles/:id | P1 |
| 5 | Persona v3 | persona_db · L2/L3/L4 | POST /v1/persona/membership · /v1/persona/stack | P1 |
| 6 | Engagement v3 | engagement_db · L5/L6 | POST /v1/engagement/encounters · /grants · /relationships | P1 |
| 7 | ReBAC v3 | rebac_db | POST /v1/rebac/check · /relationships | P1 |
| 8 | Payment | payment_db | POST /v1/payments · /refunds | P1 |
| 9 | Notification | notif_db | POST /v1/notifications/send | P1 |
| 10 | Media | media_db + S3 | POST /v1/media/upload-url | P1 |
| 11 | Audit | audit_db per pool | GET /v1/audit · /export | P1 |
| 12 | Feature Flags | flags_db | GET /v1/flags | P1 |
| 13 | Search | OpenSearch indexes per pool | GET /v1/search | P2 |
| 14 | Workflow | Temporal + wfl_db | POST /v1/workflows/:type | P3 |
| 15 | Analytics | ClickHouse warehouse · cross-pool | GET /v1/funnel · /kpi · /cohort | P3 |
| 16 | AI Gateway | ai_db | POST /v1/ai/complete · /chat · /embed | P4 |
| 17 | Knowledge / RAG | knowledge_db + vector | POST /v1/rag/index · /query | P4 |
| 18 | Conversation | conv_db | POST /v1/conversations | P4 |
| 19 | Recommendation | recs_db | GET /v1/recs/:user_id | P4 |
| 20 | Map / Geo | geo_db + PostGIS | GET /v1/geo/property/:id · /bbox · /cluster | P2 |
| 21 | Storm | storm_db + PostGIS | GET /v1/storm/active · /overlay · /intensity | P2 |
| 22 | Dispatch | dispatch_db | GET /v1/dispatch/queue · WS /v1/dispatch/live | P2 |
| 23 | Assignment | assign_db | POST /v1/assign/auto · GET /v1/agents/nearby | P3 |
| 24 | Lead Scoring | score_db | POST /v1/score/calculate · /score/lead/:id | P3 |
| 25 | Field-Ops Evidence | evidence_db + S3 | POST /v1/evidence/upload · GET /v1/evidence/:id | P2 |
| 26 | Diagnostic-Telemetry | diag_db | POST /v1/telemetry/crash · /telemetry/session | P3 |
| Service | Generalization needed | Promotion trigger |
|---|---|---|
| CRM | Neutral Contact + Lead entities keyed by persona_id; per-vertical custom_fields JSONB | Seva + Realty + FieldOps in production |
| Service Request | Tenant-configurable category + routing_rules | 3rd vertical needs ticketing |
| Content | Generic typed-content with tenant taxonomies | 3rd vertical with content needs |
| Course | Generic; keep schemas vertical-agnostic | 2nd vertical adopts |
| Event | Generic; ticketing / QR / check-in agnostic | 2nd vertical adopts |
| Campaign | Generic segment DSL over events | 3rd vertical needs journeys |
| Social Ingestion | Pluggable connectors; common social.lead.captured | 3rd vertical with social leads |
| Calendar / Appointments | Generic resource-booking over property/agent/slot — now expressed as Engagement | 3rd vertical needs booking |
| Capability | Where | Why |
|---|---|---|
| QR · Barcode · AprilTag detection | On-device (hdk-scanner) | Latency < 50ms; never leaves device |
| Scene context | On-device (hdk-camera) | Capture buffer never uploaded raw |
| Image auto-correct | On-device (hdk-image-editor) | Works offline; instant preview |
| AR length measurement | On-device (hdk-measure) | Millisecond latency |
| Roof / property AI from imagery | Cloud AI Gateway | Heavy model + map tiles |
| Sentiment / lead scoring | Cloud AI Gateway | Cross-tenant signals |
| Transcription · summarization | Cloud AI Gateway | Long-form + redaction policy |
Three independent layers, each enforced at a different boundary. A request only succeeds if all three say yes. Symmetric between web and mobile — only the enforcement points differ. v3 integration: Layer 2 now consults the full ABAC + ReBAC + Encounter Grants mesh, not just role checks.
flowchart TB
classDef l1 fill:#0f1422,stroke:#6aa9ff,color:#e6ecf5;
classDef l2 fill:#16241c,stroke:#5dd39e,color:#e6ecf5;
classDef l3 fill:#241622,stroke:#ff6b6b,color:#e6ecf5;
classDef store fill:#1a2234,stroke:#c9a86a,color:#e6ecf5;
subgraph LAY1["Layer 1 · App consent"]
C["Tenant consent + module subscription"]:::l1
end
subgraph LAY2["Layer 2 · Access Mesh · v3"]
R["ABAC + ReBAC + Encounter Grants"]:::l2
end
subgraph LAY3["Layer 3 · Device permissions"]
D["OS-level permissions"]:::l3
end
RC[("Redis permission cache")]:::store
DEV[("On-device permission cache")]:::store
C --> R --> D
R -. fetches .-> RC
R -. syncs .-> DEV
| Layer | Enforced at | What it checks | Failure mode |
|---|---|---|---|
| App consent | Tenant Mgmt + Feature Flags | Module subscribed? User accepted consent? | 403 module_not_enabled / consent_required |
| Access mesh | Policy + ReBAC + Encounter Grants | ABAC structural + ReBAC longitudinal + Encounter time-bounded | 403 forbidden — backend-driven |
| Device permission | hdk-permissions (mobile) / browser API (web) | OS access to camera / GPS / biometric | Force-permission flow → deep-link |
Twelve agents that subscribe to platform events and call platform APIs. Tenant-scoped, budget-capped, audited. v3: every agent action now resolves through ABAC + ReBAC; agents acting on behalf of a persona must respect relationships and encounter grants.
| Agent | Triggers | Calls | Outcome |
|---|---|---|---|
| Engagement | identity.user.registered · cron | Notification · Content · Course · Recommendation | Personalized nudges per persona. |
| CRM Intelligence | crm.lead.captured · lead.scored.v1 | CRM · AI Gateway · Analytics | Lead scoring, NBA, follow-up prioritization. |
| Campaign Optimizer | campaign.launched · daily metrics | Campaign · Analytics · AI Gateway | Send-time tuning, segment refinement. |
| Support Resolution | sr.opened · sr.escalated | SR · Knowledge · Notification | Summarization, suggested replies, auto-escalation. |
| Content Intelligence | content.published · media.ready | Content · Knowledge · Search | Auto-tagging, summaries, embeddings. |
| Commerce Recommender | views · cart.converted | Commerce · Recommendation | Upsell, combos, similar-property. |
| Event Engagement | event.registered · T-24h, T-2h | Event · Notification | Reminders, attendance prediction, follow-up. |
| Operations Monitor | SLA breach · anomaly | SR · Workflow · Notification · Diagnostic-Telemetry · Pool Router | Catches fulfillment delays, crash spikes, pool capacity issues. |
| Executive Intelligence | Daily / weekly cron | Analytics · AI Gateway | KPI digests, cohort trends. |
| Knowledge Curator | Periodic · new docs | Knowledge / RAG · AI Gateway | Quality scoring, dedup, refresh. |
| Onboarding | tenant.provisioned | Tenant · CRM · Notification · Workflow · Pool Router | Walks a new tenant through configuration; verifies pool assignment. |
| Cost & Safety Steward v3.1 | sdk-meter rollups · usage.softcap.warn · usage.hardcap.exceeded · daily cron | sdk-meter · sdk-billing · sdk-notification · sdk-feature-flags | Reads method-level usage across every SDK (not just AI). Recommends batching, caching, model downgrade, endpoint switches; can issue per-method kill switches via meter; opens a finance ticket for tenants approaching hard caps. |
§18 lists the 12 platform agents. This section is the runtime that makes them safely deployable at scale. Without these four primitives — capability tokens, execution TTL, deterministic replay, sandboxed memory — a single prompt-engineering mistake leaks Tenant A's context into Tenant B's agent. The leak is catastrophic and irreversible.
../../Analyze2.txt #5) flagged uncontained AI agents as one of the largest future risks: "prompt leakage · tenant contamination · runaway workflows · hallucinated actions · memory bleed become existential risks at enterprise scale." v3.1's answer is the Agent Isolation Runtime — four mandatory primitives every agent invocation must use.
sequenceDiagram participant U as User
(persona) participant AGR as sdk-agent-runtime participant POL as sdk-policy + ReBAC participant CAP as Capability Issuer
(runtime internal) participant TOOL as SDK tool
(e.g., sdk-crm) participant MET as sdk-meter participant MEM as Sandboxed Memory
(tenant namespace) participant AUD as sdk-audit participant TRC as sdk-trace U->>AGR: Trigger agent (intent, persona_id, tenant_id) AGR->>POL: Check persona may invoke this agent POL-->>AGR: ALLOW AGR->>AGR: Start TTL clock (e.g., 30s) AGR->>MEM: Load tenant-scoped context (physical namespace) AGR->>AGR: Planner produces action plan loop For each planned tool call AGR->>CAP: Mint capability token (agent_id, tool, args, scope) CAP-->>AGR: signed token AGR->>TOOL: invoke(args, token) TOOL->>MET: gate.check(token, sku, tenant) MET-->>TOOL: ALLOW (token valid, in scope) TOOL->>TOOL: execute TOOL->>AUD: state change with token id + agent_chain TOOL-->>AGR: result end AGR->>AGR: Write execution log (content-addressed) AGR->>AUD: agent run complete (log hash, deterministic) AGR->>TRC: emit trace span across the whole run AGR-->>U: result Note over AGR: If TTL expires mid-loop: terminate, cancel
in-flight tools, rollback compensable steps
| Failure mode | Prevented by |
|---|---|
| Prompt injection tricks an agent into calling a tool outside its scope | Tool refuses invocation without a valid capability token; planner cannot mint a token for an out-of-scope SKU |
| Runaway agent loops indefinitely consuming budget | Execution TTL terminates it; meter refunds beyond-TTL usage |
| Hallucinated action that takes effect and can't be undone | Deterministic replay enables rollback within retention window; sdk-approval gates irreversible actions to human approval |
| Cross-tenant leak where Tenant A's vector context contaminates Tenant B's agent | Sandboxed memory uses physical (not logical) partitions; cross-tenant prompt-leakage CI test fails closed |
| Agent abusing delegation by acting as a higher-privileged persona | Capability token names the actual persona; meter and audit record the agent_chain; ReBAC evaluates against the persona, not the agent |
| Model-upgrade regression changes agent behavior silently | Deterministic replay verifies new model produces identical outputs for past logs; regressions surface in CI before promotion |
| Tool author adds an over-privileged tool | Tool manifest enumerates the SKUs it calls; meter enforces the manifest at the gate; agent cannot invoke a tool that exceeds its declared boundary |
sdk-agent-runtime or any tool used by agents; one leak fails the merge.The contracts package @projexlight/contracts is the keystone. Every service, every agent, every vertical, every HDK module depends on it.
{
"tenant_id": "uuid",
"vertical_id": "seva | realty | music | bidwork | leadpulse | fieldops | healthcare | ecommerce | common",
"pool_index": "app-healthcare-007", // v3 · which pool produced this
"event_id": "uuid",
"event_type": "encounter.opened.v1",
"occurred_at": "2026-05-20T10:15:32Z",
"actor": {
"type": "user | agent | system | tenant_admin | super_admin | hdk_device",
"person_id": "pers_...", // v3 · master person
"persona_id": "pers_...patient", // v3 · which hat
"id": "...",
"display_name": "..."
},
"encounter_id": "enc_...", // v3 · when within an encounter scope
"payload": { ... },
"schema_version": 1,
"correlation_id": "uuid",
"causation_id": "uuid",
"device": {
"device_id": "uuid",
"platform": "ios | android",
"app_version": "...",
"online": true
}
}
schema_version, keep topic.X.v2; producers dual-write; consumers migrate; v1 retired after every consumer cuts over.These services stay in their respective verticals — lifting them would smuggle domain assumptions into Common.
| Vertical | Service | Why it stays |
|---|---|---|
| Seva | Donation | 80G / FCRA / Anushthan logic is regulator- and culture-specific. |
| Seva | Acharya Network | Ritual matching, geography, skill taxonomy. |
| Seva | Ask-Guruji Agent | Bound to single approved corpus. |
| Realty | Property | Listing schema, possession, RERA fields. |
| Realty | Visit | Site-visit lifecycle — now expressed as Engagement of kind 'site_visit'. |
| Realty | Document / e-sign | Sale-deed semantics. May lift when 3rd vertical needs e-sign. |
| Music | Catalog · Rights · Licensing · Playback | Music-industry domain logic. |
| BidWork | Bid · Auction · Settlement | Auction mechanics. |
| LeadPulse | Channel Connectors | Channel-specific intake. |
| FieldOps | Storm Estimation · Contractor Settlement · Door-to-Door · Yamuna | Insurance, trade-specific, sales-rep canvassing logic. |
| Healthcare v3 | Chart · Rx · Care plan · Clinical decision support | Specialty-specific clinical content, drug interactions, ICD/CPT coding. |
| eCommerce v3 | Catalog · Cart · Order workflow · RMA | Industry-specific catalog shape, fulfillment, returns rules. |
Practitioner + Engagement-of-kind 'session'. For Healthcare: Chart + Rx. Keep them in vertical platforms. They emit vertical.*.v1 events.@projexlight/contracts. Persona / Engagement SDKs auto-validate.@projexlight/design-system + @projexlight/branding. Don't fork.tenant: hospital-a
vertical: healthcare
isolation_tier: governed # Tier G for PHI
pool_assignment:
admin_pool_index: admin-014 # auto-assigned
app_pool_index:
healthcare: app-healthcare-007 # dedicated for Tier G
evidence_pool_index: evidence-002
region: ap-south-1
modules:
identity: enabled
pool_router: enabled
persona: enabled
engagement: enabled
rebac: enabled
profile: enabled
payment: enabled
notification: enabled
audit: enabled
feature_flags: enabled
search: enabled
workflow: enabled
analytics: enabled
media: enabled
ai_gateway: enabled
field_ops_evidence: enabled # imaging captures
diagnostic_telemetry: enabled
# not used in healthcare
storm: disabled
donation: disabled
hdk:
hdk_idp: enabled
hdk_permissions: enabled
hdk_diagnostic: enabled
hdk_camera: enabled
persona_kinds:
- patient
- doctor
- nurse
- admin
encounter_kinds:
- visit
- admission
- surgery
- er_episode
- outpatient
branding:
primary: "#0ea5e9"
logo: "hospital_a.svg"
default_lang: "en"
deployment:
android: standard
ios: standard
web: enabled
| State | Meaning | Routing behavior |
|---|---|---|
| PROVISIONING | Newly created, schema initializing. | Hidden from allocator. |
| ACTIVE | Healthy, accepting new tenants. | Default. Allocator pins new tenants. |
| DRAINING | At capacity or being retired. | Existing OK; no new pins. |
| MAINTENANCE | Schema migration in flight. | Reads OK; writes briefly paused. |
| QUARANTINE | Anomaly detected. | Routing rejected; incident response. |
| RETIRED | All tenants migrated out. | Excluded except for audit. |
MIGRATING. Router queues writes; live reads OK on source.tenant_pool_map, fan-out invalidate router caches.ACTIVE on target. Source rows tombstoned for delete after retention.The default deployment is Projexlight-operated multi-tenant SaaS in our regional cloud. Enterprise and regulated buyers often need stricter variants. v3.1 documents four variants so the SDK estate is built with these constraints in mind — not retrofitted under sales pressure.
Tenants who require their own KMS keys as a hard policy (regulated finance, healthcare networks, government suppliers) can plug their own KMS into the Vault key hierarchy.
sdk-vault abstracts the provider.For workloads that cannot run on the default commercial regions: US Federal (FedRAMP High, IL5/IL6 for DoD), China PIPL (data-must-stay-in-China + Chinese cloud), EU sovereign (no US hyperscaler control plane), Russia data localization, Saudi/UAE data localization.
For banks, defense contractors, intelligence agencies, and a small set of regulated healthcare buyers, fully on-prem deployment is a hard requirement.
v3 defined Tier-G as warm replica + failover. Some buyers (global banks, payment networks, life-critical healthcare) require active-active — writes accepted in multiple regions simultaneously.
Names below are the current primary owners. Each named owner has a deputy.
| Area | Primary owner | Notes |
|---|---|---|
| Backend architecture · SDK flows | Tanveer | Platform-wide architect. |
| Pool Router · Pool Registry v3 | Platform / TBD | Pool Capacity Steward role. |
| Web Map · Map / Geo · hdk-map | Satyam | End-to-end map module. |
| Mobile · cross-platform · hdk-diagnostic · hdk-camera | Kunal | Mobile lead. |
| Authentication · hdk-idp | Shoaib · Krunal | Offline biometric + PIN. |
| hdk-image-editor · hdk-video-editor | Shoheb | Image done; video close. |
| Lead Scoring · Recommendation · Yamuna | Prashant | Multi-source pipeline + scoring. |
| CRM · Permissions · field user data | Mayur | Permissions follow-up. |
| Persona · Engagement · ReBAC v3 | TBD | To be assigned alongside Identity track. |
| hdk-scanner · hdk-measure · hdk-watermark | TBD | Open. |
| Storm · Dispatch · Assignment · Field-Ops Evidence | TBD | FieldOps team to nominate. |
| Quarter | Common platform deliverable |
|---|---|
| Q1 | Identity · Tenant · Profile · Payment · Notification · Audit · Media · Feature Flags · Observability · @projexlight/contracts v1 — production for Seva P1 + Realty P1. v3 Pool Registry + sdk-pool-router v1. Tenant provisioning writes pool assignments. |
| Q2 | Content · Course · Event · Search · Order primitives generalized. CRM consolidated. Generated SDKs published. v3 Identity issues six-layer JWT; sdk-persona v1 (App Identity, Tenant Membership, Persona). Profile bands re-homed. Per-pool Vault KEK live. |
| Q3 | SR · Workflow · Campaign · Social · Analytics · Executive Dashboard. Onboarding agent. HDK v1: hdk-idp · hdk-permissions · hdk-diagnostic · hdk-camera. v3 sdk-engagement v1 (Encounter, Relationship); sdk-rebac v1. First healthcare scenario in staging. |
| Q4 | AI Gateway · Knowledge/RAG · Conversation · Recommendation · all 12 Common Agents. HDK v2: hdk-map · hdk-image-editor · hdk-watermark. Map/Geo + Storm live. v3 Pool migration pipeline v1; first pool retirement drill; eCommerce scenario in staging. |
| Q5 | FieldOps vertical launches: Dispatch · Assignment · Lead Scoring · Field-Ops Evidence. HDK v3: hdk-video-editor · hdk-measure · hdk-scanner. Kiosk template ready. v3 Cross-region replication (Tier G); cross-tenant relationship coordinator; cross-domain scenario in staging. |
| Q6 | Tier G isolation; multi-region; third HDK-consuming vertical live. Reuse audit ≤ 10% domain code. Data-Design / Data-Request manifests steady state. v3 Petabyte readiness: 30+ pools in production; automated capacity steward; full per-encounter retention live. |
@projexlight/contracts.tenant_id within a pool.admin-014, app-healthcare-007.(tenant_id, app_id) → pool_index → dsn.(person_id × app_id).(app_identity_id × tenant_id).usage.event.v1 envelope emitted by sdk-meter on every gated call. Carries SKU, units, dimensions (six-layer AIM tuple + pool_index + actor), occurred_at, event_id.(sdk, method, tier) referenced by every @meter decorator. Schema lives in contracts; rates live in Postgres.pricing.catalog.vN. Bills always reference the catalog version they were generated against.subject_view per (person, app, tenant) maintained by sdk-identity-resolver's background worker. Replaces six-layer runtime traversal on the hot path.trace_id resolves into a unified timeline of identity + consent + routing + pool + key + policy + meter + lineage events for a single request. Powers MTTR and /billing/verify.@projexlight/connector-{target} package implementing the sdk-connectors framework for one external system. Roster: connector-slack (P4) · connector-salesforce, microsoft365, gworkspace, jira, linear, zendesk, hubspot, zoom (P5) · connector-snowflake (P6B) · connector-twilio-voice (P15).