Security, evidence and environments
This last lesson covers three things everybody who works on or with the platform must know: the security rules that apply to every change, how a service is proven by its end-to-end test and generated documentation, and the four environments the platform runs in.
Security comes first
Security is the platform's top priority. No feature, deadline or convenience justifies weakening it. The main rules:
| Rule | In practice |
|---|---|
| Authorise on the server, always | Every route names a policy action checked by the policy decision point. Hiding a button is not authorisation |
| Least privilege | Officers see their own ministry's records. Administrators do not get silent access to citizens' personal data |
| Separation of duties | Money, keys, modules and sensitive settings need a second person (maker-checker). The proposer never approves their own request |
| Trust only verified identity | An NRC counts only when the identity provider marked it verified. Tokens are fully validated: signature, issuer, audience, expiry and nonce. Staff use a second factor |
| Protect personal data | Collect the minimum, mask sensitive fields, never log personal data, secrets or tokens, audit every read of restricted data and every state change |
| Money is tested against abuse | Every money, status or permission change has an integration test proving that double payment, wrong amount, replay and race are refused |
| Safe inputs | Validate every input; uploads and XML are parsed safely; outbound calls go only to listed hosts; public endpoints are rate-limited |
| Secrets stay secret | Secrets come from the secret store, never from code or files in a repository. Production refuses to start without them |
The policy decision point in one paragraph
One function answers "may this user do this to this thing?". It runs ordered steps (module switched on, account active, role held, ministry scope, security marking, access list, separation of duties, record state, visible fields) and the first step that fails decides. The refusal names the step, and an audit row records it.
Personal data
The platform applies the Data Protection Act No. 3 of 2021. Staff see personal fields such as NRC, phone and e-mail masked unless their role needs them for the record they opened. Seeing a masked value in full needs a reason, and the citizen can see that it happened. Citizens can ask for access to, correction of and erasure of their data from their account. The Data protection and registers course covers this in depth.
When unsure
Choose the safer default, document it, and raise the question with the product owner.
Evidence: every service is tested end to end
Every new or re-engineered service ships with an end-to-end test written with Playwright in the e2e kit. The test drives a real browser as the citizen and as each officer:
- find the service on the portal and read its passport;
- sign in as a citizen, fill the form (including checks that refuse wrong answers) and upload documents;
- pay through the ZamPay test double;
- sign in to the back office as each officer role the process names and complete every step to the decision;
- issue the output document, download it, and check its seal, its QR verification and its key fields;
- record the notifications sent.
The test runs on both seed profiles (E2E_SEED_PROFILE=mihud and neutral) and, when the service can live on a node, in federated topology. It uses provider test doubles: never the real ZamPay, ZamPass, SMS or signing services. Every run starts from a fresh database with synthetic people, organisations and staff.
Documentation that cannot drift
The same run captures screenshots at each step, in the portal and the back office, at desktop width and at 375 px for the citizen's steps. It then writes the service's documentation into e2e/docs/services/<mda-code>/<service-key>/:
- numbered screenshots;
- a Markdown description of the process in plain English;
- a Word document generated from the standard template: service summary, the step-by-step process for the citizen
and each officer role, payment, output documents (with an image of each and how it is verified), notifications, legacy mapping and version.
Because every change re-runs the test and rewrites the documentation, the documentation always describes the software as it is. Screenshots use synthetic data only.
The service status list
e2e/SERVICE-STATUS.md lists every service with five columns: correct and full form, correct workflow, correct output documents, e2e test passed, and documentation with screenshots. It is generated by the kit (npm run status:services) and never edited by hand: to change a row, fix the service and regenerate. The Developer Hub's service catalogue shows the same evidence.
Definition of done
A change is done when:
- core stays MDA-neutral and MDA-specific parts are behind a module;
- it works in combined, hub and node roles, and the portal stays generic;
- unit and integration tests pass, migrations are reversible;
- the service's end-to-end test passes and its documentation is regenerated and published to the central
documentation share;
- the service status list is regenerated;
- security has been considered and tested;
- open questions are written down with the safe default assumed.
The four environments
| Environment | Where | Rules |
|---|---|---|
| Development | A developer's machine | Local PostgreSQL with PostGIS. Integration tests create a fresh database per test file. Never point local tools at a remote database |
| Test | eservices.pamodzi.co.zm (portal) and admin.pamodzi.co.zm (back office) | Shared deployment for demonstrations and product-owner review. Deployed only when the product owner asks. Never real citizen data |
| Staging | Government servers (GSB RKE2 cluster) | Same infrastructure as production. Images promoted by digest |
| Production | Government servers (GSB RKE2 cluster) | Reached only by promotion from staging, with approvals. No direct deploys |
Configuration differs per environment only through environment variables and Helm values, never through code branches. Test and staging use provider sandboxes or stubs until real credentials are issued, and production refuses development keys.
Example: what goes wrong without these rules
A developer is in a hurry and adds if (mda === 'EXAMPLE') to the case controller to change one fee. The MDA-neutral check fails the build. The right fix is a module that contributes the fee through payment.quote, with an integration test showing that a wrong amount is refused, and a re-run of the service's end-to-end test so its documentation shows the new fee.
Check your understanding
- Why is hiding a button in the back office not enough to protect an action?
- Which four abuse cases must a money change be tested against?
- What does the end-to-end test check about an issued document?
- Why is
SERVICE-STATUS.mdnever edited by hand? - Which environment may a developer deploy to without the product owner asking, if any?
Sign in to record your progress.
Previous: Modules and reusable capabilities Go to the assessment