Skip to main content

TD3 — Contracts

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 a Contract Is

A contract is a formal, versioned interface agreement. It defines exactly what gets sent, what comes back, and what behaviour both sides commit to. It is not a registration document — Core does not need to know a module exists in order for the module to call a Core API. Any module can call a published Core API endpoint. The contract is the published specification of what that call looks like.

Without written contracts, every team builds against their own assumption of how Core works. When Core changes something, teams discover the breakage in production. Contracts make the interface explicit, versioned, and stable. They are the document that module teams build against, and the document that Core is accountable to.

A contract is not:

  • A registration of a module's existence with Core
  • Permission from Core for a module to be built
  • A description of what the module does internally

A contract is:

  • The exact interface spec for one integration point between a module and Core
  • A versioned commitment from Core that the interface will not change without notice
  • The basis for contract testing — automated verification that both sides are honouring what they agreed

2. Contract Types

There are three types of contracts on this platform, each covering a different class of integration.

Type 1 — ITS Integration Contracts

Agreements between Core and ITS. These define how Core receives identity data and master data from ITS. They are not module-facing — module teams do not need to read these. They are the foundation that makes everything else work: Core cannot validate an itsId, sync Mumineen records, or resolve a Jamaat hierarchy without these contracts being in place.

Type 2 — Core API Contracts

Agreements between Core and any module that calls a Core engine via synchronous HTTP (Pattern A from TD2). These are public — any module team can read them and build against them without asking Core's permission. Each contract covers one Core engine API endpoint or a related group of endpoints.

Type 3 — Event Contracts

Agreements about what gets published to the event bus and in what shape (Pattern B from TD2). These come in two directions: Core publishes events that modules may subscribe to (Core → Module), and modules publish events that Core engines (and other modules) subscribe to (Module → Core/Module). Each event type has its own schema contract registered with the Domain Event Schema Registry.


3. What Every Contract Must Cover

Regardless of type, every contract on this platform includes the same standard sections. This consistency is what makes contract testing possible and what makes it safe for teams to build against them independently.

SectionWhat it defines
Contract IDUnique identifier (e.g., C-005). Referenced in audit logs, changelogs, and System Definition Documents.
Contract typeITS Integration / Core API / Event
VersionSemantic version (e.g., 1.2.0). Breaking change = major version bump.
StatusDraft / Active / Deprecated / Retired
OwnerWhich team is responsible for honouring this contract (Core team for Core APIs and ITS contracts; module team for their event publish contracts)
Interface definitionFor API contracts: endpoint path, HTTP method, request schema, response schema, error response schema. For event contracts: event type name, payload schema (JSON Schema), schemaVersion value.
AuthenticationWhich call type this endpoint accepts: user-initiated (requires X-Its-Id), system-initiated (no itsId required), or both.
SLAExpected response time (for API contracts). Expected event delivery latency (for event contracts).
Versioning rulesWhat constitutes a breaking change for this contract. What the deprecation notice period is.
Deprecation datePopulated when a version enters deprecation. Subscribers must migrate before this date.
Change logHistory of changes per version with migration notes.

4. Type 1 — ITS Integration Contracts

These are the two contracts between Core and ITS that everything else depends on. Neither can be finalised until the ITS technical discovery session is complete.

📅 C-001 — ITS SSO Token Contract

Defines the format and validation behaviour of ITS authentication tokens. Core's Identity Bridge must know exactly how to validate a token and extract an itsId from it. Without this contract, the Identity Bridge cannot be built.

Important clarification — Core does not take over ITS login. All 32 modules continue using ITS login directly, exactly as they do today. The Identity Bridge only validates ITS tokens on calls coming into Core's API Gateway — it is not a login system, not an auth broker, and not a replacement for each module's existing ITS session handling. C-001 is purely about Core knowing the token format well enough to extract a verified itsId from it.

What this contract must define once the session happens:

  • Token format (JWT structure, claims, signing algorithm)
  • Token validation endpoint (where Core calls to verify a token)
  • Token expiry and refresh behaviour
  • itsId format and uniqueness guarantee
  • How the Identity Bridge handles expired or invalid tokens (error codes, response format)
  • Session duration and single-session enforcement rules

📅 C-005 — ITS ↔ Core Data Integration Contract

Defines how ITS master data flows into Core. Core's Eligibility Engine, Allocation Engine, Zone Mapping, and HR Bank all depend on this data being current and correct.

✅ The principle — confirmed

Core is the single point of ITS data ingestion. No business module syncs from ITS independently.

Today, many of the 32 modules each maintain their own copy of ITS data — their own sync process, their own subset of Mumineen records, their own stale risk. That is the same duplication problem the platform exists to solve, applied to ITS data. Core doing its own sync alongside all of them would make it the 33rd instance of the same pattern — which defeats the purpose.

The confirmed principle: Core syncs from ITS once. Modules that need ITS data query Core for it. As modules migrate to Core, their individual ITS syncs are deprecated. This consolidates 32+ sync processes into one, eliminates 32 stale copies of the same data, and makes ITS the unambiguous single source of truth with Core as its only read replica.

The value of this goes beyond architecture. A single sync means a single place to monitor for failures, a single place to audit what data Core is working from, and a single upgrade path when ITS changes its schema.

🔍 The mechanism — under analysis, pending ITS session

While the principle is confirmed, the technical mechanism for how ITS data gets into Core is not. Three options need to be evaluated with the ITS team:

Option A — Pull sync (Core-initiated, scheduled) Core runs a scheduled process that pulls from ITS on a defined cadence. A full sync on a schedule (e.g., nightly), with a delta sync process that pulls only changed records more frequently. Simple to build, straightforward to operate. The downside: there is always a window between ITS updating a record and Core having it. During that window, Core may make decisions on slightly stale data.

Option B — ITS push (event-driven, ITS-initiated) ITS publishes a change event whenever a member record is created or updated. Core subscribes and updates its copy instantly. No scheduled job, no stale window, no polling. The cleanest long-term solution. The constraint: this requires ITS to adopt event publishing — a significant change to ITS's own architecture and a meaningful ask of the ITS team. Feasibility depends on ITS's current stack and team capacity, which is unknown until the technical session.

Option C — Hybrid Full pull sync at startup and after any gap in connectivity (to ensure completeness). ITS push events for incremental changes in steady state. Gets the benefits of both: reliability of the pull for completeness, freshness of the push for ongoing changes. More complex to build and operate, but the most resilient in practice.

The right choice depends entirely on what ITS can support. This must be the first question in the ITS technical session.

📅 What this contract must define once the session happens

  • Which mechanism (A, B, or C) is agreed with the ITS team
  • The 9 ITS-managed data sets Core requires (full list, field-by-field schema)
  • Transform rules (where ITS field names or structures differ from Core's internal schema)
  • How Core signals data freshness to its dependent engines (e.g., last-sync timestamp, staleness flag)
  • Error handling — what Core does if the sync falls behind or fails entirely during a live miqaat
  • Data ownership confirmation: ITS is the source of truth; Core's copy is read-only and never written to by Core or any module
  • Migration plan for existing module ITS syncs — which modules are syncing today, and the sequenced plan to deprecate those as modules migrate to Core

Both C-001 and C-005 are on the critical path. Core's Identity Bridge and ITS Sync Engine cannot be fully specified until these sessions are complete. All module teams depending on itsId-based decisions or ITS master data should treat this as their first external dependency. Additionally, the deprecation of existing module-level ITS syncs cannot be planned until the mechanism is confirmed.


5. Type 2 — Core API Contracts

Core API contracts are the primary interface that module teams build against. They are public specifications — a module team reads the contract, builds to it, and calls the endpoint. No registration required. No approval from Core. Just call the API as documented.

✅ Contract framework — confirmed, individual contracts being written

The framework for Core API contracts is confirmed. Each contract follows the standard section structure from Section 3. Individual contracts per engine API are being drafted as part of Core's build process. The first contracts to be written are for the engines that existing modules need earliest: RBAC, Eligibility, and Config Cascade.

✅ Endpoint authorization — read/write split enforced at the gateway

Core API contracts are classified as read or write. This classification is stated explicitly in every contract and enforced at the API Gateway before the request reaches any engine:

  • Read contracts — accessible to any module with a valid API key. No per-module endpoint restriction needed.
  • Write contracts — restricted to Platform Admin and designated Core processes. A business module API key is rejected at the gateway if it attempts to call a write endpoint. No engine-level write contract is published for business modules.

This is the enforcement model for "who can call what" on the synchronous API side. Module teams only need to read read-endpoint contracts — write contracts are irrelevant to them.

✅ Standard request and response format

All Core API contracts share the same base request and response structure. This is the consistent error response format established in TD1 — regardless of which engine (FastAPI or Node.js) handles the request, the module receives the same shape.

Standard success response:

{
"status": "success",
"requestId": "uuid — for tracing, same as correlationId in events",
"data": { }
}

Standard error response:

{
"status": "error",
"requestId": "uuid",
"error": {
"code": "RBAC_DENIED / ELIGIBILITY_INELIGIBLE / CONFIG_NOT_FOUND / ...",
"message": "human-readable explanation",
"details": { }
}
}

No engine returns a Python traceback, a Node.js stack dump, or a framework-generated error page. Every error from every Core engine looks the same to the calling module.

✅ Core API contracts in scope — one per engine group

Contract IDEngineStatus
C-010RBAC / Permission Engine📝 Being drafted
C-011Eligibility Evaluation Engine📝 Being drafted
C-012Config Cascade Engine📝 Being drafted
C-013Miqaat Lifecycle — read endpoints📝 Being drafted
C-014Allocation Engine📝 Being drafted
C-015Zone ↔ Resident Mapping📝 Being drafted
C-016Vendor and Procurement Registry📝 Being drafted
C-017HR Bank — read endpoints📝 Being drafted
C-018Capacity Balance — read endpoints📝 Being drafted
C-019Task Engine — read endpoints📝 Being drafted

Write endpoints (those that mutate Core state) are restricted — only Platform Admin and designated Core processes can call them. Read endpoints are the primary interface for business modules.

✅ Example — RBAC Permission Check Contract (C-010, draft structure)

Contract: C-010
Engine: RBAC / Permission Engine
Version: 1.0.0 (draft)
Auth type: User-initiated (X-Its-Id required)
Endpoint: POST /v1/rbac/check

Request:
{
"itsId": "ITS-1234567",
"action": "approve",
"resource": "volunteer",
"miqaatId": "MQ-1447-KHI"
}

Response (allowed):
{
"status": "success",
"requestId": "uuid",
"data": {
"allowed": true,
"roleMatched": "VMS_Coordinator",
"reasonCode": "ROLE_GRANTS_ACTION"
}
}

Response (denied):
{
"status": "success",
"requestId": "uuid",
"data": {
"allowed": false,
"roleMatched": null,
"reasonCode": "NO_MATCHING_ROLE"
}
}

Error (missing itsId — system-initiated call attempted on user-only endpoint):
{
"status": "error",
"requestId": "uuid",
"error": {
"code": "AUTH_ITS_ID_REQUIRED",
"message": "This endpoint requires a verified itsId. System-initiated calls are not permitted."
}
}

6. Type 3 — Event Contracts

Event contracts define what is published to the event bus and in what shape. Unlike API contracts (where Core is always the publisher/responder), event contracts have two directions.

✅ Core → Module event contracts (Core publishes, modules subscribe)

When a Core engine publishes a decision event, the event type and payload schema are defined in a Core-owned contract. Modules subscribe knowing exactly what they will receive.

Event TypeOwner EngineContract ID
miqaat.phase.changedMiqaat LifecycleC-020
allocation.confirmedAllocation EngineC-021
allocation.waitlistedAllocation EngineC-022
capacity.imbalance.detectedCapacity Balance EngineC-023
eligibility.status.changedEligibility EngineC-024
role.assignedRBAC EngineC-025
role.revokedRBAC EngineC-026

🔍 Event publish enforcement — requirements confirmed, mechanism under analysis

Three authorization checks on every event publish are confirmed:

  1. sourceModule must match the publishing module's identity — prevents impersonation
  2. eventType must be in the module's registered event type list — prevents publishing under another module's contract
  3. Envelope and payload must conform to the registered schema for that eventType and schemaVersion

Where these checks are enforced — at a Core gateway HTTP endpoint or at the bus level using bus-level ACLs and schema validation — is under analysis. The decision is tied to the messaging technology choice (TD2 Section 6). See TD2 Section 3 for the full options and trade-offs, including the load concern with gateway-mediated publishing during peak events.

✅ Module → Platform event contracts (modules publish, Core engines and other modules subscribe)

When a module publishes events, it registers the event type and schema with the Domain Event Schema Registry. That registration becomes the contract. Other subscribers (Core engines and other modules) build against the registered schema. It also becomes the permitted publish scope enforced by the gateway above.

Example event typePublisher modulePrimary Core subscriber
registration.confirmedAMSEligibility Engine, Audit Log
khidmat.assignedVMSHR Bank, Audit Log
capacity.configuredAccommodationCapacity Balance Engine, Audit Log
payment.receivedPayment moduleEligibility Engine, Audit Log
pass.printedScanning / KioskAudit Log
volunteer.assignedVMSHR Bank, Audit Log

Module event contracts are the module team's responsibility to maintain. Breaking changes to their event payload (removing or renaming a field) require a new schemaVersion with a deprecation notice to all subscribers before the old schema is retired.

✅ Every event contract inherits the standard envelope

All event contracts build on the standard event envelope from TD2 (Section 4). The payload field is what varies per event type and is what each event contract specifically defines.


7. Contract Lifecycle

✅ Versioning — semantic versioning on all contracts

All contracts use semantic versioning (MAJOR.MINOR.PATCH).

  • MAJOR — breaking change. New major version required. Old version continues to be supported through the deprecation period. Module teams must migrate before the deprecation date.
  • MINOR — backward-compatible addition (new optional field, new optional endpoint). Existing modules are unaffected.
  • PATCH — documentation fix, clarification, no behaviour change.

✅ Breaking changes — what counts

For API contracts: removing a field from a response, renaming a field, changing a field's data type, changing an endpoint path, removing an endpoint, changing error codes that modules may be handling.

For event contracts: removing a field from the payload, renaming a field, changing a field's data type, changing the event type name.

Adding new optional fields is never a breaking change. Adding new endpoints is never a breaking change.

✅ Deprecation process

  1. New major version contract is published and marked Active
  2. Old version is marked Deprecated with a stated deprecation date
  3. Module teams have the full deprecation window to migrate (minimum 8 weeks for API contracts, confirmed by the Core team per contract)
  4. On the deprecation date, the old version is marked Retired and the endpoint or schema version stops being served
  5. All steps are logged in the contract's change log and announced to module teams

🔍 Contract format and tooling

The format in which contracts are authored, stored, and published is under analysis. Options include OpenAPI Specification (for API contracts), AsyncAPI (for event contracts), or a simpler structured markdown format. The tooling decision affects whether contract testing can be automated. A decision will be confirmed before the first contracts are finalised.

🔍 Contract storage and discoverability

Where contracts live — a Git repository, a developer portal, Confluence, or a dedicated contract registry — is under analysis. The requirement is: any module team can find and read the latest version of any Core contract without asking the Core team for it. Discoverability and version history are non-negotiable.


8. Pre-Build Contract — System Definition Document

Before a module begins building any integration with Core, it completes the System Definition Document (the seven-question process from BusinessModulesAndPolicies.md, Part 2). This document functions as the pre-build contract between the module team and the Core team. It records:

  • Which Core API contracts the module will call
  • Which event types it will publish (and therefore register with the Domain Event Schema Registry)
  • Which event types it will subscribe to
  • Which migration type applies (greenfield / migrate / rewrite)

The Core team reviews and signs off on the System Definition Document before implementation begins. This is not an approval of the module's existence — it is a review to ensure the module is not recreating something Core already provides, and that its planned integrations are consistent with platform policies.


Decision Summary

DecisionStatusNotes
Contracts are required for all Core integration points✅ DecidedFoundation of the federation model
Contracts are public — no approval needed to read or build against them✅ DecidedCore does not gate module builds
Three contract types: ITS Integration, Core API, Event✅ DecidedSee Section 2
Standard contract sections apply to all types✅ DecidedSee Section 3
Standard success and error response format for all API contracts✅ DecidedConsistent regardless of engine runtime
Semantic versioning on all contracts✅ DecidedMAJOR.MINOR.PATCH
Breaking change = new major version✅ DecidedAPI Versioning Policy
Minimum 8-week deprecation window for API contract versions✅ DecidedModule teams have time to migrate
Write endpoints restricted to Platform Admin and Core processes✅ DecidedModule teams access read endpoints only — enforced at gateway
Read/write classification stated explicitly in every contract✅ DecidedModule teams only need to read read-endpoint contracts
Event publish authorization requirements (sourceModule, eventType, schema)✅ DecidedThree checks required on every publish
Event publish enforcement mechanism🔍 Under AnalysisCore gateway vs direct bus with ACLs — see TD2 Section 3
Event type registration doubles as permitted publish scope✅ DecidedEnforced at whichever mechanism is confirmed
System Definition Document as pre-build contract✅ DecidedCore team reviews before implementation
Event contracts inherit standard envelope from TD2✅ DecidedPayload schema is what each event contract defines
Module team owns and maintains their event publish contracts✅ DecidedBreaking payload changes require new schemaVersion + notice
C-001 — ITS SSO Token Contract📅 Pending SessionITS technical session — critical path
C-005 — ITS ↔ Core Sync Contract📅 Pending SessionITS technical session — critical path
Individual Core API contracts (C-010 to C-019)📝 Being draftedWritten as each engine is built
Individual Core event contracts (C-020 to C-026)📝 Being draftedWritten as each engine is built
Contract format and tooling (OpenAPI / AsyncAPI / other)🔍 Under AnalysisImpacts contract testing automation
Contract storage and discoverability (developer portal / Git / registry)🔍 Under AnalysisMust be accessible without asking Core team