Distributed CMS — current milestones → ideal state
One page for the end goal: a single Raisd CMS product with databases and compute placed by country / region (Malaysia, Singapore, Sierra Leone, Botswana, and further campuses), each with its own white-label, feature toggles, and identity — reached step by step from today’s ADR-1 Cyberjaya path through M1–M5.
1. Ideal end state (target architecture)
Raisd ends as one CMS system (one product codebase, one Portal API contract, four role portals) with a distributed data plane: each country or regulated region keeps its own database and may run local API/portal capacity for latency and data residency. Global control-plane services own tenancy, branding, feature flags, and identity federation — not student ledgers.
Logical shape — single product, many country data planes
Portals stay thin. Writes always go to the campus’s local system of record.
flowchart TB
subgraph Users["Users worldwide"]
U1[Applicants / Students]
U2[Lecturers / Staff]
end
subgraph Edge["Edge & routing"]
DNS[Geo / campus DNS]
WL[White-label shell
theme · logo · domain]
end
subgraph GlobalCP["Global control plane — shared config only"]
TEN[Tenant registry
campus · country · region]
FF[Feature flag service]
IDF[Identity federation
IdP map · SSO policies]
SCH[Schema / release train]
end
subgraph SEA["Region SEA — example"]
subgraph MY["Malaysia · Cyberjaya"]
APImy[Portal API MY]
DBmy[(CMS DB MY)]
FSmy[(Evidence MY)]
end
subgraph SG["Singapore · hosting / campus"]
APIsg[Portal API SG]
DBsg[(CMS DB SG)]
end
end
subgraph AFR["Region Africa — example"]
subgraph SL["Sierra Leone"]
APIsl[Portal API SL]
DBsl[(CMS DB SL)]
end
subgraph BW["Botswana"]
APIbw[Portal API BW]
DBbw[(CMS DB BW)]
end
end
U1 --> DNS
U2 --> DNS
DNS --> WL
WL --> APImy
WL --> APIsg
WL --> APIsl
WL --> APIbw
TEN -.-> WL
FF -.-> APImy
FF -.-> APIsl
IDF -.-> APImy
IDF -.-> APIsl
SCH -.-> APImy
SCH -.-> APIsl
APImy --> DBmy
APImy --> FSmy
APIsg --> DBsg
APIsl --> DBsl
APIbw --> DBbw
Product layer (one)
- Applicant / Student / Lecturer / Staff portals
- Portal API (same OpenAPI contract)
- Raisd CMS domain services (post-legacy)
Control plane (global)
- Campus / country tenant registry
- White-label packs (brand, locale, legal copy)
- Feature flags per campus / country
- Identity provider mapping
Data plane (per country)
- Authoritative CMS database
- Evidence / file store
- Local Portal API replicas
- Country compliance batch jobs
Country uniqueness
- MY: EMGS, MQA/KPT, PTPTN, …
- SL / BW / others: local QA & fees
- Toggled modules — not forks
2. Journey map — step by step
Agreed delivery is SDD-03 M1–M5. The ideal distributed Raisd CMS is the post-M5 target that M5 progressive consolidation is designed to enable — not a rewrite during Admissions launch.
flowchart LR N[Now
Demo + dumps] --> M1[M1
Rules & access] M1 --> M2[M2
Admissions] M2 --> M3[M3
Core student] M3 --> M4[M4
Lesotho pilot] M4 --> M5[M5
Campus expansion
+ progressive SoR] M5 --> IDEAL[Ideal
Unified Raisd CMS
distributed DBs]
| Stage | System of record | Where data lives | Multi-country? | White-label / flags |
|---|---|---|---|---|
| Now / M1 | Legacy CMS per campus (inventoried) | Cyberjaya cmscbj; Sierra Leone ems_sierraleone (separate) |
Inventories only | Not productised |
| M2–M4 | Campus CMS family (ADR-1); Lesotho pilot first | Singapore UltaHost CMS host + k3s Postgres maps | Lesotho first Live; others parallel/M5 | Campus config seed; single brand |
| M5 | Legacy per campus, then domain-by-domain Raisd | Still primarily SEA until outside-SEA launch | Roll out campus-by-campus | Introduce tenant + flag model |
| Ideal | Raisd CMS (unified product) | DB + API per country / region | Yes — MY, SG, SL, BW, … | Full white-label + feature toggles + identity packs |
3. Now → M1 — baseline architecture
Student portal is the deepest Demo UI. Applicant / lecturer / staff Raisd apps are not Live. Cyberjaya CMS and sister-campus dumps (e.g. Sierra Leone) exist as separate legacy systems — proof that multi-country already means separate databases today, not one shared ledger.
flowchart TB
subgraph Raisd["Raisd today"]
SP[Student portal
Demo / mock]
API[Portal API
contracts + stubs]
PG[(Postgres planned
maps / audit)]
end
subgraph Legacy["Legacy estate — separate SoRs"]
CJ[Cyberjaya / LUCT CMS
cmscbj]
SL[Sierra Leone CMS
ems_sierraleone]
OTHER[Other campus CMS
Botswana · … TBC]
end
SP --> API
API -.->|stub / mock| CJ
API -.->|not connected| SL
CJ ~~~ SL
SL ~~~ OTHER
What must happen in M1
Why this matters for the ideal state
- Sister CMS dumps prove country DBs are already isolated
- CAP catalogue must stay shared across countries
- Do not force one physical DB for compliance-heavy countries
- Inventory → adapter map → later Raisd SoR cutover
4. M2–M4 — portals on campus CMS family (Lesotho pilot first)
Four portals talk only to the Portal API. Live writes succeed only after the campus CMS acknowledges them (CAP-53). First acceptance campus is Lesotho (3 Oct 2026). Hosting stays on UltaHost Singapore k3s (SDD-12). Staff desks remain on verified old CMS unless a gap is proven.
Runtime topology (M2–M4)
One acceptance campus (Lesotho). CMS family SoR. No second control plane yet.
flowchart LR
subgraph Clients
AP[apply.raisd.co]
STU[student.raisd.co]
TEA[teach.raisd.co]
STF[staff.raisd.co]
end
subgraph K3s["k3s · Singapore UltaHost"]
ING[Traefik]
API[portal-api]
PG[(Postgres
maps · session · audit)]
FS[Evidence volume]
end
CMS[Cyberjaya CMS
Premium · SoR]
IDP[CMS login / IdP]
AP --> ING
STU --> ING
TEA --> ING
STF --> ING
ING --> API
IDP --> API
API --> PG
API --> FS
API -->|CAP-53 ACL| CMS
| Milestone | Architecture change | Release condition (summary) |
|---|---|---|
| M2 | Wire Portal API to real CMS; admissions + auth slice Live; evidence store durable | Apply → evidence → offer → enrol on real records |
| M3 | Add student + lecturer portals on edge node; teaching/finance paths via API | Core student journeys + paired staff/lecturer paths Live or on old CMS |
| M4 | No new topology; restore drill, training, acceptance | Lesotho accepts pilot; ops ready |
5. M5 — multi-campus expansion and progressive unification
Expand coverage campus-by-campus. Prefer Portal API + verified local legacy CMS first. Rebuild or migrate a domain into Raisd CMS only with ownership, cutover, rollback, and training. Introduce the tenant / flag / white-label model here so Ideal is configuration, not a rewrite.
flowchart TB
subgraph Shared["Raisd shared product"]
P[Four portals]
API[Portal API
multi-tenant campus_id]
CFG[Campus config
tenant · flags · brand]
end
subgraph Adapters["Country adapters — M5 pattern"]
A1[Cyberjaya adapter
LUCT / cmscbj]
A2[Sierra Leone adapter
ems_sierraleone]
A3[Botswana adapter
TBC inventory]
end
subgraph SoR["Systems of record during M5"]
L1[(Legacy MY)]
L2[(Legacy SL)]
L3[(Legacy BW)]
R1[(Raisd domain DB
migrated modules only)]
end
P --> API
CFG --> API
API --> A1
API --> A2
API --> A3
A1 --> L1
A2 --> L2
A3 --> L3
API -->|verified gap + cutover| R1
M5 architecture workstreams
- Adapter factory: one CAP-53 pattern per legacy campus DB
- Tenant registry: campus_id, country_code, region, legal entity
- Feature flags: EMGS, local QA, payment rails, visa desks
- White-label packs: logo, colours, domain, locale, privacy text
- Domain migration: admissions → registry → teaching → finance → …
- Hosting: grow SEA cluster; plan first non-SEA region when a campus outside SEA goes Live
Expansion order (illustrative)
6. Ideal state — unified Raisd CMS, distributed databases
Legacy campus CMS instances are retired (or reduced to read-only archive). Raisd CMS is the only write path. Each country (or regulated region) runs its own database and preferably local API capacity. Global services never hold the student ledger.
Detailed ideal architecture
Global control plane + regional data planes + shared portal apps.
flowchart TB
subgraph Portals["Shared portal apps — multi-tenant"]
AP[Applicant]
SP[Student]
LP[Lecturer]
ST[Staff / admin CMS]
end
subgraph Control["Global control plane"]
TR[Tenant registry]
WL[White-label service]
FF[Feature flags]
ID[Identity router]
REL[Release / schema train]
OBS[Central observability]
end
subgraph RegionA["Region A — SEA"]
GW1[Regional API gateway]
API1[Portal API pool]
CMS1[Raisd CMS services]
DB1[(Country DBs
MY · SG · …)]
FS1[(Evidence region A)]
end
subgraph RegionB["Region B — Africa"]
GW2[Regional API gateway]
API2[Portal API pool]
CMS2[Raisd CMS services]
DB2[(Country DBs
SL · BW · …)]
FS2[(Evidence region B)]
end
AP --> WL
SP --> WL
LP --> WL
ST --> WL
WL --> GW1
WL --> GW2
TR --> WL
FF --> API1
FF --> API2
ID --> API1
ID --> API2
REL --> CMS1
REL --> CMS2
GW1 --> API1 --> CMS1
CMS1 --> DB1
CMS1 --> FS1
GW2 --> API2 --> CMS2
CMS2 --> DB2
CMS2 --> FS2
API1 --> OBS
API2 --> OBS
Scale-out unit
A country data plane: DB + files + optional API pool. Add a country by provisioning a plane and registering a tenant — not by forking the CMS.
Geo placement
Compute near data for latency and residency. DNS / gateway routes campus → region. Cross-region reads are explicit and rare.
Identity
Each country maps to its IdP / local auth rules. Tokens carry campus_id, country_code, roles. Server enforces scope.
Ops
One release train; per-country config. Backups and restore drills per data plane. Central metrics with country labels.
7. White-labelling, feature toggling, and identity
Country uniqueness is expressed as configuration on a shared product — not parallel codebases.
flowchart LR REQ[Browser request
host / campus] --> RESOLVE[Tenant resolve] RESOLVE --> BRAND[White-label pack
logo · theme · domain · locale] RESOLVE --> FLAGS[Feature flags
modules · regulators · rails] RESOLVE --> IDP[Identity pack
IdP · MFA · password policy] BRAND --> SHELL[Portal shell] FLAGS --> API[Portal API authz] IDP --> API API --> LOCAL[(Country DB)]
| Dimension | What is shared | What is per country / campus | Example |
|---|---|---|---|
| White-label | Portal layouts, CAP screens, design system | Logo, colours, public hostname, footer legal, language pack | apply.cyberjaya… vs apply.sl… |
| Feature toggles | Module code behind flags | EMGS / visa, local QA evidence, payment gateway, accommodation | MY: EMGS on · BW: EMGS off |
| Identity | Token model, RBAC engine, role names | IdP, password rules, staff directory source, SSO | CMS login vs national IdP |
| Academic rules | Capability IDs and workflows | Programme catalogue, calendar, fee schedules, grading policy | Campus config in control-plane |
| Compliance | Privacy framework, audit events | Data residency region, retention, local report jobs | DB must stay in-country when required |
tenant_id, country_code, region,
brand_pack_id, feature_flags{},
idp_ref, db_dsn_secret_ref, evidence_bucket.
Secrets never live in the portal; only the regional Portal API resolves them.
8. Data plane — residency, sync, and what must never be shared
Distributed database means partition by country / campus, not a single multi-writer global database. Cross-country data movement is exceptional and explicit.
Stays in-country
- Student master, applications, results, fees
- Evidence files and payment proofs
- Staff operational desks and audit for that campus
- Country-specific regulator extracts
May be global (non-ledger)
- Tenant registry and feature-flag definitions
- White-label assets (or CDN copies)
- Aggregated ops metrics (no PII)
- Schema version / release metadata
flowchart LR
subgraph Allowed["Allowed cross-country — explicit"]
X1[Student transfer request]
X2[Group reporting anonymised]
X3[Shared programme template publish]
end
subgraph Forbidden["Forbidden by default"]
Y1[Silent multi-master replication of ledgers]
Y2[Portal writing two country DBs in one request]
Y3[Staff token reused across countries]
end
X1 --> BUS[Controlled integration bus
consent · audit · mapping]
BUS --> TGT[(Target country DB)]
9. Cutover playbook — how a campus reaches the ideal state
Same sequence for every country. Software delay never waives an operating requirement — run on legacy or manually until Raisd is Live for that domain.
flowchart TB S1[1. Inventory legacy CMS
schema · SPs · desks] --> S2[2. Map to CAP catalogue] S2 --> S3[3. Ship Portal API adapter
legacy remains SoR] S3 --> S4[4. Campus accepts portals
on legacy SoR] S4 --> S5[5. Register tenant
white-label · flags · IdP] S5 --> S6[6. Domain cutover waves
admissions → registry → teaching → finance → …] S6 --> S7[7. Dual-run & reconcile] S7 --> S8[8. Flip SoR to Raisd DB
legacy read-only / retire] S8 --> S9[9. Optional: place API pool
in-country / in-region]
| Wave | When | Exit criteria |
|---|---|---|
| 0 — Discover | Before Live | Inventory + CAP map (Cyberjaya / Sierra Leone pattern) |
| 1 — Experience | M2–M4 / first campus acceptance | Portals Live on legacy SoR via CAP-53 |
| 2 — Tenantise | M5 | campus_id, brand pack, flags, IdP wired; second campus adapter Live |
| 3 — Domain migrate | M5+ | Per-domain: ownership, cutover, rollback, training signed |
| 4 — Distribute | Ideal | Country DB + regional API; legacy retired; residency proven |
10. Decisions still open (TBC)
Ideal-state topology is a proposed target. It must not be treated as an agreed ADR until the Working Group confirms it. Current binding ADRs remain ADR-1 (SoR), ADR-2 (Singapore UltaHost), ADR-3 (k3s).
| Topic | Options / notes | Status |
|---|---|---|
| Promote Ideal to ADR | Single product + country data planes + global control plane | Needs WG confirm |
| DB engine for Raisd SoR | Postgres (portal maps today) vs migrate legacy MySQL domains first | TBC |
| First non-SEA region host | When Africa campus goes Live — co-locate DB/API | TBC after M4 |
| Feature-flag product | In-house control-plane vs third-party | TBC |
| Cross-campus student transfer | Manual export vs event bus | TBC / rare path |
| White-label domains | Per-campus hostnames vs path-based tenancy | Prefer hostnames (matches SDD-12 cookie rule) |