Governing AI Access to African Data: A Reference Architecture for Purview and Msharti
Microsoft solved data classification and loss prevention. It did not solve letting AI reach M-Pesa or KRA. How Purview and an access layer divide that work.
The division of labour this document describes. Microsoft has already solved data classification, loss prevention and policy authoring. Purview is a mature product with a large engineering organisation behind it, and we have no intention of rebuilding any of it. What Microsoft has not solved, and has no commercial reason to solve, is letting an assistant reach M-Pesa, KRA, Sage or a Kenyan core banking system at all. That is the problem Msharti exists for. This document is how the two meet: Purview decides what is sensitive and what the rules are, Msharti enforces those rules at the moment an assistant reaches for the data, and the evidence flows back into the Microsoft estate where your compliance team already works. We are building this integration now and publishing the design early, so architects can review it and tell us where it breaks.
Why this problem is worth an architecture
Kenyan banks, SACCOs and mid sized enterprises are in an awkward position with AI.
The tooling arrived first. Microsoft 365 Copilot is sitting in tenants across Nairobi. Staff have ChatGPT open in a second tab. Boards have asked for an AI strategy. What has not arrived is any way for those assistants to see the data that actually runs the business.
That data is not in SharePoint. It is in M-Pesa Daraja, in the KRA iTax and GavaConnect stack, in a core banking system that has been running since before the cloud, in Sage, in a SACCO management platform written for exactly one market. Microsoft has never built a connector for any of it, and has no commercial reason to.
So the question every CIO in this market is actually holding is narrower and harder than “should we adopt AI”:
How do we let AI read M-Pesa transactions, KRA filings and customer records, while keeping the data in Africa, keeping every access logged, and being able to hand a regulator a complete answer when they ask who saw what?
That is a governance problem before it is an AI problem. Which is why the interesting architecture sits between the two.
The cost of getting it wrong is asymmetric
Under the Data Protection Act 2019, the Data Commissioner can issue a penalty notice of up to KES 5 million, or for an undertaking up to 1% of annual turnover for the preceding financial year, whichever is lower. Verify the current wording of Section 63 before you quote it internally, because this is the kind of number that ends up on a board slide.
The fine is rarely the real exposure. The real exposure is that an institution cannot produce the records. Accountability under the Act is demonstrated with evidence, not intent. An organisation that cannot say who accessed customer data last Tuesday has a problem that predates AI and that AI makes considerably worse, because AI increases the number of people who can pull a broad slice of customer data from a single sentence of natural language.
Meanwhile the practical alternative is worse. Staff who cannot get AI access to business data do not stop using AI. They export to CSV and paste it into a consumer chat window, which produces the same data exposure with none of the logging.
Governed access is not a tax on speed here. It is the only version of this that a regulated institution can defend, and it is faster than the ban and enforce cycle that most compliance teams are currently attempting.
Part 1: What Microsoft Purview does, and the exact point where it stops
Purview is Microsoft’s data governance, security and compliance platform. It is genuinely good at three things.
| Capability | What it does | Where it operates |
|---|---|---|
| Classification | Scans data at rest, identifies sensitive content, applies sensitivity labels | Microsoft 365, Azure data services, some third party sources through connectors |
| Protection | Encryption, access restriction, data loss prevention policies, label based controls | Documents, mail, Teams messages, endpoints |
| Evidence | Unified audit log, retention policies, eDiscovery, Compliance Manager assessments | Anything Microsoft can see |
Purview also now covers AI specifically. Data Security Posture Management for AI gives visibility into how Copilot and connected assistants touch sensitive content, sensitivity labels are honoured in Copilot responses, and Copilot interactions land in the unified audit log.
For an organisation whose data lives in Microsoft 365 and Azure, that is a complete story.
The gap, stated precisely
Purview classifies data at rest, in stores it can scan.
AI access to your internal business systems is data in motion, through APIs it cannot see.
When a relationship manager asks an assistant for a customer’s M-Pesa collection history, no document is opened. Msharti calls the Daraja API, receives JSON, and returns a result. There is no file for Purview to have labelled, no SharePoint site to have scanned, no endpoint agent in the path. The most sensitive query in the building is invisible to the governance platform.
You can partially close this by staging that data into Azure SQL and pointing a Purview scan at it. The source material for this article proposed exactly that, and it works for classification and reporting. It does not solve the live problem, for three reasons.
- It governs a copy, not the access. The scan tells you the staged table contains customer PII. It does not sit between the assistant and Daraja at the moment of the query.
- It doubles your exposure. You have now created a second copy of customer data, in a second system, with its own access model and its own residency question. Under a data minimisation obligation that is a difficult thing to justify.
- It is always stale. A five minute old copy is fine for a compliance report and useless for a balance enquiry.
The label binding problem
There is a deeper mismatch, and it is the thing most integration attempts get wrong.
A Purview sensitivity label is a property of an object. It rides along with a file or a mail item, in the metadata, and increasingly in the encryption itself. That model assumes the thing being protected is a durable artefact.
An M-Pesa API response is not a durable artefact. It exists for the duration of a request.
So label inheritance in this architecture cannot mean “apply a label to the M-Pesa data”. It has to mean something different:
Bind the label to the tool and the field, not the object.
mpesa.get_statementis permanently classified. Every response it can ever produce inherits that classification at the moment of generation, and the enforcement happens in the access layer, before the bytes leave.
That reframing is what makes the rest of the architecture possible. Purview becomes the place where classification policy is authored and evidenced. Msharti becomes the place where it is enforced at the point of access. Neither tool is being asked to do something it was not built for.
What we are not building
A note on scope, because it decides whether the rest of this is worth your time.
Msharti does not build data loss prevention. We do not classify your documents, we do not maintain a sensitive information type library on your behalf, and we do not want to own your policy. Microsoft has spent years and a large engineering organisation on exactly that problem, the result is mature, and most regulated institutions in this market are already licensed for it. Rebuilding it would be a worse version of something you already own.
What we build is reach. An assistant that can call M-Pesa Daraja, KRA GavaConnect, Sage or a core banking system, with the caller resolved to a named person, the request scoped to what that person is allowed to see, and every call recorded.
Those are complementary, not competing.
| Owns | Answers | |
|---|---|---|
| Microsoft Purview | Classification, DLP policy, sensitive information types, the compliance record | What is sensitive here, and what are the rules? |
| Msharti | Connectivity, identity at the point of access, scope enforcement, response filtering, evidence | Can this person’s assistant reach this system, and what actually happened? |
An organisation with Purview and no access layer has excellent policy governing data its AI cannot reach. An organisation with an access layer and no Purview has reach, with nothing authoritative telling it what to protect. The configuration that works is both, which is the entire reason this integration is worth building.
Part 2: How the two platforms meet
AI ASSISTANTS
Copilot · Claude · ChatGPT · internal agents
│
│ MCP over Streamable HTTP, authenticated per user
▼
MSHARTI AI ACCESS LAYER ◄── enforcement point
├── Identity and RBAC (per tenant)
├── Policy engine (label bindings, field rules)
├── Connector registry (pre-built connectors and tools)
├── Response filter (redaction, secret stripping)
└── Audit emitter
│ │
connector calls evidence stream
▼ ▼
INTERNAL SYSTEMS MICROSOFT ESTATE
M-Pesa Daraja Purview ◄── policy authoring
KRA GavaConnect ├── sensitivity label taxonomy
Core banking ├── sensitive information types
Sage 300 ├── DLP policy definitions
SACCO core └── Compliance Manager
CRB Sentinel / Log Analytics
└── Msharti audit records
The load bearing idea: policy flows one way, evidence flows the other. Purview is upstream and authoritative for what the organisation considers sensitive. Msharti is downstream and authoritative for what actually happened. Neither becomes a copy of the other.
Integration point 1: Classification sync
Who does what. Purview owns the taxonomy. Msharti reads it and binds it to connector tools, so the classification your compliance team authored once is the classification enforced at the point of access.
The problem. Your compliance team has spent weeks defining a sensitivity taxonomy in Purview. Msharti knows nothing about it, so connector access rules get defined a second time, by a different team, and the two drift within a quarter.
The approach. Msharti reads the label taxonomy from Microsoft Graph and holds a local binding table that maps labels onto connector tools and specific response fields. Compliance authors once, in the tool they already own.
What the binding table looks like:
| Connector tool | Bound label | Field level rules | Roles permitted |
|---|---|---|---|
mpesa.get_statement | Confidential, Customer Financial | msisdn masked to last 3, account_ref redacted | finance, reconciliation |
mpesa.get_transaction | Confidential, Customer Financial | msisdn masked to last 3 | finance |
kra.verify_pin | Internal, Counterparty | none | finance, procurement, compliance |
kra.check_tcc | Internal, Counterparty | none | finance, procurement, compliance |
corebanking.get_account | Highly Confidential, Customer Banking | account_number masked to last 4, national_id redacted | relationship manager (own book only) |
crb.get_score | Highly Confidential, Credit | full record to credit only, band only to others | credit, risk |
Note what the field rules do. A relationship manager asking about their customer gets a genuinely useful answer with the account number masked. They were never going to read the digits aloud anyway. The assistant loses nothing operationally, and the response that lands in the model context and in the log contains materially less regulated data. This is data minimisation implemented as an engineering default rather than a policy document.
Honest note on the Microsoft side. The sensitivity label taxonomy is readable through Microsoft Graph, but these routes have moved between beta and v1.0 more than once and the required permission scopes have changed with them. Confirm the current path and scope against Microsoft’s reference before you build. Applying labels programmatically to Microsoft objects is a separate matter and goes through the Microsoft Information Protection SDK, not a simple REST call.
# Classification sync. Reads the taxonomy, does not write to Microsoft.
# Run on a schedule; the taxonomy changes rarely, so hourly is generous.
import httpx
from msal import ConfidentialClientApplication
GRAPH = "https://graph.microsoft.com"
class LabelTaxonomySync:
def __init__(self, tenant_id: str, client_id: str, client_secret: str):
self._app = ConfidentialClientApplication(
client_id=client_id,
client_credential=client_secret,
authority=f"https://login.microsoftonline.com/{tenant_id}",
)
def _token(self) -> str:
result = self._app.acquire_token_for_client(
scopes=[f"{GRAPH}/.default"]
)
if "access_token" not in result:
raise RuntimeError(
f"token acquisition failed: {result.get('error_description')}"
)
return result["access_token"]
def fetch_labels(self) -> list[dict]:
# Confirm this path against current Graph reference before building.
# It has moved between beta and v1.0.
url = f"{GRAPH}/beta/security/informationProtection/sensitivityLabels"
with httpx.Client(timeout=30) as client:
response = client.get(
url, headers={"Authorization": f"Bearer {self._token()}"}
)
response.raise_for_status()
return response.json().get("value", [])
def reconcile(self, bindings: dict[str, str]) -> list[str]:
"""Return tools whose bound label no longer exists in Purview.
Drift is the failure mode that matters here. A label gets renamed
or retired in Purview, the binding silently points at nothing, and
the tool quietly loses its enforcement. Fail loud instead.
"""
live_ids = {label["id"] for label in self.fetch_labels()}
return [
tool for tool, label_id in bindings.items()
if label_id not in live_ids
]
That reconcile method is the part worth copying. Integrations do not usually fail loudly. They fail by drifting.
Integration point 2: Policy enforcement on AI queries
Who does what. Purview authors the policy. Msharti enforces it at the moment an assistant reaches for the data, in process and in region, with no dependency on a service being reachable mid query.
Correcting a common assumption. Purview does not expose a synchronous API where a third party system can submit an arbitrary query and receive an allow, block or redact decision. DLP evaluation runs inside Microsoft workloads on Microsoft content. If you design an architecture that depends on calling out to Purview on every AI query, it will not survive first contact with the documentation, and it would be the wrong design anyway. A network round trip out of region on every tool call, sitting in the latency path of an interactive assistant, with an availability dependency on a service you do not control.
The approach that works. Msharti evaluates locally against policy that was authored in Purview. Policy definitions synchronise on a slow loop. Decisions happen in process, in microseconds, with no external dependency.
The decision sequence, in order. Order matters, because each step narrows what the next one can see.
- Authenticate. Resolve the human. Service accounts shared across a department are the fastest way to make an audit trail worthless.
- Authorise the tool. Does this role hold this tool at all? Cheapest possible rejection.
- Authorise the scope. A relationship manager holds
corebanking.get_account, but only for their own book. Scope is enforced by injecting a constraint into the connector call, never by filtering after the fact. Filtering after the fact means the data was already retrieved, which means it was already exposed. - Inspect the request. Prompt injection screening on anything reaching a connector that can write.
- Execute.
- Filter the response. Apply field rules from the binding table before the payload returns.
- Emit evidence. Always, including on denial. Denials are the most interesting records in the log.
from dataclasses import dataclass
@dataclass(frozen=True)
class Decision:
allowed: bool
reason: str
field_rules: dict[str, str] # field name -> redact | mask_last_3 | mask_last_4
scope_filter: dict[str, str] # constraint injected into the connector call
class PolicyEngine:
def __init__(self, bindings, label_rules, scope_resolver):
self._bindings = bindings
self._label_rules = label_rules
self._scope = scope_resolver
def evaluate(self, tool: str, principal) -> Decision:
label_id = self._bindings.get(tool)
if label_id is None:
# Unbound tool. Deny. An unclassified tool is a gap in the
# taxonomy, not a tool that happens to be safe.
return Decision(False, "tool_not_classified", {}, {})
rule = self._label_rules[label_id]
if principal.role not in rule.permitted_roles:
return Decision(False, f"role_denied:{label_id}", {}, {})
return Decision(
allowed=True,
reason=f"permitted:{label_id}",
field_rules=rule.field_rules,
# Scope becomes a query constraint, applied before retrieval.
scope_filter=self._scope.constrain(tool, principal),
)
The default deny on an unclassified tool is deliberate and it will annoy people during rollout. Keep it. A new connector that nobody has classified yet is not a connector that is safe by default.
Integration point 3: Evidence into the Microsoft estate
Who does what. Msharti produces the evidence, because only the access layer sees the query. The Microsoft estate is where that evidence is read, alongside the Microsoft 365 audit data your compliance team already works in.
Correcting a second common assumption. There is no public Purview endpoint that accepts arbitrary audit records from a third party application. The unified audit log is populated by Microsoft workloads. Reading it out is well supported through the Office 365 Management Activity API. Writing into it is not a thing you can do.
The supported path. Send Msharti audit records to Azure Monitor Logs via the Logs Ingestion API, into a custom table in a Log Analytics workspace, through a Data Collection Endpoint and a Data Collection Rule. That workspace is where Microsoft Sentinel lives. Compliance and security teams then query Msharti AI access records alongside Microsoft 365 audit data in one language, and Purview Compliance Manager references the control as evidence.
This is a real, documented, supported ingestion path. It is also the answer most Microsoft aligned security teams would have given you, which is a good sign.
Record shape. Design this once and carefully, because a year from now an auditor will ask a question you did not anticipate and the only thing that will save you is having captured the right fields from the start.
| Field | Purpose |
|---|---|
event_time_utc | Ordering and retention windows |
tenant_id | Isolation boundary |
principal_upn | The human, resolved. Not a service account |
principal_role | Role at time of query, not current role |
assistant | Copilot, Claude, internal agent |
tool | Connector tool invoked |
data_sources | Systems actually touched |
sensitivity_label | Label bound at time of call |
decision | allow, deny, allow_with_redaction |
decision_reason | Machine readable policy identifier |
fields_redacted | Which fields were removed or masked |
scope_filter | The constraint that was applied |
subject_ref | Pseudonymous customer reference, never raw PII |
record_count | Volume returned. Your best bulk exfiltration signal |
query_hash | SHA 256 of query text |
latency_ms | Operational health |
Two of those deserve comment.
subject_ref must be pseudonymous. The instinct is to log the National ID so you can answer “who accessed this customer’s data”. Do not. You would be creating a permanent, widely readable, high volume store of exactly the identifiers you are trying to protect, and you would be doing it inside the compliance control. Log a stable pseudonymous reference and hold the mapping separately under tighter access.
query_hash, not query text. Users put PII in prompts. Store a hash for correlation and deduplication, and keep the plain text out of a log that a wide group can read.
import json, httpx
from datetime import datetime, timezone
class SentinelExporter:
"""Batched export to Azure Monitor Logs Ingestion API.
Buffer, batch, retry with backoff, and never drop silently. An audit
pipeline that loses records without telling you is worse than no
audit pipeline, because you will believe it.
"""
def __init__(self, dce_endpoint, rule_id, stream_name, credential):
self._url = (
f"{dce_endpoint}/dataCollectionRules/{rule_id}"
f"/streams/{stream_name}?api-version=2023-01-01"
)
self._credential = credential
self._buffer: list[dict] = []
def record(self, event: dict) -> None:
event["event_time_utc"] = datetime.now(timezone.utc).isoformat()
self._buffer.append(event)
if len(self._buffer) >= 500:
self.flush()
def flush(self) -> None:
if not self._buffer:
return
token = self._credential.get_token(
"https://monitor.azure.com/.default"
).token
batch, self._buffer = self._buffer, []
try:
with httpx.Client(timeout=30) as client:
response = client.post(
self._url,
content=json.dumps(batch),
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
)
response.raise_for_status()
except Exception:
# Return records to the front of the buffer and let the caller
# raise an operational alert. Never swallow.
self._buffer = batch + self._buffer
raise
Retention. Set it against the longest obligation touching the data, not the shortest. For a SACCO that is SASRA. For a bank, CBK and the Data Protection Act together. Confirm the current retention periods with your compliance counsel rather than taking a number from an article, including this one.
Integration point 4: Residency, enforced structurally
Who does what. Deployment is ours, tenant geo is yours, and neither is something Purview can enforce for you. Residency has to be a property of where things run, not a setting inside a governance product.
Correcting a third assumption. The source architecture for this article validated residency by checking whether a URL string contained the word “southafrica”. That is not a control. It is a comment with extra steps. Any misconfiguration, any redirect, any new endpoint with a different naming convention passes straight through it.
Residency is a deployment property, not a runtime check. You get it from where things run and what they are permitted to reach, enforced below the application:
- Compute in region. Msharti runs in our own cloud environment, or on premises for institutions whose regulator or internal policy requires it.
- Egress allowlisting at the network boundary. The connector subnet reaches Daraja, GavaConnect, the core banking endpoint and nothing else. A connector that develops an unexpected outbound habit fails at the security group, not at a string comparison in Python.
- Microsoft 365 tenant geo. Compliance and audit data location follows tenant geo. Confirm current Purview and Microsoft Purview service availability for your chosen geo directly with Microsoft, because regional availability for individual Purview capabilities is uneven and changes.
- Every subprocessor listed and located. Including the ones nobody thinks of: transactional mail, error tracking, product analytics, and, in a RAG architecture, the embedding model.
That last one is where most African residency stories quietly fail, and it is the subject of the companion article.
A note on honesty in procurement. If any element of your stack sits outside the continent, say so in the architecture document and handle it with a transfer safeguard. A bank’s security architect will find it during due diligence. Finding it themselves, after you told them everything stays in Africa, costs you the deal. Finding it in your own documentation, already addressed, costs you nothing.
Part 3: Getting Kenyan sensitive information types right
This section exists because the detection patterns in general circulation for this market are wrong, including the ones in the source material for this article. Wrong patterns produce a DLP deployment that misses real PII while burying the compliance team in false positives, and the usual outcome is that somebody turns the policy off.
Purview custom sensitive information types support a primary pattern, supporting keyword evidence within a character window, and confidence levels. Use all three. A bare regex is not enough for any of these.
KRA PIN
Format is one letter, nine digits, one letter. Individuals begin with A, entities with P.
Primary pattern: \b[AP]\d{9}[A-Z]\b
Supporting terms: KRA PIN, PIN No, PIN Number, Tax PIN, iTax, Personal Identification Number
Window: 300 characters
Confidence: 85 with supporting term, 65 without
The commonly circulated [A-Z]\d{6}[A-Z] is six digits and matches nothing real.
Kenyan National ID
Seven or eight digits, no letter. The digits alone carry almost no signal, so this one is entirely dependent on keyword evidence.
Primary pattern: \b\d{7,8}\b
Supporting terms: ID No, ID Number, National ID, Identity Card, Kitambulisho, Huduma
Window: 300 characters
Confidence: 75 with supporting term. Do not publish a rule without one.
Deploying the bare eight digit pattern will match invoice numbers, amounts, dates, phone fragments and reference codes across your entire estate. This is the single most common cause of a failed Kenyan DLP rollout.
M-Pesa transaction code
Ten character uppercase alphanumeric, typically opening with letters. QK73HG5TX9 is the shape.
Primary pattern: \b[A-Z]{2,3}[A-Z0-9]{7,8}\b
Supporting terms: M-PESA, MPESA, Confirmed, Paybill, Till, Ksh, KES
Window: 200 characters
Confidence: 80 with supporting term, 40 without
There is no fixed prefix. Any rule keyed to one specific three letter opener detects a small and arbitrary slice of real traffic.
Kenyan mobile number
Both the 07 and 01 ranges are live. Rules written before the 01 range opened miss a growing share of numbers.
Primary pattern: \b(?:\+?254|0)(?:7|1)\d{8}\b
Supporting terms: phone, mobile, simu, contact, M-PESA
Confidence: 75
Test before you enforce
Run every one of these in simulation mode for a minimum of two weeks against real traffic before enforcement. Measure both directions. Count what the rule catches, and separately sample what it missed by searching known holdings. A rule with 4,000 daily hits, of which 30 are real, will be disabled within a month regardless of how correct it is.
Part 4: Three worked scenarios
These are illustrative models, not case studies. No customer is described. Each is built from stated assumptions so you can substitute your own.
Scenario A: A Kenyan bank deploys an AI assistant to relationship managers
What it must do. Answer questions about balances, transaction history, loan status and product fit, across core banking, M-Pesa and CRB.
Walk through one query, end to end.
A relationship manager types: “Give me the full picture on Acme Traders before my 2pm.”
- Identity resolves to the individual at the access layer. Their role is
relationship_manager. Their book is a list of customer identifiers attached to the principal. - Tool authorisation. The query plan requires
corebanking.get_account,corebanking.list_transactions,mpesa.get_statementandcrb.get_score. The first three are permitted for the role.crb.get_scoreis bound to Highly Confidential, Credit, permitted to credit and risk only. It is denied, and the denial is logged. - Scope constraint. Acme Traders is checked against the principal’s book before any call is made. If it is not on their book, nothing is retrieved. The distinction between refusing to retrieve and retrieving then filtering is the entire difference between a control and a liability.
- Execution against three connectors in parallel.
- Response filtering. Account numbers masked to last four. Directors’ National IDs redacted entirely. Counterparty phone numbers on M-Pesa entries masked to last three.
- Assembly. The assistant returns balances, a twelve month collection trend, loan performance and a product suggestion, with sources cited.
- Evidence. Seven records land in Sentinel: four allows, one deny with reason
role_denied, and the scope constraint that was applied.
What the RM sees: a genuinely useful brief in about thirty seconds.
What the compliance officer sees, three months later, when asked who accessed Acme Traders’ records in July: a complete answer, including the credit score request that was refused, which is the record that demonstrates the control was live rather than merely documented.
Where this scenario is hard. Core banking systems in this market frequently have no usable API. Budget for a read only integration layer, and expect that to be the critical path item rather than anything on the AI side.
Scenario B: A SACCO automates loan assessment under SASRA
What it must do. Pull member savings history, CRB standing, M-Pesa income evidence and guarantor status into an assessment pack, with a recommendation and a human decision.
The control that matters most here is not access. It is explainability.
SASRA supervision is interested in whether lending decisions are consistent, documented and defensible. An AI that produces a recommendation without a reconstructable basis creates a supervisory problem that is worse than the manual process it replaced.
So the binding table for this deployment carries an extra obligation: every tool call that contributed to a recommendation is recorded with its inputs, outputs and the policy decision, and the recommendation record references those call identifiers. When a member disputes a rejection, or a supervisor samples a file, the entire basis reconstructs from the log.
A correction on employment verification. The source material proposed checking employment through NHIF. NHIF was replaced by the Social Health Authority under SHIF in 2024, and neither exposes an employment verification API to third parties. Verifying employment against a health insurance scheme would in any case involve processing health related data for a lending decision, which is a category of processing that requires its own justification under the Act. Do not build this. Use payslip documents supplied by the member, or employer confirmation, and record the source.
What stays human. The decision. The model assembles evidence and proposes; a loan officer approves or rejects. This is not caution for its own sake. It is the design that keeps the accountability chain intact and the process defensible.
Scenario C: A mid sized enterprise gives finance an assistant
What it must do. Reconcile M-Pesa collections against Sage invoices, validate supplier KRA PINs and TCC status, flag exceptions.
This is the easiest of the three and the best place to start. The data is commercial rather than personal for the most part, the regulatory surface is narrower, the connectors are ready made, and the outcome is measurable within a single monthly close.
The one genuine risk. Supplier records contain KRA PINs, and KRA PINs are personal data when the supplier is a sole trader, which in this market is a large share of the supplier base. The binding table should treat kra.verify_pin results as Internal rather than Public, and the reconciliation output should not be dropped into a shared drive that the whole company can read. This is the failure mode: the controls hold perfectly at the access layer and then somebody exports the result to Excel and mails it around.
Which is a reminder that this architecture governs AI access to data. It does not govern what a human does with the answer. That still needs Purview DLP on the endpoint and in mail, doing the job it was actually built for.
Part 5: Compliance mapping, with the caveats attached
Read this as a map of which control addresses which obligation. It is not a compliance certification, and no vendor architecture can deliver one on your behalf. Compliance is a property of your organisation, assessed against your processing, by your regulator.
Data Protection Act 2019 and ODPC
| Obligation | Purview contributes | Msharti contributes |
|---|---|---|
| Lawful basis and purpose limitation | Label taxonomy encodes intended use | Role to tool bindings restrict access to stated purpose |
| Data minimisation | Sensitive information types identify what is regulated | Field level redaction and scope constraints return less by default |
| Integrity and confidentiality | Encryption, DLP, endpoint controls | Encryption in transit and at rest, secret stripping, injection screening |
| Accountability and records | Unified audit log, Compliance Manager | Query level evidence with user attribution, exported to Sentinel |
| Cross border transfer | Tenant geo controls | Deployment in our cloud or on premises, egress allowlisting |
| Data subject rights | eDiscovery across Microsoft content | Access records retrievable by pseudonymous subject reference |
Two things this table cannot do for you. It cannot register you with the ODPC as a data controller or processor, which is a statutory obligation independent of any technology you deploy. And it cannot produce your Data Protection Impact Assessment, which for an AI system processing customer data you should expect to need.
CBK, SASRA and KRA
The pattern repeats: labels and policies author the intent, the access layer enforces it, the evidence stream demonstrates it. What differs between the three is retention duration and the specific supervisory questions you must be able to answer.
Set retention deliberately rather than by default. Ask your compliance counsel for the record retention obligation attaching to each data category under each applicable instrument, take the longest of them, apply it to the Log Analytics table, and document the mapping so the decision is defensible later.
One practical warning. Retention periods circulate widely in vendor material, usually as a confident single number per regulator, and they are frequently wrong or out of date. A retention policy is a legal position, not a configuration default. Get it from counsel, write down where it came from, and review it annually.
On third party risk. Any regulated institution will run vendor due diligence covering security certification, financial standing, business continuity and subprocessor disclosure. Ask for current certification status in writing from any AI infrastructure vendor, including us, and ask what is on the roadmap and when. A vendor who answers that question precisely is telling you something useful about how they operate.
Part 6: A six week implementation
This assumes an existing Microsoft 365 tenant with Purview licensing, and a decision already taken on deployment region.
Weeks 1 and 2: Classify
| Task | Owner | Done when |
|---|---|---|
| Data inventory covering internal systems, not just Microsoft | IT and compliance | Every system holding personal or financial data is listed with an owner |
| Sensitivity taxonomy, five to seven labels | Compliance | Published, and a person can be shown a record and place it correctly |
| Kenyan sensitive information types built and tuned | Compliance and security | Simulation running, false positive rate measured, not assumed |
| Deployment region confirmed with Microsoft | Cloud team | Written confirmation of capability availability in the chosen geo |
Resist expanding the taxonomy. Five labels people apply correctly beat fifteen that get guessed.
Weeks 3 and 4: Connect
| Task | Owner | Done when |
|---|---|---|
| Msharti control plane deployed | DevOps | Live in our cloud or on premises, monitored, backed up |
| Three to five connectors configured | Integration | Authenticated, tested against real credentials, rate limits understood |
| Roles defined and mapped to real job functions | Security and business | Every role traces to an actual role in the organisation chart |
| Binding table populated | Security and compliance | Every enabled tool has a label. No exceptions |
| Audit emission running | Security | Records reaching the buffer with the full field set |
The binding table is the deliverable that matters. If it is incomplete at the end of week four, the timeline slips. Do not go live with unclassified tools.
Week 5: Integrate
| Task | Owner | Done when |
|---|---|---|
| Taxonomy sync with drift detection | Engineering | Renaming a label in Purview raises an alert within the hour |
| Policy enforcement live | Engineering | Denials occur and are correct |
| Sentinel export live | Engineering | Records queryable in Log Analytics, retention set |
| Egress allowlisting | Network | Connector subnet reaches permitted endpoints only, verified by attempting others |
Week 6: Prove it
This week is adversarial. The point is to try to break your own controls before somebody else does.
| Test | Expected result |
|---|---|
| Unauthenticated call to a connector | Rejected, logged |
| Authenticated user, tool outside their role | Denied with reason, logged |
| Authenticated user, permitted tool, record outside their scope | No retrieval occurs, denial logged |
| Query engineered to elicit a National ID | Field redacted in response, redaction recorded |
| Bulk extraction attempt, high record count | Completes but flagged by volume, alert raised |
| Prompt injection through a document into a write capable tool | Blocked, logged |
| Retention check on the Sentinel table | Matches documented obligation |
| Full reconstruction: pick one customer, produce every AI access to their data in the period | Complete answer in under ten minutes |
That last test is the one that matters. It is the question a regulator actually asks, and until you have answered it against your own live system you do not know whether the architecture works.
What this architecture does not solve
An honest closing, because the gaps determine whether this is right for you.
It does not govern what happens after the answer. Once a person has the response, it lives on their screen and can go anywhere from there. Endpoint DLP and mail DLP still matter, and that remains Purview’s job rather than ours. This is precisely why the two belong together: we control what the assistant can reach and record what it did, Purview controls what the human does with the result afterwards.
It does not remove the need to register with the ODPC, appoint a data protection officer where required, or conduct impact assessments.
It does not make a model correct. Governed access to accurate data still produces confident wrong answers. Every deployment described here keeps a human on consequential decisions for that reason.
It adds latency. Policy evaluation is microseconds and irrelevant. Connector calls to Daraja and CRB are hundreds of milliseconds and very relevant. Design for it.
It creates a new concentration of authority. A system that can reach M-Pesa, core banking and CRB through one endpoint is a high value target. It requires the operational security you would give any privileged access system, including break glass procedures and independent monitoring of the monitor.
Core banking integration is usually the hard part. Not the AI. Not the governance. The twenty year old system with no API and a vendor who charges by the question.
Purview handles classification, policy and the compliance record. Msharti handles reach, enforcement at the point of access, and the evidence of what happened. If your organisation already runs Purview and your assistants still cannot see the systems that matter, that is the gap this closes. Talk to us.
See it running against your own systems.
Book a 20-minute demo. We'll connect one of your systems live.
Talk to Us