Omissia — Loophole Detection
Loading plaza… | reconciling ANPR ↔ transactions ↔ e-tag SQLite · live API
Prepared by … for …
ANPR camera reads
—
Recorded transactions
—
E-tag / RFID passes
—
Bank settlement lines
—
—
Vehicles passed (ANPR)
—
Flagged events
—
Est. leakage (this shift)
—
Projected annual leakage
—
Live camera monitor every ANPR read as it arrives · —
Batch CSV import means the operator finds a ghost pass hours later. A live RTSP/ONVIF feed pushes each read within seconds — with camera ID, OCR confidence, and a per-read anomaly scan (no-payment window, low OCR confidence, unauthorised exemption). This is the ambient monitor for the control room.
Reads (last 50)
—
Alarms in view
—
Low-confidence
—
Poll
off
Cameras
| Camera ID | Plaza | Lane | Reads | Mean confidence | Low-confidence | Last seen |
|---|---|---|---|---|---|---|
| Loading cameras… | ||||||
Live stream
| When | Plaza · Lane | Camera | Plate | Conf | Alarm |
|---|---|---|---|---|---|
| Start the poll to see reads stream in. | |||||
Fleet overview every plaza in the deployment · —
The reconciliation engine runs per plaza. In production a deployment covers 17–31 gates; opening a tab per gate is not a strategy. This view rolls the latest run for every plaza into one snapshot — status, flags, and revenue leak — so operations can spot the outlier gate at a glance and drill in.
Gates
—
Lanes
—
Critical flags
—
Revenue leak (latest run)
—
| Status | Gate | Province | Lanes | Critical | High | Medium | Leak (USD) | Last run | Latest alert | |
|---|---|---|---|---|---|---|---|---|---|---|
| Loading fleet… | ||||||||||
Revenue-leakage trend last 30 days ·
A single shift is a snapshot; the trend shows direction. Leakage and the bank-settlement gap are drifting upward — the case for putting a detection layer in place.
Cashier exemption-rate drift the "creep" a snapshot misses
Each line is one cashier's gov-exempt booking rate over 30 days.
Flagged events — ranked by value filter:
| Time | Lane / Cashier | Loophole click row → fix & prevent | Evidence | Value |
|---|---|---|---|---|
| Run the reconciliation to populate flags. | ||||
Leakage by loophole type
Leakage across the shift (by hour)
Cashier outliers — peer comparison (this shift) gov-exempt booking rate vs. lane peers
| Cashier | Lane | Transactions | Exempt-booking % | Peer avg | Flag |
|---|---|---|---|---|---|
| Run the reconciliation to profile cashiers. | |||||
Statistical drift — 30-day baseline (cashiers & plates) z-scores vs own history & peer pool
Sits alongside the deterministic detectors and surfaces patterns the rules weren’t written for. Score ≥ 3.0 = HIGH, ≥ 2.0 = MED. Cashiers are scored against their own 30-day exempt-rate history (SELF) and the day’s peer pool (PEER); plates are scored against the population on (uses · distinct cashiers · exempt share).
Cashier anomalies — top 5
| Cashier | Today | z(self) | z(peer) | Severity |
|---|---|---|---|---|
| Run the reconciliation. | ||||
Plates of interest — top 5
| Plate | Uses | Score | Why |
|---|---|---|---|
| Run the reconciliation. | |||
Raw feed inspector — the rows behind the flags
The proof a technical reviewer asks for: for each flagged vehicle, the three feeds side by side. The gap between what ANPR saw and what was recorded is the leak.
| Time | Lane | ANPR saw | Transaction logged | E-tag debit | Verdict |
|---|---|---|---|---|---|
| Run the reconciliation, then click “Show raw rows”. | |||||
Bank-settlement reconciliation — does the collected money reach the bank? 4-way match: road → lane → account → bank
The road-side checks prove a vehicle was charged. This proves the money actually arrived in the operator's bank account — the gap between collected and banked is settlement leakage / cash skim.
Run the reconciliation to trace the money to the bank
— reached the bank
— collected but never banked
| Channel | Collected / charged | Reached bank | Gap |
|---|---|---|---|
| Run the reconciliation to trace settlement. | |||
Mobile money reconciliation EcoCash · OneMoney · Telecash · —
Zimbabwe's dominant retail rails are USSD-initiated. The driver dials *151# at the booth, the lane records "paid" — but the settlement into the operator's bank account can lag hours or fail silently. The Gweru $9.8M audit finding was exactly this gap. This checks every lane-side mobile-money transaction against the provider's settlement feed and surfaces uncleared, failed, and amount-mismatch cases.
Cleared
—
Not cleared
—
Amount mismatch
—
USSD leak (USD)
—
| When | Provider | USSD ref | Plate | Type | Severity | Amount | Evidence |
|---|---|---|---|---|---|---|---|
| Run the reconciliation to see mobile-money gaps. | |||||||
Tamper-evident ledger cryptographic audit trail · SHA-256 hash chain
Every recorded transaction is hash-chained and sealed, and each trusted checkpoint is also written to an append-only, hash-chained anchor log outside the database. So even an admin who tampers with a row and re-seals can't launder it: verification still flags that the live head no longer matches the trusted anchor. (In production the anchor lives in an append-only / off-box store.)
Run the reconciliation, or click “Verify ledger”.
Admin action trail hash-chained · append-only · auditor-readable
Every privileged action lands here — threshold and rule edits, violation decisions and escalation sign-offs, user disable/reset, data-subject erasures, imports, re-seals and re-anchors. The chain is verified on every load: a deleted, reordered or edited entry breaks it visibly.
Loading trail…
| When | Actor | Action | Target | Detail |
|---|---|---|---|---|
| Loading… | ||||
Velocity / cloned-tag detection network-wide · impossible travel between plazas
Compares each e-tag's consecutive passes across plazas. A tag appearing at two plazas too far apart to drive between in the elapsed time is a cloned or shared tag — the same method used to catch cloned phones.
| Tag | From | To | Distance | Elapsed | Implied speed |
|---|---|---|---|---|---|
| Run the reconciliation to scan the network. | |||||
Active alerts evaluated on every reconciliation run
Threshold rules below are checked each run; breaches raise alerts here and (if a webhook is configured) POST to your ops channel.
Run the reconciliation to evaluate alerts.
Alert thresholds
Tune what raises an alert. Numeric rules use the threshold field; presence rules fire whenever the condition occurs.
| On | Rule | Threshold | Severity |
|---|---|---|---|
| Loading rules… | |||
Recent alert history
| When | Severity | Alert |
|---|---|---|
| No alerts recorded yet. | ||
Data-subject rights (POPIA) access · erasure · legal-hold report
Respond to a POPIA data-subject request from a client's information officer. Access returns a JSON aggregate of everything Omissia holds; erasure anonymises where possible and reports what is retained under a legitimate-interest / legal-obligation exception (financial records, enforcement notices, hash-chained audit trail). Erasure is dry-run by default — you must explicitly execute to mutate. Every executed erasure is recorded to the admin action chain. See docs/compliance/popia.md.
User accounts disable a compromised account or reset a forgotten password
Actions here are audit-logged (see the admin action chain). Disabling an account terminates any live session on the next request; resetting a password does the same and forces a change on next login. You cannot disable your own account or the last enabled admin — the deployment must always have at least one usable admin.
| Username | Role | MFA | Must change pw | Status | Created | |
|---|---|---|---|---|---|---|
| Loading… | ||||||
SMS / WhatsApp recipients provider: —
Webhook → Slack/Teams is useless for a Zimbabwe field supervisor. Recipients configured here receive an SMS or WhatsApp message on every alert whose severity is in their list and whose plaza matches (or "all"). The provider is set via TOLL_SMS_PROVIDER — stub (default) persists to the audit log without hitting a gateway; twilio or africastalking deliver over the real rails when credentials are set.
| Name | Phone | Channel | Severities | Plaza | Active | |
|---|---|---|---|---|---|---|
| Loading… | ||||||
Recent messages
| Sent | To | Channel | Provider | Alert | Severity | Status | Body |
|---|---|---|---|---|---|---|---|
| No messages yet. | |||||||
Exempt Plate Registry the single source of truth for authorised exemptions
Only plates listed here may receive an exempt-code booking without raising an "Unauthorized exemption" flag. Adding or revoking a plate is logged to the admin-action audit trail.
| Plate | Category | Description | Status | |
|---|---|---|---|---|
| Loading registry… | ||||
Header badge "Prepared by … for …" line at the top of every page
Set once during client provisioning (or here). Stored in the deployment-settings table — same value for every user on this VPS. Edits are logged to the admin-action audit trail.
Violation queue human-in-the-loop reviewer workflow
Penalty-worthy flags are promoted into a queue on every reconciliation run. Confirm or dismiss each one, then issue a printable penalty notice.
| When | Lane | Plate | Type | Tier | Severity | Why flagged | Staff | Fee | Penalty | Status | Notice ID | Action |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Run reconciliation to populate the queue. | ||||||||||||
Incident management —
A detected flag either becomes an auto-issued violation notice or nothing. Real incidents — internal investigations, ZACC escalations, supplier disputes — need somewhere to live with a full audit trail. Open one here; every status change, note, and evidence attachment is appended to the case log.
Open
—
Investigating
—
Escalated
—
Resolved
—
| ID | Title | Category | Severity | Status | Assignee | Plaza | Opened | Events | |
|---|---|---|---|---|---|---|---|---|---|
| Loading incidents… | |||||||||
Messages 1:1 conversations with the deployment operator
Direct messages between the deployment admin (Univern operator) and non-admin accounts (ZINARA auditor, partner viewer). Every message is stored inside this deployment — no central service. Client-to-client messaging is disabled by design. Retention: message rows are never auto-pruned (see docs/compliance/data-retention.md).
No conversations yet.
Select a conversation to view the thread.
—
Vehicle history every event across the network for a single plate
When ZACC arrests a syndicate, they need to trace a plate across months and gates. This view merges every ANPR read, cashier transaction, e-tag pass, weighbridge weighing, and violation for a plate into one time-ordered timeline — with a running tally of what was paid vs. what's still owing.
Staff named individuals behind cashier codes
Add the people working this plaza. Once a staff member is assigned to a booth below, every flag, cashier outlier, and violation for that cashier code resolves to their name (and their supervisor's) instead of just a code.
| Name | Role | Status | |
|---|---|---|---|
| Loading staff… | |||
Shift assignments cashier code → person, per shift window
Assign a staff member to a booth code (e.g. C-07) for a shift window. Resolution uses minutes-of-day, so flags timestamped within the window attribute correctly even across overlapping or back-to-back shifts.
| Cashier code | Staff | Supervisor | Shift window | Status | |
|---|---|---|---|---|---|
| Loading shifts… | |||||
Streaming ingestion HTTP NDJSON · idempotent · dead-letter logged
Partner systems POST events as they happen — one JSON object per row, newline-delimited. Each row is deduped on its natural key. Bad rows are logged to the dead-letter queue with the reason.
| Feed | Endpoint | Inserted (1h) | Duplicates | Rejected | Rate / min | Last seen |
|---|---|---|---|---|---|---|
| No events ingested yet. | ||||||
Dead-letter queue most recent 20 rejected rows
Rows the validator rejected (missing fields, unknown plaza/lane, bad timestamp). The full payload preview is kept so an operator can fix and re-post.
| When | Feed | Reason | Payload preview |
|---|---|---|---|
| No rejected rows. | |||
Send a test event
Smoke-test the live endpoint from the dashboard. Pick a feed; an example payload is loaded so you can edit and submit.
API keys Bearer token auth for partner systems — no login session needed
Hand one of these to a partner's integrator (camera vendor, POS system, RFID reader) instead of a user account — it authenticates POST /api/ingest/<feed> only, nothing else. The raw key is shown once, at creation; only its hash is stored after that.
| Label | Created | Last used | Status | |
|---|---|---|---|---|
| No keys yet. | ||||
Operator reports finance & ZINARA-ready
Standard daily reports generated live from the source tables. Pick one, choose plaza + date if applicable, then preview or download CSV (Excel / Google Sheets friendly).
| Pick a report and click Run. |
Scheduled reports runs automatically on each scheduler cycle
Add a standing order: pick a report, cadence, and plaza. The scheduler runs it automatically and stores the CSV — browse run history and download any past output below. No email required; pipe to Slack/Teams via the alert webhook.
| Report | Cadence | Plaza | Status | |
|---|---|---|---|---|
| Loading schedules… | ||||
Bulk CSV import six feeds · preview before commit · upsert-safe
Drop the partner's CSV exports for any of the six toll feeds, set a plaza name,
click Preview to see what would land (no rows written), then Confirm
to commit. With Upsert ticked, an import re-uses an existing plaza by name
rather than duplicating it — the safe choice for monthly re-imports. Feed columns
match
samples/ (see toll-loophole-data-request.md).
If a partner's CSV doesn't use our column names, each feed below flags it and lets you map their columns to ours — pick a saved preset to auto-fill, or map once and save under a vendor name so the 2nd plaza from the same partner is instant.
ANPR camera reads
columns: timestamp, lane, plate, vehicle_class, axles
Transactions
columns: timestamp, lane, plate, amount, cashier, fee_type, exempt_code
E-tag / RFID passes
columns: timestamp, lane, plate, status, debit
Bank settlement
columns: channel, batch_date, ref_lane, ref_ts, gross, fee, net
Weighbridge
columns: timestamp, lane, plate, axles, gross_kg
OBU / GPS traces
columns: timestamp, plate, lat, lon, speed_kmh
First-run setup shown automatically until dismissed
Six things worth doing before handing this deployment to a client. Nothing here is
required to keep using Omissia — dismiss any time and revisit this tab later.
1. DNS
Confirm the domain's A record points at this VPS:
dig +short <your-domain>.2. Admin MFA
Enrol two-factor authentication on this admin account from Account & security (top-right menu). This step ticks itself off once enabled.
3. Partner data import
Bring in the client's real feed exports from the tab.
4. Off-site backups
Generates a fresh backup-encryption key. This is not saved anywhere — copy it immediately into
/etc/omissia.env on the server and restart the service.5. Alert webhook
Generates a signing secret for a Slack / Teams / email-gateway webhook URL. The secret is not saved anywhere — copy it immediately.
6. Auditor account optional
Create a read-only account for the client. They'll be asked to set their own password on first login.
Financial-audit pack platform engine · invoices + vendors
Same reconciliation + anomaly engine, different vertical. Runs three flagship audit-analytics detectors against an AP-style invoice ledger: Benford's-Law digit distribution on amounts, threshold-clustering for "just-under" gaming of approval gates, and split-PO detection across vendor + approver windows.
Upload your own data CSV / Peppol UBL · idempotent re-upload · admin only
Pick a vendor master and / or an invoice ledger, or drop in a Peppol/UBL e-invoice XML directly. The natural-key dedup (invoice_no) means re-uploading the same file doesn't duplicate rows — useful for partner data that's still being curated.
Vendor master
columns: name, address, bank_account, tax_id, registered_at
Invoice ledger
columns: vendor_name, invoice_no, invoice_date, posted_date, amount, currency, status, approver, gl_account, department, po_no
Peppol / UBL e-invoice
one BIS Billing 3.0 / UBL 2.1 Invoice XML document per upload — the format SARS's e-invoicing framework is expected to use from 2028
Findings queue triage · drill-down · decide
Every detector finding lands here. Click a row to see the underlying invoices that drove the score; mark each one Confirmed / Dismissed / False positive. Decisions are preserved across reruns, so re-importing data doesn't reset reviewer work.
| Sev | Detector | Subject | Evidence | Amount | Status | Reviewer | Actions |
|---|---|---|---|---|---|---|---|
| Run the audit to populate. | |||||||
1 · Benford's Law leading-digit distribution test
A clean population of finance numbers follows Benford's expected log distribution. A χ² above 20 at 8 degrees of freedom (the 99% critical value) is a strong manipulation signal. Vendors with their OWN χ² above 25 over ≥40 invoices are flagged individually.
Observed vs expected, leading digit 1–9
Vendors flagged
| Vendor | Invoices | χ² | Severity |
|---|---|---|---|
| Run the audit to populate. | |||
2 · Threshold clustering "just under the approval gate"
For each approval threshold, compare invoices in the 90–99.9% just-under band against the 75–89% comparison band. A z-score above 3.0 indicates a deliberate spike. Approvers concentrated in the just-under band are listed below.
Configured approval thresholds — edit these to match your policy
| Label | Amount | |
|---|---|---|
| Loading… | ||
Invoices clustered just under each gate
| Threshold | Just-under count | Expected | z | Severity |
|---|---|---|---|---|
| Run the audit. | ||||
Approvers concentrated below a gate
| Approver | Below gate | Invoices | Total | Severity |
|---|---|---|---|---|
| — | ||||
3 · Splitting / serial sub-threshold payments classic split-PO scheme
Same vendor + same approver + multiple invoices in a 14-day window that individually stay under Tier-1 but collectively exceed it. The most common way a director-tier sign-off is circumvented.
| Vendor | Approver | Count | Total | Window | Crossed threshold | Severity |
|---|---|---|---|---|---|---|
| Run the audit. | ||||||
4 · E-invoice vs ledger gap SARS e-invoicing readiness
A Peppol/UBL e-invoice (uploaded on the panel above) that SARS's own network has an independent record of, but that has never once shown up in this client's own CSV/ERP feed — money the tax authority can see that never made it into the books. The inverse (a CSV invoice with no e-invoice yet) isn't flagged: e-invoicing isn't mandatory in South Africa until 2028, phased in by taxpayer size, so most CSV-only invoices today are just business as usual, not a gap.
| Vendor | Invoice | Date | Amount | Severity |
|---|---|---|---|---|
| Run the audit. | ||||
5 · E-invoicing readiness exposure, not a guess
There's no legitimate way for this system to know which of your vendors SARS classifies as a large VAT taxpayer — that's confidential SARS-side data. What it can show honestly: which vendors have actually sent at least one confirmed Peppol/UBL e-invoice, weighted by spend rather than headcount, since SARS's own rollout targets the largest taxpayers first (voluntary pilots from 2026 → mandatory for the largest by 2028 → mid-size after → small/micro later, no date yet).
| Vendor | Invoices | Spend | E-invoice on file? |
|---|---|---|---|
| Run the audit. | |||