TD1 — Core Tech Stack and Architecture
Decision Status Labels ✅ Decided — confirmed in a stakeholder session. Can be built against. 🔍 Under Analysis — options identified. Do not build against yet. 📅 Pending Session — requires a specific session or external input before a decision can be made.
1. What Core Is — Architecturally
✅ Core is a decision layer, not a data store
Core does not own module operational data. It owns decisions — eligibility outcomes, allocation results, permission verdicts, configuration values, lifecycle state. The data that informs those decisions comes from two paths only:
- Path 1 — ITS sync: Master data (Mumineen records, Jamaat hierarchy, ITS-managed tables) synced into Core via the ITS ↔ Core Sync Contract.
- Path 2 — Module events: Significant state changes published by business modules to the event bus. Core subscribes to the events it needs (e.g.,
capacity.configured,khidmat.assigned) and updates its own decision state accordingly.
Core never reads a module's database directly. If Core needs something a module owns, that module must publish it as an event.
✅ Core is a single platform authority
Core is one platform — not 32 parallel systems, not a shared database, not a set of conventions. It is a deployed platform that every business module calls. One team owns it and operates it. From a governance perspective it is one unit. Internally, it is built as purpose-based microservices — different engines use the technology stack best suited to their workload (see Section 3). Business modules deploy independently on their own clock; Core deploys on its own.
✅ Federation model
The platform operates on three layers:
Core — decision authority. Owns engines, policies, event schema, RBAC.
Modules — business logic. Own their data, workflows, and UIs.
Contracts — the agreed boundary between Core and each module.
(Contracts are defined in TD3.)
No module talks to another module directly. Cross-module communication routes through Core APIs (for decisions) or through the event bus (for state changes).
2. Core Engines — The Confirmed List
✅ These engines are confirmed as belonging inside Core
All 15 engines below passed the three-question test: shared by more than one module, encodes a business rule, and duplicating would cause incorrect decisions.
Miqaat and Structure
| Engine | What it owns |
|---|---|
| Miqaat Lifecycle Management | Create, phase, configure, and archive miqaats. No module can create or transition a miqaat without Core. |
| Config Cascade Engine | Hierarchical configuration: Global → Miqaat → [type-specific context levels]. The intermediate levels (e.g., City → Zone for Ashara Mubaraka, Phase → Session for Istefadah) are defined by the Miqaat type at setup. One change propagates to all modules. |
| Zone ↔ Resident Mapping | Maps ITS Jamaat data to miqaat zones. Modules query Core for resident counts by zone — they do not derive this themselves. |
| Capacity Balance Engine | Subscribes to capacity.configured events. Checks Vaaz / Mawaid / Kitchen ratios. Publishes capacity.imbalance.detected when ratios are violated. |
Identity and Access
| Engine | What it owns |
|---|---|
| Identity Bridge | Wraps ITS SSO. Validates ITS tokens, extracts itsId, enforces session policy. Modules never handle tokens directly. |
| RBAC / Permission Engine | Single permission authority for all 32 modules. Role definitions, role-to-permission mappings, runtime evaluation. Every module calls this for every access boundary check. |
Rules and Decisions
| Engine | What it owns |
|---|---|
| Rule Engine and Policy Runtime | Eligibility rules, filter criteria, allocation logic — configured here, not hardcoded in modules or SQL. Ops teams change rules without engineers. |
| Eligibility Evaluation Engine | Evaluates composite eligibility: ITS data, prior attendance, learning records, custom rules. Modules define which rules apply; this engine evaluates them. |
| Allocation Engine | City seat allocation, capacity slot distribution. Modules submit allocation requests; this engine resolves them. |
Data and Events
| Engine | What it owns |
|---|---|
| ITS Sync Engine | Full sync and delta sync of 9 ITS-managed tables into Core. Feeds Eligibility, Allocation, Zone Mapping, and HR Bank engines. |
| Audit Log Backbone | Immutable, append-only event stream. Every allocation decision, permission check, role change, config override is recorded. Cannot be modified or deleted. |
| Domain Event Schema Registry | Versioned schema definitions for all events published on the bus. Modules must conform to Core's envelope schema. |
| HR Bank | Cross-event khidmat history per itsId. Enforces the one-primary-khidmat rule. Feeds eligibility and volunteer matching decisions. Extension of the Mumin Info Aggregator. |
Operational
| Engine | What it owns |
|---|---|
| Vendor and Procurement Registry | Vendor registry, item master, budget envelope check API. Modules own indent creation and approval workflows; Core provides the reference data and budget check only. |
| Task Engine (Platform Layer) | Platform-level task and checklist definitions that span multiple modules or miqaat phases. Module-specific checklists stay in the module. |
3. Core Runtime Technology
✅ Purpose-based multi-language microservices — FastAPI (Python) + Node.js
Core is not built on a single technology stack. Each engine is built on the stack best suited to its workload. The two confirmed runtimes are FastAPI (Python) and Node.js. The principle is: choose the stack that matches the engine's primary characteristic — computation and data processing go to FastAPI; high-concurrency I/O and event-driven workloads go to Node.js.
FastAPI (Python) — computation-heavy and data-processing engines
FastAPI is chosen for engines that do significant computation, data transformation, or will integrate closely with AI/ML utilities. Python's ecosystem (data processing libraries, AI/ML tooling) makes it the natural fit here.
| Engine | Why FastAPI |
|---|---|
| Eligibility Evaluation Engine | Complex rule evaluation across multiple data sources — computational logic, not I/O |
| Rule Engine and Policy Runtime | Business rule evaluation; Python's expressive syntax suits complex conditional logic |
| Allocation Engine | Optimisation algorithms for seat and slot distribution — computation-intensive |
| ITS Sync Engine | ETL-like data transformation of 9 ITS tables — data processing workload |
| Capacity Balance Engine | Ratio calculations across event capacity data |
| HR Bank | Data aggregation, cross-miqaat history computation |
Node.js — high-concurrency I/O and event-driven engines
Node.js is chosen for engines that handle a very high volume of small, fast requests or that are inherently event-driven. The RBAC engine in particular is called on every access boundary across all 32 modules — it needs to handle thousands of concurrent permission checks with minimal latency.
| Engine | Why Node.js |
|---|---|
| RBAC / Permission Engine | Highest-frequency engine on the platform — called on every access check, needs low-latency concurrent handling |
| Identity Bridge | Token validation at the API Gateway layer — pure I/O, high concurrency |
| Config Cascade Engine | Fast hierarchical key-value lookups with caching — I/O bound |
| Notification Dispatch Engine | Event-driven fan-out to multiple delivery channels — naturally async I/O |
| Audit Log Backbone | High write-throughput, streaming append-only log |
| Miqaat Lifecycle Management | Workflow state transitions, primarily I/O and event publication |
| Domain Event Schema Registry | Schema validation and lookup — I/O bound |
| Zone ↔ Resident Mapping | Lookup-heavy, low computation |
| Vendor and Procurement Registry | CRUD + reference data lookups |
| Task Engine (Platform Layer) | Workflow and checklist state management |
Note: The engine-to-language mapping above is the working direction. The Core team will finalise the exact assignment for each engine before build begins. Some engines may warrant a different split once detailed design is done.
✅ What must be shared across both runtimes
Two different technology stacks inside Core only work if the seams between them are explicitly managed. The following must be consistent regardless of which runtime is serving a request:
API Gateway — single entry point All Core API calls from modules route through a single API Gateway. Modules call one platform URL and never need to know whether the engine behind it is FastAPI or Node.js. The gateway handles token validation (Identity Bridge), routes to the correct engine, and enforces rate limits.
Event envelope schema — language-agnostic JSON The event schema Core owns is defined as a JSON Schema specification — not tied to Python or Node.js. Both runtimes produce and consume events that conform to the same envelope. The Domain Event Schema Registry (Node.js) validates schema conformance regardless of which engine published the event.
Consistent error response format Every Core API returns errors in the same structure, regardless of which runtime produced the error. This is defined once in the platform contract (TD3) and both FastAPI and Node.js services implement it. Modules should never receive a Python traceback or a Node.js stack dump in a production error response.
Structured logging format Both runtimes emit logs in the same JSON schema. This is what allows the Audit Log Backbone and observability tooling to work across the whole platform without language-specific parsers.
Health and readiness endpoints
Every Core service — whether FastAPI or Node.js — exposes /health and /ready endpoints in the same response format. Platform Admin's Event Bus Health monitor and deployment tooling depend on this consistency.
Authentication passthrough The Identity Bridge (Node.js, at the gateway) validates the ITS token and extracts the itsId before the request reaches any engine. Downstream FastAPI and Node.js services receive the verified itsId in a standardised request header — they never re-validate the token themselves.
Configuration access Both runtimes read configuration from Core's Config Cascade Engine via a standard API call. No hardcoded environment-specific values in either runtime.
🔍 Internal service communication — within Core
When a FastAPI engine needs data from a Node.js engine (e.g., the Eligibility Engine calling the Config Cascade Engine), the internal communication pattern is under analysis. Options are synchronous HTTP calls between services (consistent with the module-to-Core pattern) or a more performant internal protocol (gRPC). Decision to be confirmed by the Core team before build begins.
🔍 Database technology per service
Each microservice within Core manages its own persistence. The database technology is not mandated to be the same across all services — the RBAC engine's query patterns (role hierarchy lookups) differ significantly from the Audit Log's (append-only, high write throughput) and the Config Cascade's (hierarchical key-value). Database choices per service are under analysis and will be confirmed before each engine's build begins.
4. API Gateway
✅ Core has a single API Gateway — concept confirmed
Core is built as multiple microservices (FastAPI and Node.js engines). Without something in front of them, a module calling Core would need to know which service handles which endpoint — RBAC on one address, Eligibility on another, Config Cascade on a third. That breaks the abstraction entirely. Modules must call one Core URL. The API Gateway is what makes that possible.
Beyond routing, three things already confirmed in this document require a single entry point:
- API key validation — must happen before any engine sees the request (TD2, Section 3)
- ITS token validation — the Identity Bridge extracts itsId before passing to any engine (Section 6 below)
- Rate limiting per module — enforced at one point, not duplicated across 15 services
The API Gateway is a Core responsibility. It sits in front of all Core engine services. It is the only entry point modules call. Internally it:
- Receives the inbound request from the module
- Validates the API key → resolves
X-Module-Id - Validates the ITS token if present → resolves
X-Its-Id - Routes the request to the correct Core engine (FastAPI or Node.js)
- Returns the engine's response to the module
No engine is exposed directly to modules. All traffic enters through the gateway.
🔍 API Gateway technology — under analysis
The technology implementing the gateway is under analysis and tied to the infrastructure decision in TD5. Three realistic options:
| Option | Description | Trade-off |
|---|---|---|
| Managed gateway (AWS API Gateway, Kong Cloud, Azure APIM) | Fully managed — routing, auth plugins, rate limiting, SSL included | Low operational burden but ties to a cloud provider |
| Self-hosted gateway (Kong, Traefik, Nginx, Envoy) | Cloud-agnostic, Core team operates it | More control, more operational responsibility |
| Core-built lightweight gateway (Node.js service) | Simple service built and owned by Core team | Simplest to control, trades features for ownership |
The decision will be confirmed in TD5 once the infrastructure session is complete. Until then, no Core service should be built with a hard dependency on a specific gateway technology.
5. Core's API Surface — How Modules Talk to Core
✅ Synchronous HTTP for decisions
When a module needs an answer before it can proceed — eligibility check, RBAC check, config lookup, allocation request — it makes a synchronous HTTP call to the relevant Core engine API. Core computes and returns the answer. This pattern works with any module stack (.NET 4.5, FastAPI, Node.js — all can make HTTP calls).
✅ API versioning — breaking change = major version bump
All Core APIs are versioned. A change that removes a field, renames a field, or changes a response behaviour is a breaking change and requires a new major version. The old version must be supported through a defined deprecation window. Modules are not required to be on the latest version but must upgrade before the deprecation window closes.
This is a platform commitment, not a preference. Teams building on Core APIs need this guarantee to deploy independently.
🔍 API protocol — HTTP/REST vs alternatives
The synchronous decision call pattern is confirmed as HTTP. The specific API design standard (REST, gRPC, GraphQL) is under analysis. REST is the working assumption given compatibility with the widest range of existing module stacks, but this will be confirmed before the first Core API contract is written (see TD3).
6. Core's Internal Data
✅ What Core must persist
Core is a decision layer but it is not stateless. It must persist the inputs and state that its engines need to make decisions:
| Data | Owner engine | Notes |
|---|---|---|
| Miqaat state (phase, dates, capacity, rules) | Miqaat Lifecycle | Created and updated only through Core |
| Role assignments (itsId → role → scope) | RBAC Engine | Written by Platform Admin only |
| Config cascade values (global / miqaat / type-specific context levels) | Config Cascade | Hierarchical overrides tracked with change history. Intermediate levels vary by Miqaat type. |
| ITS-synced master data (9 tables) | ITS Sync Engine | Read from ITS, owned by ITS — Core holds a sync copy |
| Audit log (immutable event stream) | Audit Log Backbone | Append-only. Never modified. |
| Event schema versions | Domain Event Schema Registry | Versioned schema definitions |
| Khidmat history per itsId | HR Bank | Cross-miqaat record |
| Vendor and item master | Procurement Registry | Reference data only — no indent or PO data |
🔍 Database technology per engine
As noted in Section 3, each Core microservice manages its own persistence and may use a different database technology suited to its query patterns. The choice per engine is under analysis. A decision will be confirmed before each engine's build begins.
7. Identity Bridge — ITS Integration
✅ ITS is the only identity provider
Confirmed under the Authentication Policy. No module creates its own login, issues its own token, or maintains its own user records. Every user on the platform is authenticated via ITS. Core's Identity Bridge is the single point through which ITS tokens are validated before any module receives a verified itsId.
✅ Core's role in identity
The Identity Bridge engine (Node.js, API Gateway layer) intercepts every inbound request, validates the ITS token, extracts the itsId, and passes it downstream as a verified header. Both FastAPI and Node.js Core services downstream receive the itsId already validated — they never see or handle the raw token.
📅 Token format and session detail — pending ITS technical session
The exact format of the ITS SSO token, session duration, refresh mechanism, and validation endpoint are not yet confirmed. These require a direct technical session with the ITS engineering team. Until that session is complete:
- The Identity Bridge engine cannot be fully specified
- The ITS ↔ Core Sync Contract (C-005) cannot be finalised
- Any module that depends on itsId behaviour beyond "ITS provides it" should flag this as a dependency
This is the first external dependency on the critical path.
8. Event Bus — Architecture Position
The event bus is where asynchronous communication between modules happens. Its architectural position is confirmed: Core owns the event envelope schema, Core monitors bus health via Platform Admin, and all cross-module state changes flow through it. Module-to-module direct calls are prohibited.
The communication patterns — synchronous Core API calls for decisions, event publication for state changes — are fully decided and documented in TD2 — Event Driven Structure.
🔍 Messaging technology
The specific technology powering the event bus is under analysis. Options include managed services and self-hosted alternatives. The decision will be confirmed in TD2 after the analysis is complete. No Core engine or module should be built with a hard dependency on a specific messaging technology before that decision is made.
9. Core Deployment Model
✅ Core is one platform, internally decomposed as microservices
Core is owned and operated by one team. Externally it presents as one platform. Internally it is decomposed into purpose-built microservices — each Core engine is a deployable service, built in either FastAPI or Node.js as appropriate, each managing its own data. The Core team owns all of these services collectively.
✅ Modules deploy independently
Core's deployment cannot break existing module deployments. The API versioning policy (Section 4) and the Deployment Independence Policy enforce this. A Core release that changes behaviour must do so under a new major version. Existing module integrations continue working until they choose to upgrade.
📅 Deployment infrastructure — pending ITS infrastructure session
Cloud provider, container orchestration, environment strategy (dev / staging / prod), and scaling model are not yet confirmed. These will be covered in TD5 — Infrastructure Recommendations after the ITS infrastructure session is completed. Both FastAPI and Node.js services will be containerised (Docker), making the choice of orchestration platform independent of the runtime choice.
Decision Summary
| Decision | Status | Notes |
|---|---|---|
| Core is a decision layer, not a data store | ✅ Decided | Foundational to the entire architecture |
| Core is a single platform authority, one team | ✅ Decided | Internally decomposed as microservices |
| Federation model (Core + Modules + Contracts) | ✅ Decided | Cross-module calls prohibited |
| 15 Core engines confirmed | ✅ Decided | See Section 2 |
| Core runtime: FastAPI (Python) + Node.js, purpose-based | ✅ Decided | See Section 3 for engine-to-language mapping |
| Shared API Gateway — single entry point for modules | ✅ Decided | Hides internal multi-language decomposition |
| Event envelope schema — language-agnostic JSON Schema | ✅ Decided | Both runtimes conform to the same spec |
| Consistent error response format across both runtimes | ✅ Decided | Defined in TD3 |
| Structured logging format — shared JSON schema | ✅ Decided | Enables unified audit and observability |
| Health/readiness endpoint standard across all services | ✅ Decided | /health and /ready on every service |
| Authentication passthrough via Identity Bridge at gateway | ✅ Decided | Downstream services receive verified itsId only |
| API Gateway exists — single entry point for all module calls to Core | ✅ Decided | Handles routing, API key validation, token validation |
| API Gateway technology (managed / self-hosted / Core-built) | 🔍 Under Analysis | Decision in TD5 after infrastructure session |
| Synchronous HTTP for decision calls | ✅ Decided | All module stacks can call HTTP |
| API versioning — breaking change = major version | ✅ Decided | Platform commitment to modules |
| ITS is the only identity provider | ✅ Decided | Authentication Policy |
| Core owns event envelope schema | ✅ Decided | Full detail in TD2 |
| Modules deploy independently of Core | ✅ Decided | Deployment Independence Policy |
| Both runtimes containerised (Docker) | ✅ Decided | Orchestration platform TBD in TD5 |
| Engine-to-language mapping (final) | 🔍 Under Analysis | Core team to confirm before each build |
| Internal service communication within Core | 🔍 Under Analysis | HTTP vs gRPC — Core team decision |
| Database technology per engine | 🔍 Under Analysis | Confirmed before each engine build begins |
| API protocol (REST vs gRPC vs GraphQL) | 🔍 Under Analysis | REST is working assumption |
| Messaging / event bus technology | 🔍 Under Analysis | Decision in TD2 |
| ITS token format and session detail | 📅 Pending Session | ITS technical session — critical path |
| Deployment infrastructure | 📅 Pending Session | ITS infrastructure session → TD5 |