Capstone · final report, Nov 2026 Working system · pre-registered results Invite-only access

AegisVeritas

Two bidders. One owner.

AegisVeritas joins Kenya's published contract awards to the ownership and contact details firms disclose, and flags supplier pairs that are likely under common control but that a name check cannot see. At bid opening it checks one tender's bidders in under a second. Every flag shows its evidence, and a person makes every decision.

Role
Sole engineer and researcher
Context
BSc Software Engineering (ML) capstone, African Leadership University
Supervisor
Kevin Sebineza
Stack
FastAPI, PostgreSQL, Neo4j, LightGBM, Next.js
evidence card · real records, names masked
Evidence card for one supplier pair, Firm 5SDZX and Firm I2VPJ: likelihood of common control above 99 percent, what to check next, the evidence by kind and the verdict form
AegisVeritas home screen on a phone
The evidence card for one supplier pair: what links them, how strong each kind of evidence proved in testing, and what to check next. Real records, firm names masked as stable codes.
0.146vs 0.067
Average precision on 7,884 held-back Kenyan pairs, against the Adamic-Adar baseline.
Pre-registered · passed
0.85vs 0.57
AUC on Estonian firms the model never saw, checked against Estonia's open ownership register.
Pre-registered · passed
25of 100
Real links among the top 100 flags. Picking pairs at random finds about 2.
Kenyan holdout
210×
More often, owner-linked pairs share a P.O. Box in road-authority registers the model never saw. p = 1.0e-42.
Corroborated · post hoc
01 · Problem

What the committee can't see

A debarred company rarely bids under its own name. It bids through a clean-looking company that shares a director, owner, phone number or address with it.

A name check passes that company, because there is no name to match. Two "competing" bidders can also belong to the same person, so the committee compares them, picks the cheaper one and signs, and the competition was never real. Public procurement is about 60 per cent of Kenya's government budget. Since 2022 the owners of firms awarded tenders have been public, but nobody checks them against the bidders, and an evaluation committee sees one tender at a time.

Six names, one group
Rockey Africa LimitedAerospace AviationEva-Top AgenciesSony Commercial AgenciesBeta Trading CompanyMadujey Global Services

Debarred together by the African Development Bank in 2021 for fraud in tenders on a Kenyan education project. On a tender table, nothing connects the names. The affiliates were found by tracing common officers.

Source: African Development Bank press release, debarments effective 5 August 2021
The rule exists. Nobody can check it.
57%

of 2024/25 contracts went to firms that declared no owner: 14,819 of 25,994. When ownership is declared, it sits in records no committee can search across the whole market in the time it has.

Source: PPRA annual report, reported by Business Daily, 5 August 2026
02 · System

What I built

A full system, not a notebook. It joins 144,097 published contract records with 12,441 declared ownership links, scores supplier pairs, and serves an investigator-facing web app. It runs on one laptop with Docker Compose, and invited evaluators reach it through Cloudflare Tunnel and Access.

Sources
Procurement recordsKenya's PPIP portal, PPRA award files, OCDS and BODS
Adverse listsWorld Bank, AfDB, OpenSanctions, ICIJ, EACC gazette
Data
Ingestion and entity resolutionOne-command pipeline; checksums and code commit recorded per run
PostgreSQL + Neo4jRelational store and the ownership graph
Intelligence
Evidence engineScores supplier pairs; trains, gates and publishes on a schedule
Risk engineList match, then shortest path to a listed party, then model or rules
Access
FastAPIJWT, roles, row-level security, hash-chained audit log
Next.js app and e-GP checkReview queue, evidence cards, pre-award tender check
01

Bidder screening

Checks a supplier against sanctions and debarment lists, then follows owners and directors two steps out to find exposure a name check misses.

02

Review queue and evidence cards

A short list of supplier pairs for an investigator, each with its evidence, how strong that kind of evidence has proven, and what to check next.

03

Pre-award tender check

Takes the bidders on one tender and reports any links between them: median 0.07 seconds, the way Kenya's e-GP system would call it at bid opening.

04

Ownership path tracing

Draws the path from a supplier to a listed party on an investigation canvas. The one practitioner tester rated it the most helpful part.

05

Tamper-evident audit trail

Every screening, verdict and tender check is appended to a SHA-256 hash chain. The app's own database account can add entries but cannot change or delete them.

06

Role-based access

Only investigator, auditor and admin roles see named pairs. Each institution sees only its own data, enforced by PostgreSQL row-level security.

03 · Method

My first approach failed

The proposal asked "is this one firm bad?" After the data was cleaned, only 18 confirmed Kenyan cases remained, and 11 of them had fewer than three recorded links.

A graph attention network had reported 91 per cent recall on a synthetic benchmark; once label leakage was removed, it fell to between 24 and 48 per cent, and on real Kenyan data no graph model beat a baseline that only looked at company size. So I changed the question to "are these two firms under one control?" For that question, the ownership details Kenyan awards must disclose give 12,441 labelled links. Kenya has no open ownership register to test hidden links against, so I also validated the method in Estonia, where both procurement data and beneficial ownership are public.

Three kinds of evidence, one probability

Candidate pairs are firms that win from the same buyer within 30 days, or that share a disclosed record. Each pair gets evidence from three channels, each a log-likelihood ratio, combined Fellegi-Sunter style so every part of the score stays explainable. A contact shared by many firms counts for less, and the behaviour model is capped so it can never outweigh an ID match.

How much each clue multiplies the odds that two firms share an owner

Starting weights in the evidence engine, log scale. They are replaced by weights learned from investigators' verdicts.

Learned behaviour model (LightGBM)Record match
Show as table
EvidenceOdds multiplierLog-likelihood ratio
Same owner, matched by ID number×1,0006.9
Same director, matched by ID number×2005.3
Behaves like one company (behaviour model, cap)up to ×55±4.0
Same phone or email×503.9
Same address or P.O. Box×102.3
Owner with the same name only×51.6
Behaviour model

LightGBM on 20 award-pattern features, trained on Kenyan and Estonian pairs, with isotonic calibration. Calibration error 0.010. The strongest features: tenders won together, and the shortest gap in days between two firms' awards.

Knows when to abstain

Silent on pairs where a firm has fewer than 3 awards, and on pairs unlike its training data: 44 per cent of held-back pairs. Refusing to judge is a result, not a failure.

Gated releases

A retrained model goes live only if it beats both the baseline and the current live model on a fixed holdout. The rule is in the code, and it caught a weaker model running live.

04 · Results

Results

Tests marked pre-registered had their protocol and pass mark committed to GitHub before the run, so the rules could not move after the results were seen. The baseline throughout is Adamic-Adar, the standard link-prediction score.

Pre-registered · passed

Estonia: firms the model never saw

AUC, with true owners hidden from the model and answers checked against Estonia's open register. 0.5 is a coin flip.

AegisVeritasLightGBM
0.85
Adamic-Adarbaseline
0.57
Show as table
MethodAUC
AegisVeritas (LightGBM)0.85
Adamic-Adar0.57
Pre-registered test estonia-v1
Pre-registered · passed

Estonia: the whole system

Share of all register-linked pairs found in the top 1,000, before and after adding register candidates to behaviour.

Recallearlier system
4%
Recallfull system
32%
Precisionearlier system
6%
Precisionfull system
51%
Full systemEarlier system
Show as table
Top 1,000EarlierFull system
Recall4%32%
Precision6%51%
Pre-registered test system-v1
Pre-registered · passed

Kenya: held-back supplier pairs

Average precision on 7,884 pairs the model never trained on, only 178 truly linked. Reproduced on a full retrain.

AegisVeritaslive model
0.146
Adamic-Adarbaseline
0.067
Randombase rate
0.023
Show as table
MethodAverage precision
AegisVeritas, Estonia + Kenya pooled0.146
Kenya only0.144
Adamic-Adar0.067
Estonia only (failed)0.065
Random (base rate)0.023
Pre-registered test kenya-transfer-v1; calibration error 0.010
Kenyan holdout

What a reviewer gets

Each square is one supplier pair. Filled squares are real links.

AegisVeritas top 100
25 linked

100 random pairs
about 2 linked

Precision at 100 = 0.25, about 11 times the 2.3% base rate (178 of 7,884)
Final model · final code, Nov 2026

Linked pairs found as the review queue grows

The model that runs in the app, on 7,783 held-back pairs it never saw (178 linked), against Adamic-Adar. Queue size on a log scale.

0.141average precision (95% CI 0.106 to 0.193), against 0.066
0.820ROC AUC, against 0.649 for Adamic-Adar
0.146 ± 0.007average precision across five random seeds
+0.076gap over Adamic-Adar (95% CI 0.034 to 0.126), above zero in every resample
Show as table
Top k pairsLinked found, modelPrecision, modelLinked found, Adamic-Adar
1040.402
50150.307
100250.2510
200380.1920
500680.1437
1,000960.1072
Final model evaluation, November 2026; calibration error 0.010, Brier score 0.0213
Corroborated · not pre-registered

A register the model never saw

KeRRA, the rural roads authority, publishes contractor registers that list each firm's P.O. Box. The model flags pairs from ownership records only; the registers were never part of its data. This check was run after the detector was built.

Owner-linked pairs30 of 253
11.86%
Same-buyer controls5 of 8,852
0.056%

209.9 times more often than same-buyer controls, and at least 63.8 times at the cautious end of the 95% interval (Fisher's exact test, p = 1.0e-42). Against random pairs from the same region: 118.6 times, at least 23.2 times (p = 2.7e-27). A shared box shows a relationship, not wrongdoing, and the p-values assume independent pairs, so they are indicative.

Source: KeRRA contractor registers (22 files, 20 regions); verification run of 28 October 2026
What the Kenyan result shows

The Kenyan labels come from disclosed ownership, so the Kenyan result shows that award behaviour recovers links that were disclosed. Estonia's official register is what supports the claim for hidden links. There, 68 per cent of linked pairs had won the same lot together; without those features the model still reached 0.83, but on pairs that never won together it fell to 0.66 against 0.56.

A lead from the watchlists

Screening the Kenyan records against the final watchlists found one company under World Bank cross-debarment since July 2013 that won two published Kenyan contracts, in 2019 and 2020. A development bank debarment does not by itself bar a firm from Kenyan tenders, so this is a lead for due diligence, not a finding that any rule was broken.

05 · Objectives

Every objective, against its target

The proposal set a target for each objective before the work began. This is how each one ended.

Met

Review existing mechanisms

Existing checks rely on names and on firms declaring their own owners, so they miss nominees, relatives and affiliates.

Partly met

Build the graph

144,097 contract records and 12,441 declared ownership links joined. Entity resolution precision on a hand-checked sample was not measured.

Not met · replaced

Graph representation learning

No graph model beat the size-only baseline on real data. The replacement pair model reached 0.141 against 0.066, over five seeds.

Met · four days late

Web application

Every feature built and verified by tests: 687 of 701 tests passed, none failed, and no system check failed.

Not met

Practitioner evaluation

The target was 12 to 15 practitioner interviews; one practitioner took part. Ease of use was then tested with ten general users.

Met

Conditions for success

It works where firms leave traces in public records, and it abstains on 44 per cent of pairs, mostly firms with fewer than three awards.

06 · What failed

Reported in full

Failed and withdrawn results sit in the results ledger next to the ones that passed. They are the reason the passing results are worth believing.

91% → 24%

A benchmark that leaked its labels

A graph attention network's recall on a synthetic benchmark fell from 91 per cent to between 24 and 48 per cent once the leakage was removed. On real Kenyan data, no graph model beat company size alone.

Changed the question to supplier pairs, where 12,441 disclosed links give labels.
0.065

A model trained on Estonia alone failed in Kenya

On the Kenyan holdout it scored 0.065, no better than the 0.067 baseline.

Markets differ. Each country needs its own labels, so the live model learns from Kenyan data.
2×

Two outside tests missed their pass marks

The pre-registered KeRRA tests on firms with no disclosed link found no signal at their primary endpoint, and a back-test on later ownership disclosures was underpowered at every cut-off.

The claim for firms that declare nothing rests on Estonia only, and the page says so.
0.0745

My own safety gate caught a weaker model running live

The model then running live used an importance weighting that was never tested, and scored 0.0745 on the held-back test.

Replaced by the tested model, which scores 0.146.
9 Nov

A defect found in the last week

Testing showed the tender check matching two invented bidder names to the same real supplier, then reporting no links.

Fixed: only exact names or IDs now match, with three new tests.
0.87

A result I withdrew

An Estonia-to-Kenya AUC of 0.87 turned out to have read the synthetic sample market, not real data.

Withdrawn and replaced by the real-data runs above.
242× → 210×

Every number rerun after submission

The submitted figures came from a run on an older database that still held duplicate company records. After a clean rebuild, concealed pairs went from 3,976 to 3,916 and the P.O. Box ratio from 242 to 210 times. The submission is tagged in git, and every run is kept.

The conclusion is unchanged; this page reports the rerun.
07 · Engineering

Built to be trusted

Because it names firms, the system has to be correct, explainable and hard to misuse. Every test had its pass mark fixed before it ran, on the final code between 7 and 10 November 2026.

687/701
backend tests passed, none failed; 14 skipped need a live database. 81 to 97% coverage in the engine, screening and service code
80/80
browser tests across Chromium, Firefox and WebKit, on desktop and phone
67/69
system checks passed on real data, none failed; the 2 warnings were the role rule refusing an analyst, as designed
100
Lighthouse accessibility score on all 8 pages tested, after three fixes
0.07 s
median tender check, 0.10 s at the 95th percentile; the pass mark was 1 second
32/32
unusual and invalid inputs answered as expected, with no server error
613/613
audit-chain entries verified intact after every test had run
0
failed requests out of about 56,000 under load, up to 1,500 clients at once
Breaking point found

Review queue under rising load

95th-percentile response time with 100 to 1,500 clients calling at once for 30 seconds each, on one laptop that also ran the load generator. Throughput levels off at about 350 requests a second.

100 clients390 req/s
0.44 s
300 clients351 req/s
1.03 s
600 clients339 req/s
2.06 s
1,000 clients378 req/s
2.82 s
1,500 clients318 req/s
4.04 s
Within the pass markOver it

One laptop serves between 300 and 600 users at once within the pass mark, with no failed requests at any level. A separate server, or more workers, would raise the ceiling.

Show as table
Clients at onceRequests per secondMedian / 95th percentileFailedResult
1003900.23 s / 0.44 s0 of 11,857Pass
3003510.84 s / 1.03 s0 of 10,785Pass
6003391.77 s / 2.06 s0 of 10,627Fail (1.66 s on 8 Nov)
1,0003782.58 s / 2.82 s0 of 12,282Fail
1,5003182.98 s / 4.04 s0 of 10,436Fail
Load test, 10 November 2026, 30 seconds per level
Over a real network
ConnectionTender checkPage ready
No throttling0.49 s3.87 s
Fast 4G0.49 s1.63 s
Slow 4G0.59 s4.56 s
3G fail2.04 s16.09 s

Through the live link on 7 November. A dropped connection now shows "Could not reach the server" instead of a raw error.

Algorithms
AlgorithmWhy
Breadth-first searchShortest path to a listed party, skipping buyers
Union-findFirm groups, so no group crosses the holdout
Sliding time windowCandidate pairs: same buyer within 30 days
Fellegi-Sunter fusionCombine evidence, keep each part explainable
SHA-256 hash chainTamper-evident audit log
Design

Seven data connectors implement one abstract connector; two risk models share one interface, so the graph model can be swapped for rules. A full retrain and scoring of 1.44 million candidate pairs takes 3 minutes 45 seconds and 2.6 GB of memory. Start-up checks stop the app with a clear message if a database, table or model file is missing.

Responsible by design

A person always decides, as Kenya's Data Protection Act requires. The API serves links by kind and date, never people's names, and firm names can be masked as stable codes. The system is not on the open internet, because its findings name firms that have not been found to do anything wrong: invited evaluators reach it through Cloudflare Access.

Python 3.12FastAPISQLAlchemy 2.0 asyncPostgreSQL 15Neo4j 5Redis + CeleryLightGBMscikit-learnPyTorch GeometricNext.jsTypeScriptPlaywrightpytestDocker ComposeCloudflare Access
08 · Evaluation

Tested with people

Ten general users opened bids, read evidence and screened suppliers. They finished 39 of 40 tasks, and rated the app 74 out of 100 for ease of use.

System Usability Scale
74.0mean, out of 100

Median 72.5, range 45 to 90. Seven of the ten scored at or above the published average of 68.

Four tasks, ten people

Open a tender's bids and explain the warning

9 · 1 · 0

Read a pair's evidence and record a verdict

8 · 2 · 0

Screen a supplier against the lists

7 · 2 · 1

Show an auditor no decision was changed

6 · 4 · 0
Without helpWith helpNot done
One practitioner

A supply chain researcher finished all six guided tasks and gave a usability score of 67.5. The ownership path was rated the most helpful part, 5 of 5, and easier to trust than the score; explaining a result to an auditor was rated 3 of 5.

What they asked for

Clearer labels for warnings and risk levels, plain explanations of terms such as "common control", links to the original evidence, and an easier audit history. One asked whether a warning meant the supplier should be rejected automatically. It does not.

The target was 12 to 15 practitioner interviews, under ALU ethics approval SEPT26-BEL-070. One practitioner took part, and six of the ten general users described work in audit, banking, risk or procurement, so these results are early feedback, not proof of usefulness in practice.

09 · Screens

The product

Real records with firm names masked as stable codes. The screening example uses an invented demo company.

e-GP bid opening · simulated screen, real check
Simulated e-GP bid opening for a three-bidder tender, with an Open bids button that sends the bidders to AegisVeritas
At bid opening, a simulated e-GP screen sends one tender's bidders to AegisVeritas. Only the e-GP screen is simulated: the check and its answer are real.
Review queue listing supplier pairs ranked by likelihood of common control, firm names masked
The review queue ranks supplier pairs by the probability of common control. The reviewer opens the top pair.
Evidence card for one supplier pair with evidence by kind and the verdict form
The evidence card separates identity, contacts and behaviour, says how reliable each proved in testing, and records a verdict with a written reason.
Related bidders: supplier pairs with a disclosed link that win from the same procuring entity, firm names masked
Related bidders: linked firms with unrelated names that win from the same procuring entity. These are the leads.
Screening result for an invented demo company: a direct sanctions-list match with the factors behind the score
Screening checks a bidder against sanctions and debarment lists and shows every factor behind the score. The company is an invented demo.
Audit log of screenings and tender checks with a Verify chain button
Every screening, verdict and tender check is written to a hash-chained audit log that the app can verify.

At bid opening, a simulated e-GP screen sends one tender's bidders to AegisVeritas. Only the e-GP screen is simulated: the check and its answer are real.

home · dark theme
AegisVeritas home screen in the dark theme
The home screen in the dark theme. Every screen has a light and a dark version.
AegisVeritas on a phone

Tested on Pixel 7 and iPhone 14 sizes

See it working

The system is invite-only. Evaluators and recruiters can ask for a guided session.

Request access

Walkthrough

A recorded tour of the system: screening, the review queue, an evidence card and the pre-award tender check.

10 · Limits and next

A reason to look, not an accusation

Known limitations

The system sees only what is disclosed: about 48 per cent of linked pairs left no behavioural or contact trace, and 75 of the top 100 Kenyan pairs are not confirmed links. Suppliers are matched by normalised name, and entity resolution precision has not been measured on a hand-checked sample. The bid-opening check was tested on a simulated tender, the page takes about 16 seconds on 3G, and the model's reach to firms that declare no owner rests on the Estonian result.

Recommendations

The PPRA should add a pre-award check to Kenya's e-GP system: when bids close, send the bidders to the check and give the committee a brief on any links, so it can ask bidders to declare common control before signing. It should also enforce ownership disclosure and publish it in machine-readable form. Banks and development finance institutions could use the same engine in onboarding, trade finance and bid-bond lending.

Next

A pilot with an organisation that works with procurement authorities. Batch scoring to lift the memory limit, an early-stopping split kept apart from the held-back pairs, a feature that discounts batch awards on the same day, and data from Uganda's and Rwanda's procurement portals.

Status

The final report was submitted in November 2026. The system has run invite-only since 28 October 2026, under ALU ethics approval SEPT26-BEL-070. The repository is private; access is given on request.

When the public pays for competition, it should get competition.