Concepts

Security & compliance

Authentication modes, signing requirements, data-classification model, and the seven Ugandan regulatory frameworks the architecture addresses by design — not by retrofit.

Authentication

Every endpoint outside the public catalogue requires a Bearer token. Tokens are scoped per integrator, carry a documented set of permissions, and are revocable at any time without affecting other clients. Both Authorization: Bearer <key> and X-Sente-Authorization: Bearer <key> are accepted on every authenticated endpoint — use the standard Authorization header for compatibility with the bulk of client libraries; the custom header is retained for cases where another middleware on the network path may strip or rewrite Authorization.

Bearer tokens

Default for server-to-server integrations. The token is opaque, ~40 characters, prefixed with sk_sandbox_ in sandbox or sk_live_ in production. Send it on every request:

http
GET /v1/citizens HTTP/1.1
Host: sente-rails.space
X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx

OAuth 2.0 client_credentials

For partner platforms (a city ERP, an enterprise integrator) the rail issues OAuth 2.0 client_credentials tokens with configurable scopes — for example citizens.read, assessments.write, oversight.read. The flow follows RFC 6749 §4.4 with no client-side deviations; any standard OAuth library works without modification.

mTLS for high-risk endpoints

Payment confirmation webhooks, oversight read endpoints under the OAG scope, and credential rotation are gated behind mutual TLS in addition to the Bearer token. The rail pins the client certificate against a registered per-integrator fingerprint set.

Request signing

Inbound webhooks from aggregators (MoMo, Airtel) and EFRIS-side callbacks are signed with HMAC-SHA256 over the raw request body using a shared secret. Verify before parsing:

Webhook signature verification · python
import hmac, hashlib

def verify(secret: str, body: bytes, signature_header: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    # Constant-time compare against the value carried in X-Sente-Signature
    return hmac.compare_digest(expected, signature_header)

Data classification

Three tiers govern how the rail handles each data class. Higher tiers carry stricter logging, retention, and access controls.

  • Tier 1 — Personal. National Identification Numbers, phone numbers, addresses, consent metadata. Never appears in URLs or log lines; only in indexed body fields with role-gated read. Soft- archive on erasure request preserves referential integrity for paid receipts under PFMA retention rules.
  • Tier 2 — Financial. Payment Intent references, aggregator transaction IDs, FDN values, assessment totals. Audit- logged on every read and write. Aggregator-side balances are never persisted by the rail.
  • Tier 3 — Operational. MDA catalogue, service catalogue, public statistics, integration status. Catalogue endpoints are public-read; statistics aggregate only.

Regulatory frameworks

Seven Ugandan regulatory frameworks fall within scope. Each is addressed by design — the architecture does the regulatory work, not a retrofitted policy layer.

FrameworkArchitectural posture

Personal Data and Privacy Act

2019

Consent metadata is captured on every Citizen record. National Identification Numbers never appear in URLs or document names. Right-to-erasure is honoured via soft-archive (status flip + audit trail) so referential integrity on paid receipts stays intact.

Tax Procedures Code Act

2014 §73A–73B

Every Service with efris_taxable=true routes through the EFRIS fiscal adapter at assessment time. Each Assessment Line carries a per-line Fiscal Document Number. The full fiscal round-trip — generate PRN, post invoice, retrieve FDN — runs live end-to-end in the sandbox.

Public Finance Management Act

2015 §43

The rail never holds public money. Citizen payments flow directly from the citizen's wallet to a licensed aggregator on a per-MDA payable account. Cross-MDA assessments split at the aggregator, never at Sente Rails. There is no single rail wallet that accumulates revenue.

e-Government Interoperability Framework

API-first by construction. The complete /v1 surface is documented in OpenAPI 3.1 across twenty-two endpoints in seven modules. All payloads are JSON over REST. The UGHub gateway adapter is scaffolded for the standard NITA-U integration path.

Access to Information Act

2005

Oversight bodies (OAG, MoFPED, UBOS, MoLG) operate as Mode C Read Consumers — scoped reads only, never collect on behalf of any MDA. Aggregate statistics are open by default; itemised reads require role-scoped credentials with full audit logging.

Computer Misuse Act

2011, amended 2022

Authentication logs are immutable. Rate limiting at the nginx edge plus per-endpoint application throttling. The underlying platform's administrative interface (/app, /desk) returns 404 at the edge — there is no public administrative surface. Intrusion detection and penetration testing are sequenced for pre-production hardening.

National Payment Systems Act

2020

All payment processing is mediated by licensed aggregators — MTN MoMo (sandbox live), Airtel Money (sandbox pending), Pesapal (planned). Card and bank settlement routes are scaffolded for production. Sente Rails never registers as a PSP; by architectural posture it does not need to.

Audit trail

Every state-changing operation is captured in an immutable audit log: actor, timestamp, target document, before / after payload. Oversight Mode C consumers read against this log scoped to their statutory remit.

Audit log entry (shape) · json
{
  "doctype": "Assessment",
  "name": "ASSESS-2026-000123",
  "action": "submit",
  "actor": { "user": "clerk@gulu.gov.ug", "role": "Sente Rails Clerk" },
  "context": { "shift": "SHIFT-2026-000045", "mda": "GULU", "ip": "10.x.x.x" },
  "before": null,
  "after": { "status": "Assessed", "total_amount": 50000 },
  "occurred_at": "2026-05-25T08:30:11Z"
}

Production hardening

Two items remain queued for the pre-production pass before any live traffic moves through the rail:

  • Intrusion detection. Edge-side request anomaly scoring with per-integrator baselines. Slated for the production hardening pass.
  • Formal penetration test. Independent third-party test against the live URL, results published as an appendix to this documentation under the API explorer.

Source of truth

The standalone compliance matrix — the same content as the table above, on one page — lives in the repository at docs/COMPLIANCE_MATRIX.md. Every row above quotes it.